1
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
71 public final class StandardServer extends LifecycleMBeanBase implements Server {
72
73 private static final Log log = LogFactory.getLog(StandardServer.class);
74
75
76
77
78
79
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
100
101
102
105 private javax.naming.Context globalNamingContext = null;
106
107
108
111 private NamingResourcesImpl globalNamingResources = null;
112
113
114
117 private final NamingContextListener namingContextListener;
118
119
120
123 private int port = 8005;
124
125 private int portOffset = 0;
126
127
130 private String address = "localhost";
131
132
133
137 private Random random = null;
138
139
140
143 private Service services[] = new Service[0];
144 private final Object servicesLock = new Object();
145
146
147
150 private String shutdown = "SHUTDOWN";
151
152
153
156 private static final StringManager sm =
157 StringManager.getManager(Constants.Package);
158
159
160
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
174 private volatile Thread awaitThread = null;
175
176
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
190 protected int utilityThreads = 2;
191
192
195 protected boolean utilityThreadsAsDaemon = false;
196
197
200 private ScheduledThreadPoolExecutor utilityExecutor = null;
201
202
205 private ScheduledExecutorService utilityExecutorWrapper = null;
206
207
208
211 private ScheduledFuture<?> periodicLifecycleEventFuture = null;
212 private ScheduledFuture<?> monitorFuture;
213
214
215
218 protected int periodicEventDelay = 10;
219
220
221
222
223 @Override
224 public Object getNamingToken() {
225 return namingToken;
226 }
227
228
229
232 @Override
233 public javax.naming.Context getGlobalNamingContext() {
234 return this.globalNamingContext;
235 }
236
237
238
243 public void setGlobalNamingContext(javax.naming.Context globalNamingContext) {
244 this.globalNamingContext = globalNamingContext;
245 }
246
247
248
251 @Override
252 public NamingResourcesImpl getGlobalNamingResources() {
253 return this.globalNamingResources;
254 }
255
256
257
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
281 public String getServerInfo() {
282 return ServerInfo.getServerInfo();
283 }
284
285
286
290 public String getServerBuilt() {
291 return ServerInfo.getServerBuilt();
292 }
293
294
295
299 public String getServerNumber() {
300 return ServerInfo.getServerNumber();
301 }
302
303
304
307 @Override
308 public int getPort() {
309 return this.port;
310 }
311
312
313
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
343
344 int port = getPort();
345 if (port > 0) {
346 return port + getPortOffset();
347 } else {
348 return port;
349 }
350 }
351
352
353
356 @Override
357 public String getAddress() {
358 return this.address;
359 }
360
361
362
367 @Override
368 public void setAddress(String address) {
369 this.address = address;
370 }
371
372
375 @Override
376 public String getShutdown() {
377 return this.shutdown;
378 }
379
380
381
386 @Override
387 public void setShutdown(String shutdown) {
388 this.shutdown = shutdown;
389 }
390
391
392
395 @Override
396 public Catalina getCatalina() {
397 return catalina;
398 }
399
400
401
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
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
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
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
466 public boolean getUtilityThreadsAsDaemon() {
467 return utilityThreadsAsDaemon;
468 }
469
470
471
475 public void setUtilityThreadsAsDaemon(boolean utilityThreadsAsDaemon) {
476 this.utilityThreadsAsDaemon = utilityThreadsAsDaemon;
477 }
478
479
480
483 public final int getPeriodicEventDelay() {
484 return periodicEventDelay;
485 }
486
487
488
493 public final void setPeriodicEventDelay(int periodicEventDelay) {
494 this.periodicEventDelay = periodicEventDelay;
495 }
496
497
498
499
500
501
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
522 }
523 }
524
525
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
542 }
543 }
544 t.interrupt();
545 try {
546 t.join(1000);
547 } catch (InterruptedException e) {
548
549 }
550 }
551 }
552
553
558 @Override
559 public void await() {
560
561 if (getPortWithOffset() == -2) {
562
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
573 }
574 }
575 } finally {
576 awaitThread = null;
577 }
578 return;
579 }
580
581
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
596 while (!stopAwait) {
597 ServerSocket serverSocket = awaitSocket;
598 if (serverSocket == null) {
599 break;
600 }
601
602
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);
611 stream = socket.getInputStream();
612 } catch (SocketTimeoutException ste) {
613
614
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
624 break;
625 }
626 log.error(sm.getString("standardServer.accept.error"), e);
627 break;
628 }
629
630
631 int expected = 1024;
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
646 if (ch < 32 || ch == 127) {
647 break;
648 }
649 command.append((char) ch);
650 expected--;
651 }
652 } finally {
653
654 try {
655 if (socket != null) {
656 socket.close();
657 }
658 } catch (IOException e) {
659
660 }
661 }
662
663
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
677 if (serverSocket != null) {
678 try {
679 serverSocket.close();
680 } catch (IOException e) {
681
682 }
683 }
684 }
685 }
686
687
688
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
713 @Override
714 public Service[] findServices() {
715 return services;
716 }
717
718
721 public ObjectName[] getServiceNames() {
722 ObjectName onames[]=new ObjectName[ services.length ];
723 for( int i=0; i<services.length; i++ ) {
724 onames[i]=((StandardService)services[i]).getObjectName();
725 }
726 return onames;
727 }
728
729
730
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
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
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
799
800
805 public void addPropertyChangeListener(PropertyChangeListener listener) {
806
807 support.addPropertyChangeListener(listener);
808
809 }
810
811
812
817 public void removePropertyChangeListener(PropertyChangeListener listener) {
818
819 support.removePropertyChangeListener(listener);
820
821 }
822
823
824
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
848 public synchronized void storeConfig() throws InstanceNotFoundException, MBeanException {
849 try {
850
851 ObjectName sname = new ObjectName("Catalina:type=StoreConfig");
852 MBeanServer server = Registry.getRegistry(null, null).getMBeanServer();
853 if (server.isRegistered(sname)) {
854 server.invoke(sname, "storeConfig", null, null);
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
878 public synchronized void storeContext(Context context) throws InstanceNotFoundException, MBeanException {
879 try {
880
881 ObjectName sname = new ObjectName("Catalina:type=StoreConfig");
882 MBeanServer server = Registry.getRegistry(null, null).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
900 private boolean isUseNaming() {
901 boolean useNaming = true;
902
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
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
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
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
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
991 for (int i = 0; i < services.length; i++) {
992 services[i].stop();
993 }
994
995 globalNamingResources.stop();
996
997 stopAwait();
998 }
999
1000
1004 @Override
1005 protected void initInternal() throws LifecycleException {
1006
1007 super.initInternal();
1008
1009
1010 reconfigureUtilityExecutor(getUtilityThreadsInternal(utilityThreads));
1011 register(utilityExecutor, "type=UtilityExecutor");
1012
1013
1014
1015
1016
1017 onameStringCache = register(new StringCache(), "type=StringCache");
1018
1019
1020 MBeanFactory factory = new MBeanFactory();
1021 factory.setContainer(this);
1022 onameMBeanFactory = register(factory, "type=MBeanFactory");
1023
1024
1025 globalNamingResources.init();
1026
1027
1028
1029 if (getCatalina() != null) {
1030 ClassLoader cl = getCatalina().getParentClassLoader();
1031
1032
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
1046 } catch (IOException e) {
1047
1048 }
1049 }
1050 }
1051 }
1052 cl = cl.getParent();
1053 }
1054 }
1055
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
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
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
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
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