View | Details | Raw Unified | Return to bug 46264
Collapse All | Expand All

(-)java/org/apache/catalina/Container.java (+17 lines)
Lines 476-479 Link Here
476
     * that the request/response still appears in the correct access logs.
476
     * that the request/response still appears in the correct access logs.
477
     */
477
     */
478
    public AccessLog getAccessLog();
478
    public AccessLog getAccessLog();
479
    
480
    
481
    /**
482
     * Returns the number of threads available for starting and stopping any
483
     * children associated with this container. This allows start/stop calls to
484
     * children to be processed in parallel.
485
     */
486
    public int getStartStopThreads();
487
488
489
    /**
490
     * Sets the number of threads available for starting and stopping any
491
     * children associated with this container. This allows start/stop calls to
492
     * children to be processed in parallel.
493
     * @param   startStopThreads    The new number of threads to be used  
494
     */
495
    public void setStartStopThreads(int startStopThreads);
479
}
496
}
(-)java/org/apache/catalina/Host.java (-1 / +9 lines)
Lines 17-22 Link Here
17
package org.apache.catalina;
17
package org.apache.catalina;
18
18
19
import java.io.File;
19
import java.io.File;
20
import java.util.concurrent.ExecutorService;
20
import java.util.regex.Pattern;
21
import java.util.regex.Pattern;
21
22
22
23
Lines 180-188 Link Here
180
    public void setDeployIgnore(String deployIgnore);
181
    public void setDeployIgnore(String deployIgnore);
181
182
182
183
184
    /**
185
     * Return the executor that is used for starting and stopping contexts. This
186
     * is primarily for use by components deploying contexts that want to do
187
     * this in a multi-threaded manner.
188
     */
189
    public ExecutorService getStartStopExecutor();
190
    
191
    
183
    // --------------------------------------------------------- Public Methods
192
    // --------------------------------------------------------- Public Methods
184
193
185
186
    /**
194
    /**
187
     * Add an alias name that should be mapped to this same Host.
195
     * Add an alias name that should be mapped to this same Host.
188
     *
196
     *
(-)java/org/apache/catalina/core/ContainerBase.java (-2 / +135 lines)
Lines 26-31 Link Here
26
import java.util.HashMap;
26
import java.util.HashMap;
27
import java.util.Hashtable;
27
import java.util.Hashtable;
28
import java.util.Iterator;
28
import java.util.Iterator;
29
import java.util.List;
30
import java.util.concurrent.BlockingQueue;
31
import java.util.concurrent.Callable;
32
import java.util.concurrent.Future;
33
import java.util.concurrent.LinkedBlockingQueue;
34
import java.util.concurrent.ThreadPoolExecutor;
35
import java.util.concurrent.TimeUnit;
29
36
30
import javax.management.ObjectName;
37
import javax.management.ObjectName;
31
import javax.naming.directory.DirContext;
38
import javax.naming.directory.DirContext;
Lines 274-283 Link Here
274
    protected volatile AccessLog accessLog = null;
281
    protected volatile AccessLog accessLog = null;
275
    private volatile boolean accessLogScanComplete = false;
282
    private volatile boolean accessLogScanComplete = false;
276
283
284
285
    /**
286
     * The number of threads available to process start and stop events for any
287
     * children associated with this container.
288
     */
289
    private int startStopThreads = 1;
290
    protected ThreadPoolExecutor startStopExecutor;
291
    
277
    // ------------------------------------------------------------- Properties
292
    // ------------------------------------------------------------- Properties
278
293
294
    @Override
295
    public int getStartStopThreads() {
296
        return startStopThreads;
297
    }
279
298
280
    /**
299
    /**
300
     * Handles the special values.
301
     */
302
    private int getStartStopThreadsInternal() {
303
        int result = getStartStopThreads();
304
        
305
        // Positive values are unchanged 
306
        if (result > 0) {
307
            return result;
308
        }
309
        
310
        // Zero == Runtime.getRuntime().availableProcessors()
311
        // -ve  == Runtime.getRuntime().availableProcessors() - value
312
        // These two are the same
313
        result = Runtime.getRuntime().availableProcessors() - result;
314
        if (result < 1) {
315
            result = 1;
316
        }
317
        return result;
318
    }
319
320
    @Override
321
    public void setStartStopThreads(int startStopThreads) {
322
        this.startStopThreads = startStopThreads;
323
        
324
        // Use local copies to ensure thread safety
325
        ThreadPoolExecutor executor = startStopExecutor;
326
        if (executor != null) {
327
            int newThreads = getStartStopThreadsInternal();
328
            executor.setMaximumPoolSize(newThreads);
329
            executor.setCorePoolSize(newThreads);
330
        }
331
    }
332
333
334
    /**
281
     * Get the delay between the invocation of the backgroundProcess method on
335
     * Get the delay between the invocation of the backgroundProcess method on
282
     * this container and its children. Child containers will not be invoked
336
     * this container and its children. Child containers will not be invoked
283
     * if their delay value is not negative (which would mean they are using 
337
     * if their delay value is not negative (which would mean they are using 
Lines 1001-1006 Link Here
1001
    }
1055
    }
1002
1056
1003
1057
1058
    @Override
1059
    protected void initInternal() throws LifecycleException {
1060
        BlockingQueue<Runnable> startStopQueue =
1061
            new LinkedBlockingQueue<Runnable>();
1062
        startStopExecutor = new ThreadPoolExecutor(
1063
                getStartStopThreadsInternal(),
1064
                getStartStopThreadsInternal(), 10, TimeUnit.SECONDS,
1065
                startStopQueue);
1066
        // Allow executor thread pool size to drop to zero of not required
1067
        startStopExecutor.allowCoreThreadTimeOut(true);
1068
        super.initInternal();
1069
    }
1070
1071
1004
    /**
1072
    /**
1005
     * Start this component and implement the requirements
1073
     * Start this component and implement the requirements
1006
     * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
1074
     * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
Lines 1029-1037 Link Here
1029
1097
1030
        // Start our child containers, if any
1098
        // Start our child containers, if any
1031
        Container children[] = findChildren();
1099
        Container children[] = findChildren();
1100
        List<Future<Void>> results = new ArrayList<Future<Void>>();
1032
        for (int i = 0; i < children.length; i++) {
1101
        for (int i = 0; i < children.length; i++) {
1033
            children[i].start();
1102
            results.add(startStopExecutor.submit(new StartChild(children[i])));
1034
        }
1103
        }
1104
        Iterator<Future<Void>> iter = results.iterator();
1105
        boolean fail = false;
1106
        while (iter.hasNext()) {
1107
            try {
1108
                iter.next().get();
1109
            } catch (Exception e) {
1110
                // TODO Log this
1111
                fail = true;
1112
            }
1113
        }
1114
        if (fail) {
1115
            // TODO Add a useful message
1116
            throw new LifecycleException();
1117
        }
1035
1118
1036
        // Start the Valves in our pipeline (including the basic), if any
1119
        // Start the Valves in our pipeline (including the basic), if any
1037
        if (pipeline instanceof Lifecycle)
1120
        if (pipeline instanceof Lifecycle)
Lines 1069-1077 Link Here
1069
1152
1070
        // Stop our child containers, if any
1153
        // Stop our child containers, if any
1071
        Container children[] = findChildren();
1154
        Container children[] = findChildren();
1155
        List<Future<Void>> results = new ArrayList<Future<Void>>();
1072
        for (int i = 0; i < children.length; i++) {
1156
        for (int i = 0; i < children.length; i++) {
1073
            children[i].stop();
1157
            results.add(startStopExecutor.submit(new StopChild(children[i])));
1074
        }
1158
        }
1159
        Iterator<Future<Void>> iter = results.iterator();
1160
        boolean fail = false;
1161
        while (iter.hasNext()) {
1162
            try {
1163
                iter.next().get();
1164
            } catch (Exception e) {
1165
                // TODO Log this
1166
                fail = true;
1167
            }
1168
        }
1169
        if (fail) {
1170
            // TODO Add a useful message
1171
            throw new LifecycleException();
1172
        }
1075
1173
1076
        // Stop our subordinate components, if any
1174
        // Stop our subordinate components, if any
1077
        if ((resources != null) && (resources instanceof Lifecycle)) {
1175
        if ((resources != null) && (resources instanceof Lifecycle)) {
Lines 1113-1118 Link Here
1113
            parent.removeChild(this);
1211
            parent.removeChild(this);
1114
        }
1212
        }
1115
1213
1214
        startStopExecutor.shutdownNow();
1215
1116
        super.destroyInternal();
1216
        super.destroyInternal();
1117
    }
1217
    }
1118
1218
Lines 1412-1415 Link Here
1412
            }
1512
            }
1413
        }
1513
        }
1414
    }
1514
    }
1515
    
1516
    
1517
    // ----------------------------- Inner classes used with start/stop Executor
1518
    
1519
    private static class StartChild implements Callable<Void> {
1520
1521
        private Container child;
1522
        
1523
        public StartChild(Container child) {
1524
            this.child = child;
1525
        }
1526
1527
        @Override
1528
        public Void call() throws LifecycleException {
1529
            child.start();
1530
            return null;
1531
        }
1532
    }
1533
1534
    private static class StopChild implements Callable<Void> {
1535
1536
        private Container child;
1537
        
1538
        public StopChild(Container child) {
1539
            this.child = child;
1540
        }
1541
1542
        @Override
1543
        public Void call() throws LifecycleException {
1544
            child.stop();
1545
            return null;
1546
        }
1547
    }
1415
}
1548
}
(-)java/org/apache/catalina/core/StandardHost.java (-1 / +7 lines)
Lines 24-29 Link Here
24
import java.util.Locale;
24
import java.util.Locale;
25
import java.util.Map;
25
import java.util.Map;
26
import java.util.WeakHashMap;
26
import java.util.WeakHashMap;
27
import java.util.concurrent.ExecutorService;
27
import java.util.regex.Pattern;
28
import java.util.regex.Pattern;
28
29
29
import org.apache.catalina.Container;
30
import org.apache.catalina.Container;
Lines 183-190 Link Here
183
184
184
    // ------------------------------------------------------------- Properties
185
    // ------------------------------------------------------------- Properties
185
186
187
     @Override
188
     public ExecutorService getStartStopExecutor() {
189
         return startStopExecutor;
190
     }
186
191
187
    /**
192
193
     /**
188
     * Return the application root for this Host.  This can be an absolute
194
     * Return the application root for this Host.  This can be an absolute
189
     * pathname, a relative pathname, or a URL.
195
     * pathname, a relative pathname, or a URL.
190
     */
196
     */
(-)java/org/apache/catalina/core/mbeans-descriptors.xml (+8 lines)
Lines 1011-1016 Link Here
1011
               description="Will children be started automatically when they are added."
1011
               description="Will children be started automatically when they are added."
1012
               type="boolean"/>  
1012
               type="boolean"/>  
1013
1013
1014
    <attribute name="startStopThreads"
1015
               description="The number of threads to use when starting and stopping child Hosts"
1016
               type="int"/>            
1017
1014
    <attribute name="stateName"
1018
    <attribute name="stateName"
1015
               description="The name of the LifecycleState that this component is currently in"
1019
               description="The name of the LifecycleState that this component is currently in"
1016
               type="java.lang.String"
1020
               type="java.lang.String"
Lines 1198-1203 Link Here
1198
               description="Will children be started automatically when they are added?"
1202
               description="Will children be started automatically when they are added?"
1199
               type="boolean"/>            
1203
               type="boolean"/>            
1200
1204
1205
    <attribute name="startStopThreads"
1206
               description="The number of threads to use when starting, stopping and deploying child Contexts"
1207
               type="int"/>            
1208
1201
    <attribute name="stateName"
1209
    <attribute name="stateName"
1202
               description="The name of the LifecycleState that this component is currently in"
1210
               description="The name of the LifecycleState that this component is currently in"
1203
               type="java.lang.String"
1211
               type="java.lang.String"
(-)java/org/apache/catalina/startup/HostConfig.java (-8 / +100 lines)
Lines 14-21 Link Here
14
 * See the License for the specific language governing permissions and
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
15
 * limitations under the License.
16
 */
16
 */
17
18
19
package org.apache.catalina.startup;
17
package org.apache.catalina.startup;
20
18
21
19
Lines 30-39 Link Here
30
import java.util.ArrayList;
28
import java.util.ArrayList;
31
import java.util.HashMap;
29
import java.util.HashMap;
32
import java.util.HashSet;
30
import java.util.HashSet;
31
import java.util.Iterator;
33
import java.util.LinkedHashMap;
32
import java.util.LinkedHashMap;
34
import java.util.List;
33
import java.util.List;
35
import java.util.Locale;
34
import java.util.Locale;
36
import java.util.Set;
35
import java.util.Set;
36
import java.util.concurrent.ExecutorService;
37
import java.util.concurrent.Future;
37
import java.util.jar.JarEntry;
38
import java.util.jar.JarEntry;
38
import java.util.jar.JarFile;
39
import java.util.jar.JarFile;
39
import java.util.regex.Matcher;
40
import java.util.regex.Matcher;
Lines 520-525 Link Here
520
        if (files == null)
521
        if (files == null)
521
            return;
522
            return;
522
        
523
        
524
        ExecutorService es = host.getStartStopExecutor();
525
        List<Future<?>> results = new ArrayList<Future<?>>();
526
523
        for (int i = 0; i < files.length; i++) {
527
        for (int i = 0; i < files.length; i++) {
524
            File contextXml = new File(configBase, files[i]);
528
            File contextXml = new File(configBase, files[i]);
525
529
Lines 530-538 Link Here
530
                if (isServiced(name))
534
                if (isServiced(name))
531
                    continue;
535
                    continue;
532
                
536
                
533
                deployDescriptor(cn, contextXml);
537
                results.add(
538
                        es.submit(new DeployDescriptor(this, cn, contextXml)));
534
            }
539
            }
535
        }
540
        }
541
        
542
        Iterator<Future<?>> iter = results.iterator();
543
        while (iter.hasNext()) {
544
            try {
545
                iter.next().get();
546
            } catch (Exception e) {
547
                // TODO Log this. Should never happen. 
548
            }
549
        }
536
    }
550
    }
537
551
538
552
Lines 670-683 Link Here
670
        if (files == null)
684
        if (files == null)
671
            return;
685
            return;
672
        
686
        
687
        ExecutorService es = host.getStartStopExecutor();
688
        List<Future<?>> results = new ArrayList<Future<?>>();
689
673
        for (int i = 0; i < files.length; i++) {
690
        for (int i = 0; i < files.length; i++) {
674
            
691
            
675
            if (files[i].equalsIgnoreCase("META-INF"))
692
            if (files[i].equalsIgnoreCase("META-INF"))
676
                continue;
693
                continue;
677
            if (files[i].equalsIgnoreCase("WEB-INF"))
694
            if (files[i].equalsIgnoreCase("WEB-INF"))
678
                continue;
695
                continue;
679
            File dir = new File(appBase, files[i]);
696
            File war = new File(appBase, files[i]);
680
            if (files[i].toLowerCase(Locale.ENGLISH).endsWith(".war") && dir.isFile()
697
            if (files[i].toLowerCase(Locale.ENGLISH).endsWith(".war") && war.isFile()
681
                    && !invalidWars.contains(files[i]) ) {
698
                    && !invalidWars.contains(files[i]) ) {
682
                
699
                
683
                ContextName cn = new ContextName(files[i]);
700
                ContextName cn = new ContextName(files[i]);
Lines 693-701 Link Here
693
                if (isServiced(cn.getName()))
710
                if (isServiced(cn.getName()))
694
                    continue;
711
                    continue;
695
                
712
                
696
                deployWAR(cn, dir);
713
                results.add(es.submit(new DeployWar(this, cn, war)));
697
            }
714
            }
698
        }
715
        }
716
        
717
        Iterator<Future<?>> iter = results.iterator();
718
        while (iter.hasNext()) {
719
            try {
720
                iter.next().get();
721
            } catch (Exception e) {
722
                // TODO Log this. Should never happen. 
723
            }
724
        }
699
    }
725
    }
700
726
701
727
Lines 935-940 Link Here
935
        if (files == null)
961
        if (files == null)
936
            return;
962
            return;
937
        
963
        
964
        ExecutorService es = host.getStartStopExecutor();
965
        List<Future<?>> results = new ArrayList<Future<?>>();
966
938
        for (int i = 0; i < files.length; i++) {
967
        for (int i = 0; i < files.length; i++) {
939
968
940
            if (files[i].equalsIgnoreCase("META-INF"))
969
            if (files[i].equalsIgnoreCase("META-INF"))
Lines 948-956 Link Here
948
                if (isServiced(cn.getName()))
977
                if (isServiced(cn.getName()))
949
                    continue;
978
                    continue;
950
979
951
                deployDirectory(cn, dir);
980
                results.add(es.submit(new DeployDirectory(this, cn, dir)));
952
            }
981
            }
953
        }
982
        }
983
        
984
        Iterator<Future<?>> iter = results.iterator();
985
        while (iter.hasNext()) {
986
            try {
987
                iter.next().get();
988
            } catch (Exception e) {
989
                // TODO Log this. Should never happen. 
990
            }
991
        }
954
    }
992
    }
955
993
956
    
994
    
Lines 1450-1456 Link Here
1450
        /**
1488
        /**
1451
         * Instant where the application was last put in service.
1489
         * Instant where the application was last put in service.
1452
         */
1490
         */
1453
     public long timestamp = System.currentTimeMillis();
1491
        public long timestamp = System.currentTimeMillis();
1454
    }
1492
    }
1455
1493
1494
    private static class DeployDescriptor implements Runnable {
1495
1496
        private HostConfig config;
1497
        private ContextName cn;
1498
        private File descriptor;
1499
        
1500
        public DeployDescriptor(HostConfig config, ContextName cn,
1501
                File descriptor) {
1502
            this.config = config;
1503
            this.cn = cn;
1504
            this.descriptor= descriptor;
1505
        }
1506
1507
        @Override
1508
        public void run() {
1509
            config.deployDescriptor(cn, descriptor);
1510
        }
1511
    }
1512
1513
    private static class DeployWar implements Runnable {
1514
1515
        private HostConfig config;
1516
        private ContextName cn;
1517
        private File war;
1518
        
1519
        public DeployWar(HostConfig config, ContextName cn, File war) {
1520
            this.config = config;
1521
            this.cn = cn;
1522
            this.war = war;
1523
        }
1524
1525
        @Override
1526
        public void run() {
1527
            config.deployWAR(cn, war);
1528
        }
1529
    }
1530
1531
    private static class DeployDirectory implements Runnable {
1532
1533
        private HostConfig config;
1534
        private ContextName cn;
1535
        private File dir;
1536
        
1537
        public DeployDirectory(HostConfig config, ContextName cn, File dir) {
1538
            this.config = config;
1539
            this.cn = cn;
1540
            this.dir = dir;
1541
        }
1542
1543
        @Override
1544
        public void run() {
1545
            config.deployDirectory(cn, dir);
1546
        }
1547
    }
1456
}
1548
}
(-)webapps/docs/config/engine.xml (+11 lines)
Lines 102-107 Link Here
102
        name.</em></p>
102
        name.</em></p>
103
      </attribute>
103
      </attribute>
104
104
105
      <attribute name="startStopThreads" required="false">
106
        <p>The number of threads this <strong>Engine</strong> will use to start
107
        child <a href="host.html">Host</a> elements in parallel. If not
108
        specified, the default value of 1 will be used. The special value of 0
109
        will result in the value of
110
        <code>Runtime.getRuntime().availableProcessors()</code> being used.
111
        Negative values will result in
112
        <code>Runtime.getRuntime().availableProcessors() - value</code> being
113
        used unless this is less than 1 in which case 1 thread will be used.</p>
114
      </attribute>
115
105
    </attributes>
116
    </attributes>
106
117
107
  </subsection>
118
  </subsection>
(-)webapps/docs/config/host.xml (+13 lines)
Lines 188-193 Link Here
188
        virtual host.</p>
188
        virtual host.</p>
189
      </attribute>
189
      </attribute>
190
190
191
      <attribute name="startStopThreads" required="false">
192
        <p>The number of threads this <strong>Host</strong> will use to start
193
        child <a href="context.html">Context</a> elements in parallel. The same
194
        thread pool will be used to deploy new
195
        <a href="context.html">Context</a>s if automatic deployment is being
196
        used. If not specified, the default value of 1 will be used. The special
197
        value of 0 will result in the value of
198
        <code>Runtime.getRuntime().availableProcessors()</code> being used.
199
        Negative values will result in
200
        <code>Runtime.getRuntime().availableProcessors() - value</code> being
201
        used unless this is less than 1 in which case 1 thread will be used.</p>
202
      </attribute>
203
191
    </attributes>
204
    </attributes>
192
205
193
  </subsection>
206
  </subsection>

Return to bug 46264