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

(-)a/java/org/apache/catalina/Container.java (+17 lines)
Lines 467-470 public interface Container extends Lifecycle { Link Here
467
     * that the request/response still appears in the correct access logs.
467
     * that the request/response still appears in the correct access logs.
468
     */
468
     */
469
    public AccessLog getAccessLog();
469
    public AccessLog getAccessLog();
470
471
472
    /**
473
     * Returns the number of threads available for starting and stopping any
474
     * children associated with this container. This allows start/stop calls to
475
     * children to be processed in parallel.
476
     */
477
    public int getStartStopThreads();
478
479
480
    /**
481
     * Sets the number of threads available for starting and stopping any
482
     * children associated with this container. This allows start/stop calls to
483
     * children to be processed in parallel.
484
     * @param   startStopThreads    The new number of threads to be used
485
     */
486
    public void setStartStopThreads(int startStopThreads);
470
}
487
}
(-)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 (-3 / +137 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 60-66 import org.apache.tomcat.util.res.StringManager; Link Here
60
/**
67
/**
61
 * Abstract implementation of the <b>Container</b> interface, providing common
68
 * Abstract implementation of the <b>Container</b> interface, providing common
62
 * functionality required by nearly every implementation.  Classes extending
69
 * functionality required by nearly every implementation.  Classes extending
63
 * this base class must may implement a replacement for <code>invoke()</code>.
70
 * this base class must implement <code>getInfo()</code>, and may implement
71
 * a replacement for <code>invoke()</code>.
64
 * <p>
72
 * <p>
65
 * All subclasses of this abstract base class will include support for a
73
 * All subclasses of this abstract base class will include support for a
66
 * Pipeline object that defines the processing to be performed for each request
74
 * Pipeline object that defines the processing to be performed for each request
Lines 273-280 public abstract class ContainerBase extends LifecycleMBeanBase Link Here
273
    protected volatile AccessLog accessLog = null;
281
    protected volatile AccessLog accessLog = null;
274
    private volatile boolean accessLogScanComplete = false;
282
    private volatile boolean accessLogScanComplete = false;
275
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
276
    // ------------------------------------------------------------- Properties
292
    // ------------------------------------------------------------- Properties
277
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
278
333
279
    /**
334
    /**
280
     * Get the delay between the invocation of the backgroundProcess method on
335
     * Get the delay between the invocation of the backgroundProcess method on
Lines 989-994 public abstract class ContainerBase extends LifecycleMBeanBase Link Here
989
    }
1044
    }
990
1045
991
1046
1047
    @Override
1048
    protected void initInternal() throws LifecycleException {
1049
        BlockingQueue<Runnable> startStopQueue =
1050
            new LinkedBlockingQueue<Runnable>();
1051
        startStopExecutor = new ThreadPoolExecutor(
1052
                getStartStopThreadsInternal(),
1053
                getStartStopThreadsInternal(), 10, TimeUnit.SECONDS,
1054
                startStopQueue);
1055
        startStopExecutor.allowCoreThreadTimeOut(true);
1056
        super.initInternal();
1057
    }
1058
1059
992
    /**
1060
    /**
993
     * Start this component and implement the requirements
1061
     * Start this component and implement the requirements
994
     * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
1062
     * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
Lines 1017-1024 public abstract class ContainerBase extends LifecycleMBeanBase Link Here
1017
1085
1018
        // Start our child containers, if any
1086
        // Start our child containers, if any
1019
        Container children[] = findChildren();
1087
        Container children[] = findChildren();
1088
        List<Future<Void>> results = new ArrayList<Future<Void>>();
1020
        for (int i = 0; i < children.length; i++) {
1089
        for (int i = 0; i < children.length; i++) {
1021
            children[i].start();
1090
            results.add(startStopExecutor.submit(new StartChild(children[i])));
1091
        }
1092
1093
        boolean fail = false;
1094
        for (Future<Void> result : results) {
1095
            try {
1096
                result.get();
1097
            } catch (Exception e) {
1098
                log.error(sm.getString("containerBase.threadedStartFailed"), e);
1099
                fail = true;
1100
            }
1101
1102
        }
1103
        if (fail) {
1104
            throw new LifecycleException(
1105
                    sm.getString("containerBase.threadedStartFailed"));
1022
        }
1106
        }
1023
1107
1024
        // Start the Valves in our pipeline (including the basic), if any
1108
        // Start the Valves in our pipeline (including the basic), if any
Lines 1057-1064 public abstract class ContainerBase extends LifecycleMBeanBase Link Here
1057
1141
1058
        // Stop our child containers, if any
1142
        // Stop our child containers, if any
1059
        Container children[] = findChildren();
1143
        Container children[] = findChildren();
1144
        List<Future<Void>> results = new ArrayList<Future<Void>>();
1060
        for (int i = 0; i < children.length; i++) {
1145
        for (int i = 0; i < children.length; i++) {
1061
            children[i].stop();
1146
            results.add(startStopExecutor.submit(new StopChild(children[i])));
1147
        }
1148
1149
        boolean fail = false;
1150
        for (Future<Void> result : results) {
1151
            try {
1152
                result.get();
1153
            } catch (Exception e) {
1154
                log.error(sm.getString("containerBase.threadedStopFailed"), e);
1155
                fail = true;
1156
            }
1157
        }
1158
        if (fail) {
1159
            throw new LifecycleException(
1160
                    sm.getString("containerBase.threadedStopFailed"));
1062
        }
1161
        }
1063
1162
1064
        // Stop our subordinate components, if any
1163
        // Stop our subordinate components, if any
Lines 1101-1106 public abstract class ContainerBase extends LifecycleMBeanBase Link Here
1101
            parent.removeChild(this);
1200
            parent.removeChild(this);
1102
        }
1201
        }
1103
1202
1203
        startStopExecutor.shutdownNow();
1204
1104
        super.destroyInternal();
1205
        super.destroyInternal();
1105
    }
1206
    }
1106
1207
Lines 1400-1403 public abstract class ContainerBase extends LifecycleMBeanBase Link Here
1400
            }
1501
            }
1401
        }
1502
        }
1402
    }
1503
    }
1504
1505
1506
    // ----------------------------- Inner classes used with start/stop Executor
1507
1508
    private static class StartChild implements Callable<Void> {
1509
1510
        private Container child;
1511
1512
        public StartChild(Container child) {
1513
            this.child = child;
1514
        }
1515
1516
        @Override
1517
        public Void call() throws LifecycleException {
1518
            child.start();
1519
            return null;
1520
        }
1521
    }
1522
1523
    private static class StopChild implements Callable<Void> {
1524
1525
        private Container child;
1526
1527
        public StopChild(Container child) {
1528
            this.child = child;
1529
        }
1530
1531
        @Override
1532
        public Void call() throws LifecycleException {
1533
            child.stop();
1534
            return null;
1535
        }
1536
    }
1403
}
1537
}
(-)a/java/org/apache/catalina/core/LocalStrings.properties (+2 lines)
Lines 53-58 aprListener.sslInit=Failed to initialize the SSLEngine. Link Here
53
aprListener.tcnValid=Loaded APR based Apache Tomcat Native library {0}.
53
aprListener.tcnValid=Loaded APR based Apache Tomcat Native library {0}.
54
aprListener.flags=APR capabilities: IPv6 [{0}], sendfile [{1}], accept filters [{2}], random [{3}].
54
aprListener.flags=APR capabilities: IPv6 [{0}], sendfile [{1}], accept filters [{2}], random [{3}].
55
asyncContextImpl.requestEnded=The request associated with the AsyncContext has already completed processing.
55
asyncContextImpl.requestEnded=The request associated with the AsyncContext has already completed processing.
56
containerBase.threadedStartFailed=A child container failed during start
57
containerBase.threadedStopFailed=A child container failed during stop
56
containerBase.backgroundProcess.cluster=Exception processing cluster {0} background process
58
containerBase.backgroundProcess.cluster=Exception processing cluster {0} background process
57
containerBase.backgroundProcess.loader=Exception processing loader {0} background process
59
containerBase.backgroundProcess.loader=Exception processing loader {0} background process
58
containerBase.backgroundProcess.manager=Exception processing manager {0} background process
60
containerBase.backgroundProcess.manager=Exception processing manager {0} background process
(-)a/java/org/apache/catalina/core/StandardContext.java (-101 / +26 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 160-165 public class StandardContext extends ContainerBase Link Here
160
158
161
159
162
    /**
160
    /**
161
     * The descriptive information string for this implementation.
162
     */
163
    private static final String info =
164
        "org.apache.catalina.core.StandardContext/1.0";
165
166
167
    /**
163
     * Array containing the safe characters set.
168
     * Array containing the safe characters set.
164
     */
169
     */
165
    protected static URLEncoder urlEncoder;
170
    protected static URLEncoder urlEncoder;
Lines 5182-5190 public class StandardContext extends ContainerBase Link Here
5182
            }
5187
            }
5183
        }
5188
        }
5184
5189
5185
        DedicatedThreadExecutor temporaryExecutor = new DedicatedThreadExecutor();
5186
        try {
5190
        try {
5187
5188
            // Create context attributes that will be required
5191
            // Create context attributes that will be required
5189
            if (ok) {
5192
            if (ok) {
5190
                getServletContext().setAttribute(
5193
                getServletContext().setAttribute(
Lines 5209-5230 public class StandardContext extends ContainerBase Link Here
5209
5212
5210
            // Configure and call application event listeners
5213
            // Configure and call application event listeners
5211
            if (ok) {
5214
            if (ok) {
5212
                // we do it in a dedicated thread for memory leak protection, in
5215
                if (!listenerStart()) {
5213
                // case the Listeners registers some ThreadLocals that they
5214
                // forget to cleanup
5215
                Boolean listenerStarted =
5216
                    temporaryExecutor.execute(new Callable<Boolean>() {
5217
                        @Override
5218
                        public Boolean call() throws Exception {
5219
                            ClassLoader old = bindThread();
5220
                            try {
5221
                                return Boolean.valueOf(listenerStart());
5222
                            } finally {
5223
                                unbindThread(old);
5224
                            }
5225
                        }
5226
                    });
5227
                if (!listenerStarted.booleanValue()) {
5228
                    log.error( "Error listenerStart");
5216
                    log.error( "Error listenerStart");
5229
                    ok = false;
5217
                    ok = false;
5230
                }
5218
                }
Lines 5245-5266 public class StandardContext extends ContainerBase Link Here
5245
5233
5246
            // Configure and call application filters
5234
            // Configure and call application filters
5247
            if (ok) {
5235
            if (ok) {
5248
                // we do it in a dedicated thread for memory leak protection, in
5236
                if (!filterStart()) {
5249
                // case the Filters register some ThreadLocals that they forget
5250
                // to cleanup
5251
                Boolean filterStarted =
5252
                    temporaryExecutor.execute(new Callable<Boolean>() {
5253
                        @Override
5254
                        public Boolean call() throws Exception {
5255
                            ClassLoader old = bindThread();
5256
                            try {
5257
                                return Boolean.valueOf(filterStart());
5258
                            } finally {
5259
                                unbindThread(old);
5260
                            }
5261
                        }
5262
                    });
5263
                if (!filterStarted.booleanValue()) {
5264
                    log.error("Error filterStart");
5237
                    log.error("Error filterStart");
5265
                    ok = false;
5238
                    ok = false;
5266
                }
5239
                }
Lines 5268-5294 public class StandardContext extends ContainerBase Link Here
5268
5241
5269
            // Load and initialize all "load on startup" servlets
5242
            // Load and initialize all "load on startup" servlets
5270
            if (ok) {
5243
            if (ok) {
5271
                // we do it in a dedicated thread for memory leak protection, in
5244
                loadOnStartup(findChildren());
5272
                // case the Servlets register some ThreadLocals that they forget
5273
                // to cleanup
5274
                temporaryExecutor.execute(new Callable<Void>() {
5275
                    @Override
5276
                    public Void call() throws Exception {
5277
                        ClassLoader old = bindThread();
5278
                        try {
5279
                            loadOnStartup(findChildren());
5280
                            return null;
5281
                        } finally {
5282
                            unbindThread(old);
5283
                        }
5284
                    }
5285
                });
5286
            }
5245
            }
5287
5246
5288
        } finally {
5247
        } finally {
5289
            // Unbinding thread
5248
            // Unbinding thread
5290
            unbindThread(oldCCL);
5249
            unbindThread(oldCCL);
5291
            temporaryExecutor.shutdown();
5292
        }
5250
        }
5293
5251
5294
        // Set available status depending upon startup success
5252
        // Set available status depending upon startup success
Lines 5428-5488 public class StandardContext extends ContainerBase Link Here
5428
5386
5429
            // Stop our child containers, if any
5387
            // Stop our child containers, if any
5430
            final Container[] children = findChildren();
5388
            final Container[] children = findChildren();
5431
            // we do it in a dedicated thread for memory leak protection, in
5432
            // case some webapp code registers some ThreadLocals that they
5433
            // forget to cleanup
5434
            // TODO Figure out why DedicatedThreadExecutor hangs randomly in the
5435
            //      unit tests if used here
5436
            RunnableWithLifecycleException stop =
5437
                    new RunnableWithLifecycleException() {
5438
                @Override
5439
                public void run() {
5440
                    ClassLoader old = bindThread();
5441
                    try {
5442
                        for (int i = 0; i < children.length; i++) {
5443
                            try {
5444
                                children[i].stop();
5445
                            } catch (LifecycleException e) {
5446
                                le = e;
5447
                                return;
5448
                            }
5449
                        }
5450
5389
5451
                        // Stop our filters
5390
            ClassLoader old = bindThread();
5452
                        filterStop();
5391
            try {
5392
                for (int i = 0; i < children.length; i++) {
5393
                    children[i].stop();
5394
                }
5453
5395
5454
                        // Stop ContainerBackgroundProcessor thread
5396
                // Stop our filters
5455
                        threadStop();
5397
                filterStop();
5456
5398
5457
                        if (manager != null && manager instanceof Lifecycle &&
5399
                // Stop ContainerBackgroundProcessor thread
5458
                                ((Lifecycle) manager).getState().isAvailable()) {
5400
                threadStop();
5459
                            try {
5460
                                ((Lifecycle) manager).stop();
5461
                            } catch (LifecycleException e) {
5462
                                le = e;
5463
                                return;
5464
                            }
5465
                        }
5466
5401
5467
                        // Stop our application listeners
5402
                if (manager != null && manager instanceof Lifecycle &&
5468
                        listenerStop();
5403
                        ((Lifecycle) manager).getState().isAvailable()) {
5469
                    }finally{
5404
                    ((Lifecycle) manager).stop();
5470
                        unbindThread(old);
5471
                    }
5472
                }
5405
                }
5473
            };
5474
5406
5475
            Thread t = new Thread(stop);
5407
                // Stop our application listeners
5476
            t.setName("stop children - " + getObjectName().toString());
5408
                listenerStop();
5477
            t.start();
5409
            } finally{
5478
            try {
5410
                unbindThread(old);
5479
                t.join();
5480
            } catch (InterruptedException e) {
5481
                // Shouldn't happen
5482
                throw new LifecycleException(e);
5483
            }
5484
            if (stop.getLifecycleException() != null) {
5485
                throw stop.getLifecycleException();
5486
            }
5411
            }
5487
5412
5488
            // Finalize our character set mapper
5413
            // Finalize our character set mapper
(-)a/java/org/apache/catalina/core/StandardHost.java (+12 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 140-145 public class StandardHost extends ContainerBase implements Host { Link Here
140
    private String errorReportValveClass =
141
    private String errorReportValveClass =
141
        "org.apache.catalina.valves.ErrorReportValve";
142
        "org.apache.catalina.valves.ErrorReportValve";
142
143
144
    /**
145
     * The descriptive information string for this implementation.
146
     */
147
    private static final String info =
148
        "org.apache.catalina.core.StandardHost/1.0";
149
143
150
144
    /**
151
    /**
145
     * Unpack WARs property.
152
     * Unpack WARs property.
Lines 177-182 public class StandardHost extends ContainerBase implements Host { Link Here
177
184
178
    // ------------------------------------------------------------- Properties
185
    // ------------------------------------------------------------- Properties
179
186
187
     @Override
188
     public ExecutorService getStartStopExecutor() {
189
         return startStopExecutor;
190
     }
191
180
192
181
    /**
193
    /**
182
     * 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 (-165 / +85 lines)
Lines 127-138 public class ContextConfig Link Here
127
    protected static final LoginConfig DUMMY_LOGIN_CONFIG =
127
    protected static final LoginConfig DUMMY_LOGIN_CONFIG =
128
        new LoginConfig("NONE", null, null, null);
128
        new LoginConfig("NONE", null, null, null);
129
129
130
    /**
131
     * The <code>Digester</code> we will use to process web application
132
     * context files.
133
     */
134
    protected static Digester contextDigester = null;
135
136
130
137
    /**
131
    /**
138
     * The set of Authenticators that we know how to configure.  The key is
132
     * The set of Authenticators that we know how to configure.  The key is
Lines 143-174 public class ContextConfig Link Here
143
137
144
138
145
    /**
139
    /**
146
     * The <code>Digester</code>s available to process web deployment descriptor
147
     * files.
148
     */
149
    protected static Digester[] webDigesters = new Digester[4];
150
151
152
    /**
153
     * The <code>Digester</code>s available to process web fragment deployment
154
     * descriptor files.
155
     */
156
    protected static Digester[] webFragmentDigesters = new Digester[4];
157
158
159
    /**
160
     * The <code>Rule</code>s used to parse the web.xml
161
     */
162
    protected static WebRuleSet webRuleSet = new WebRuleSet(false);
163
164
165
    /**
166
     * The <code>Rule</code>s used to parse the web-fragment.xml
167
     */
168
    protected static WebRuleSet webFragmentRuleSet = new WebRuleSet(true);
169
170
171
    /**
172
     * Deployment count.
140
     * Deployment count.
173
     */
141
     */
174
    protected static long deploymentCount = 0L;
142
    protected static long deploymentCount = 0L;
Lines 236-247 public class ContextConfig Link Here
236
     * deployment descriptor files.
204
     * deployment descriptor files.
237
     */
205
     */
238
    protected Digester webDigester = null;
206
    protected Digester webDigester = null;
207
    protected WebRuleSet webRuleSet = null;
239
208
240
    /**
209
    /**
241
     * The <code>Digester</code> we will use to process web fragment
210
     * The <code>Digester</code> we will use to process web fragment
242
     * deployment descriptor files.
211
     * deployment descriptor files.
243
     */
212
     */
244
    protected Digester webFragmentDigester = null;
213
    protected Digester webFragmentDigester = null;
214
    protected WebRuleSet webFragmentRuleSet = null;
245
215
246
216
247
    // ------------------------------------------------------------- Properties
217
    // ------------------------------------------------------------- Properties
Lines 486-545 public class ContextConfig Link Here
486
456
487
457
488
    /**
458
    /**
489
     * Create (if necessary) and return a Digester configured to process the
459
     * Create and return a Digester configured to process the
490
     * web application deployment descriptor (web.xml).
460
     * web application deployment descriptor (web.xml).
491
     */
461
     */
492
    public void createWebXmlDigester(boolean namespaceAware,
462
    public void createWebXmlDigester(boolean namespaceAware,
493
            boolean validation) {
463
            boolean validation) {
494
464
495
        if (!namespaceAware && !validation) {
465
        webRuleSet = new WebRuleSet(false);
496
            if (webDigesters[0] == null) {
466
        webDigester = DigesterFactory.newDigester(validation,
497
                webDigesters[0] = DigesterFactory.newDigester(validation,
467
                namespaceAware, webRuleSet);
498
                        namespaceAware, webRuleSet);
468
        webDigester.getParser();
499
                webFragmentDigesters[0] = DigesterFactory.newDigester(validation,
500
                        namespaceAware, webFragmentRuleSet);
501
                webDigesters[0].getParser();
502
                webFragmentDigesters[0].getParser();
503
            }
504
            webDigester = webDigesters[0];
505
            webFragmentDigester = webFragmentDigesters[0];
506
507
        } else if (!namespaceAware && validation) {
508
            if (webDigesters[1] == null) {
509
                webDigesters[1] = DigesterFactory.newDigester(validation,
510
                        namespaceAware, webRuleSet);
511
                webFragmentDigesters[1] = DigesterFactory.newDigester(validation,
512
                        namespaceAware, webFragmentRuleSet);
513
                webDigesters[1].getParser();
514
                webFragmentDigesters[1].getParser();
515
            }
516
            webDigester = webDigesters[1];
517
            webFragmentDigester = webFragmentDigesters[1];
518
519
        } else if (namespaceAware && !validation) {
520
            if (webDigesters[2] == null) {
521
                webDigesters[2] = DigesterFactory.newDigester(validation,
522
                        namespaceAware, webRuleSet);
523
                webFragmentDigesters[2] = DigesterFactory.newDigester(validation,
524
                        namespaceAware, webFragmentRuleSet);
525
                webDigesters[2].getParser();
526
                webFragmentDigesters[2].getParser();
527
            }
528
            webDigester = webDigesters[2];
529
            webFragmentDigester = webFragmentDigesters[2];
530
469
531
        } else {
470
        webFragmentRuleSet = new WebRuleSet(true);
532
            if (webDigesters[3] == null) {
471
        webFragmentDigester = DigesterFactory.newDigester(validation,
533
                webDigesters[3] = DigesterFactory.newDigester(validation,
472
                namespaceAware, webFragmentRuleSet);
534
                        namespaceAware, webRuleSet);
473
        webFragmentDigester.getParser();
535
                webFragmentDigesters[3] = DigesterFactory.newDigester(validation,
536
                        namespaceAware, webFragmentRuleSet);
537
                webDigesters[3].getParser();
538
                webFragmentDigesters[3].getParser();
539
            }
540
            webDigester = webDigesters[3];
541
            webFragmentDigester = webFragmentDigesters[3];
542
        }
543
    }
474
    }
544
475
545
476
Lines 577-583 public class ContextConfig Link Here
577
    /**
508
    /**
578
     * Process the default configuration file, if it exists.
509
     * Process the default configuration file, if it exists.
579
     */
510
     */
580
    protected void contextConfig() {
511
    protected void contextConfig(Digester digester) {
581
512
582
        // Open the default context.xml file, if it exists
513
        // Open the default context.xml file, if it exists
583
        if( defaultContextXml==null && context instanceof StandardContext ) {
514
        if( defaultContextXml==null && context instanceof StandardContext ) {
Lines 596-602 public class ContextConfig Link Here
596
            if (defaultContextFile.exists()) {
527
            if (defaultContextFile.exists()) {
597
                try {
528
                try {
598
                    URL defaultContextUrl = defaultContextFile.toURI().toURL();
529
                    URL defaultContextUrl = defaultContextFile.toURI().toURL();
599
                    processContextConfig(defaultContextUrl);
530
                    processContextConfig(digester, defaultContextUrl);
600
                } catch (MalformedURLException e) {
531
                } catch (MalformedURLException e) {
601
                    log.error(sm.getString(
532
                    log.error(sm.getString(
602
                            "contextConfig.badUrl", defaultContextFile), e);
533
                            "contextConfig.badUrl", defaultContextFile), e);
Lines 608-614 public class ContextConfig Link Here
608
            if (hostContextFile.exists()) {
539
            if (hostContextFile.exists()) {
609
                try {
540
                try {
610
                    URL hostContextUrl = hostContextFile.toURI().toURL();
541
                    URL hostContextUrl = hostContextFile.toURI().toURL();
611
                    processContextConfig(hostContextUrl);
542
                    processContextConfig(digester, hostContextUrl);
612
                } catch (MalformedURLException e) {
543
                } catch (MalformedURLException e) {
613
                    log.error(sm.getString(
544
                    log.error(sm.getString(
614
                            "contextConfig.badUrl", hostContextFile), e);
545
                            "contextConfig.badUrl", hostContextFile), e);
Lines 616-622 public class ContextConfig Link Here
616
            }
547
            }
617
        }
548
        }
618
        if (context.getConfigFile() != null) {
549
        if (context.getConfigFile() != null) {
619
            processContextConfig(context.getConfigFile());
550
            processContextConfig(digester, context.getConfigFile());
620
        }
551
        }
621
552
622
    }
553
    }
Lines 625-631 public class ContextConfig Link Here
625
    /**
556
    /**
626
     * Process a context.xml.
557
     * Process a context.xml.
627
     */
558
     */
628
    protected void processContextConfig(URL contextXml) {
559
    protected void processContextConfig(Digester digester, URL contextXml) {
629
560
630
        if (log.isDebugEnabled()) {
561
        if (log.isDebugEnabled()) {
631
            log.debug("Processing context [" + context.getName()
562
            log.debug("Processing context [" + context.getName()
Lines 653-697 public class ContextConfig Link Here
653
        if (source == null) {
584
        if (source == null) {
654
            return;
585
            return;
655
        }
586
        }
656
        synchronized (contextDigester) {
587
657
            try {
588
        try {
658
                source.setByteStream(stream);
589
            source.setByteStream(stream);
659
                contextDigester.setClassLoader(this.getClass().getClassLoader());
590
            digester.setClassLoader(this.getClass().getClassLoader());
660
                contextDigester.setUseContextClassLoader(false);
591
            digester.setUseContextClassLoader(false);
661
                contextDigester.push(context.getParent());
592
            digester.push(context.getParent());
662
                contextDigester.push(context);
593
            digester.push(context);
663
                XmlErrorHandler errorHandler = new XmlErrorHandler();
594
            XmlErrorHandler errorHandler = new XmlErrorHandler();
664
                contextDigester.setErrorHandler(errorHandler);
595
            digester.setErrorHandler(errorHandler);
665
                contextDigester.parse(source);
596
            digester.parse(source);
666
                if (errorHandler.getWarnings().size() > 0 ||
597
            if (errorHandler.getWarnings().size() > 0 ||
667
                        errorHandler.getErrors().size() > 0) {
598
                    errorHandler.getErrors().size() > 0) {
668
                    errorHandler.logFindings(log, contextXml.toString());
599
                errorHandler.logFindings(log, contextXml.toString());
669
                    ok = false;
670
                }
671
                if (log.isDebugEnabled()) {
672
                    log.debug("Successfully processed context [" + context.getName()
673
                            + "] configuration file [" + contextXml + "]");
674
                }
675
            } catch (SAXParseException e) {
676
                log.error(sm.getString("contextConfig.contextParse",
677
                        context.getName()), e);
678
                log.error(sm.getString("contextConfig.defaultPosition",
679
                                 "" + e.getLineNumber(),
680
                                 "" + e.getColumnNumber()));
681
                ok = false;
682
            } catch (Exception e) {
683
                log.error(sm.getString("contextConfig.contextParse",
684
                        context.getName()), e);
685
                ok = false;
600
                ok = false;
686
            } finally {
601
            }
687
                contextDigester.reset();
602
            if (log.isDebugEnabled()) {
688
                try {
603
                log.debug("Successfully processed context [" + context.getName()
689
                    if (stream != null) {
604
                        + "] configuration file [" + contextXml + "]");
690
                        stream.close();
605
            }
691
                    }
606
        } catch (SAXParseException e) {
692
                } catch (IOException e) {
607
            log.error(sm.getString("contextConfig.contextParse",
693
                    log.error(sm.getString("contextConfig.contextClose"), e);
608
                    context.getName()), e);
609
            log.error(sm.getString("contextConfig.defaultPosition",
610
                             "" + e.getLineNumber(),
611
                             "" + e.getColumnNumber()));
612
            ok = false;
613
        } catch (Exception e) {
614
            log.error(sm.getString("contextConfig.contextParse",
615
                    context.getName()), e);
616
            ok = false;
617
        } finally {
618
            try {
619
                if (stream != null) {
620
                    stream.close();
694
                }
621
                }
622
            } catch (IOException e) {
623
                log.error(sm.getString("contextConfig.contextClose"), e);
695
            }
624
            }
696
        }
625
        }
697
    }
626
    }
Lines 846-855 public class ContextConfig Link Here
846
    protected void init() {
775
    protected void init() {
847
        // Called from StandardContext.init()
776
        // Called from StandardContext.init()
848
777
849
        if (contextDigester == null){
778
        Digester contextDigester = createContextDigester();
850
            contextDigester = createContextDigester();
779
        contextDigester.getParser();
851
            contextDigester.getParser();
852
        }
853
780
854
        if (log.isDebugEnabled()) {
781
        if (log.isDebugEnabled()) {
855
            log.debug(sm.getString("contextConfig.init"));
782
            log.debug(sm.getString("contextConfig.init"));
Lines 857-863 public class ContextConfig Link Here
857
        context.setConfigured(false);
784
        context.setConfigured(false);
858
        ok = true;
785
        ok = true;
859
786
860
        contextConfig();
787
        contextConfig(contextDigester);
861
788
862
        createWebXmlDigester(context.getXmlNamespaceAware(),
789
        createWebXmlDigester(context.getXmlNamespaceAware(),
863
                context.getXmlValidation());
790
                context.getXmlValidation());
Lines 1416-1423 public class ContextConfig Link Here
1416
        }
1343
        }
1417
1344
1418
        // Parsing global web.xml is relatively expensive. Use a sync block to
1345
        // Parsing global web.xml is relatively expensive. Use a sync block to
1419
        // make sure it only happens once
1346
        // make sure it only happens once. Use the pipeline since a lock will
1420
        synchronized (host) {
1347
        // already be held on the host by another thread
1348
        synchronized (host.getPipeline()) {
1421
            entry = hostWebXmlCache.get(host);
1349
            entry = hostWebXmlCache.get(host);
1422
            if (entry != null && entry.getGlobalTimeStamp() == globalTimeStamp &&
1350
            if (entry != null && entry.getGlobalTimeStamp() == globalTimeStamp &&
1423
                    entry.getHostTimeStamp() == hostTimeStamp) {
1351
                    entry.getHostTimeStamp() == hostTimeStamp) {
Lines 1679-1685 public class ContextConfig Link Here
1679
        return getWebXmlSource(defaultWebXml, getBaseDir());
1607
        return getWebXmlSource(defaultWebXml, getBaseDir());
1680
    }
1608
    }
1681
1609
1682
1683
    /**
1610
    /**
1684
     * Identify the host web.xml to be used and obtain an input source for
1611
     * Identify the host web.xml to be used and obtain an input source for
1685
     * it.
1612
     * it.
Lines 1806-1814 public class ContextConfig Link Here
1806
1733
1807
        XmlErrorHandler handler = new XmlErrorHandler();
1734
        XmlErrorHandler handler = new XmlErrorHandler();
1808
1735
1809
        // Web digesters and rulesets are shared between contexts but are not
1810
        // thread safe. Whilst there should only be one thread at a time
1811
        // processing a config, play safe and sync.
1812
        Digester digester;
1736
        Digester digester;
1813
        WebRuleSet ruleSet;
1737
        WebRuleSet ruleSet;
1814
        if (fragment) {
1738
        if (fragment) {
Lines 1819-1859 public class ContextConfig Link Here
1819
            ruleSet = webRuleSet;
1743
            ruleSet = webRuleSet;
1820
        }
1744
        }
1821
1745
1822
        // Sync on the ruleSet since the same ruleSet is shared across all four
1746
        digester.push(dest);
1823
        // digesters
1747
        digester.setErrorHandler(handler);
1824
        synchronized(ruleSet) {
1825
1748
1826
            digester.push(dest);
1749
        if(log.isDebugEnabled()) {
1827
            digester.setErrorHandler(handler);
1750
            log.debug(sm.getString("contextConfig.applicationStart",
1828
1751
                    source.getSystemId()));
1829
            if(log.isDebugEnabled()) {
1752
        }
1830
                log.debug(sm.getString("contextConfig.applicationStart",
1831
                        source.getSystemId()));
1832
            }
1833
1753
1834
            try {
1754
        try {
1835
                digester.parse(source);
1755
            digester.parse(source);
1836
1756
1837
                if (handler.getWarnings().size() > 0 ||
1757
            if (handler.getWarnings().size() > 0 ||
1838
                        handler.getErrors().size() > 0) {
1758
                    handler.getErrors().size() > 0) {
1839
                    ok = false;
1840
                    handler.logFindings(log, source.getSystemId());
1841
                }
1842
            } catch (SAXParseException e) {
1843
                log.error(sm.getString("contextConfig.applicationParse",
1844
                        source.getSystemId()), e);
1845
                log.error(sm.getString("contextConfig.applicationPosition",
1846
                                 "" + e.getLineNumber(),
1847
                                 "" + e.getColumnNumber()));
1848
                ok = false;
1759
                ok = false;
1849
            } catch (Exception e) {
1760
                handler.logFindings(log, source.getSystemId());
1850
                log.error(sm.getString("contextConfig.applicationParse",
1761
            }
1851
                        source.getSystemId()), e);
1762
        } catch (SAXParseException e) {
1852
                ok = false;
1763
            log.error(sm.getString("contextConfig.applicationParse",
1853
            } finally {
1764
                    source.getSystemId()), e);
1854
                digester.reset();
1765
            log.error(sm.getString("contextConfig.applicationPosition",
1855
                ruleSet.recycle();
1766
                             "" + e.getLineNumber(),
1856
            }
1767
                             "" + e.getColumnNumber()));
1768
            ok = false;
1769
        } catch (Exception e) {
1770
            log.error(sm.getString("contextConfig.applicationParse",
1771
                    source.getSystemId()), e);
1772
            ok = false;
1773
        } finally {
1774
            digester.reset();
1775
            ruleSet.recycle();
1857
        }
1776
        }
1858
    }
1777
    }
1859
1778
Lines 2244-2250 public class ContextConfig Link Here
2244
2163
2245
    /**
2164
    /**
2246
     * process filter annotation and merge with existing one!
2165
     * process filter annotation and merge with existing one!
2247
     * FIXME: refactoring method to long and has redundant subroutines with processAnnotationWebServlet!
2166
     * FIXME: refactoring method too long and has redundant subroutines with
2167
     *        processAnnotationWebServlet!
2248
     * @param className
2168
     * @param className
2249
     * @param ae
2169
     * @param ae
2250
     * @param fragment
2170
     * @param fragment
(-)a/java/org/apache/catalina/startup/HostConfig.java (-21 / +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 33-39 import java.util.HashSet; Link Here
33
import java.util.LinkedHashMap;
31
import java.util.LinkedHashMap;
34
import java.util.List;
32
import java.util.List;
35
import java.util.Locale;
33
import java.util.Locale;
34
import java.util.Map;
36
import java.util.Set;
35
import java.util.Set;
36
import java.util.concurrent.ConcurrentHashMap;
37
import java.util.concurrent.ExecutorService;
38
import java.util.concurrent.Future;
37
import java.util.jar.JarEntry;
39
import java.util.jar.JarEntry;
38
import java.util.jar.JarFile;
40
import java.util.jar.JarFile;
39
import java.util.regex.Matcher;
41
import java.util.regex.Matcher;
Lines 138-145 public class HostConfig Link Here
138
    /**
140
    /**
139
     * Map of deployed applications.
141
     * Map of deployed applications.
140
     */
142
     */
141
    protected HashMap<String, DeployedApplication> deployed =
143
    protected Map<String, DeployedApplication> deployed =
142
        new HashMap<String, DeployedApplication>();
144
        new ConcurrentHashMap<String, DeployedApplication>();
143
145
144
146
145
    /**
147
    /**
Lines 497-502 public class HostConfig Link Here
497
        ContextName cn = new ContextName(name);
499
        ContextName cn = new ContextName(name);
498
        String baseName = cn.getBaseName();
500
        String baseName = cn.getBaseName();
499
501
502
        if (deploymentExists(baseName)) {
503
            return;
504
        }
505
500
        // Deploy XML descriptors from configBase
506
        // Deploy XML descriptors from configBase
501
        File xml = new File(configBase, baseName + ".xml");
507
        File xml = new File(configBase, baseName + ".xml");
502
        if (xml.exists())
508
        if (xml.exists())
Lines 520-536 public class HostConfig Link Here
520
        if (files == null)
526
        if (files == null)
521
            return;
527
            return;
522
528
529
        ExecutorService es = host.getStartStopExecutor();
530
        List<Future<?>> results = new ArrayList<Future<?>>();
531
523
        for (int i = 0; i < files.length; i++) {
532
        for (int i = 0; i < files.length; i++) {
524
            File contextXml = new File(configBase, files[i]);
533
            File contextXml = new File(configBase, files[i]);
525
534
526
            if (files[i].toLowerCase(Locale.ENGLISH).endsWith(".xml")) {
535
            if (files[i].toLowerCase(Locale.ENGLISH).endsWith(".xml")) {
527
                ContextName cn = new ContextName(files[i]);
536
                ContextName cn = new ContextName(files[i]);
528
                String name = cn.getName();
529
537
530
                if (isServiced(name))
538
                if (isServiced(cn.getName()) || deploymentExists(cn.getName()))
531
                    continue;
539
                    continue;
532
540
533
                deployDescriptor(cn, contextXml);
541
                results.add(
542
                        es.submit(new DeployDescriptor(this, cn, contextXml)));
543
            }
544
        }
545
546
        for (Future<?> result : results) {
547
            try {
548
                result.get();
549
            } catch (Exception e) {
550
                log.error(sm.getString(
551
                        "hostConfig.deployDescriptor.threaded.error"), e);
534
            }
552
            }
535
        }
553
        }
536
    }
554
    }
Lines 541-549 public class HostConfig Link Here
541
     * @param contextXml
559
     * @param contextXml
542
     */
560
     */
543
    protected void deployDescriptor(ContextName cn, File contextXml) {
561
    protected void deployDescriptor(ContextName cn, File contextXml) {
544
        if (deploymentExists(cn.getName())) {
545
            return;
546
        }
547
562
548
        DeployedApplication deployedApp = new DeployedApplication(cn.getName());
563
        DeployedApplication deployedApp = new DeployedApplication(cn.getName());
549
564
Lines 670-675 public class HostConfig Link Here
670
        if (files == null)
685
        if (files == null)
671
            return;
686
            return;
672
687
688
        ExecutorService es = host.getStartStopExecutor();
689
        List<Future<?>> results = new ArrayList<Future<?>>();
690
673
        for (int i = 0; i < files.length; i++) {
691
        for (int i = 0; i < files.length; i++) {
674
692
675
            if (files[i].equalsIgnoreCase("META-INF"))
693
            if (files[i].equalsIgnoreCase("META-INF"))
Lines 690-699 public class HostConfig Link Here
690
                    continue;
708
                    continue;
691
                }
709
                }
692
710
693
                if (isServiced(cn.getName()))
711
                if (isServiced(cn.getName()) || deploymentExists(cn.getName()))
694
                    continue;
712
                    continue;
695
713
696
                deployWAR(cn, war);
714
                results.add(es.submit(new DeployWar(this, cn, war)));
715
            }
716
        }
717
718
        for (Future<?> result : results) {
719
            try {
720
                result.get();
721
            } catch (Exception e) {
722
                log.error(sm.getString(
723
                        "hostConfig.deployWar.threaded.error"), e);
697
            }
724
            }
698
        }
725
        }
699
    }
726
    }
Lines 741-749 public class HostConfig Link Here
741
     */
768
     */
742
    protected void deployWAR(ContextName cn, File war) {
769
    protected void deployWAR(ContextName cn, File war) {
743
770
744
        if (deploymentExists(cn.getName()))
745
            return;
746
747
        // Checking for a nested /META-INF/context.xml
771
        // Checking for a nested /META-INF/context.xml
748
        JarFile jar = null;
772
        JarFile jar = null;
749
        JarEntry entry = null;
773
        JarEntry entry = null;
Lines 935-940 public class HostConfig Link Here
935
        if (files == null)
959
        if (files == null)
936
            return;
960
            return;
937
961
962
        ExecutorService es = host.getStartStopExecutor();
963
        List<Future<?>> results = new ArrayList<Future<?>>();
964
938
        for (int i = 0; i < files.length; i++) {
965
        for (int i = 0; i < files.length; i++) {
939
966
940
            if (files[i].equalsIgnoreCase("META-INF"))
967
            if (files[i].equalsIgnoreCase("META-INF"))
Lines 945-954 public class HostConfig Link Here
945
            if (dir.isDirectory()) {
972
            if (dir.isDirectory()) {
946
                ContextName cn = new ContextName(files[i]);
973
                ContextName cn = new ContextName(files[i]);
947
974
948
                if (isServiced(cn.getName()))
975
                if (isServiced(cn.getName()) || deploymentExists(cn.getName()))
949
                    continue;
976
                    continue;
950
977
951
                deployDirectory(cn, dir);
978
                results.add(es.submit(new DeployDirectory(this, cn, dir)));
979
            }
980
        }
981
982
        for (Future<?> result : results) {
983
            try {
984
                result.get();
985
            } catch (Exception e) {
986
                log.error(sm.getString(
987
                        "hostConfig.deployDir.threaded.error"), e);
952
            }
988
            }
953
        }
989
        }
954
    }
990
    }
Lines 960-968 public class HostConfig Link Here
960
     */
996
     */
961
    protected void deployDirectory(ContextName cn, File dir) {
997
    protected void deployDirectory(ContextName cn, File dir) {
962
998
963
        if (deploymentExists(cn.getName()))
964
            return;
965
966
        DeployedApplication deployedApp = new DeployedApplication(cn.getName());
999
        DeployedApplication deployedApp = new DeployedApplication(cn.getName());
967
1000
968
        // Deploy the application in this directory
1001
        // Deploy the application in this directory
Lines 1450-1456 public class HostConfig Link Here
1450
        /**
1483
        /**
1451
         * Instant where the application was last put in service.
1484
         * Instant where the application was last put in service.
1452
         */
1485
         */
1453
     public long timestamp = System.currentTimeMillis();
1486
        public long timestamp = System.currentTimeMillis();
1487
    }
1488
1489
    private static class DeployDescriptor implements Runnable {
1490
1491
        private HostConfig config;
1492
        private ContextName cn;
1493
        private File descriptor;
1494
1495
        public DeployDescriptor(HostConfig config, ContextName cn,
1496
                File descriptor) {
1497
            this.config = config;
1498
            this.cn = cn;
1499
            this.descriptor= descriptor;
1500
        }
1501
1502
        @Override
1503
        public void run() {
1504
            config.deployDescriptor(cn, descriptor);
1505
        }
1506
    }
1507
1508
    private static class DeployWar implements Runnable {
1509
1510
        private HostConfig config;
1511
        private ContextName cn;
1512
        private File war;
1513
1514
        public DeployWar(HostConfig config, ContextName cn, File war) {
1515
            this.config = config;
1516
            this.cn = cn;
1517
            this.war = war;
1518
        }
1519
1520
        @Override
1521
        public void run() {
1522
            config.deployWAR(cn, war);
1523
        }
1454
    }
1524
    }
1455
1525
1526
    private static class DeployDirectory implements Runnable {
1527
1528
        private HostConfig config;
1529
        private ContextName cn;
1530
        private File dir;
1531
1532
        public DeployDirectory(HostConfig config, ContextName cn, File dir) {
1533
            this.config = config;
1534
            this.cn = cn;
1535
            this.dir = dir;
1536
        }
1537
1538
        @Override
1539
        public void run() {
1540
            config.deployDirectory(cn, dir);
1541
        }
1542
    }
1456
}
1543
}
(-)a/java/org/apache/catalina/startup/LocalStrings.properties (+8 lines)
Lines 80-90 hostConfig.context.restart=Error during context [{0}] restart Link Here
80
hostConfig.createDirs=Unable to create directory for deployment: {0}
80
hostConfig.createDirs=Unable to create directory for deployment: {0}
81
hostConfig.deployDescriptor=Deploying configuration descriptor {0}
81
hostConfig.deployDescriptor=Deploying configuration descriptor {0}
82
hostConfig.deployDescriptor.error=Error deploying configuration descriptor {0}
82
hostConfig.deployDescriptor.error=Error deploying configuration descriptor {0}
83
hostConfig.deployDescriptor.threaded.error=Error waiting for multi-thread deployment of context descriptors to complete
83
hostConfig.deployDescriptor.localDocBaseSpecified=A docBase {0} inside the host appBase has been specified, and will be ignored
84
hostConfig.deployDescriptor.localDocBaseSpecified=A docBase {0} inside the host appBase has been specified, and will be ignored
84
hostConfig.deployDir=Deploying web application directory {0}
85
hostConfig.deployDir=Deploying web application directory {0}
85
hostConfig.deployDir.error=Error deploying web application directory {0}
86
hostConfig.deployDir.error=Error deploying web application directory {0}
87
hostConfig.deployDir.threaded.error=Error waiting for multi-thread deployment of directories to completehostConfig.deployWar=Deploying web application archive {0}
86
hostConfig.deployWar=Deploying web application archive {0}
88
hostConfig.deployWar=Deploying web application archive {0}
87
hostConfig.deployWar.error=Error deploying web application archive {0}
89
hostConfig.deployWar.error=Error deploying web application archive {0}
90
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}
92
hostConfig.deploying=Deploying discovered web applications
93
hostConfig.expand=Expanding web application archive {0}
94
hostConfig.expand.error=Exception while expanding web application archive {0}
95
hostConfig.expanding=Expanding discovered web application archives
88
hostConfig.ignorePath=Ignoring path [{0}] in appBase for automatic deployment
96
hostConfig.ignorePath=Ignoring path [{0}] in appBase for automatic deployment
89
hostConfig.illegalWarName=The war name [{0}] is invalid. The archive will be ignored.
97
hostConfig.illegalWarName=The war name [{0}] is invalid. The archive will be ignored.
90
hostConfig.jmx.register=Register context [{0}] failed
98
hostConfig.jmx.register=Register context [{0}] failed
(-)a/java/org/apache/coyote/AbstractProtocol.java (+45 lines)
Lines 493-498 public abstract class AbstractProtocol implements ProtocolHandler, Link Here
493
                SocketStatus status) {
493
                SocketStatus status) {
494
            P processor = connections.remove(socket.getSocket());
494
            P processor = connections.remove(socket.getSocket());
495
495
496
            if (getLog().isDebugEnabled()) {
497
                getLog().debug("process() entry " +
498
                        "Socket: [" + logHashcode(socket.getSocket()) + "], " +
499
                        "Processor [" + logHashcode(processor) + "]");
500
            }
501
496
            socket.setAsync(false);
502
            socket.setAsync(false);
497
503
498
            try {
504
            try {
Lines 503-522 public abstract class AbstractProtocol implements ProtocolHandler, Link Here
503
                    processor = createProcessor();
509
                    processor = createProcessor();
504
                }
510
                }
505
511
512
                if (getLog().isDebugEnabled()) {
513
                    getLog().debug("process() gotProcessor " +
514
                            "Socket: [" + logHashcode(socket.getSocket()) + "], " +
515
                            "Processor [" + logHashcode(processor) + "]");
516
                }
517
506
                initSsl(socket, processor);
518
                initSsl(socket, processor);
507
519
508
                SocketState state = SocketState.CLOSED;
520
                SocketState state = SocketState.CLOSED;
509
                do {
521
                do {
510
                    if (processor.isAsync() || state == SocketState.ASYNC_END) {
522
                    if (processor.isAsync() || state == SocketState.ASYNC_END) {
511
                        state = processor.asyncDispatch(status);
523
                        state = processor.asyncDispatch(status);
524
                        if (getLog().isDebugEnabled()) {
525
                            getLog().debug("process() asyncDispatch " +
526
                                    "Socket: [" + logHashcode(socket.getSocket()) + "], " +
527
                                    "Processor: [" + logHashcode(processor) + "], " +
528
                                    "State: [" + state.toString() + "]");
529
                        }
512
                    } else if (processor.isComet()) {
530
                    } else if (processor.isComet()) {
513
                        state = processor.event(status);
531
                        state = processor.event(status);
532
                        if (getLog().isDebugEnabled()) {
533
                            getLog().debug("process() event " +
534
                                    "Socket: [" + logHashcode(socket.getSocket()) + "], " +
535
                                    "Processor: [" + logHashcode(processor) + "], " +
536
                                    "State: [" + state.toString() + "]");
537
                        }
514
                    } else {
538
                    } else {
515
                        state = processor.process(socket);
539
                        state = processor.process(socket);
540
                        if (getLog().isDebugEnabled()) {
541
                            getLog().debug("process() process " +
542
                                    "Socket: [" + logHashcode(socket.getSocket()) + "], " +
543
                                    "Processor: [" + logHashcode(processor) + "], " +
544
                                    "State: [" + state.toString() + "]");
545
                        }
516
                    }
546
                    }
517
547
518
                    if (state != SocketState.CLOSED && processor.isAsync()) {
548
                    if (state != SocketState.CLOSED && processor.isAsync()) {
519
                        state = processor.asyncPostProcess();
549
                        state = processor.asyncPostProcess();
550
                        if (getLog().isDebugEnabled()) {
551
                            getLog().debug("process() asyncPostProcess " +
552
                                    "Socket: [" + logHashcode(socket.getSocket()) + "], " +
553
                                    "Processor: [" + logHashcode(processor) + "], " +
554
                                    "State: [" + state.toString() + "]");
555
                        }
556
520
                    }
557
                    }
521
                } while (state == SocketState.ASYNC_END);
558
                } while (state == SocketState.ASYNC_END);
522
559
Lines 562-567 public abstract class AbstractProtocol implements ProtocolHandler, Link Here
562
            return SocketState.CLOSED;
599
            return SocketState.CLOSED;
563
        }
600
        }
564
601
602
        private String logHashcode (Object o) {
603
            if (o == null) {
604
                return "null";
605
            } else {
606
                return Integer.toString(o.hashCode());
607
            }
608
        }
609
565
        protected abstract P createProcessor();
610
        protected abstract P createProcessor();
566
        protected abstract void initSsl(SocketWrapper<S> socket, P processor);
611
        protected abstract void initSsl(SocketWrapper<S> socket, P processor);
567
        protected abstract void longPoll(SocketWrapper<S> socket, P processor);
612
        protected abstract void longPoll(SocketWrapper<S> socket, P processor);
(-)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