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