%line | %branch | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
org.apache.turbine.services.BaseServiceBroker |
|
|
1 | package org.apache.turbine.services; |
|
2 | ||
3 | /* |
|
4 | * Copyright 2001-2005 The Apache Software Foundation. |
|
5 | * |
|
6 | * Licensed under the Apache License, Version 2.0 (the "License") |
|
7 | * you may not use this file except in compliance with the License. |
|
8 | * You may obtain a copy of the License at |
|
9 | * |
|
10 | * http://www.apache.org/licenses/LICENSE-2.0 |
|
11 | * |
|
12 | * Unless required by applicable law or agreed to in writing, software |
|
13 | * distributed under the License is distributed on an "AS IS" BASIS, |
|
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
15 | * See the License for the specific language governing permissions and |
|
16 | * limitations under the License. |
|
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 | 29 | public abstract class BaseServiceBroker implements ServiceBroker |
49 | { |
|
50 | /** |
|
51 | * Mapping of Service names to class names. |
|
52 | */ |
|
53 | 58 | protected Configuration mapping = new BaseConfiguration(); |
54 | ||
55 | /** |
|
56 | * A repository of Service instances. |
|
57 | */ |
|
58 | 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 | 58 | protected Hashtable serviceObjects = new Hashtable(); |
91 | ||
92 | /** Logging */ |
|
93 | 87 | 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 | 58 | { |
108 | 58 | } |
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 | 100 | this.configuration = configuration; |
121 | 100 | } |
122 | ||
123 | /** |
|
124 | * Get the configuration for this service manager. |
|
125 | * |
|
126 | * @return Broker configuration. |
|
127 | */ |
|
128 | public Configuration getConfiguration() |
|
129 | { |
|
130 | 12 | return configuration; |
131 | } |
|
132 | ||
133 | /** |
|
134 | * Initialize this service manager. |
|
135 | */ |
|
136 | public void init() throws InitializationException |
|
137 | { |
|
138 | // Check: |
|
139 | // |
|
140 | // 1. The configuration has been set. |
|
141 | // 2. Make sure the application root has been set. |
|
142 | ||
143 | // FIXME: Make some service framework exceptions to throw in |
|
144 | // the event these requirements aren't satisfied. |
|
145 | ||
146 | // Create the mapping between service names |
|
147 | // and their classes. |
|
148 | 70 | initMapping(); |
149 | ||
150 | // Start services that have their 'earlyInit' |
|
151 | // property set to 'true'. |
|
152 | 70 | initServices(false); |
153 | 70 | } |
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 | 0 | serviceObjects.put(name, value); |
166 | 0 | } |
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 | 0 | 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 | * These keys returned in an order that corresponds |
|
198 | * to the order the services are listed in |
|
199 | * the TR.props. |
|
200 | * |
|
201 | * When the mapping is created we use a Configuration |
|
202 | * object to ensure that the we retain the order |
|
203 | * in which the order the keys are returned. |
|
204 | * |
|
205 | * There's no point in retrieving an ordered set |
|
206 | * of keys if they aren't kept in order :-) |
|
207 | */ |
|
208 | 2159 | for (Iterator keys = configuration.getKeys(); keys.hasNext();) |
209 | { |
|
210 | 4108 | String key = (String) keys.next(); |
211 | 4108 | String[] keyParts = StringUtils.split(key, "."); |
212 | ||
213 | 4108 | if ((keyParts.length == 3) |
214 | && (keyParts[0] + ".").equals(SERVICE_PREFIX) |
|
215 | && ("." + keyParts[2]).equals(CLASSNAME_SUFFIX)) |
|
216 | { |
|
217 | 542 | String serviceKey = keyParts[1]; |
218 | 542 | log.info("Added Mapping for Service: " + serviceKey); |
219 | ||
220 | 542 | if (!mapping.containsKey(serviceKey)) |
221 | { |
|
222 | 496 | mapping.setProperty(serviceKey, |
223 | configuration.getString(key)); |
|
224 | } |
|
225 | } |
|
226 | } |
|
227 | 70 | } |
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 | 42 | 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 | 80 | 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 | 0 | 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 | // Calling getServiceInstance(name) assures that the Service |
|
276 | // implementation has its name and broker reference set before |
|
277 | // initialization. |
|
278 | 114 | Service instance = getServiceInstance(name); |
279 | ||
280 | 114 | if (!instance.getInit()) |
281 | { |
|
282 | // this call might result in an indirect recursion |
|
283 | 72 | instance.init(); |
284 | } |
|
285 | 114 | } |
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 | 0 | initServices(false); |
298 | } |
|
299 | 0 | catch (InstantiationException notThrown) |
300 | { |
|
301 | 0 | log.debug("Caught non fatal exception", notThrown); |
302 | } |
|
303 | 0 | catch (InitializationException notThrown) |
304 | { |
|
305 | 0 | log.debug("Caught non fatal exception", notThrown); |
306 | 0 | } |
307 | 0 | } |
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 | 70 | if (report) |
321 | { |
|
322 | // Throw exceptions |
|
323 | 0 | for (Iterator names = getServiceNames(); names.hasNext();) |
324 | { |
|
325 | 0 | doInitService((String) names.next()); |
326 | } |
|
327 | } |
|
328 | else |
|
329 | { |
|
330 | // Eat exceptions |
|
331 | 376 | for (Iterator names = getServiceNames(); names.hasNext();) |
332 | { |
|
333 | try |
|
334 | { |
|
335 | 542 | doInitService((String) names.next()); |
336 | 271 | } |
337 | // In case of an exception, file an error message; the |
|
338 | // system may be still functional, though. |
|
339 | 0 | catch (InstantiationException e) |
340 | { |
|
341 | 0 | log.error(e); |
342 | } |
|
343 | 0 | catch (InitializationException e) |
344 | { |
|
345 | 0 | log.error(e); |
346 | 271 | } |
347 | } |
|
348 | } |
|
349 | 70 | log.info("Finished initializing all services!"); |
350 | 70 | } |
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 | // Only start up services that have their earlyInit flag set. |
|
360 | 542 | if (getConfiguration(name).getBoolean("earlyInit", false)) |
361 | { |
|
362 | 114 | log.info("Start Initializing service (early): " + name); |
363 | 114 | initService(name); |
364 | 114 | log.info("Finish Initializing service (early): " + name); |
365 | } |
|
366 | 542 | } |
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 | 70 | Service service = getServiceInstance(name); |
381 | 70 | if (service != null && service.getInit()) |
382 | { |
|
383 | 52 | service.shutdown(); |
384 | 52 | if (service.getInit() && service instanceof BaseService) |
385 | { |
|
386 | // BaseService::shutdown() does this by default, |
|
387 | // but could've been overriden poorly. |
|
388 | 0 | ((BaseService) service).setInit(false); |
389 | } |
|
390 | } |
|
391 | 35 | } |
392 | 0 | catch (InstantiationException e) |
393 | { |
|
394 | // Assuming harmless -- log the error and continue. |
|
395 | 0 | log.error("Shutdown of a nonexistent Service '" |
396 | + name + "' was requested", e); |
|
397 | 35 | } |
398 | 70 | } |
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 | 10 | log.info("Shutting down all services!"); |
407 | ||
408 | 10 | String serviceName = null; |
409 | ||
410 | /* |
|
411 | * Now we want to reverse the order of |
|
412 | * this list. This functionality should be added to |
|
413 | * the ExtendedProperties in the commons but |
|
414 | * this will fix the problem for now. |
|
415 | */ |
|
416 | ||
417 | 10 | ArrayList reverseServicesList = new ArrayList(); |
418 | ||
419 | 50 | for (Iterator serviceNames = getServiceNames(); serviceNames.hasNext();) |
420 | { |
|
421 | 70 | serviceName = (String) serviceNames.next(); |
422 | 70 | reverseServicesList.add(0, serviceName); |
423 | } |
|
424 | ||
425 | 50 | for (Iterator serviceNames = reverseServicesList.iterator(); serviceNames.hasNext();) |
426 | { |
|
427 | 70 | serviceName = (String) serviceNames.next(); |
428 | 70 | log.info("Shutting down service: " + serviceName); |
429 | 70 | shutdownService(serviceName); |
430 | } |
|
431 | 10 | } |
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 | 2538 | service = getServiceInstance(name); |
447 | 2538 | if (!service.getInit()) |
448 | { |
|
449 | 324 | synchronized (service.getClass()) |
450 | { |
|
451 | 324 | if (!service.getInit()) |
452 | { |
|
453 | 324 | log.info("Start Initializing service (late): " + name); |
454 | 324 | service.init(); |
455 | 324 | log.info("Finish Initializing service (late): " + name); |
456 | } |
|
457 | 324 | } |
458 | } |
|
459 | 2538 | if (!service.getInit()) |
460 | { |
|
461 | // this exception will be caught & rethrown by this very method. |
|
462 | // getInit() returning false indicates some initialization issue, |
|
463 | // which in turn prevents the InitableBroker from passing a |
|
464 | // reference to a working instance of the initable to the client. |
|
465 | 0 | throw new InitializationException( |
466 | "init() failed to initialize service " + name); |
|
467 | } |
|
468 | 2538 | return service; |
469 | } |
|
470 | 0 | catch (InitializationException e) |
471 | { |
|
472 | 0 | 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 | 2722 | Service service = (Service) services.get(name); |
498 | ||
499 | 2722 | if (service == null) |
500 | { |
|
501 | 396 | String className = mapping.getString(name); |
502 | 396 | if (StringUtils.isEmpty(className)) |
503 | { |
|
504 | 0 | throw new InstantiationException( |
505 | "ServiceBroker: unknown service " + name |
|
506 | + " requested"); |
|
507 | } |
|
508 | try |
|
509 | { |
|
510 | 396 | service = (Service) services.get(className); |
511 | ||
512 | 396 | if (service == null) |
513 | { |
|
514 | try |
|
515 | { |
|
516 | 396 | service = (Service) |
517 | Class.forName(className).newInstance(); |
|
518 | 198 | } |
519 | // those two errors must be passed to the VM |
|
520 | 0 | catch (ThreadDeath t) |
521 | { |
|
522 | 0 | throw t; |
523 | } |
|
524 | 0 | catch (OutOfMemoryError t) |
525 | { |
|
526 | 0 | throw t; |
527 | } |
|
528 | 0 | catch (Throwable t) |
529 | { |
|
530 | // Used to indicate error condition. |
|
531 | 0 | String msg = null; |
532 | ||
533 | 0 | if (t instanceof NoClassDefFoundError) |
534 | { |
|
535 | 0 | msg = "A class referenced by " + className + |
536 | " is unavailable. Check your jars and classes."; |
|
537 | } |
|
538 | 0 | else if (t instanceof ClassNotFoundException) |
539 | { |
|
540 | 0 | msg = "Class " + className + |
541 | " is unavailable. Check your jars and classes."; |
|
542 | } |
|
543 | 0 | else if (t instanceof ClassCastException) |
544 | { |
|
545 | 0 | msg = "Class " + className + |
546 | " doesn't implement the Service interface"; |
|
547 | } |
|
548 | else |
|
549 | { |
|
550 | 0 | msg = "Failed to instantiate " + className; |
551 | } |
|
552 | ||
553 | 0 | throw new InstantiationException(msg, t); |
554 | 198 | } |
555 | } |
|
556 | 198 | } |
557 | 0 | catch (ClassCastException e) |
558 | { |
|
559 | 0 | throw new InstantiationException("ServiceBroker: Class " |
560 | + className |
|
561 | + " does not implement Service interface.", e); |
|
562 | } |
|
563 | 0 | catch (InstantiationException e) |
564 | { |
|
565 | 0 | throw new InstantiationException( |
566 | "Failed to instantiate service " + name, e); |
|
567 | 198 | } |
568 | 396 | service.setServiceBroker(this); |
569 | 396 | service.setName(name); |
570 | 396 | services.put(name, service); |
571 | } |
|
572 | ||
573 | 2722 | 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 | 900 | 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 | 100 | this.applicationRoot = applicationRoot; |
595 | 100 | } |
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 | 0 | return applicationRoot; |
606 | } |
|
607 | } |
This report is generated by jcoverage, Maven and Maven JCoverage Plugin. |