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 (log.isInfoEnabled() && started) {
499 log.info(sm.getString("standardService.start.started"));
500 }
501
502 if( ! initialized )
503 init();
504
505 // Notify our interested LifecycleListeners
506 lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
507 if(log.isInfoEnabled())
508 log.info(sm.getString("standardService.start.name", this.name));
509 lifecycle.fireLifecycleEvent(START_EVENT, null);
510 started = true;
511
512 // Start our defined Container first
513 if (container != null) {
514 synchronized (container) {
515 if (container instanceof Lifecycle) {
516 ((Lifecycle) container).start();
517 }
518 }
519 }
520
521 synchronized (executors) {
522 for ( int i=0; i<executors.size(); i++ ) {
523 executors.get(i).start();
524 }
525 }
526
527 // Start our defined Connectors second
528 synchronized (connectors) {
529 for (int i = 0; i < connectors.length; i++) {
530 if (connectors[i] instanceof Lifecycle)
531 ((Lifecycle) connectors[i]).start();
532 }
533 }
534
535 // Notify our interested LifecycleListeners
536 lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
537
538 }
539
540
541 /**
542 * Gracefully terminate the active use of the public methods of this
543 * component. This method should be the last one called on a given
544 * instance of this component. It should also send a LifecycleEvent
545 * of type STOP_EVENT to any registered listeners.
546 *
547 * @exception LifecycleException if this component detects a fatal error
548 * that needs to be reported
549 */
550 public void stop() throws LifecycleException {
551
552 // Validate and update our current component state
553 if (!started) {
554 return;
555 }
556
557 // Notify our interested LifecycleListeners
558 lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
559
560 // Stop our defined Connectors first
561 synchronized (connectors) {
562 for (int i = 0; i < connectors.length; i++) {
563 connectors[i].pause();
564 }
565 }
566
567 // Heuristic: Sleep for a while to ensure pause of the connector
568 try {
569 Thread.sleep(1000);
570 } catch (InterruptedException e) {
571 // Ignore
572 }
573
574 lifecycle.fireLifecycleEvent(STOP_EVENT, null);
575 if(log.isInfoEnabled())
576 log.info
577 (sm.getString("standardService.stop.name", this.name));
578 started = false;
579
580 // Stop our defined Container second
581 if (container != null) {
582 synchronized (container) {
583 if (container instanceof Lifecycle) {
584 ((Lifecycle) container).stop();
585 }
586 }
587 }
588 // FIXME pero -- Why container stop first? KeepAlive connetions can send request!
589 // Stop our defined Connectors first
590 synchronized (connectors) {
591 for (int i = 0; i < connectors.length; i++) {
592 if (connectors[i] instanceof Lifecycle)
593 ((Lifecycle) connectors[i]).stop();
594 }
595 }
596
597 synchronized (executors) {
598 for ( int i=0; i<executors.size(); i++ ) {
599 executors.get(i).stop();
600 }
601 }
602
603 if( oname==controller ) {
604 // we registered ourself on init().
605 // That should be the typical case - this object is just for
606 // backward compat, nobody should bother to load it explicitely
607 Registry.getRegistry(null, null).unregisterComponent(oname);
608 Executor[] executors = findExecutors();
609 for (int i = 0; i < executors.length; i++) {
610 try {
611 ObjectName executorObjectName =
612 new ObjectName(domain + ":type=Executor,name=" + executors[i].getName());
613 Registry.getRegistry(null, null).unregisterComponent(executorObjectName);
614 } catch (Exception e) {
615 // Ignore (invalid ON, which cannot happen)
616 }
617 }
618 }
619
620
621 // Notify our interested LifecycleListeners
622 lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
623
624 }
625
626
627 /**
628 * Invoke a pre-startup initialization. This is used to allow connectors
629 * to bind to restricted ports under Unix operating environments.
630 */
631 public void initialize()
632 throws LifecycleException
633 {
634 // Service shouldn't be used with embeded, so it doesn't matter
635 if (initialized) {
636 if(log.isInfoEnabled())
637 log.info(sm.getString("standardService.initialize.initialized"));
638 return;
639 }
640 initialized = true;
641
642 if( oname==null ) {
643 try {
644 // Hack - Server should be deprecated...
645 Container engine=this.getContainer();
646 domain=engine.getName();
647 oname=new ObjectName(domain + ":type=Service,serviceName="+name);
648 this.controller=oname;
649 Registry.getRegistry(null, null)
650 .registerComponent(this, oname, null);
651
652 Executor[] executors = findExecutors();
653 for (int i = 0; i < executors.length; i++) {
654 ObjectName executorObjectName =
655 new ObjectName(domain + ":type=Executor,name=" + executors[i].getName());
656 Registry.getRegistry(null, null)
657 .registerComponent(executors[i], executorObjectName, null);
658 }
659
660 } catch (Exception e) {
661 log.error(sm.getString("standardService.register.failed",domain),e);
662 }
663
664
665 }
666 if( server==null ) {
667 // Register with the server
668 // HACK: ServerFactory should be removed...
669
670 ServerFactory.getServer().addService(this);
671 }
672
673
674 // Initialize our defined Connectors
675 synchronized (connectors) {
676 for (int i = 0; i < connectors.length; i++) {
677 connectors[i].initialize();
678 }
679 }
680 }
681
682 public void destroy() throws LifecycleException {
683 if( started ) stop();
684 // FIXME unregister should be here probably -- stop doing that ?
685 }
686
687 public void init() {
688 try {
689 initialize();
690 } catch( Throwable t ) {
691 log.error(sm.getString("standardService.initialize.failed",domain),t);
692 }
693 }
694
695 protected String type;
696 protected String domain;
697 protected String suffix;
698 protected ObjectName oname;
699 protected ObjectName controller;
700 protected MBeanServer mserver;
701
702 public ObjectName getObjectName() {
703 return oname;
704 }
705
706 public String getDomain() {
707 return domain;
708 }
709
710 public ObjectName preRegister(MBeanServer server,
711 ObjectName name) throws Exception {
712 oname=name;
713 mserver=server;
714 domain=name.getDomain();
715 return name;
716 }
717
718 public void postRegister(Boolean registrationDone) {
719 }
720
721 public void preDeregister() throws Exception {
722 }
723
724 public void postDeregister() {
725 }
726
727 }