1 package org.apache.turbine.services;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import java.util.ArrayList;
20 import java.util.Hashtable;
21 import java.util.Iterator;
22
23 import org.apache.commons.configuration.BaseConfiguration;
24 import org.apache.commons.configuration.Configuration;
25 import org.apache.commons.lang.StringUtils;
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28
29 /***
30 * A generic implementation of a <code>ServiceBroker</code> which
31 * provides:
32 *
33 * <ul>
34 * <li>Maintaining service name to class name mapping, allowing
35 * plugable service implementations.</li>
36 * <li>Providing <code>Services</code> with a configuration based on
37 * system wide configuration mechanism.</li>
38 * </ul>
39 *
40 * @author <a href="mailto:burton@apache.org">Kevin Burton</a>
41 * @author <a href="mailto:krzewski@e-point.pl">Rafal Krzewski</a>
42 * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
43 * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
44 * @author <a href="mailto:mpoeschl@marmot.at">Martin Poeschl</a>
45 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
46 * @version $Id: BaseServiceBroker.java 264148 2005-08-29 14:21:04Z henning $
47 */
48 public abstract class BaseServiceBroker implements ServiceBroker
49 {
50 /***
51 * Mapping of Service names to class names.
52 */
53 protected Configuration mapping = new BaseConfiguration();
54
55 /***
56 * A repository of Service instances.
57 */
58 protected Hashtable services = new Hashtable();
59
60 /***
61 * Configuration for the services broker.
62 * The configuration should be set by the application
63 * in which the services framework is running.
64 */
65 protected Configuration configuration;
66
67 /***
68 * A prefix for <code>Service</code> properties in
69 * TurbineResource.properties.
70 */
71 public static final String SERVICE_PREFIX = "services.";
72
73 /***
74 * A <code>Service</code> property determining its implementing
75 * class name .
76 */
77 public static final String CLASSNAME_SUFFIX = ".classname";
78
79 /***
80 * These are objects that the parent application
81 * can provide so that application specific
82 * services have a mechanism to retrieve specialized
83 * information. For example, in Turbine there are services
84 * that require the RunData object: these services can
85 * retrieve the RunData object that Turbine has placed
86 * in the service manager. This alleviates us of
87 * the requirement of having init(Object) all
88 * together.
89 */
90 protected Hashtable serviceObjects = new Hashtable();
91
92 /*** Logging */
93 private static Log log = LogFactory.getLog(BaseServiceBroker.class);
94
95 /***
96 * Application root path as set by the
97 * parent application.
98 */
99 protected String applicationRoot;
100
101 /***
102 * Default constructor, protected as to only be useable by subclasses.
103 *
104 * This constructor does nothing.
105 */
106 protected BaseServiceBroker()
107 {
108 }
109
110 /***
111 * Set the configuration object for the services broker.
112 * This is the configuration that contains information
113 * about all services in the care of this service
114 * manager.
115 *
116 * @param configuration Broker configuration.
117 */
118 public void setConfiguration(Configuration configuration)
119 {
120 this.configuration = configuration;
121 }
122
123 /***
124 * Get the configuration for this service manager.
125 *
126 * @return Broker configuration.
127 */
128 public Configuration getConfiguration()
129 {
130 return configuration;
131 }
132
133 /***
134 * Initialize this service manager.
135 */
136 public void init() throws InitializationException
137 {
138
139
140
141
142
143
144
145
146
147
148 initMapping();
149
150
151
152 initServices(false);
153 }
154
155 /***
156 * Set an application specific service object
157 * that can be used by application specific
158 * services.
159 *
160 * @param name name of service object
161 * @param value value of service object
162 */
163 public void setServiceObject(String name, Object value)
164 {
165 serviceObjects.put(name, value);
166 }
167
168 /***
169 * Get an application specific service object.
170 *
171 * @return Object application specific service object
172 */
173 public Object getServiceObject(String name)
174 {
175 return serviceObjects.get(name);
176 }
177
178 /***
179 * Creates a mapping between Service names and class names.
180 *
181 * The mapping is built according to settings present in
182 * TurbineResources.properties. The entries should have the
183 * following form:
184 *
185 * <pre>
186 * services.MyService.classname=com.mycompany.MyServiceImpl
187 * services.MyOtherService.classname=com.mycompany.MyOtherServiceImpl
188 * </pre>
189 *
190 * <br>
191 *
192 * Generic ServiceBroker provides no Services.
193 */
194 protected void initMapping()
195 {
196
197
198
199
200
201
202
203
204
205
206
207
208 for (Iterator keys = configuration.getKeys(); keys.hasNext();)
209 {
210 String key = (String) keys.next();
211 String[] keyParts = StringUtils.split(key, ".");
212
213 if ((keyParts.length == 3)
214 && (keyParts[0] + ".").equals(SERVICE_PREFIX)
215 && ("." + keyParts[2]).equals(CLASSNAME_SUFFIX))
216 {
217 String serviceKey = keyParts[1];
218 log.info("Added Mapping for Service: " + serviceKey);
219
220 if (!mapping.containsKey(serviceKey))
221 {
222 mapping.setProperty(serviceKey,
223 configuration.getString(key));
224 }
225 }
226 }
227 }
228
229 /***
230 * Determines whether a service is registered in the configured
231 * <code>TurbineResources.properties</code>.
232 *
233 * @param serviceName The name of the service whose existance to check.
234 * @return Registration predicate for the desired services.
235 */
236 public boolean isRegistered(String serviceName)
237 {
238 return (services.get(serviceName) != null);
239 }
240
241 /***
242 * Returns an Iterator over all known service names.
243 *
244 * @return An Iterator of service names.
245 */
246 public Iterator getServiceNames()
247 {
248 return mapping.getKeys();
249 }
250
251 /***
252 * Returns an Iterator over all known service names beginning with
253 * the provided prefix.
254 *
255 * @param prefix The prefix against which to test.
256 * @return An Iterator of service names which match the prefix.
257 */
258 public Iterator getServiceNames(String prefix)
259 {
260 return mapping.getKeys(prefix);
261 }
262
263 /***
264 * Performs early initialization of specified service.
265 *
266 * @param name The name of the service (generally the
267 * <code>SERVICE_NAME</code> constant of the service's interface
268 * definition).
269 * @exception InitializationException Initialization of this
270 * service was not successful.
271 */
272 public synchronized void initService(String name)
273 throws InitializationException
274 {
275
276
277
278 Service instance = getServiceInstance(name);
279
280 if (!instance.getInit())
281 {
282
283 instance.init();
284 }
285 }
286
287 /***
288 * Performs early initialization of all services. Failed early
289 * initialization of a Service may be non-fatal to the system,
290 * thus any exceptions are logged and the initialization process
291 * continues.
292 */
293 public void initServices()
294 {
295 try
296 {
297 initServices(false);
298 }
299 catch (InstantiationException notThrown)
300 {
301 log.debug("Caught non fatal exception", notThrown);
302 }
303 catch (InitializationException notThrown)
304 {
305 log.debug("Caught non fatal exception", notThrown);
306 }
307 }
308
309 /***
310 * Performs early initialization of all services. You can decide
311 * to handle failed initializations if you wish, but then
312 * after one service fails, the other will not have the chance
313 * to initialize.
314 *
315 * @param report <code>true</code> if you want exceptions thrown.
316 */
317 public void initServices(boolean report)
318 throws InstantiationException, InitializationException
319 {
320 if (report)
321 {
322
323 for (Iterator names = getServiceNames(); names.hasNext();)
324 {
325 doInitService((String) names.next());
326 }
327 }
328 else
329 {
330
331 for (Iterator names = getServiceNames(); names.hasNext();)
332 {
333 try
334 {
335 doInitService((String) names.next());
336 }
337
338
339 catch (InstantiationException e)
340 {
341 log.error(e);
342 }
343 catch (InitializationException e)
344 {
345 log.error(e);
346 }
347 }
348 }
349 log.info("Finished initializing all services!");
350 }
351
352 /***
353 * Internal utility method for use in {@link #initServices(boolean)}
354 * to prevent duplication of code.
355 */
356 private void doInitService(String name)
357 throws InstantiationException, InitializationException
358 {
359
360 if (getConfiguration(name).getBoolean("earlyInit", false))
361 {
362 log.info("Start Initializing service (early): " + name);
363 initService(name);
364 log.info("Finish Initializing service (early): " + name);
365 }
366 }
367
368 /***
369 * Shuts down a <code>Service</code>, releasing resources
370 * allocated by an <code>Service</code>, and returns it to its
371 * initial (uninitialized) state.
372 *
373 * @param name The name of the <code>Service</code> to be
374 * uninitialized.
375 */
376 public synchronized void shutdownService(String name)
377 {
378 try
379 {
380 Service service = getServiceInstance(name);
381 if (service != null && service.getInit())
382 {
383 service.shutdown();
384 if (service.getInit() && service instanceof BaseService)
385 {
386
387
388 ((BaseService) service).setInit(false);
389 }
390 }
391 }
392 catch (InstantiationException e)
393 {
394
395 log.error("Shutdown of a nonexistent Service '"
396 + name + "' was requested", e);
397 }
398 }
399
400 /***
401 * Shuts down all Turbine services, releasing allocated resources and
402 * returning them to their initial (uninitialized) state.
403 */
404 public void shutdownServices()
405 {
406 log.info("Shutting down all services!");
407
408 String serviceName = null;
409
410
411
412
413
414
415
416
417 ArrayList reverseServicesList = new ArrayList();
418
419 for (Iterator serviceNames = getServiceNames(); serviceNames.hasNext();)
420 {
421 serviceName = (String) serviceNames.next();
422 reverseServicesList.add(0, serviceName);
423 }
424
425 for (Iterator serviceNames = reverseServicesList.iterator(); serviceNames.hasNext();)
426 {
427 serviceName = (String) serviceNames.next();
428 log.info("Shutting down service: " + serviceName);
429 shutdownService(serviceName);
430 }
431 }
432
433 /***
434 * Returns an instance of requested Service.
435 *
436 * @param name The name of the Service requested.
437 * @return An instance of requested Service.
438 * @exception InstantiationException if the service is unknown or
439 * can't be initialized.
440 */
441 public Service getService(String name) throws InstantiationException
442 {
443 Service service;
444 try
445 {
446 service = getServiceInstance(name);
447 if (!service.getInit())
448 {
449 synchronized (service.getClass())
450 {
451 if (!service.getInit())
452 {
453 log.info("Start Initializing service (late): " + name);
454 service.init();
455 log.info("Finish Initializing service (late): " + name);
456 }
457 }
458 }
459 if (!service.getInit())
460 {
461
462
463
464
465 throw new InitializationException(
466 "init() failed to initialize service " + name);
467 }
468 return service;
469 }
470 catch (InitializationException e)
471 {
472 throw new InstantiationException("Service " + name +
473 " failed to initialize", e);
474 }
475 }
476
477 /***
478 * Retrieves an instance of a Service without triggering late
479 * initialization.
480 *
481 * Early initialization of a Service can require access to Service
482 * properties. The Service must have its name and serviceBroker
483 * set by then. Therefore, before calling
484 * Initable.initClass(Object), the class must be instantiated with
485 * InitableBroker.getInitableInstance(), and
486 * Service.setServiceBroker() and Service.setName() must be
487 * called. This calls for two - level accessing the Services
488 * instances.
489 *
490 * @param name The name of the service requested.
491 * @exception InstantiationException The service is unknown or
492 * can't be initialized.
493 */
494 protected Service getServiceInstance(String name)
495 throws InstantiationException
496 {
497 Service service = (Service) services.get(name);
498
499 if (service == null)
500 {
501 String className = mapping.getString(name);
502 if (StringUtils.isEmpty(className))
503 {
504 throw new InstantiationException(
505 "ServiceBroker: unknown service " + name
506 + " requested");
507 }
508 try
509 {
510 service = (Service) services.get(className);
511
512 if (service == null)
513 {
514 try
515 {
516 service = (Service)
517 Class.forName(className).newInstance();
518 }
519
520 catch (ThreadDeath t)
521 {
522 throw t;
523 }
524 catch (OutOfMemoryError t)
525 {
526 throw t;
527 }
528 catch (Throwable t)
529 {
530
531 String msg = null;
532
533 if (t instanceof NoClassDefFoundError)
534 {
535 msg = "A class referenced by " + className +
536 " is unavailable. Check your jars and classes.";
537 }
538 else if (t instanceof ClassNotFoundException)
539 {
540 msg = "Class " + className +
541 " is unavailable. Check your jars and classes.";
542 }
543 else if (t instanceof ClassCastException)
544 {
545 msg = "Class " + className +
546 " doesn't implement the Service interface";
547 }
548 else
549 {
550 msg = "Failed to instantiate " + className;
551 }
552
553 throw new InstantiationException(msg, t);
554 }
555 }
556 }
557 catch (ClassCastException e)
558 {
559 throw new InstantiationException("ServiceBroker: Class "
560 + className
561 + " does not implement Service interface.", e);
562 }
563 catch (InstantiationException e)
564 {
565 throw new InstantiationException(
566 "Failed to instantiate service " + name, e);
567 }
568 service.setServiceBroker(this);
569 service.setName(name);
570 services.put(name, service);
571 }
572
573 return service;
574 }
575
576 /***
577 * Returns the configuration for the specified service.
578 *
579 * @param name The name of the service.
580 * @return Configuration of requested Service.
581 */
582 public Configuration getConfiguration(String name)
583 {
584 return configuration.subset(SERVICE_PREFIX + name);
585 }
586
587 /***
588 * Set the application root.
589 *
590 * @param applicationRoot application root
591 */
592 public void setApplicationRoot(String applicationRoot)
593 {
594 this.applicationRoot = applicationRoot;
595 }
596
597 /***
598 * Get the application root as set by
599 * the parent application.
600 *
601 * @return String application root
602 */
603 public String getApplicationRoot()
604 {
605 return applicationRoot;
606 }
607 }