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

(-)a/java/org/apache/catalina/Container.java (+17 lines)
Lines 476-479 public interface Container extends Lifecycle { 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
}
(-)a/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 public interface Host extends Container { Link Here
180
    public void setDeployIgnore(String deployIgnore);
181
    public void setDeployIgnore(String deployIgnore);
181
182
182
183
183
    // --------------------------------------------------------- Public Methods
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();
184
190
185
191
192
    // --------------------------------------------------------- Public Methods
193
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
     *
(-)a/java/org/apache/catalina/core/ContainerBase.java (-2 / +133 lines)
Lines 26-31 import java.util.ArrayList; 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-281 public abstract class ContainerBase extends LifecycleMBeanBase 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
    }
298
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
279
333
280
    /**
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
Lines 1001-1006 public abstract class ContainerBase extends LifecycleMBeanBase 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(0,
1063
                getStartStopThreadsInternal(), 10, TimeUnit.SECONDS,
1064
                startStopQueue);
1065
        super.initInternal();
1066
    }
1067
1068
1004
    /**
1069
    /**
1005
     * Start this component and implement the requirements
1070
     * Start this component and implement the requirements
1006
     * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
1071
     * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
Lines 1029-1036 public abstract class ContainerBase extends LifecycleMBeanBase Link Here
1029
1094
1030
        // Start our child containers, if any
1095
        // Start our child containers, if any
1031
        Container children[] = findChildren();
1096
        Container children[] = findChildren();
1097
        List<Future<Void>> results = new ArrayList<Future<Void>>();
1032
        for (int i = 0; i < children.length; i++) {
1098
        for (int i = 0; i < children.length; i++) {
1033
            children[i].start();
1099
            results.add(startStopExecutor.submit(new StartChild(children[i])));
1100
        }
1101
1102
        boolean fail = false;
1103
        for (Future<Void> result : results) {
1104
            try {
1105
                result.get();
1106
            } catch (Exception e) {
1107
                log.error(sm.getString("containerBase.threadedStartFailed"), e);
1108
                fail = true;
1109
            }
1110
            
1111
        }
1112
        if (fail) {
1113
            throw new LifecycleException(
1114
                    sm.getString("containerBase.threadedStartFailed"));
1034
        }
1115
        }
1035
1116
1036
        // Start the Valves in our pipeline (including the basic), if any
1117
        // Start the Valves in our pipeline (including the basic), if any
Lines 1069-1076 public abstract class ContainerBase extends LifecycleMBeanBase Link Here
1069
1150
1070
        // Stop our child containers, if any
1151
        // Stop our child containers, if any
1071
        Container children[] = findChildren();
1152
        Container children[] = findChildren();
1153
        List<Future<Void>> results = new ArrayList<Future<Void>>();
1072
        for (int i = 0; i < children.length; i++) {
1154
        for (int i = 0; i < children.length; i++) {
1073
            children[i].stop();
1155
            results.add(startStopExecutor.submit(new StopChild(children[i])));
1156
        }
1157
        
1158
        boolean fail = false;
1159
        for (Future<Void> result : results) {
1160
            try {
1161
                result.get();
1162
            } catch (Exception e) {
1163
                log.error(sm.getString("containerBase.threadedStopFailed"), e);
1164
                fail = true;
1165
            }
1166
        }
1167
        if (fail) {
1168
            throw new LifecycleException(
1169
                    sm.getString("containerBase.threadedStopFailed"));
1074
        }
1170
        }
1075
1171
1076
        // Stop our subordinate components, if any
1172
        // Stop our subordinate components, if any
Lines 1113-1118 public abstract class ContainerBase extends LifecycleMBeanBase Link Here
1113
            parent.removeChild(this);
1209
            parent.removeChild(this);
1114
        }
1210
        }
1115
1211
1212
        startStopExecutor.shutdownNow();
1213
1116
        super.destroyInternal();
1214
        super.destroyInternal();
1117
    }
1215
    }
1118
1216
Lines 1412-1415 public abstract class ContainerBase extends LifecycleMBeanBase Link Here
1412
            }
1510
            }
1413
        }
1511
        }
1414
    }
1512
    }
1513
    
1514
    
1515
    // ----------------------------- Inner classes used with start/stop Executor
1516
    
1517
    private static class StartChild implements Callable<Void> {
1518
1519
        private Container child;
1520
        
1521
        public StartChild(Container child) {
1522
            this.child = child;
1523
        }
1524
1525
        @Override
1526
        public Void call() throws LifecycleException {
1527
            child.start();
1528
            return null;
1529
        }
1530
    }
1531
1532
    private static class StopChild implements Callable<Void> {
1533
1534
        private Container child;
1535
        
1536
        public StopChild(Container child) {
1537
            this.child = child;
1538
        }
1539
1540
        @Override
1541
        public Void call() throws LifecycleException {
1542
            child.stop();
1543
            return null;
1544
        }
1545
    }
1415
}
1546
}
(-)a/java/org/apache/catalina/core/LocalStrings.properties (+2 lines)
Lines 63-68 asyncContextImpl.requestEnded=The request associated with the AsyncContext has a Link Here
63
containerBase.alreadyStarted=Container {0} has already been started
63
containerBase.alreadyStarted=Container {0} has already been started
64
containerBase.notConfigured=No basic Valve has been configured
64
containerBase.notConfigured=No basic Valve has been configured
65
containerBase.notStarted=Container {0} has not been started
65
containerBase.notStarted=Container {0} has not been started
66
containerBase.threadedStartFailed=A child container failed during start
67
containerBase.threadedStopFailed=A child container failed during stop
66
containerBase.backgroundProcess.cluster=Exception processing cluster {0} background process
68
containerBase.backgroundProcess.cluster=Exception processing cluster {0} background process
67
containerBase.backgroundProcess.loader=Exception processing loader {0} background process
69
containerBase.backgroundProcess.loader=Exception processing loader {0} background process
68
containerBase.backgroundProcess.manager=Exception processing manager {0} background process
70
containerBase.backgroundProcess.manager=Exception processing manager {0} background process
(-)a/java/org/apache/catalina/core/StandardContext.java (-85 / +3 lines)
Lines 37-43 import java.util.Map; Link Here
37
import java.util.Set;
37
import java.util.Set;
38
import java.util.Stack;
38
import java.util.Stack;
39
import java.util.TreeMap;
39
import java.util.TreeMap;
40
import java.util.concurrent.Callable;
41
import java.util.concurrent.atomic.AtomicLong;
40
import java.util.concurrent.atomic.AtomicLong;
42
41
43
import javax.management.ListenerNotFoundException;
42
import javax.management.ListenerNotFoundException;
Lines 118-124 import org.apache.tomcat.JarScanner; Link Here
118
import org.apache.tomcat.util.ExceptionUtils;
117
import org.apache.tomcat.util.ExceptionUtils;
119
import org.apache.tomcat.util.modeler.Registry;
118
import org.apache.tomcat.util.modeler.Registry;
120
import org.apache.tomcat.util.scan.StandardJarScanner;
119
import org.apache.tomcat.util.scan.StandardJarScanner;
121
import org.apache.tomcat.util.threads.DedicatedThreadExecutor;
122
120
123
/**
121
/**
124
 * Standard implementation of the <b>Context</b> interface.  Each
122
 * Standard implementation of the <b>Context</b> interface.  Each
Lines 5201-5209 public class StandardContext extends ContainerBase Link Here
5201
            }
5199
            }
5202
        }
5200
        }
5203
5201
5204
        DedicatedThreadExecutor temporaryExecutor = new DedicatedThreadExecutor();
5205
        try {
5202
        try {
5206
            
5207
            // Create context attributes that will be required
5203
            // Create context attributes that will be required
5208
            if (ok) {
5204
            if (ok) {
5209
                getServletContext().setAttribute(
5205
                getServletContext().setAttribute(
Lines 5228-5249 public class StandardContext extends ContainerBase Link Here
5228
5224
5229
            // Configure and call application event listeners
5225
            // Configure and call application event listeners
5230
            if (ok) {
5226
            if (ok) {
5231
                // we do it in a dedicated thread for memory leak protection, in
5227
                if (!listenerStart()) {
5232
                // case the Listeners registers some ThreadLocals that they
5233
                // forget to cleanup
5234
                Boolean listenerStarted =
5235
                    temporaryExecutor.execute(new Callable<Boolean>() {
5236
                        @Override
5237
                        public Boolean call() throws Exception {
5238
                            ClassLoader old = bindThread();
5239
                            try {
5240
                                return Boolean.valueOf(listenerStart());
5241
                            } finally {
5242
                                unbindThread(old);
5243
                            }
5244
                        }
5245
                    });
5246
                if (!listenerStarted.booleanValue()) {
5247
                    log.error( "Error listenerStart");
5228
                    log.error( "Error listenerStart");
5248
                    ok = false;
5229
                    ok = false;
5249
                }
5230
                }
Lines 5264-5285 public class StandardContext extends ContainerBase Link Here
5264
5245
5265
            // Configure and call application filters
5246
            // Configure and call application filters
5266
            if (ok) {
5247
            if (ok) {
5267
                // we do it in a dedicated thread for memory leak protection, in
5248
                if (!filterStart()) {
5268
                // case the Filters register some ThreadLocals that they forget
5269
                // to cleanup
5270
                Boolean filterStarted =
5271
                    temporaryExecutor.execute(new Callable<Boolean>() {
5272
                        @Override
5273
                        public Boolean call() throws Exception {
5274
                            ClassLoader old = bindThread();
5275
                            try {
5276
                                return Boolean.valueOf(filterStart());
5277
                            } finally {
5278
                                unbindThread(old);
5279
                            }
5280
                        }
5281
                    });
5282
                if (!filterStarted.booleanValue()) {
5283
                    log.error("Error filterStart");
5249
                    log.error("Error filterStart");
5284
                    ok = false;
5250
                    ok = false;
5285
                }
5251
                }
Lines 5287-5313 public class StandardContext extends ContainerBase Link Here
5287
            
5253
            
5288
            // Load and initialize all "load on startup" servlets
5254
            // Load and initialize all "load on startup" servlets
5289
            if (ok) {
5255
            if (ok) {
5290
                // we do it in a dedicated thread for memory leak protection, in
5291
                // case the Servlets register some ThreadLocals that they forget
5292
                // to cleanup
5293
                temporaryExecutor.execute(new Callable<Void>() {
5294
                    @Override
5295
                    public Void call() throws Exception {
5296
                        ClassLoader old = bindThread();
5297
                        try {
5298
                            loadOnStartup(findChildren());
5256
                            loadOnStartup(findChildren());
5299
                            return null;
5300
                        } finally {
5301
                            unbindThread(old);
5302
                        }
5303
                    }
5304
                });
5305
            }
5257
            }
5306
            
5258
            
5307
        } finally {
5259
        } finally {
5308
            // Unbinding thread
5260
            // Unbinding thread
5309
            unbindThread(oldCCL);
5261
            unbindThread(oldCCL);
5310
            temporaryExecutor.shutdown();
5311
        }
5262
        }
5312
5263
5313
        // Set available status depending upon startup success
5264
        // Set available status depending upon startup success
Lines 5447-5470 public class StandardContext extends ContainerBase Link Here
5447
5398
5448
            // Stop our child containers, if any
5399
            // Stop our child containers, if any
5449
            final Container[] children = findChildren();
5400
            final Container[] children = findChildren();
5450
            // we do it in a dedicated thread for memory leak protection, in
5401
5451
            // case some webapp code registers some ThreadLocals that they
5452
            // forget to cleanup
5453
            // TODO Figure out why DedicatedThreadExecutor hangs randomly in the
5454
            //      unit tests if used here
5455
            RunnableWithLifecycleException stop =
5456
                    new RunnableWithLifecycleException() {
5457
                @Override
5458
                public void run() {
5459
                    ClassLoader old = bindThread();
5402
                    ClassLoader old = bindThread();
5460
                    try {
5403
                    try {
5461
                        for (int i = 0; i < children.length; i++) {
5404
                        for (int i = 0; i < children.length; i++) {
5462
                            try {
5463
                                children[i].stop();
5405
                                children[i].stop();
5464
                            } catch (LifecycleException e) {
5465
                                le = e;
5466
                                return;
5467
                            }
5468
                        }
5406
                        }
5469
            
5407
            
5470
                        // Stop our filters
5408
                        // Stop our filters
Lines 5475-5486 public class StandardContext extends ContainerBase Link Here
5475
            
5413
            
5476
                        if (manager != null && manager instanceof Lifecycle &&
5414
                        if (manager != null && manager instanceof Lifecycle &&
5477
                                ((Lifecycle) manager).getState().isAvailable()) {
5415
                                ((Lifecycle) manager).getState().isAvailable()) {
5478
                            try {
5479
                                ((Lifecycle) manager).stop();
5416
                                ((Lifecycle) manager).stop();
5480
                            } catch (LifecycleException e) {
5481
                                le = e;
5482
                                return;
5483
                            }
5484
                        }
5417
                        }
5485
            
5418
            
5486
                        // Stop our application listeners
5419
                        // Stop our application listeners
Lines 5488-5508 public class StandardContext extends ContainerBase Link Here
5488
                    }finally{
5421
                    }finally{
5489
                        unbindThread(old);
5422
                        unbindThread(old);
5490
                    }
5423
                    }
5491
                }
5492
            };
5493
            
5494
            Thread t = new Thread(stop);
5495
            t.setName("stop children - " + getObjectName().toString());
5496
            t.start();
5497
            try {
5498
                t.join();
5499
            } catch (InterruptedException e) {
5500
                // Shouldn't happen
5501
                throw new LifecycleException(e);
5502
            }
5503
            if (stop.getLifecycleException() != null) {
5504
                throw stop.getLifecycleException();
5505
            }
5506
5424
5507
            // Finalize our character set mapper
5425
            // Finalize our character set mapper
5508
            setCharsetMapper(null);
5426
            setCharsetMapper(null);
(-)a/java/org/apache/catalina/core/StandardHost.java (+6 lines)
Lines 24-29 import java.util.List; 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-188 public class StandardHost extends ContainerBase implements Host { Link Here
183
184
184
    // ------------------------------------------------------------- Properties
185
    // ------------------------------------------------------------- Properties
185
186
187
     @Override
188
     public ExecutorService getStartStopExecutor() {
189
         return startStopExecutor;
190
     }
191
186
192
187
    /**
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
(-)a/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"
(-)a/java/org/apache/catalina/startup/ContextConfig.java (-105 / +25 lines)
Lines 125-136 public class ContextConfig Link Here
125
    protected static final LoginConfig DUMMY_LOGIN_CONFIG =
125
    protected static final LoginConfig DUMMY_LOGIN_CONFIG =
126
        new LoginConfig("NONE", null, null, null);
126
        new LoginConfig("NONE", null, null, null);
127
127
128
    /**
129
     * The <code>Digester</code> we will use to process web application
130
     * context files.
131
     */
132
    protected static Digester contextDigester = null;
133
    
134
128
135
    /**
129
    /**
136
     * The set of Authenticators that we know how to configure.  The key is
130
     * The set of Authenticators that we know how to configure.  The key is
Lines 141-172 public class ContextConfig Link Here
141
135
142
136
143
    /**
137
    /**
144
     * The <code>Digester</code>s available to process web deployment descriptor
145
     * files.
146
     */
147
    protected static Digester[] webDigesters = new Digester[4];
148
149
150
    /**
151
     * The <code>Digester</code>s available to process web fragment deployment
152
     * descriptor files.
153
     */
154
    protected static Digester[] webFragmentDigesters = new Digester[4];
155
156
157
    /**
158
     * The <code>Rule</code>s used to parse the web.xml
159
     */
160
    protected static WebRuleSet webRuleSet = new WebRuleSet(false);
161
162
163
    /**
164
     * The <code>Rule</code>s used to parse the web-fragment.xml
165
     */
166
    protected static WebRuleSet webFragmentRuleSet = new WebRuleSet(true);
167
168
169
    /**
170
     * Deployment count.
138
     * Deployment count.
171
     */
139
     */
172
    protected static long deploymentCount = 0L;
140
    protected static long deploymentCount = 0L;
Lines 227-238 public class ContextConfig Link Here
227
     * deployment descriptor files.
195
     * deployment descriptor files.
228
     */
196
     */
229
    protected Digester webDigester = null;
197
    protected Digester webDigester = null;
198
    protected WebRuleSet webRuleSet = null;
230
199
231
    /**
200
    /**
232
     * The <code>Digester</code> we will use to process web fragment
201
     * The <code>Digester</code> we will use to process web fragment
233
     * deployment descriptor files.
202
     * deployment descriptor files.
234
     */
203
     */
235
    protected Digester webFragmentDigester = null;
204
    protected Digester webFragmentDigester = null;
205
    protected WebRuleSet webFragmentRuleSet = null;
236
206
237
    
207
    
238
    // ------------------------------------------------------------- Properties
208
    // ------------------------------------------------------------- Properties
Lines 476-535 public class ContextConfig Link Here
476
446
477
447
478
    /**
448
    /**
479
     * Create (if necessary) and return a Digester configured to process the
449
     * Create and return a Digester configured to process the
480
     * web application deployment descriptor (web.xml).
450
     * web application deployment descriptor (web.xml).
481
     */
451
     */
482
    public void createWebXmlDigester(boolean namespaceAware,
452
    public void createWebXmlDigester(boolean namespaceAware,
483
            boolean validation) {
453
            boolean validation) {
484
        
454
        
485
        if (!namespaceAware && !validation) {
455
        webRuleSet = new WebRuleSet(false);
486
            if (webDigesters[0] == null) {
456
        webDigester = DigesterFactory.newDigester(validation,
487
                webDigesters[0] = DigesterFactory.newDigester(validation,
488
                        namespaceAware, webRuleSet);
457
                        namespaceAware, webRuleSet);
489
                webFragmentDigesters[0] = DigesterFactory.newDigester(validation,
458
        webDigester.getParser();
490
                        namespaceAware, webFragmentRuleSet);
491
                webDigesters[0].getParser();
492
                webFragmentDigesters[0].getParser();
493
            }
494
            webDigester = webDigesters[0];
495
            webFragmentDigester = webFragmentDigesters[0];
496
            
497
        } else if (!namespaceAware && validation) {
498
            if (webDigesters[1] == null) {
499
                webDigesters[1] = DigesterFactory.newDigester(validation,
500
                        namespaceAware, webRuleSet);
501
                webFragmentDigesters[1] = DigesterFactory.newDigester(validation,
502
                        namespaceAware, webFragmentRuleSet);
503
                webDigesters[1].getParser();
504
                webFragmentDigesters[1].getParser();
505
            }
506
            webDigester = webDigesters[1];
507
            webFragmentDigester = webFragmentDigesters[1];
508
            
509
        } else if (namespaceAware && !validation) {
510
            if (webDigesters[2] == null) {
511
                webDigesters[2] = DigesterFactory.newDigester(validation,
512
                        namespaceAware, webRuleSet);
513
                webFragmentDigesters[2] = DigesterFactory.newDigester(validation,
514
                        namespaceAware, webFragmentRuleSet);
515
                webDigesters[2].getParser();
516
                webFragmentDigesters[2].getParser();
517
            }
518
            webDigester = webDigesters[2];
519
            webFragmentDigester = webFragmentDigesters[2];
520
            
459
            
521
        } else {
460
        webFragmentRuleSet = new WebRuleSet(true);
522
            if (webDigesters[3] == null) {
461
        webFragmentDigester = DigesterFactory.newDigester(validation,
523
                webDigesters[3] = DigesterFactory.newDigester(validation,
524
                        namespaceAware, webRuleSet);
525
                webFragmentDigesters[3] = DigesterFactory.newDigester(validation,
526
                        namespaceAware, webFragmentRuleSet);
462
                        namespaceAware, webFragmentRuleSet);
527
                webDigesters[3].getParser();
463
        webFragmentDigester.getParser();
528
                webFragmentDigesters[3].getParser();
529
            }
530
            webDigester = webDigesters[3];
531
            webFragmentDigester = webFragmentDigesters[3];
532
        }
533
    }
464
    }
534
465
535
    
466
    
Lines 567-573 public class ContextConfig Link Here
567
    /**
498
    /**
568
     * Process the default configuration file, if it exists.
499
     * Process the default configuration file, if it exists.
569
     */
500
     */
570
    protected void contextConfig() {
501
    protected void contextConfig(Digester digester) {
571
        
502
        
572
        // Open the default context.xml file, if it exists
503
        // Open the default context.xml file, if it exists
573
        if( defaultContextXml==null && context instanceof StandardContext ) {
504
        if( defaultContextXml==null && context instanceof StandardContext ) {
Lines 584-590 public class ContextConfig Link Here
584
            if (defaultContextFile.exists()) {
515
            if (defaultContextFile.exists()) {
585
                try {
516
                try {
586
                    URL defaultContextUrl = defaultContextFile.toURI().toURL();
517
                    URL defaultContextUrl = defaultContextFile.toURI().toURL();
587
                    processContextConfig(defaultContextUrl);
518
                    processContextConfig(digester, defaultContextUrl);
588
                } catch (MalformedURLException e) {
519
                } catch (MalformedURLException e) {
589
                    log.error(sm.getString(
520
                    log.error(sm.getString(
590
                            "contextConfig.badUrl", defaultContextFile), e);
521
                            "contextConfig.badUrl", defaultContextFile), e);
Lines 596-602 public class ContextConfig Link Here
596
            if (hostContextFile.exists()) {
527
            if (hostContextFile.exists()) {
597
                try {
528
                try {
598
                    URL hostContextUrl = hostContextFile.toURI().toURL();
529
                    URL hostContextUrl = hostContextFile.toURI().toURL();
599
                    processContextConfig(hostContextUrl);
530
                    processContextConfig(digester, hostContextUrl);
600
                } catch (MalformedURLException e) {
531
                } catch (MalformedURLException e) {
601
                    log.error(sm.getString(
532
                    log.error(sm.getString(
602
                            "contextConfig.badUrl", hostContextFile), e);
533
                            "contextConfig.badUrl", hostContextFile), e);
Lines 604-610 public class ContextConfig Link Here
604
            }
535
            }
605
        }
536
        }
606
        if (context.getConfigFile() != null)
537
        if (context.getConfigFile() != null)
607
            processContextConfig(context.getConfigFile());
538
            processContextConfig(digester, context.getConfigFile());
608
        
539
        
609
    }
540
    }
610
541
Lines 612-618 public class ContextConfig Link Here
612
    /**
543
    /**
613
     * Process a context.xml.
544
     * Process a context.xml.
614
     */
545
     */
615
    protected void processContextConfig(URL contextXml) {
546
    protected void processContextConfig(Digester digester, URL contextXml) {
616
        
547
        
617
        if (log.isDebugEnabled())
548
        if (log.isDebugEnabled())
618
            log.debug("Processing context [" + context.getName() 
549
            log.debug("Processing context [" + context.getName() 
Lines 638-653 public class ContextConfig Link Here
638
        
569
        
639
        if (source == null)
570
        if (source == null)
640
            return;
571
            return;
641
        synchronized (contextDigester) {
572
642
            try {
573
            try {
643
                source.setByteStream(stream);
574
                source.setByteStream(stream);
644
                contextDigester.setClassLoader(this.getClass().getClassLoader());
575
            digester.setClassLoader(this.getClass().getClassLoader());
645
                contextDigester.setUseContextClassLoader(false);
576
            digester.setUseContextClassLoader(false);
646
                contextDigester.push(context.getParent());
577
            digester.push(context.getParent());
647
                contextDigester.push(context);
578
            digester.push(context);
648
                XmlErrorHandler errorHandler = new XmlErrorHandler();
579
                XmlErrorHandler errorHandler = new XmlErrorHandler();
649
                contextDigester.setErrorHandler(errorHandler);
580
            digester.setErrorHandler(errorHandler);
650
                contextDigester.parse(source);
581
            digester.parse(source);
651
                if (errorHandler.getWarnings().size() > 0 ||
582
                if (errorHandler.getWarnings().size() > 0 ||
652
                        errorHandler.getErrors().size() > 0) {
583
                        errorHandler.getErrors().size() > 0) {
653
                    errorHandler.logFindings(log, contextXml.toString());
584
                    errorHandler.logFindings(log, contextXml.toString());
Lines 668-674 public class ContextConfig Link Here
668
                        context.getName()), e);
599
                        context.getName()), e);
669
                ok = false;
600
                ok = false;
670
            } finally {
601
            } finally {
671
                contextDigester.reset();
672
                try {
602
                try {
673
                    if (stream != null) {
603
                    if (stream != null) {
674
                        stream.close();
604
                        stream.close();
Lines 678-684 public class ContextConfig Link Here
678
                }
608
                }
679
            }
609
            }
680
        }
610
        }
681
    }
682
611
683
    
612
    
684
    /**
613
    /**
Lines 828-844 public class ContextConfig Link Here
828
    protected void init() {
757
    protected void init() {
829
        // Called from StandardContext.init()
758
        // Called from StandardContext.init()
830
759
831
        if (contextDigester == null){
760
        Digester contextDigester = createContextDigester();
832
            contextDigester = createContextDigester();
833
            contextDigester.getParser();
761
            contextDigester.getParser();
834
        }
835
762
836
        if (log.isDebugEnabled())
763
        if (log.isDebugEnabled())
837
            log.debug(sm.getString("contextConfig.init"));
764
            log.debug(sm.getString("contextConfig.init"));
838
        context.setConfigured(false);
765
        context.setConfigured(false);
839
        ok = true;
766
        ok = true;
840
        
767
        
841
        contextConfig();
768
        contextConfig(contextDigester);
842
        
769
        
843
        createWebXmlDigester(context.getXmlNamespaceAware(),
770
        createWebXmlDigester(context.getXmlNamespaceAware(),
844
                context.getXmlValidation());
771
                context.getXmlValidation());
Lines 1715-1723 public class ContextConfig Link Here
1715
1642
1716
        XmlErrorHandler handler = new XmlErrorHandler();
1643
        XmlErrorHandler handler = new XmlErrorHandler();
1717
1644
1718
        // Web digesters and rulesets are shared between contexts but are not
1719
        // thread safe. Whilst there should only be one thread at a time
1720
        // processing a config, play safe and sync.
1721
        Digester digester;
1645
        Digester digester;
1722
        WebRuleSet ruleSet;
1646
        WebRuleSet ruleSet;
1723
        if (fragment) {
1647
        if (fragment) {
Lines 1728-1737 public class ContextConfig Link Here
1728
            ruleSet = webRuleSet;
1652
            ruleSet = webRuleSet;
1729
        }
1653
        }
1730
        
1654
        
1731
        // Sync on the ruleSet since the same ruleSet is shared across all four
1732
        // digesters
1733
        synchronized(ruleSet) {
1734
            
1735
            digester.push(dest);
1655
            digester.push(dest);
1736
            digester.setErrorHandler(handler);
1656
            digester.setErrorHandler(handler);
1737
            
1657
            
Lines 1764-1770 public class ContextConfig Link Here
1764
                ruleSet.recycle();
1684
                ruleSet.recycle();
1765
            }
1685
            }
1766
        }
1686
        }
1767
    }
1768
1687
1769
1688
1770
    /**
1689
    /**
Lines 2152-2158 public class ContextConfig Link Here
2152
2071
2153
    /**
2072
    /**
2154
     * process filter annotation and merge with existing one!
2073
     * process filter annotation and merge with existing one!
2155
     * FIXME: refactoring method to long and has redundant subroutines with processAnnotationWebServlet!
2074
     * FIXME: refactoring method too long and has redundant subroutines with
2075
     *        processAnnotationWebServlet!
2156
     * @param className
2076
     * @param className
2157
     * @param ae
2077
     * @param ae
2158
     * @param fragment
2078
     * @param fragment
(-)a/java/org/apache/catalina/startup/HostConfig.java (-20 / +108 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 import java.net.URL; 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;
35
import java.util.Map;
36
import java.util.Set;
36
import java.util.Set;
37
import java.util.concurrent.ConcurrentHashMap;
38
import java.util.concurrent.ExecutorService;
39
import java.util.concurrent.Future;
37
import java.util.jar.JarEntry;
40
import java.util.jar.JarEntry;
38
import java.util.jar.JarFile;
41
import java.util.jar.JarFile;
39
import java.util.regex.Matcher;
42
import java.util.regex.Matcher;
Lines 138-145 public class HostConfig Link Here
138
    /**
141
    /**
139
     * Map of deployed applications.
142
     * Map of deployed applications.
140
     */
143
     */
141
    protected HashMap<String, DeployedApplication> deployed =
144
    protected Map<String, DeployedApplication> deployed =
142
        new HashMap<String, DeployedApplication>();
145
        new ConcurrentHashMap<String, DeployedApplication>();
143
146
144
    
147
    
145
    /**
148
    /**
Lines 497-502 public class HostConfig Link Here
497
        ContextName cn = new ContextName(name);
500
        ContextName cn = new ContextName(name);
498
        String baseName = cn.getBaseName();
501
        String baseName = cn.getBaseName();
499
        
502
        
503
        if (deploymentExists(baseName)) {
504
            return;
505
        }
506
        
500
        // Deploy XML descriptors from configBase
507
        // Deploy XML descriptors from configBase
501
        File xml = new File(configBase, baseName + ".xml");
508
        File xml = new File(configBase, baseName + ".xml");
502
        if (xml.exists())
509
        if (xml.exists())
Lines 520-536 public class HostConfig Link Here
520
        if (files == null)
527
        if (files == null)
521
            return;
528
            return;
522
        
529
        
530
        ExecutorService es = host.getStartStopExecutor();
531
        List<Future<?>> results = new ArrayList<Future<?>>();
532
523
        for (int i = 0; i < files.length; i++) {
533
        for (int i = 0; i < files.length; i++) {
524
            File contextXml = new File(configBase, files[i]);
534
            File contextXml = new File(configBase, files[i]);
525
535
526
            if (files[i].toLowerCase(Locale.ENGLISH).endsWith(".xml")) {
536
            if (files[i].toLowerCase(Locale.ENGLISH).endsWith(".xml")) {
527
                ContextName cn = new ContextName(files[i]);
537
                ContextName cn = new ContextName(files[i]);
528
                String name = cn.getName();
529
538
530
                if (isServiced(name))
539
                if (isServiced(cn.getName()) || deploymentExists(cn.getName()))
531
                    continue;
540
                    continue;
532
                
541
                
533
                deployDescriptor(cn, contextXml);
542
                results.add(
543
                        es.submit(new DeployDescriptor(this, cn, contextXml)));
544
            }
545
        }
546
        
547
        for (Future<?> result : results) {
548
            try {
549
                result.get();
550
            } catch (Exception e) {
551
                log.error(sm.getString(
552
                        "hostConfig.deployDescriptor.threaded.error"), e);
534
            }
553
            }
535
        }
554
        }
536
    }
555
    }
Lines 541-549 public class HostConfig Link Here
541
     * @param contextXml
560
     * @param contextXml
542
     */
561
     */
543
    protected void deployDescriptor(ContextName cn, File contextXml) {
562
    protected void deployDescriptor(ContextName cn, File contextXml) {
544
        if (deploymentExists(cn.getName())) {
545
            return;
546
        }
547
        
563
        
548
        DeployedApplication deployedApp = new DeployedApplication(cn.getName());
564
        DeployedApplication deployedApp = new DeployedApplication(cn.getName());
549
565
Lines 670-675 public class HostConfig Link Here
670
        if (files == null)
686
        if (files == null)
671
            return;
687
            return;
672
        
688
        
689
        ExecutorService es = host.getStartStopExecutor();
690
        List<Future<?>> results = new ArrayList<Future<?>>();
691
673
        for (int i = 0; i < files.length; i++) {
692
        for (int i = 0; i < files.length; i++) {
674
            
693
            
675
            if (files[i].equalsIgnoreCase("META-INF"))
694
            if (files[i].equalsIgnoreCase("META-INF"))
Lines 690-699 public class HostConfig Link Here
690
                    continue;
709
                    continue;
691
                }
710
                }
692
711
693
                if (isServiced(cn.getName()))
712
                if (isServiced(cn.getName()) || deploymentExists(cn.getName()))
694
                    continue;
713
                    continue;
695
                
714
                
696
                deployWAR(cn, war);
715
                results.add(es.submit(new DeployWar(this, cn, war)));
716
            }
717
        }
718
        
719
        for (Future<?> result : results) {
720
            try {
721
                result.get();
722
            } catch (Exception e) {
723
                log.error(sm.getString(
724
                        "hostConfig.deployWar.threaded.error"), e);
697
            }
725
            }
698
        }
726
        }
699
    }
727
    }
Lines 741-749 public class HostConfig Link Here
741
     */
769
     */
742
    protected void deployWAR(ContextName cn, File war) {
770
    protected void deployWAR(ContextName cn, File war) {
743
        
771
        
744
        if (deploymentExists(cn.getName()))
745
            return;
746
        
747
        // Checking for a nested /META-INF/context.xml
772
        // Checking for a nested /META-INF/context.xml
748
        JarFile jar = null;
773
        JarFile jar = null;
749
        JarEntry entry = null;
774
        JarEntry entry = null;
Lines 935-940 public class HostConfig Link Here
935
        if (files == null)
960
        if (files == null)
936
            return;
961
            return;
937
        
962
        
963
        ExecutorService es = host.getStartStopExecutor();
964
        List<Future<?>> results = new ArrayList<Future<?>>();
965
938
        for (int i = 0; i < files.length; i++) {
966
        for (int i = 0; i < files.length; i++) {
939
967
940
            if (files[i].equalsIgnoreCase("META-INF"))
968
            if (files[i].equalsIgnoreCase("META-INF"))
Lines 945-954 public class HostConfig Link Here
945
            if (dir.isDirectory()) {
973
            if (dir.isDirectory()) {
946
                ContextName cn = new ContextName(files[i]);
974
                ContextName cn = new ContextName(files[i]);
947
975
948
                if (isServiced(cn.getName()))
976
                if (isServiced(cn.getName()) || deploymentExists(cn.getName()))
949
                    continue;
977
                    continue;
950
978
951
                deployDirectory(cn, dir);
979
                results.add(es.submit(new DeployDirectory(this, cn, dir)));
980
            }
981
        }
982
        
983
        for (Future<?> result : results) {
984
            try {
985
                result.get();
986
            } catch (Exception e) {
987
                log.error(sm.getString(
988
                        "hostConfig.deployDir.threaded.error"), e);
952
            }
989
            }
953
        }
990
        }
954
    }
991
    }
Lines 960-968 public class HostConfig Link Here
960
     */
997
     */
961
    protected void deployDirectory(ContextName cn, File dir) {
998
    protected void deployDirectory(ContextName cn, File dir) {
962
        
999
        
963
        if (deploymentExists(cn.getName()))
964
            return;
965
966
        DeployedApplication deployedApp = new DeployedApplication(cn.getName());
1000
        DeployedApplication deployedApp = new DeployedApplication(cn.getName());
967
1001
968
        // Deploy the application in this directory
1002
        // Deploy the application in this directory
Lines 1453-1456 public class HostConfig Link Here
1453
     public long timestamp = System.currentTimeMillis();
1487
     public long timestamp = System.currentTimeMillis();
1454
    }
1488
    }
1455
1489
1490
    private static class DeployDescriptor implements Runnable {
1491
1492
        private HostConfig config;
1493
        private ContextName cn;
1494
        private File descriptor;
1495
        
1496
        public DeployDescriptor(HostConfig config, ContextName cn,
1497
                File descriptor) {
1498
            this.config = config;
1499
            this.cn = cn;
1500
            this.descriptor= descriptor;
1501
        }
1502
1503
        @Override
1504
        public void run() {
1505
            config.deployDescriptor(cn, descriptor);
1506
        }
1507
    }
1508
1509
    private static class DeployWar implements Runnable {
1510
1511
        private HostConfig config;
1512
        private ContextName cn;
1513
        private File war;
1514
        
1515
        public DeployWar(HostConfig config, ContextName cn, File war) {
1516
            this.config = config;
1517
            this.cn = cn;
1518
            this.war = war;
1519
        }
1520
1521
        @Override
1522
        public void run() {
1523
            config.deployWAR(cn, war);
1524
        }
1525
    }
1526
1527
    private static class DeployDirectory implements Runnable {
1528
1529
        private HostConfig config;
1530
        private ContextName cn;
1531
        private File dir;
1532
        
1533
        public DeployDirectory(HostConfig config, ContextName cn, File dir) {
1534
            this.config = config;
1535
            this.cn = cn;
1536
            this.dir = dir;
1537
        }
1538
1539
        @Override
1540
        public void run() {
1541
            config.deployDirectory(cn, dir);
1542
        }
1543
    }
1456
}
1544
}
(-)a/java/org/apache/catalina/startup/LocalStrings.properties (+3 lines)
Lines 83-93 hostConfig.createDirs=Unable to create directory for deployment: {0} Link Here
83
hostConfig.deploy=Deploying web application directory {0}
83
hostConfig.deploy=Deploying web application directory {0}
84
hostConfig.deployDescriptor=Deploying configuration descriptor {0}
84
hostConfig.deployDescriptor=Deploying configuration descriptor {0}
85
hostConfig.deployDescriptor.error=Error deploying configuration descriptor {0}
85
hostConfig.deployDescriptor.error=Error deploying configuration descriptor {0}
86
hostConfig.deployDescriptor.threaded.error=Error waiting for multi-thread deployment of context descriptors to complete
86
hostConfig.deployDescriptor.localDocBaseSpecified=A docBase {0} inside the host appBase has been specified, and will be ignored
87
hostConfig.deployDescriptor.localDocBaseSpecified=A docBase {0} inside the host appBase has been specified, and will be ignored
87
hostConfig.deployDir=Deploying web application directory {0}
88
hostConfig.deployDir=Deploying web application directory {0}
88
hostConfig.deployDir.error=Error deploying web application directory {0}
89
hostConfig.deployDir.error=Error deploying web application directory {0}
90
hostConfig.deployDir.threaded.error=Error waiting for multi-thread deployment of directories to completehostConfig.deployWar=Deploying web application archive {0}
89
hostConfig.deployWar=Deploying web application archive {0}
91
hostConfig.deployWar=Deploying web application archive {0}
90
hostConfig.deployWar.error=Error deploying web application archive {0}
92
hostConfig.deployWar.error=Error deploying web application archive {0}
93
hostConfig.deployWar.threaded.error=Error waiting for multi-thread deployment of WAR files to complete
91
hostConfig.deploy.error=Exception while deploying web application directory {0}
94
hostConfig.deploy.error=Exception while deploying web application directory {0}
92
hostConfig.deploying=Deploying discovered web applications
95
hostConfig.deploying=Deploying discovered web applications
93
hostConfig.expand=Expanding web application archive {0}
96
hostConfig.expand=Expanding web application archive {0}
(-)a/java/org/apache/tomcat/util/threads/DedicatedThreadExecutor.java (-137 lines)
Lines 1-137 Link Here
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 * 
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 * 
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
package org.apache.tomcat.util.threads;
18
19
import java.util.concurrent.Callable;
20
import java.util.concurrent.ExecutionException;
21
import java.util.concurrent.ExecutorService;
22
import java.util.concurrent.Executors;
23
import java.util.concurrent.Future;
24
import java.util.concurrent.ThreadFactory;
25
26
/**
27
 * A utility class to execute a {@link Callable} in a dedicated thread.
28
 * It can be used either with an instance to reuse the same thread for each call
29
 * to {@link #execute(Callable)} or with the static method
30
 * {@link #executeInOwnThread(Callable)}. When using an instance,
31
 * {@link #shutdown()} must be called when the instance is no longer needed to
32
 * dispose of the dedicated thread.
33
 */
34
public class DedicatedThreadExecutor {
35
    private final SingleThreadFactory singleThreadFactory =
36
        new SingleThreadFactory();
37
    private final ExecutorService executorService =
38
        Executors.newSingleThreadExecutor(singleThreadFactory);
39
40
    /**
41
     * Executes the given {@link Callable} with the thread spawned for the
42
     * current {@link DedicatedThreadExecutor} instance, and returns its result.
43
     * 
44
     * @param <V>
45
     *            the type of the returned value
46
     * @param callable
47
     * @return the completed result
48
     */
49
    public <V> V execute(final Callable<V> callable) {
50
        final Future<V> futureTask = executorService.submit(callable);
51
52
        boolean interrupted = false;
53
        V result;
54
        while (true) {
55
            try {
56
                result = futureTask.get();
57
                break;
58
            } catch (InterruptedException e) {
59
                // keep waiting
60
                interrupted = true;
61
            } catch (ExecutionException e) {
62
                throw new RuntimeException(e);
63
            }
64
        }
65
        if (interrupted) {
66
            // set interruption status so that the caller may react to it
67
            Thread.currentThread().interrupt();
68
        }
69
        return result;
70
    }
71
72
    /**
73
     * Stops the dedicated thread and waits for its death.
74
     */
75
    public void shutdown() {
76
        executorService.shutdown();
77
        if (singleThreadFactory.singleThread != null) {
78
            boolean interrupted = false;
79
            while (true) {
80
                try {
81
                    singleThreadFactory.singleThread.join();
82
                    singleThreadFactory.singleThread = null;
83
                    break;
84
                } catch (InterruptedException e) {
85
                    // keep waiting
86
                    interrupted = true;
87
                }
88
            }
89
            if (interrupted) {
90
                // set interruption status so that the caller may react to it
91
                Thread.currentThread().interrupt();
92
            }
93
        }
94
    }
95
96
    /**
97
     * Executes the given {@link Callable} in a new thread and returns the
98
     * result after the thread is stopped.
99
     * 
100
     * @param <V>
101
     * @param callable
102
     * @return the completed result
103
     */
104
    public static <V> V executeInOwnThread(
105
        final Callable<V> callable) {
106
        DedicatedThreadExecutor executor = new DedicatedThreadExecutor();
107
        try {
108
            return executor.execute(callable);
109
        } finally {
110
            executor.shutdown();
111
        }
112
113
    }
114
115
    // we use a ThreadFactory so that we can later call Thread.join().
116
    // Indeed, calling shutdown() on an ExecutorService will eventually stop the
117
    // thread but it might still be alive when execute() returns (race
118
    // condition).
119
    // This can lead to false alarms about potential memory leaks because the
120
    // thread may have a web application class loader for its context class
121
    // loader.
122
    private static class SingleThreadFactory implements ThreadFactory {
123
        private volatile Thread singleThread;
124
125
        @Override
126
        public Thread newThread(Runnable r) {
127
            if (singleThread != null) {
128
                throw new IllegalStateException(
129
                    "should not have been called more than once");
130
            }
131
            singleThread = new Thread(r);
132
            singleThread.setDaemon(true);
133
            return singleThread;
134
        }
135
136
    }
137
}
(-)a/test/org/apache/tomcat/util/threads/DedicatedThreadExecutorTest.java (-74 lines)
Lines 1-74 Link Here
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 * 
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 * 
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
package org.apache.tomcat.util.threads;
18
19
import java.util.concurrent.Callable;
20
21
import static org.junit.Assert.assertEquals;
22
import static org.junit.Assert.assertFalse;
23
import static org.junit.Assert.assertNotSame;
24
import static org.junit.Assert.assertSame;
25
26
import org.junit.Test;
27
28
public class DedicatedThreadExecutorTest {
29
    private Thread dedicatedThread;
30
31
    @Test
32
    public void testExecute() {
33
        final Thread testingThread = Thread.currentThread();
34
        DedicatedThreadExecutor executor = new DedicatedThreadExecutor();
35
        Long result = executor.execute(new Callable<Long>() {
36
            @Override
37
            public Long call() throws Exception {
38
                dedicatedThread = Thread.currentThread();
39
                assertNotSame(testingThread, dedicatedThread);
40
                return Long.valueOf(123);
41
            }
42
        });
43
        assertEquals(123, result.longValue());
44
45
        //check that the same thread is reused
46
        executor.execute(new Callable<Void>() {
47
            @Override
48
            public Void call() throws Exception {
49
                assertSame(dedicatedThread, Thread.currentThread());
50
                return null;
51
            }
52
        });
53
54
        executor.shutdown();
55
        assertFalse(dedicatedThread.isAlive());
56
    }
57
58
    @Test
59
    public void testExecuteInOwnThread() {
60
        final Thread testingThread = Thread.currentThread();
61
        Long result =
62
            DedicatedThreadExecutor.executeInOwnThread(new Callable<Long>() {
63
                @Override
64
                public Long call() throws Exception {
65
                    dedicatedThread = Thread.currentThread();
66
                    assertNotSame(testingThread, dedicatedThread);
67
                    return Long.valueOf(456);
68
                }
69
            });
70
        assertEquals(456, result.longValue());
71
        assertFalse(dedicatedThread.isAlive());
72
    }
73
74
}
(-)a/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. The special
108
        value of 0 will result in the value of
109
        <code>Runtime.getRuntime().availableProcessors()</code> being used.
110
        Negative values will result in
111
        <code>Runtime.getRuntime().availableProcessors() + value</code> being
112
        used unless this is less than 1 in which case 1 thread will be used. If
113
        not specified, the default value of 1 will be used. </p>
114
      </attribute>
115
105
    </attributes>
116
    </attributes>
106
117
107
  </subsection>
118
  </subsection>
(-)a/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. The special value of 0 will result in the value of
197
        <code>Runtime.getRuntime().availableProcessors()</code> being used.
198
        Negative values will result in
199
        <code>Runtime.getRuntime().availableProcessors() + value</code> being
200
        used unless this is less than 1 in which case 1 thread will be used. If
201
        not specified, the default value of 1 will be used.</p>
202
      </attribute>
203
191
    </attributes>
204
    </attributes>
192
205
193
  </subsection>
206
  </subsection>

Return to bug 46264