001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.catalina.core;
019:
020: import java.beans.PropertyChangeListener;
021: import java.beans.PropertyChangeSupport;
022: import javax.management.MBeanRegistration;
023: import javax.management.MBeanServer;
024: import javax.management.ObjectName;
025: import org.apache.catalina.Container;
026: import org.apache.catalina.Engine;
027: import org.apache.catalina.Lifecycle;
028: import org.apache.catalina.LifecycleException;
029: import org.apache.catalina.LifecycleListener;
030: import org.apache.catalina.Server;
031: import org.apache.catalina.Service;
032: import org.apache.catalina.ServerFactory;
033: import org.apache.catalina.connector.Connector;
034: import org.apache.catalina.util.LifecycleSupport;
035: import org.apache.catalina.util.StringManager;
036: import org.apache.juli.logging.Log;
037: import org.apache.juli.logging.LogFactory;
038: import org.apache.tomcat.util.modeler.Registry;
039: import java.util.ArrayList;
040: import org.apache.catalina.Executor;
041:
042: /**
043: * Standard implementation of the <code>Service</code> interface. The
044: * associated Container is generally an instance of Engine, but this is
045: * not required.
046: *
047: * @author Craig R. McClanahan
048: */
049:
050: public class StandardService implements Lifecycle, Service,
051: MBeanRegistration {
052: private static Log log = LogFactory.getLog(StandardService.class);
053:
054: // ----------------------------------------------------- Instance Variables
055:
056: /**
057: * Descriptive information about this component implementation.
058: */
059: private static final String info = "org.apache.catalina.core.StandardService/1.0";
060:
061: /**
062: * The name of this service.
063: */
064: private String name = null;
065:
066: /**
067: * The lifecycle event support for this component.
068: */
069: private LifecycleSupport lifecycle = new LifecycleSupport(this );
070:
071: /**
072: * The string manager for this package.
073: */
074: private static final StringManager sm = StringManager
075: .getManager(Constants.Package);
076:
077: /**
078: * The <code>Server</code> that owns this Service, if any.
079: */
080: private Server server = null;
081:
082: /**
083: * Has this component been started?
084: */
085: private boolean started = false;
086:
087: /**
088: * The property change support for this component.
089: */
090: protected PropertyChangeSupport support = new PropertyChangeSupport(
091: this );
092:
093: /**
094: * The set of Connectors associated with this Service.
095: */
096: protected Connector connectors[] = new Connector[0];
097:
098: /**
099: *
100: */
101: protected ArrayList<Executor> executors = new ArrayList<Executor>();
102:
103: /**
104: * The Container associated with this Service. (In the case of the
105: * org.apache.catalina.startup.Embedded subclass, this holds the most
106: * recently added Engine.)
107: */
108: protected Container container = null;
109:
110: /**
111: * Has this component been initialized?
112: */
113: protected boolean initialized = false;
114:
115: // ------------------------------------------------------------- Properties
116:
117: /**
118: * Return the <code>Container</code> that handles requests for all
119: * <code>Connectors</code> associated with this Service.
120: */
121: public Container getContainer() {
122:
123: return (this .container);
124:
125: }
126:
127: /**
128: * Set the <code>Container</code> that handles requests for all
129: * <code>Connectors</code> associated with this Service.
130: *
131: * @param container The new Container
132: */
133: public void setContainer(Container container) {
134:
135: Container oldContainer = this .container;
136: if ((oldContainer != null) && (oldContainer instanceof Engine))
137: ((Engine) oldContainer).setService(null);
138: this .container = container;
139: if ((this .container != null)
140: && (this .container instanceof Engine))
141: ((Engine) this .container).setService(this );
142: if (started && (this .container != null)
143: && (this .container instanceof Lifecycle)) {
144: try {
145: ((Lifecycle) this .container).start();
146: } catch (LifecycleException e) {
147: ;
148: }
149: }
150: synchronized (connectors) {
151: for (int i = 0; i < connectors.length; i++)
152: connectors[i].setContainer(this .container);
153: }
154: if (started && (oldContainer != null)
155: && (oldContainer instanceof Lifecycle)) {
156: try {
157: ((Lifecycle) oldContainer).stop();
158: } catch (LifecycleException e) {
159: ;
160: }
161: }
162:
163: // Report this property change to interested listeners
164: support.firePropertyChange("container", oldContainer,
165: this .container);
166:
167: }
168:
169: public ObjectName getContainerName() {
170: if (container instanceof ContainerBase) {
171: return ((ContainerBase) container).getJmxName();
172: }
173: return null;
174: }
175:
176: /**
177: * Return descriptive information about this Service implementation and
178: * the corresponding version number, in the format
179: * <code><description>/<version></code>.
180: */
181: public String getInfo() {
182:
183: return (info);
184:
185: }
186:
187: /**
188: * Return the name of this Service.
189: */
190: public String getName() {
191:
192: return (this .name);
193:
194: }
195:
196: /**
197: * Set the name of this Service.
198: *
199: * @param name The new service name
200: */
201: public void setName(String name) {
202:
203: this .name = name;
204:
205: }
206:
207: /**
208: * Return the <code>Server</code> with which we are associated (if any).
209: */
210: public Server getServer() {
211:
212: return (this .server);
213:
214: }
215:
216: /**
217: * Set the <code>Server</code> with which we are associated (if any).
218: *
219: * @param server The server that owns this Service
220: */
221: public void setServer(Server server) {
222:
223: this .server = server;
224:
225: }
226:
227: // --------------------------------------------------------- Public Methods
228:
229: /**
230: * Add a new Connector to the set of defined Connectors, and associate it
231: * with this Service's Container.
232: *
233: * @param connector The Connector to be added
234: */
235: public void addConnector(Connector connector) {
236:
237: synchronized (connectors) {
238: connector.setContainer(this .container);
239: connector.setService(this );
240: Connector results[] = new Connector[connectors.length + 1];
241: System.arraycopy(connectors, 0, results, 0,
242: connectors.length);
243: results[connectors.length] = connector;
244: connectors = results;
245:
246: if (initialized) {
247: try {
248: connector.initialize();
249: } catch (LifecycleException e) {
250: log.error("Connector.initialize", e);
251: }
252: }
253:
254: if (started && (connector instanceof Lifecycle)) {
255: try {
256: ((Lifecycle) connector).start();
257: } catch (LifecycleException e) {
258: log.error("Connector.start", e);
259: }
260: }
261:
262: // Report this property change to interested listeners
263: support.firePropertyChange("connector", null, connector);
264: }
265:
266: }
267:
268: public ObjectName[] getConnectorNames() {
269: ObjectName results[] = new ObjectName[connectors.length];
270: for (int i = 0; i < results.length; i++) {
271: results[i] = connectors[i].getObjectName();
272: }
273: return results;
274: }
275:
276: /**
277: * Add a property change listener to this component.
278: *
279: * @param listener The listener to add
280: */
281: public void addPropertyChangeListener(
282: PropertyChangeListener listener) {
283:
284: support.addPropertyChangeListener(listener);
285:
286: }
287:
288: /**
289: * Find and return the set of Connectors associated with this Service.
290: */
291: public Connector[] findConnectors() {
292:
293: return (connectors);
294:
295: }
296:
297: /**
298: * Remove the specified Connector from the set associated from this
299: * Service. The removed Connector will also be disassociated from our
300: * Container.
301: *
302: * @param connector The Connector to be removed
303: */
304: public void removeConnector(Connector connector) {
305:
306: synchronized (connectors) {
307: int j = -1;
308: for (int i = 0; i < connectors.length; i++) {
309: if (connector == connectors[i]) {
310: j = i;
311: break;
312: }
313: }
314: if (j < 0)
315: return;
316: if (started && (connectors[j] instanceof Lifecycle)) {
317: try {
318: ((Lifecycle) connectors[j]).stop();
319: } catch (LifecycleException e) {
320: log.error("Connector.stop", e);
321: }
322: }
323: connectors[j].setContainer(null);
324: connector.setService(null);
325: int k = 0;
326: Connector results[] = new Connector[connectors.length - 1];
327: for (int i = 0; i < connectors.length; i++) {
328: if (i != j)
329: results[k++] = connectors[i];
330: }
331: connectors = results;
332:
333: // Report this property change to interested listeners
334: support.firePropertyChange("connector", connector, null);
335: }
336:
337: }
338:
339: /**
340: * Remove a property change listener from this component.
341: *
342: * @param listener The listener to remove
343: */
344: public void removePropertyChangeListener(
345: PropertyChangeListener listener) {
346:
347: support.removePropertyChangeListener(listener);
348:
349: }
350:
351: /**
352: * Return a String representation of this component.
353: */
354: public String toString() {
355:
356: StringBuffer sb = new StringBuffer("StandardService[");
357: sb.append(getName());
358: sb.append("]");
359: return (sb.toString());
360:
361: }
362:
363: // ------------------------------------------------------ Lifecycle Methods
364:
365: /**
366: * Add a LifecycleEvent listener to this component.
367: *
368: * @param listener The listener to add
369: */
370: public void addLifecycleListener(LifecycleListener listener) {
371:
372: lifecycle.addLifecycleListener(listener);
373:
374: }
375:
376: /**
377: * Get the lifecycle listeners associated with this lifecycle. If this
378: * Lifecycle has no listeners registered, a zero-length array is returned.
379: */
380: public LifecycleListener[] findLifecycleListeners() {
381:
382: return lifecycle.findLifecycleListeners();
383:
384: }
385:
386: /**
387: * Remove a LifecycleEvent listener from this component.
388: *
389: * @param listener The listener to remove
390: */
391: public void removeLifecycleListener(LifecycleListener listener) {
392:
393: lifecycle.removeLifecycleListener(listener);
394:
395: }
396:
397: /**
398: * Adds a named executor to the service
399: * @param ex Executor
400: */
401: public void addExecutor(Executor ex) {
402: synchronized (executors) {
403: if (!executors.contains(ex)) {
404: executors.add(ex);
405: if (started)
406: try {
407: ex.start();
408: } catch (LifecycleException x) {
409: log.error("Executor.start", x);
410: }
411: }
412: }
413: }
414:
415: /**
416: * Retrieves all executors
417: * @return Executor[]
418: */
419: public Executor[] findExecutors() {
420: synchronized (executors) {
421: Executor[] arr = new Executor[executors.size()];
422: executors.toArray(arr);
423: return arr;
424: }
425: }
426:
427: /**
428: * Retrieves executor by name, null if not found
429: * @param name String
430: * @return Executor
431: */
432: public Executor getExecutor(String name) {
433: synchronized (executors) {
434: for (int i = 0; i < executors.size(); i++) {
435: if (name.equals(executors.get(i).getName()))
436: return executors.get(i);
437: }
438: }
439: return null;
440: }
441:
442: /**
443: * Removes an executor from the service
444: * @param ex Executor
445: */
446: public void removeExecutor(Executor ex) {
447: synchronized (executors) {
448: if (executors.remove(ex) && started) {
449: try {
450: ex.stop();
451: } catch (LifecycleException e) {
452: log.error("Executor.stop", e);
453: }
454: }
455: }
456: }
457:
458: /**
459: * Prepare for the beginning of active use of the public methods of this
460: * component. This method should be called before any of the public
461: * methods of this component are utilized. It should also send a
462: * LifecycleEvent of type START_EVENT to any registered listeners.
463: *
464: * @exception LifecycleException if this component detects a fatal error
465: * that prevents this component from being used
466: */
467: public void start() throws LifecycleException {
468:
469: // Validate and update our current component state
470: if (log.isInfoEnabled() && started) {
471: log.info(sm.getString("standardService.start.started"));
472: }
473:
474: if (!initialized)
475: init();
476:
477: // Notify our interested LifecycleListeners
478: lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
479: if (log.isInfoEnabled())
480: log.info(sm.getString("standardService.start.name",
481: this .name));
482: lifecycle.fireLifecycleEvent(START_EVENT, null);
483: started = true;
484:
485: // Start our defined Container first
486: if (container != null) {
487: synchronized (container) {
488: if (container instanceof Lifecycle) {
489: ((Lifecycle) container).start();
490: }
491: }
492: }
493:
494: synchronized (executors) {
495: for (int i = 0; i < executors.size(); i++) {
496: executors.get(i).start();
497: }
498: }
499:
500: // Start our defined Connectors second
501: synchronized (connectors) {
502: for (int i = 0; i < connectors.length; i++) {
503: if (connectors[i] instanceof Lifecycle)
504: ((Lifecycle) connectors[i]).start();
505: }
506: }
507:
508: // Notify our interested LifecycleListeners
509: lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
510:
511: }
512:
513: /**
514: * Gracefully terminate the active use of the public methods of this
515: * component. This method should be the last one called on a given
516: * instance of this component. It should also send a LifecycleEvent
517: * of type STOP_EVENT to any registered listeners.
518: *
519: * @exception LifecycleException if this component detects a fatal error
520: * that needs to be reported
521: */
522: public void stop() throws LifecycleException {
523:
524: // Validate and update our current component state
525: if (!started) {
526: return;
527: }
528:
529: // Notify our interested LifecycleListeners
530: lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
531:
532: // Stop our defined Connectors first
533: synchronized (connectors) {
534: for (int i = 0; i < connectors.length; i++) {
535: connectors[i].pause();
536: }
537: }
538:
539: // Heuristic: Sleep for a while to ensure pause of the connector
540: try {
541: Thread.sleep(1000);
542: } catch (InterruptedException e) {
543: // Ignore
544: }
545:
546: lifecycle.fireLifecycleEvent(STOP_EVENT, null);
547: if (log.isInfoEnabled())
548: log.info(sm.getString("standardService.stop.name",
549: this .name));
550: started = false;
551:
552: // Stop our defined Container second
553: if (container != null) {
554: synchronized (container) {
555: if (container instanceof Lifecycle) {
556: ((Lifecycle) container).stop();
557: }
558: }
559: }
560: // FIXME pero -- Why container stop first? KeepAlive connetions can send request!
561: // Stop our defined Connectors first
562: synchronized (connectors) {
563: for (int i = 0; i < connectors.length; i++) {
564: if (connectors[i] instanceof Lifecycle)
565: ((Lifecycle) connectors[i]).stop();
566: }
567: }
568:
569: synchronized (executors) {
570: for (int i = 0; i < executors.size(); i++) {
571: executors.get(i).stop();
572: }
573: }
574:
575: if (oname == controller) {
576: // we registered ourself on init().
577: // That should be the typical case - this object is just for
578: // backward compat, nobody should bother to load it explicitely
579: Registry.getRegistry(null, null).unregisterComponent(oname);
580: Executor[] executors = findExecutors();
581: for (int i = 0; i < executors.length; i++) {
582: try {
583: ObjectName executorObjectName = new ObjectName(
584: domain + ":type=Executor,name="
585: + executors[i].getName());
586: Registry.getRegistry(null, null)
587: .unregisterComponent(executorObjectName);
588: } catch (Exception e) {
589: // Ignore (invalid ON, which cannot happen)
590: }
591: }
592: }
593:
594: // Notify our interested LifecycleListeners
595: lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
596:
597: }
598:
599: /**
600: * Invoke a pre-startup initialization. This is used to allow connectors
601: * to bind to restricted ports under Unix operating environments.
602: */
603: public void initialize() throws LifecycleException {
604: // Service shouldn't be used with embeded, so it doesn't matter
605: if (initialized) {
606: if (log.isInfoEnabled())
607: log
608: .info(sm
609: .getString("standardService.initialize.initialized"));
610: return;
611: }
612: initialized = true;
613:
614: if (oname == null) {
615: try {
616: // Hack - Server should be deprecated...
617: Container engine = this .getContainer();
618: domain = engine.getName();
619: oname = new ObjectName(domain
620: + ":type=Service,serviceName=" + name);
621: this .controller = oname;
622: Registry.getRegistry(null, null).registerComponent(
623: this , oname, null);
624:
625: Executor[] executors = findExecutors();
626: for (int i = 0; i < executors.length; i++) {
627: ObjectName executorObjectName = new ObjectName(
628: domain + ":type=Executor,name="
629: + executors[i].getName());
630: Registry.getRegistry(null, null).registerComponent(
631: executors[i], executorObjectName, null);
632: }
633:
634: } catch (Exception e) {
635: log.error(sm.getString(
636: "standardService.register.failed", domain), e);
637: }
638:
639: }
640: if (server == null) {
641: // Register with the server
642: // HACK: ServerFactory should be removed...
643:
644: ServerFactory.getServer().addService(this );
645: }
646:
647: // Initialize our defined Connectors
648: synchronized (connectors) {
649: for (int i = 0; i < connectors.length; i++) {
650: connectors[i].initialize();
651: }
652: }
653: }
654:
655: public void destroy() throws LifecycleException {
656: if (started)
657: stop();
658: // FIXME unregister should be here probably -- stop doing that ?
659: }
660:
661: public void init() {
662: try {
663: initialize();
664: } catch (Throwable t) {
665: log.error(sm.getString("standardService.initialize.failed",
666: domain), t);
667: }
668: }
669:
670: protected String type;
671: protected String domain;
672: protected String suffix;
673: protected ObjectName oname;
674: protected ObjectName controller;
675: protected MBeanServer mserver;
676:
677: public ObjectName getObjectName() {
678: return oname;
679: }
680:
681: public String getDomain() {
682: return domain;
683: }
684:
685: public ObjectName preRegister(MBeanServer server, ObjectName name)
686: throws Exception {
687: oname = name;
688: mserver = server;
689: domain = name.getDomain();
690: return name;
691: }
692:
693: public void postRegister(Boolean registrationDone) {
694: }
695:
696: public void preDeregister() throws Exception {
697: }
698:
699: public void postDeregister() {
700: }
701:
702: }
|