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.catalina.core;
18
19 import java.beans.PropertyChangeListener;
20 import java.beans.PropertyChangeSupport;
21 import java.io.File;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.net.InetAddress;
25 import java.net.ServerSocket;
26 import java.net.Socket;
27 import java.net.SocketTimeoutException;
28 import java.net.URISyntaxException;
29 import java.net.URL;
30 import java.net.URLClassLoader;
31 import java.security.AccessControlException;
32 import java.util.Random;
33 import java.util.concurrent.ExecutionException;
34 import java.util.concurrent.ScheduledExecutorService;
35 import java.util.concurrent.ScheduledFuture;
36 import java.util.concurrent.ScheduledThreadPoolExecutor;
37 import java.util.concurrent.TimeUnit;
38
39 import javax.management.InstanceNotFoundException;
40 import javax.management.MBeanException;
41 import javax.management.MBeanServer;
42 import javax.management.ObjectName;
43
44 import org.apache.catalina.Context;
45 import org.apache.catalina.Lifecycle;
46 import org.apache.catalina.LifecycleException;
47 import org.apache.catalina.LifecycleState;
48 import org.apache.catalina.Server;
49 import org.apache.catalina.Service;
50 import org.apache.catalina.deploy.NamingResourcesImpl;
51 import org.apache.catalina.mbeans.MBeanFactory;
52 import org.apache.catalina.startup.Catalina;
53 import org.apache.catalina.util.ExtensionValidator;
54 import org.apache.catalina.util.LifecycleMBeanBase;
55 import org.apache.catalina.util.ServerInfo;
56 import org.apache.juli.logging.Log;
57 import org.apache.juli.logging.LogFactory;
58 import org.apache.tomcat.util.ExceptionUtils;
59 import org.apache.tomcat.util.buf.StringCache;
60 import org.apache.tomcat.util.modeler.Registry;
61 import org.apache.tomcat.util.res.StringManager;
62 import org.apache.tomcat.util.threads.TaskThreadFactory;
63
64
65 /**
66  * Standard implementation of the <b>Server</b> interface, available for use
67  * (but not required) when deploying and starting Catalina.
68  *
69  * @author Craig R. McClanahan
70  */

71 public final class StandardServer extends LifecycleMBeanBase implements Server {
72
73     private static final Log log = LogFactory.getLog(StandardServer.class);
74
75
76     // ------------------------------------------------------------ Constructor
77
78
79     /**
80      * Construct a default instance of this class.
81      */

82     public StandardServer() {
83
84         super();
85
86         globalNamingResources = new NamingResourcesImpl();
87         globalNamingResources.setContainer(this);
88
89         if (isUseNaming()) {
90             namingContextListener = new NamingContextListener();
91             addLifecycleListener(namingContextListener);
92         } else {
93             namingContextListener = null;
94         }
95
96     }
97
98
99     // ----------------------------------------------------- Instance Variables
100
101
102     /**
103      * Global naming resources context.
104      */

105     private javax.naming.Context globalNamingContext = null;
106
107
108     /**
109      * Global naming resources.
110      */

111     private NamingResourcesImpl globalNamingResources = null;
112
113
114     /**
115      * The naming context listener for this web application.
116      */

117     private final NamingContextListener namingContextListener;
118
119
120     /**
121      * The port number on which we wait for shutdown commands.
122      */

123     private int port = 8005;
124
125     private int portOffset = 0;
126
127     /**
128      * The address on which we wait for shutdown commands.
129      */

130     private String address = "localhost";
131
132
133     /**
134      * A random number generator that is <strong>only</strong> used if
135      * the shutdown command string is longer than 1024 characters.
136      */

137     private Random random = null;
138
139
140     /**
141      * The set of Services associated with this Server.
142      */

143     private Service services[] = new Service[0];
144     private final Object servicesLock = new Object();
145
146
147     /**
148      * The shutdown command string we are looking for.
149      */

150     private String shutdown = "SHUTDOWN";
151
152
153     /**
154      * The string manager for this package.
155      */

156     private static final StringManager sm =
157         StringManager.getManager(Constants.Package);
158
159
160     /**
161      * The property change support for this component.
162      */

163     final PropertyChangeSupport support = new PropertyChangeSupport(this);
164
165     private volatile boolean stopAwait = false;
166
167     private Catalina catalina = null;
168
169     private ClassLoader parentClassLoader = null;
170
171     /**
172      * Thread that currently is inside our await() method.
173      */

174     private volatile Thread awaitThread = null;
175
176     /**
177      * Server socket that is used to wait for the shutdown command.
178      */

179     private volatile ServerSocket awaitSocket = null;
180
181     private File catalinaHome = null;
182
183     private File catalinaBase = null;
184
185     private final Object namingToken = new Object();
186
187     /**
188      * The number of threads available to process utility tasks in this service.
189      */

190     protected int utilityThreads = 2;
191
192     /**
193      * The utility threads daemon flag.
194      */

195     protected boolean utilityThreadsAsDaemon = false;
196
197     /**
198      * Utility executor with scheduling capabilities.
199      */

200     private ScheduledThreadPoolExecutor utilityExecutor = null;
201
202     /**
203      * Utility executor wrapper.
204      */

205     private ScheduledExecutorService utilityExecutorWrapper = null;
206
207
208     /**
209      * Controller for the periodic lifecycle event.
210      */

211     private ScheduledFuture<?> periodicLifecycleEventFuture = null;
212     private ScheduledFuture<?> monitorFuture;
213
214
215     /**
216      * The lifecycle event period in seconds.
217      */

218     protected int periodicEventDelay = 10;
219
220
221     // ------------------------------------------------------------- Properties
222
223     @Override
224     public Object getNamingToken() {
225         return namingToken;
226     }
227
228
229     /**
230      * Return the global naming resources context.
231      */

232     @Override
233     public javax.naming.Context getGlobalNamingContext() {
234         return this.globalNamingContext;
235     }
236
237
238     /**
239      * Set the global naming resources context.
240      *
241      * @param globalNamingContext The new global naming resource context
242      */

243     public void setGlobalNamingContext(javax.naming.Context globalNamingContext) {
244         this.globalNamingContext = globalNamingContext;
245     }
246
247
248     /**
249      * Return the global naming resources.
250      */

251     @Override
252     public NamingResourcesImpl getGlobalNamingResources() {
253         return this.globalNamingResources;
254     }
255
256
257     /**
258      * Set the global naming resources.
259      *
260      * @param globalNamingResources The new global naming resources
261      */

262     @Override
263     public void setGlobalNamingResources
264         (NamingResourcesImpl globalNamingResources) {
265
266         NamingResourcesImpl oldGlobalNamingResources =
267             this.globalNamingResources;
268         this.globalNamingResources = globalNamingResources;
269         this.globalNamingResources.setContainer(this);
270         support.firePropertyChange("globalNamingResources",
271                                    oldGlobalNamingResources,
272                                    this.globalNamingResources);
273
274     }
275
276
277     /**
278      * Report the current Tomcat Server Release number
279      * @return Tomcat release identifier
280      */

281     public String getServerInfo() {
282         return ServerInfo.getServerInfo();
283     }
284
285
286     /**
287      * Return the current server built timestamp
288      * @return server built timestamp.
289      */

290     public String getServerBuilt() {
291         return ServerInfo.getServerBuilt();
292     }
293
294
295     /**
296      * Return the current server's version number.
297      * @return server's version number.
298      */

299     public String getServerNumber() {
300         return ServerInfo.getServerNumber();
301     }
302
303
304     /**
305      * Return the port number we listen to for shutdown commands.
306      */

307     @Override
308     public int getPort() {
309         return this.port;
310     }
311
312
313     /**
314      * Set the port number we listen to for shutdown commands.
315      *
316      * @param port The new port number
317      */

318     @Override
319     public void setPort(int port) {
320         this.port = port;
321     }
322
323
324     @Override
325     public int getPortOffset() {
326         return portOffset;
327     }
328
329
330     @Override
331     public void setPortOffset(int portOffset) {
332         if (portOffset < 0) {
333             throw new IllegalArgumentException(
334                     sm.getString("standardServer.portOffset.invalid", Integer.valueOf(portOffset)));
335         }
336         this.portOffset = portOffset;
337     }
338
339
340     @Override
341     public int getPortWithOffset() {
342         // Non-positive port values have special meanings and the offset should
343         // not apply.
344         int port = getPort();
345         if (port > 0) {
346             return port + getPortOffset();
347         } else {
348             return port;
349         }
350     }
351
352
353     /**
354      * Return the address on which we listen to for shutdown commands.
355      */

356     @Override
357     public String getAddress() {
358         return this.address;
359     }
360
361
362     /**
363      * Set the address on which we listen to for shutdown commands.
364      *
365      * @param address The new address
366      */

367     @Override
368     public void setAddress(String address) {
369         this.address = address;
370     }
371
372     /**
373      * Return the shutdown command string we are waiting for.
374      */

375     @Override
376     public String getShutdown() {
377         return this.shutdown;
378     }
379
380
381     /**
382      * Set the shutdown command we are waiting for.
383      *
384      * @param shutdown The new shutdown command
385      */

386     @Override
387     public void setShutdown(String shutdown) {
388         this.shutdown = shutdown;
389     }
390
391
392     /**
393      * Return the outer Catalina startup/shutdown component if present.
394      */

395     @Override
396     public Catalina getCatalina() {
397         return catalina;
398     }
399
400
401     /**
402      * Set the outer Catalina startup/shutdown component if present.
403      */

404     @Override
405     public void setCatalina(Catalina catalina) {
406         this.catalina = catalina;
407     }
408
409
410     @Override
411     public int getUtilityThreads() {
412         return utilityThreads;
413     }
414
415
416     /**
417      * Handles the special values.
418      */

419     private static int getUtilityThreadsInternal(int utilityThreads) {
420         int result = utilityThreads;
421         if (result <= 0) {
422             result = Runtime.getRuntime().availableProcessors() + result;
423             if (result < 2) {
424                 result = 2;
425             }
426         }
427         return result;
428     }
429
430
431     @Override
432     public void setUtilityThreads(int utilityThreads) {
433         // Use local copies to ensure thread safety
434         int oldUtilityThreads = this.utilityThreads;
435         if (getUtilityThreadsInternal(utilityThreads) < getUtilityThreadsInternal(oldUtilityThreads)) {
436             return;
437         }
438         this.utilityThreads = utilityThreads;
439         if (oldUtilityThreads != utilityThreads && utilityExecutor != null) {
440             reconfigureUtilityExecutor(getUtilityThreadsInternal(utilityThreads));
441         }
442     }
443
444
445     private synchronized void reconfigureUtilityExecutor(int threads) {
446         // The ScheduledThreadPoolExecutor doesn't use MaximumPoolSize, only CorePoolSize is available
447         if (utilityExecutor != null) {
448             utilityExecutor.setCorePoolSize(threads);
449         } else {
450             ScheduledThreadPoolExecutor scheduledThreadPoolExecutor =
451                     new ScheduledThreadPoolExecutor(threads,
452                             new TaskThreadFactory("Catalina-utility-", utilityThreadsAsDaemon, Thread.MIN_PRIORITY));
453             scheduledThreadPoolExecutor.setKeepAliveTime(10, TimeUnit.SECONDS);
454             scheduledThreadPoolExecutor.setRemoveOnCancelPolicy(true);
455             scheduledThreadPoolExecutor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
456             utilityExecutor = scheduledThreadPoolExecutor;
457             utilityExecutorWrapper = new org.apache.tomcat.util.threads.ScheduledThreadPoolExecutor(utilityExecutor);
458         }
459     }
460
461
462     /**
463      * Get if the utility threads are daemon threads.
464      * @return the threads daemon flag
465      */

466     public boolean getUtilityThreadsAsDaemon() {
467         return utilityThreadsAsDaemon;
468     }
469
470
471     /**
472      * Set the utility threads daemon flag. The default value is true.
473      * @param utilityThreadsAsDaemon the new thread daemon flag
474      */

475     public void setUtilityThreadsAsDaemon(boolean utilityThreadsAsDaemon) {
476         this.utilityThreadsAsDaemon = utilityThreadsAsDaemon;
477     }
478
479
480     /**
481      * @return The period between two lifecycle events, in seconds
482      */

483     public final int getPeriodicEventDelay() {
484         return periodicEventDelay;
485     }
486
487
488     /**
489      * Set the new period between two lifecycle events in seconds.
490      * @param periodicEventDelay The period in seconds, negative or zero will
491      *  disable events
492      */

493     public final void setPeriodicEventDelay(int periodicEventDelay) {
494         this.periodicEventDelay = periodicEventDelay;
495     }
496
497
498     // --------------------------------------------------------- Server Methods
499
500
501     /**
502      * Add a new Service to the set of defined Services.
503      *
504      * @param service The Service to be added
505      */

506     @Override
507     public void addService(Service service) {
508
509         service.setServer(this);
510
511         synchronized (servicesLock) {
512             Service results[] = new Service[services.length + 1];
513             System.arraycopy(services, 0, results, 0, services.length);
514             results[services.length] = service;
515             services = results;
516
517             if (getState().isAvailable()) {
518                 try {
519                     service.start();
520                 } catch (LifecycleException e) {
521                     // Ignore
522                 }
523             }
524
525             // Report this property change to interested listeners
526             support.firePropertyChange("service"null, service);
527         }
528
529     }
530
531     public void stopAwait() {
532         stopAwait=true;
533         Thread t = awaitThread;
534         if (t != null) {
535             ServerSocket s = awaitSocket;
536             if (s != null) {
537                 awaitSocket = null;
538                 try {
539                     s.close();
540                 } catch (IOException e) {
541                     // Ignored
542                 }
543             }
544             t.interrupt();
545             try {
546                 t.join(1000);
547             } catch (InterruptedException e) {
548                 // Ignored
549             }
550         }
551     }
552
553     /**
554      * Wait until a proper shutdown command is received, then return.
555      * This keeps the main thread alive - the thread pool listening for http
556      * connections is daemon threads.
557      */

558     @Override
559     public void await() {
560         // Negative values - don't wait on port - tomcat is embedded or we just don't like ports
561         if (getPortWithOffset() == -2) {
562             // undocumented yet - for embedding apps that are around, alive.
563             return;
564         }
565         if (getPortWithOffset() == -1) {
566             try {
567                 awaitThread = Thread.currentThread();
568                 while(!stopAwait) {
569                     try {
570                         Thread.sleep( 10000 );
571                     } catch( InterruptedException ex ) {
572                         // continue and check the flag
573                     }
574                 }
575             } finally {
576                 awaitThread = null;
577             }
578             return;
579         }
580
581         // Set up a server socket to wait on
582         try {
583             awaitSocket = new ServerSocket(getPortWithOffset(), 1,
584                     InetAddress.getByName(address));
585         } catch (IOException e) {
586             log.error(sm.getString("standardServer.awaitSocket.fail", address,
587                     String.valueOf(getPortWithOffset()), String.valueOf(getPort()),
588                     String.valueOf(getPortOffset())), e);
589             return;
590         }
591
592         try {
593             awaitThread = Thread.currentThread();
594
595             // Loop waiting for a connection and a valid command
596             while (!stopAwait) {
597                 ServerSocket serverSocket = awaitSocket;
598                 if (serverSocket == null) {
599                     break;
600                 }
601
602                 // Wait for the next connection
603                 Socket socket = null;
604                 StringBuilder command = new StringBuilder();
605                 try {
606                     InputStream stream;
607                     long acceptStartTime = System.currentTimeMillis();
608                     try {
609                         socket = serverSocket.accept();
610                         socket.setSoTimeout(10 * 1000);  // Ten seconds
611                         stream = socket.getInputStream();
612                     } catch (SocketTimeoutException ste) {
613                         // This should never happen but bug 56684 suggests that
614                         // it does.
615                         log.warn(sm.getString("standardServer.accept.timeout",
616                                 Long.valueOf(System.currentTimeMillis() - acceptStartTime)), ste);
617                         continue;
618                     } catch (AccessControlException ace) {
619                         log.warn(sm.getString("standardServer.accept.security"), ace);
620                         continue;
621                     } catch (IOException e) {
622                         if (stopAwait) {
623                             // Wait was aborted with socket.close()
624                             break;
625                         }
626                         log.error(sm.getString("standardServer.accept.error"), e);
627                         break;
628                     }
629
630                     // Read a set of characters from the socket
631                     int expected = 1024; // Cut off to avoid DoS attack
632                     while (expected < shutdown.length()) {
633                         if (random == null)
634                             random = new Random();
635                         expected += (random.nextInt() % 1024);
636                     }
637                     while (expected > 0) {
638                         int ch = -1;
639                         try {
640                             ch = stream.read();
641                         } catch (IOException e) {
642                             log.warn(sm.getString("standardServer.accept.readError"), e);
643                             ch = -1;
644                         }
645                         // Control character or EOF (-1) terminates loop
646                         if (ch < 32 || ch == 127) {
647                             break;
648                         }
649                         command.append((char) ch);
650                         expected--;
651                     }
652                 } finally {
653                     // Close the socket now that we are done with it
654                     try {
655                         if (socket != null) {
656                             socket.close();
657                         }
658                     } catch (IOException e) {
659                         // Ignore
660                     }
661                 }
662
663                 // Match against our command string
664                 boolean match = command.toString().equals(shutdown);
665                 if (match) {
666                     log.info(sm.getString("standardServer.shutdownViaPort"));
667                     break;
668                 } else
669                     log.warn(sm.getString("standardServer.invalidShutdownCommand", command.toString()));
670             }
671         } finally {
672             ServerSocket serverSocket = awaitSocket;
673             awaitThread = null;
674             awaitSocket = null;
675
676             // Close the server socket and return
677             if (serverSocket != null) {
678                 try {
679                     serverSocket.close();
680                 } catch (IOException e) {
681                     // Ignore
682                 }
683             }
684         }
685     }
686
687
688     /**
689      * @return the specified Service (if it exists); otherwise return
690      * <code>null</code>.
691      *
692      * @param name Name of the Service to be returned
693      */

694     @Override
695     public Service findService(String name) {
696         if (name == null) {
697             return null;
698         }
699         synchronized (servicesLock) {
700             for (int i = 0; i < services.length; i++) {
701                 if (name.equals(services[i].getName())) {
702                     return services[i];
703                 }
704             }
705         }
706         return null;
707     }
708
709
710     /**
711      * @return the set of Services defined within this Server.
712      */

713     @Override
714     public Service[] findServices() {
715         return services;
716     }
717
718     /**
719      * @return the JMX service names.
720      */

721     public ObjectName[] getServiceNames() {
722         ObjectName onames[]=new ObjectName[ services.length ];
723         forint i=0; i<services.length; i++ ) {
724             onames[i]=((StandardService)services[i]).getObjectName();
725         }
726         return onames;
727     }
728
729
730     /**
731      * Remove the specified Service from the set associated from this
732      * Server.
733      *
734      * @param service The Service to be removed
735      */

736     @Override
737     public void removeService(Service service) {
738
739         synchronized (servicesLock) {
740             int j = -1;
741             for (int i = 0; i < services.length; i++) {
742                 if (service == services[i]) {
743                     j = i;
744                     break;
745                 }
746             }
747             if (j < 0)
748                 return;
749             try {
750                 services[j].stop();
751             } catch (LifecycleException e) {
752                 // Ignore
753             }
754             int k = 0;
755             Service results[] = new Service[services.length - 1];
756             for (int i = 0; i < services.length; i++) {
757                 if (i != j)
758                     results[k++] = services[i];
759             }
760             services = results;
761
762             // Report this property change to interested listeners
763             support.firePropertyChange("service", service, null);
764         }
765
766     }
767
768
769     @Override
770     public File getCatalinaBase() {
771         if (catalinaBase != null) {
772             return catalinaBase;
773         }
774
775         catalinaBase = getCatalinaHome();
776         return catalinaBase;
777     }
778
779
780     @Override
781     public void setCatalinaBase(File catalinaBase) {
782         this.catalinaBase = catalinaBase;
783     }
784
785
786     @Override
787     public File getCatalinaHome() {
788         return catalinaHome;
789     }
790
791
792     @Override
793     public void setCatalinaHome(File catalinaHome) {
794         this.catalinaHome = catalinaHome;
795     }
796
797
798     // --------------------------------------------------------- Public Methods
799
800     /**
801      * Add a property change listener to this component.
802      *
803      * @param listener The listener to add
804      */

805     public void addPropertyChangeListener(PropertyChangeListener listener) {
806
807         support.addPropertyChangeListener(listener);
808
809     }
810
811
812     /**
813      * Remove a property change listener from this component.
814      *
815      * @param listener The listener to remove
816      */

817     public void removePropertyChangeListener(PropertyChangeListener listener) {
818
819         support.removePropertyChangeListener(listener);
820
821     }
822
823
824     /**
825      * Return a String representation of this component.
826      */

827     @Override
828     public String toString() {
829         StringBuilder sb = new StringBuilder("StandardServer[");
830         sb.append(getPort());
831         sb.append("]");
832         return sb.toString();
833     }
834
835
836     /**
837      * Write the configuration information for this entire <code>Server</code>
838      * out to the server.xml configuration file.
839      *
840      * @exception InstanceNotFoundException
841      *            if the managed resource object cannot be found
842      * @exception MBeanException
843      *            if the initializer of the object throws an exception, or
844      *            persistence is not supported
845      * @exception javax.management.RuntimeOperationsException
846      *            if an exception is reported by the persistence mechanism
847      */

848     public synchronized void storeConfig() throws InstanceNotFoundException, MBeanException {
849         try {
850             // Note: Hard-coded domain used since this object is per Server/JVM
851             ObjectName sname = new ObjectName("Catalina:type=StoreConfig");
852             MBeanServer server = Registry.getRegistry(nullnull).getMBeanServer();
853             if (server.isRegistered(sname)) {
854                 server.invoke(sname, "storeConfig"nullnull);
855             } else {
856                 log.error(sm.getString("standardServer.storeConfig.notAvailable", sname));
857             }
858         } catch (Throwable t) {
859             ExceptionUtils.handleThrowable(t);
860             log.error(sm.getString("standardServer.storeConfig.error"), t);
861         }
862     }
863
864
865     /**
866      * Write the configuration information for <code>Context</code>
867      * out to the specified configuration file.
868      *
869      * @param context the context which should save its configuration
870      * @exception InstanceNotFoundException
871      *            if the managed resource object cannot be found
872      * @exception MBeanException
873      *            if the initializer of the object throws an exception
874      *            or persistence is not supported
875      * @exception javax.management.RuntimeOperationsException
876      *            if an exception is reported by the persistence mechanism
877      */

878     public synchronized void storeContext(Context context) throws InstanceNotFoundException, MBeanException {
879         try {
880             // Note: Hard-coded domain used since this object is per Server/JVM
881             ObjectName sname = new ObjectName("Catalina:type=StoreConfig");
882             MBeanServer server = Registry.getRegistry(nullnull).getMBeanServer();
883             if (server.isRegistered(sname)) {
884                 server.invoke(sname, "store",
885                     new Object[] {context},
886                     new String [] { "java.lang.String"});
887             } else {
888                 log.error(sm.getString("standardServer.storeConfig.notAvailable", sname));
889             }
890         } catch (Throwable t) {
891             ExceptionUtils.handleThrowable(t);
892             log.error(sm.getString("standardServer.storeConfig.contextError", context.getName()), t);
893         }
894     }
895
896
897     /**
898      * @return <code>true</code> if naming should be used.
899      */

900     private boolean isUseNaming() {
901         boolean useNaming = true;
902         // Reading the "catalina.useNaming" environment variable
903         String useNamingProperty = System.getProperty("catalina.useNaming");
904         if ((useNamingProperty != null)
905             && (useNamingProperty.equals("false"))) {
906             useNaming = false;
907         }
908         return useNaming;
909     }
910
911
912     /**
913      * Start nested components ({@link Service}s) and implement the requirements
914      * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
915      *
916      * @exception LifecycleException if this component detects a fatal error
917      *  that prevents this component from being used
918      */

919     @Override
920     protected void startInternal() throws LifecycleException {
921
922         fireLifecycleEvent(CONFIGURE_START_EVENT, null);
923         setState(LifecycleState.STARTING);
924
925         globalNamingResources.start();
926
927         // Start our defined Services
928         synchronized (servicesLock) {
929             for (int i = 0; i < services.length; i++) {
930                 services[i].start();
931             }
932         }
933
934         if (periodicEventDelay > 0) {
935             monitorFuture = getUtilityExecutor().scheduleWithFixedDelay(
936                     new Runnable() {
937                         @Override
938                         public void run() {
939                             startPeriodicLifecycleEvent();
940                         }
941                     }, 0, 60, TimeUnit.SECONDS);
942         }
943     }
944
945
946     protected void startPeriodicLifecycleEvent() {
947         if (periodicLifecycleEventFuture == null || (periodicLifecycleEventFuture != null && periodicLifecycleEventFuture.isDone())) {
948             if (periodicLifecycleEventFuture != null && periodicLifecycleEventFuture.isDone()) {
949                 // There was an error executing the scheduled task, get it and log it
950                 try {
951                     periodicLifecycleEventFuture.get();
952                 } catch (InterruptedException | ExecutionException e) {
953                     log.error(sm.getString("standardServer.periodicEventError"), e);
954                 }
955             }
956             periodicLifecycleEventFuture = getUtilityExecutor().scheduleAtFixedRate(
957                     new Runnable() {
958                         @Override
959                         public void run() {
960                             fireLifecycleEvent(Lifecycle.PERIODIC_EVENT, null);
961                         }
962                     }, periodicEventDelay, periodicEventDelay, TimeUnit.SECONDS);
963         }
964     }
965
966
967     /**
968      * Stop nested components ({@link Service}s) and implement the requirements
969      * of {@link org.apache.catalina.util.LifecycleBase#stopInternal()}.
970      *
971      * @exception LifecycleException if this component detects a fatal error
972      *  that needs to be reported
973      */

974     @Override
975     protected void stopInternal() throws LifecycleException {
976
977         setState(LifecycleState.STOPPING);
978
979         if (monitorFuture != null) {
980             monitorFuture.cancel(true);
981             monitorFuture = null;
982         }
983         if (periodicLifecycleEventFuture != null) {
984             periodicLifecycleEventFuture.cancel(false);
985             periodicLifecycleEventFuture = null;
986         }
987
988         fireLifecycleEvent(CONFIGURE_STOP_EVENT, null);
989
990         // Stop our defined Services
991         for (int i = 0; i < services.length; i++) {
992             services[i].stop();
993         }
994
995         globalNamingResources.stop();
996
997         stopAwait();
998     }
999
1000     /**
1001      * Invoke a pre-startup initialization. This is used to allow connectors
1002      * to bind to restricted ports under Unix operating environments.
1003      */

1004     @Override
1005     protected void initInternal() throws LifecycleException {
1006
1007         super.initInternal();
1008
1009         // Initialize utility executor
1010         reconfigureUtilityExecutor(getUtilityThreadsInternal(utilityThreads));
1011         register(utilityExecutor, "type=UtilityExecutor");
1012
1013         // Register global String cache
1014         // Note although the cache is global, if there are multiple Servers
1015         // present in the JVM (may happen when embedding) then the same cache
1016         // will be registered under multiple names
1017         onameStringCache = register(new StringCache(), "type=StringCache");
1018
1019         // Register the MBeanFactory
1020         MBeanFactory factory = new MBeanFactory();
1021         factory.setContainer(this);
1022         onameMBeanFactory = register(factory, "type=MBeanFactory");
1023
1024         // Register the naming resources
1025         globalNamingResources.init();
1026
1027         // Populate the extension validator with JARs from common and shared
1028         // class loaders
1029         if (getCatalina() != null) {
1030             ClassLoader cl = getCatalina().getParentClassLoader();
1031             // Walk the class loader hierarchy. Stop at the system class loader.
1032             // This will add the shared (if present) and common class loaders
1033             while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
1034                 if (cl instanceof URLClassLoader) {
1035                     URL[] urls = ((URLClassLoader) cl).getURLs();
1036                     for (URL url : urls) {
1037                         if (url.getProtocol().equals("file")) {
1038                             try {
1039                                 File f = new File (url.toURI());
1040                                 if (f.isFile() &&
1041                                         f.getName().endsWith(".jar")) {
1042                                     ExtensionValidator.addSystemResource(f);
1043                                 }
1044                             } catch (URISyntaxException e) {
1045                                 // Ignore
1046                             } catch (IOException e) {
1047                                 // Ignore
1048                             }
1049                         }
1050                     }
1051                 }
1052                 cl = cl.getParent();
1053             }
1054         }
1055         // Initialize our defined Services
1056         for (int i = 0; i < services.length; i++) {
1057             services[i].init();
1058         }
1059     }
1060
1061     @Override
1062     protected void destroyInternal() throws LifecycleException {
1063         // Destroy our defined Services
1064         for (int i = 0; i < services.length; i++) {
1065             services[i].destroy();
1066         }
1067
1068         globalNamingResources.destroy();
1069
1070         unregister(onameMBeanFactory);
1071
1072         unregister(onameStringCache);
1073
1074         if (utilityExecutor != null) {
1075             utilityExecutor.shutdownNow();
1076             unregister("type=UtilityExecutor");
1077             utilityExecutor = null;
1078         }
1079
1080         super.destroyInternal();
1081     }
1082
1083     /**
1084      * Return the parent class loader for this component.
1085      */

1086     @Override
1087     public ClassLoader getParentClassLoader() {
1088         if (parentClassLoader != null)
1089             return parentClassLoader;
1090         if (catalina != null) {
1091             return catalina.getParentClassLoader();
1092         }
1093         return ClassLoader.getSystemClassLoader();
1094     }
1095
1096     /**
1097      * Set the parent class loader for this server.
1098      *
1099      * @param parent The new parent class loader
1100      */

1101     @Override
1102     public void setParentClassLoader(ClassLoader parent) {
1103         ClassLoader oldParentClassLoader = this.parentClassLoader;
1104         this.parentClassLoader = parent;
1105         support.firePropertyChange("parentClassLoader", oldParentClassLoader,
1106                                    this.parentClassLoader);
1107     }
1108
1109
1110     private ObjectName onameStringCache;
1111     private ObjectName onameMBeanFactory;
1112
1113     /**
1114      * Obtain the MBean domain for this server. The domain is obtained using
1115      * the following search order:
1116      * <ol>
1117      * <li>Name of first {@link org.apache.catalina.Engine}.</li>
1118      * <li>Name of first {@link Service}.</li>
1119      * </ol>
1120      */

1121     @Override
1122     protected String getDomainInternal() {
1123
1124         String domain = null;
1125
1126         Service[] services = findServices();
1127         if (services.length > 0) {
1128             Service service = services[0];
1129             if (service != null) {
1130                 domain = service.getDomain();
1131             }
1132         }
1133         return domain;
1134     }
1135
1136
1137     @Override
1138     protected final String getObjectNameKeyProperties() {
1139         return "type=Server";
1140     }
1141
1142     @Override
1143     public ScheduledExecutorService getUtilityExecutor() {
1144         return utilityExecutorWrapper;
1145     }
1146 }
1147