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