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
18
19 package org.apache.catalina.core;
20
21
22 import java.beans.PropertyChangeListener;
23 import java.beans.PropertyChangeSupport;
24 import javax.management.MBeanRegistration;
25 import javax.management.MBeanServer;
26 import javax.management.ObjectName;
27 import org.apache.catalina.Container;
28 import org.apache.catalina.Engine;
29 import org.apache.catalina.Lifecycle;
30 import org.apache.catalina.LifecycleException;
31 import org.apache.catalina.LifecycleListener;
32 import org.apache.catalina.Server;
33 import org.apache.catalina.Service;
34 import org.apache.catalina.ServerFactory;
35 import org.apache.catalina.connector.Connector;
36 import org.apache.catalina.util.LifecycleSupport;
37 import org.apache.catalina.util.StringManager;
38 import org.apache.juli.logging.Log;
39 import org.apache.juli.logging.LogFactory;
40 import org.apache.tomcat.util.modeler.Registry;
41 import java.util.ArrayList;
42 import org.apache.catalina.Executor;
43
44
45 /**
46 * Standard implementation of the <code>Service</code> interface. The
47 * associated Container is generally an instance of Engine, but this is
48 * not required.
49 *
50 * @author Craig R. McClanahan
51 */
52
53 public class StandardService
54 implements Lifecycle, Service, MBeanRegistration
55 {
56 private static Log log = LogFactory.getLog(StandardService.class);
57
58
59 // ----------------------------------------------------- Instance Variables
60
61
62 /**
63 * Descriptive information about this component implementation.
64 */
65 private static final String info =
66 "org.apache.catalina.core.StandardService/1.0";
67
68
69 /**
70 * The name of this service.
71 */
72 private String name = null;
73
74
75 /**
76 * The lifecycle event support for this component.
77 */
78 private LifecycleSupport lifecycle = new LifecycleSupport(this);
79
80
81 /**
82 * The string manager for this package.
83 */
84 private static final StringManager sm =
85 StringManager.getManager(Constants.Package);
86
87 /**
88 * The <code>Server</code> that owns this Service, if any.
89 */
90 private Server server = null;
91
92 /**
93 * Has this component been started?
94 */
95 private boolean started = false;
96
97
98 /**
99 * The property change support for this component.
100 */
101 protected PropertyChangeSupport support = new PropertyChangeSupport(this);
102
103
104 /**
105 * The set of Connectors associated with this Service.
106 */
107 protected Connector connectors[] = new Connector[0];
108
109 /**
110 *
111 */
112 protected ArrayList<Executor> executors = new ArrayList<Executor>();
113
114 /**
115 * The Container associated with this Service. (In the case of the
116 * org.apache.catalina.startup.Embedded subclass, this holds the most
117 * recently added Engine.)
118 */
119 protected Container container = null;
120
121
122 /**
123 * Has this component been initialized?
124 */
125 protected boolean initialized = false;
126
127
128 // ------------------------------------------------------------- Properties
129
130
131 /**
132 * Return the <code>Container</code> that handles requests for all
133 * <code>Connectors</code> associated with this Service.
134 */
135 public Container getContainer() {
136
137 return (this.container);
138
139 }
140
141
142 /**
143 * Set the <code>Container</code> that handles requests for all
144 * <code>Connectors</code> associated with this Service.
145 *
146 * @param container The new Container
147 */
148 public void setContainer(Container container) {
149
150 Container oldContainer = this.container;
151 if ((oldContainer != null) && (oldContainer instanceof Engine))
152 ((Engine) oldContainer).setService(null);
153 this.container = container;
154 if ((this.container != null) && (this.container instanceof Engine))
155 ((Engine) this.container).setService(this);
156 if (started && (this.container != null) &&
157 (this.container instanceof Lifecycle)) {
158 try {
159 ((Lifecycle) this.container).start();
160 } catch (LifecycleException e) {
161 ;
162 }
163 }
164 synchronized (connectors) {
165 for (int i = 0; i < connectors.length; i++)
166 connectors[i].setContainer(this.container);
167 }
168 if (started && (oldContainer != null) &&
169 (oldContainer instanceof Lifecycle)) {
170 try {
171 ((Lifecycle) oldContainer).stop();
172 } catch (LifecycleException e) {
173 ;
174 }
175 }
176
177 // Report this property change to interested listeners
178 support.firePropertyChange("container", oldContainer, this.container);
179
180 }
181
182 public ObjectName getContainerName() {
183 if( container instanceof ContainerBase ) {
184 return ((ContainerBase)container).getJmxName();
185 }
186 return null;
187 }
188
189
190 /**
191 * Return descriptive information about this Service implementation and
192 * the corresponding version number, in the format
193 * <code><description>/<version></code>.
194 */
195 public String getInfo() {
196
197 return (info);
198
199 }
200
201
202 /**
203 * Return the name of this Service.
204 */
205 public String getName() {
206
207 return (this.name);
208
209 }
210
211
212 /**
213 * Set the name of this Service.
214 *
215 * @param name The new service name
216 */
217 public void setName(String name) {
218
219 this.name = name;
220
221 }
222
223
224 /**
225 * Return the <code>Server</code> with which we are associated (if any).
226 */
227 public Server getServer() {
228
229 return (this.server);
230
231 }
232
233
234 /**
235 * Set the <code>Server</code> with which we are associated (if any).
236 *
237 * @param server The server that owns this Service
238 */
239 public void setServer(Server server) {
240
241 this.server = server;
242
243 }
244
245
246 // --------------------------------------------------------- Public Methods
247
248
249 /**
250 * Add a new Connector to the set of defined Connectors, and associate it
251 * with this Service's Container.
252 *
253 * @param connector The Connector to be added
254 */
255 public void addConnector(Connector connector) {
256
257 synchronized (connectors) {
258 connector.setContainer(this.container);
259 connector.setService(this);
260 Connector results[] = new Connector[connectors.length + 1];
261 System.arraycopy(connectors, 0, results, 0, 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 results[i] = connectors[i].getObjectName();
291 }
292 return results;
293 }
294
295
296 /**
297 * Add a property change listener to this component.
298 *
299 * @param listener The listener to add
300 */
301 public void addPropertyChangeListener(PropertyChangeListener listener) {
302
303 support.addPropertyChangeListener(listener);
304
305 }
306
307
308 /**
309 * Find and return the set of Connectors associated with this Service.
310 */
311 public Connector[] findConnectors() {
312
313 return (connectors);
314
315 }
316
317
318 /**
319 * Remove the specified Connector from the set associated from this
320 * Service. The removed Connector will also be disassociated from our
321 * Container.
322 *
323 * @param connector The Connector to be removed
324 */
325 public void removeConnector(Connector connector) {
326
327 synchronized (connectors) {
328 int j = -1;
329 for (int i = 0; i < connectors.length; i++) {
330 if (connector == connectors[i]) {
331 j = i;
332 break;
333 }
334 }
335 if (j < 0)
336 return;
337 if (started && (connectors[j] instanceof Lifecycle)) {
338 try {
339 ((Lifecycle) connectors[j]).stop();
340 } catch (LifecycleException e) {
341 log.error("Connector.stop", e);
342 }
343 }
344 connectors[j].setContainer(null);
345 connector.setService(null);
346 int k = 0;
347 Connector results[] = new Connector[connectors.length - 1];
348 for (int i = 0; i < connectors.length; i++) {
349 if (i != j)
350 results[k++] = connectors[i];
351 }
352 connectors = results;
353
354 // Report this property change to interested listeners
355 support.firePropertyChange("connector", connector, null);
356 }
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(PropertyChangeListener listener) {
367
368 support.removePropertyChangeListener(listener);
369
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
386 // ------------------------------------------------------ Lifecycle Methods
387
388
389 /**
390 * Add a LifecycleEvent listener to this component.
391 *
392 * @param listener The listener to add
393 */
394 public void addLifecycleListener(LifecycleListener listener) {
395
396 lifecycle.addLifecycleListener(listener);
397
398 }
399
400
401 /**
402 * Get the lifecycle listeners associated with this lifecycle. If this
403 * Lifecycle has no listeners registered, a zero-length array is returned.
404 */
405 public LifecycleListener[] findLifecycleListeners() {
406
407 return lifecycle.findLifecycleListeners();
408
409 }
410
411
412 /**
413 * Remove a LifecycleEvent listener from this component.
414 *
415 * @param listener The listener to remove
416 */
417 public void removeLifecycleListener(LifecycleListener listener) {
418
419 lifecycle.removeLifecycleListener(listener);
420
421 }
422
423 /**
424 * Adds a named executor to the service
425 * @param ex Executor
426 */
427 public void addExecutor(Executor ex) {
428 synchronized (executors) {
429 if (!executors.contains(ex)) {
430 executors.add(ex);
431 if (started)
432 try {
433 ex.start();
434 } catch (LifecycleException x) {
435 log.error("Executor.start", x);
436 }
437 }
438 }
439 }
440
441 /**
442 * Retrieves all executors
443 * @return Executor[]
444 */
445 public Executor[] findExecutors() {
446 synchronized (executors) {
447 Executor[] arr = new Executor[executors.size()];
448 executors.toArray(arr);
449 return arr;
450 }
451 }
452
453 /**
454 * Retrieves executor by name, null if not found
455 * @param name String
456 * @return Executor
457 */
458 public Executor getExecutor(String name) {
459 synchronized (executors) {
460 for (int i = 0; i < executors.size(); i++) {
461 if (name.equals(executors.get(i).getName()))
462 return executors.get(i);
463 }
464 }
465 return null;
466 }
467
468 /**
469 * Removes an executor from the service
470 * @param ex Executor
471 */
472 public void removeExecutor(Executor ex) {
473 synchronized (executors) {
474 if ( executors.remove(ex) && started ) {
475 try {
476 ex.stop();
477 } catch (LifecycleException e) {
478 log.error("Executor.stop", e);
479 }
480 }
481 }
482 }
483
484
485
486 /**
487 * Prepare for the beginning of active use of the public methods of this
488 * component. This method should be called before any of the public
489 * methods of this component are utilized. It should also send a
490 * LifecycleEvent of type START_EVENT to any registered listeners.
491 *
492 * @exception LifecycleException if this component detects a fatal error
493 * that prevents this component from being used
494 */
495 public void start() throws LifecycleException {
496
497 // Validate and update our current component state
498 if (started) {
499 if (log.isInfoEnabled()) {
500 log.info(sm.getString("standardService.start.started"));
501 }
502 return;
503 }
504
505 if( ! initialized )
506 init();
507
508 // Notify our interested LifecycleListeners
509 lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
510 if(log.isInfoEnabled())
511 log.info(sm.getString("standardService.start.name", this.name));
512 lifecycle.fireLifecycleEvent(START_EVENT, null);
513 started = true;
514
515 // Start our defined Container first
516 if (container != null) {
517 synchronized (container) {
518 if (container instanceof Lifecycle) {
519 ((Lifecycle) container).start();
520 }
521 }
522 }
523
524 synchronized (executors) {
525 for ( int i=0; i<executors.size(); i++ ) {
526 executors.get(i).start();
527 }
528 }
529
530 // Start our defined Connectors second
531 synchronized (connectors) {
532 for (int i = 0; i < connectors.length; i++) {
533 if (connectors[i] instanceof Lifecycle)
534 ((Lifecycle) connectors[i]).start();
535 }
536 }
537
538 // Notify our interested LifecycleListeners
539 lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
540
541 }
542
543
544 /**
545 * Gracefully terminate the active use of the public methods of this
546 * component. This method should be the last one called on a given
547 * instance of this component. It should also send a LifecycleEvent
548 * of type STOP_EVENT to any registered listeners.
549 *
550 * @exception LifecycleException if this component detects a fatal error
551 * that needs to be reported
552 */
553 public void stop() throws LifecycleException {
554
555 // Validate and update our current component state
556 if (!started) {
557 return;
558 }
559
560 // Notify our interested LifecycleListeners
561 lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
562
563 // Stop our defined Connectors first
564 synchronized (connectors) {
565 for (int i = 0; i < connectors.length; i++) {
566 connectors[i].pause();
567 }
568 }
569
570 // Heuristic: Sleep for a while to ensure pause of the connector
571 try {
572 Thread.sleep(1000);
573 } catch (InterruptedException e) {
574 // Ignore
575 }
576
577 lifecycle.fireLifecycleEvent(STOP_EVENT, null);
578 if(log.isInfoEnabled())
579 log.info
580 (sm.getString("standardService.stop.name", this.name));
581 started = false;
582
583 // Stop our defined Container second
584 if (container != null) {
585 synchronized (container) {
586 if (container instanceof Lifecycle) {
587 ((Lifecycle) container).stop();
588 }
589 }
590 }
591 // FIXME pero -- Why container stop first? KeepAlive connetions can send request!
592 // Stop our defined Connectors first
593 synchronized (connectors) {
594 for (int i = 0; i < connectors.length; i++) {
595 if (connectors[i] instanceof Lifecycle)
596 ((Lifecycle) connectors[i]).stop();
597 }
598 }
599
600 synchronized (executors) {
601 for ( int i=0; i<executors.size(); i++ ) {
602 executors.get(i).stop();
603 }
604 }
605
606 if( oname==controller ) {
607 // we registered ourself on init().
608 // That should be the typical case - this object is just for
609 // backward compat, nobody should bother to load it explicitely
610 Registry.getRegistry(null, null).unregisterComponent(oname);
611 Executor[] executors = findExecutors();
612 for (int i = 0; i < executors.length; i++) {
613 try {
614 ObjectName executorObjectName =
615 new ObjectName(domain + ":type=Executor,name=" + executors[i].getName());
616 Registry.getRegistry(null, null).unregisterComponent(executorObjectName);
617 } catch (Exception e) {
618 // Ignore (invalid ON, which cannot happen)
619 }
620 }
621 }
622
623
624 // Notify our interested LifecycleListeners
625 lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
626
627 }
628
629
630 /**
631 * Invoke a pre-startup initialization. This is used to allow connectors
632 * to bind to restricted ports under Unix operating environments.
633 */
634 public void initialize()
635 throws LifecycleException
636 {
637 // Service shouldn't be used with embeded, so it doesn't matter
638 if (initialized) {
639 if(log.isInfoEnabled())
640 log.info(sm.getString("standardService.initialize.initialized"));
641 return;
642 }
643 initialized = true;
644
645 if( oname==null ) {
646 try {
647 // Hack - Server should be deprecated...
648 Container engine=this.getContainer();
649 domain=engine.getName();
650 oname=new ObjectName(domain + ":type=Service,serviceName="+name);
651 this.controller=oname;
652 Registry.getRegistry(null, null)
653 .registerComponent(this, oname, null);
654
655 Executor[] executors = findExecutors();
656 for (int i = 0; i < executors.length; i++) {
657 ObjectName executorObjectName =
658 new ObjectName(domain + ":type=Executor,name=" + executors[i].getName());
659 Registry.getRegistry(null, null)
660 .registerComponent(executors[i], executorObjectName, null);
661 }
662
663 } catch (Exception e) {
664 log.error(sm.getString("standardService.register.failed",domain),e);
665 }
666
667
668 }
669 if( server==null ) {
670 // Register with the server
671 // HACK: ServerFactory should be removed...
672
673 ServerFactory.getServer().addService(this);
674 }
675
676
677 // Initialize our defined Connectors
678 synchronized (connectors) {
679 for (int i = 0; i < connectors.length; i++) {
680 connectors[i].initialize();
681 }
682 }
683 }
684
685 public void destroy() throws LifecycleException {
686 if( started ) stop();
687 // FIXME unregister should be here probably -- stop doing that ?
688 }
689
690 public void init() {
691 try {
692 initialize();
693 } catch( Throwable t ) {
694 log.error(sm.getString("standardService.initialize.failed",domain),t);
695 }
696 }
697
698 protected String type;
699 protected String domain;
700 protected String suffix;
701 protected ObjectName oname;
702 protected ObjectName controller;
703 protected MBeanServer mserver;
704
705 public ObjectName getObjectName() {
706 return oname;
707 }
708
709 public String getDomain() {
710 return domain;
711 }
712
713 public ObjectName preRegister(MBeanServer server,
714 ObjectName name) throws Exception {
715 oname=name;
716 mserver=server;
717 domain=name.getDomain();
718 return name;
719 }
720
721 public void postRegister(Boolean registrationDone) {
722 }
723
724 public void preDeregister() throws Exception {
725 }
726
727 public void postDeregister() {
728 }
729
730 }