KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > turbine > services > BaseServiceBroker


1 package org.apache.turbine.services;
2
3 /*
4  * Copyright 2001-2004 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 JavaDoc;
20 import java.util.Hashtable JavaDoc;
21 import java.util.Iterator JavaDoc;
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,v 1.16.2.2 2004/05/20 03:05:18 seade Exp $
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 JavaDoc services = new Hashtable JavaDoc();
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 JavaDoc SERVICE_PREFIX = "services.";
72
73     /**
74      * A <code>Service</code> property determining its implementing
75      * class name .
76      */

77     public static final String JavaDoc 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 JavaDoc serviceObjects = new Hashtable JavaDoc();
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 JavaDoc 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         // 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
initMapping();
149
150         // Start services that have their 'earlyInit'
151
// property set to 'true'.
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 JavaDoc name, Object JavaDoc 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 JavaDoc getServiceObject(String JavaDoc 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          * 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         for (Iterator JavaDoc keys = configuration.getKeys(); keys.hasNext();)
209         {
210             String JavaDoc key = (String JavaDoc) keys.next();
211             String JavaDoc[] 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc getServiceNames(String JavaDoc 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 JavaDoc 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
Service instance = getServiceInstance(name);
279
280         if (!instance.getInit())
281         {
282             // this call might result in an indirect recursion
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 JavaDoc 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 JavaDoc, InitializationException
319     {
320         if (report)
321         {
322             // Throw exceptions
323
for (Iterator JavaDoc names = getServiceNames(); names.hasNext();)
324             {
325                 doInitService((String JavaDoc) names.next());
326             }
327         }
328         else
329         {
330             // Eat exceptions
331
for (Iterator JavaDoc names = getServiceNames(); names.hasNext();)
332             {
333                 try
334                 {
335                     doInitService((String JavaDoc) names.next());
336                 }
337                         // In case of an exception, file an error message; the
338
// system may be still functional, though.
339
catch (InstantiationException JavaDoc 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 JavaDoc name)
357             throws InstantiationException JavaDoc, InitializationException
358     {
359         // Only start up services that have their earlyInit flag set.
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 JavaDoc 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                     // BaseService::shutdown() does this by default,
387
// but could've been overriden poorly.
388
((BaseService) service).setInit(false);
389                 }
390             }
391         }
392         catch (InstantiationException JavaDoc e)
393         {
394             // Assuming harmless -- log the error and continue.
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 JavaDoc 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         ArrayList JavaDoc reverseServicesList = new ArrayList JavaDoc();
418
419         for (Iterator JavaDoc serviceNames = getServiceNames(); serviceNames.hasNext();)
420         {
421             serviceName = (String JavaDoc) serviceNames.next();
422             reverseServicesList.add(0, serviceName);
423         }
424
425         for (Iterator JavaDoc serviceNames = reverseServicesList.iterator(); serviceNames.hasNext();)
426         {
427             serviceName = (String JavaDoc) 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 JavaDoc name) throws InstantiationException JavaDoc
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                 // 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
throw new InitializationException(
466                         "init() failed to initialize service " + name);
467             }
468             return service;
469         }
470         catch (InitializationException e)
471         {
472             throw new InstantiationException JavaDoc("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 JavaDoc name)
495             throws InstantiationException JavaDoc
496     {
497         Service service = (Service) services.get(name);
498
499         if (service == null)
500         {
501             String JavaDoc className = mapping.getString(name);
502             if (StringUtils.isEmpty(className))
503             {
504                 throw new InstantiationException JavaDoc(
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                     // those two errors must be passed to the VM
520
catch (ThreadDeath JavaDoc t)
521                     {
522                         throw t;
523                     }
524                     catch (OutOfMemoryError JavaDoc t)
525                     {
526                         throw t;
527                     }
528                     catch (Throwable JavaDoc t)
529                     {
530                         // Used to indicate error condition.
531
String JavaDoc msg = null;
532
533                         if (t instanceof NoClassDefFoundError JavaDoc)
534                         {
535                             msg = "A class referenced by " + className +
536                                     " is unavailable. Check your jars and classes.";
537                         }
538                         else if (t instanceof ClassNotFoundException JavaDoc)
539                         {
540                             msg = "Class " + className +
541                                     " is unavailable. Check your jars and classes.";
542                         }
543                         else if (t instanceof ClassCastException JavaDoc)
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 JavaDoc(msg, t);
554                     }
555                 }
556             }
557             catch (ClassCastException JavaDoc e)
558             {
559                 throw new InstantiationException JavaDoc("ServiceBroker: Class "
560                         + className
561                         + " does not implement Service interface.", e);
562             }
563             catch (InstantiationException JavaDoc e)
564             {
565                 throw new InstantiationException JavaDoc(
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 JavaDoc 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 JavaDoc 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 JavaDoc getApplicationRoot()
604     {
605         return applicationRoot;
606     }
607 }
608
Popular Tags