##### Coverage Report - org.apache.turbine.services.BaseServiceBroker

Classes in this File | Line Coverage | Branch Coverage | Complexity
BaseServiceBroker |
72% | 127/176
76% | 60/78
| 3.92;3,92

1 |  |
     package org.apache.turbine.services;
2 |  |
3 |  |
     /*
4 |  |
      * Licensed to the Apache Software Foundation (ASF) under one
5 |  |
      * or more contributor license agreements.  See the NOTICE file
6 |  |
      * distributed with this work for additional information
7 |  |
      * regarding copyright ownership.  The ASF licenses this file
8 |  |
      * to you under the Apache License, Version 2.0 (the
9 |  |
      * "License"); you may not use this file except in compliance
10 |  |
      * with the License.  You may obtain a copy of the License at
11 |  |
      *
12 |  |
      *   http://www.apache.org/licenses/LICENSE-2.0
13 |  |
      *
14 |  |
      * Unless required by applicable law or agreed to in writing,
15 |  |
      * software distributed under the License is distributed on an
16 |  |
      * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 |  |
      * KIND, either express or implied.  See the License for the
18 |  |
      * specific language governing permissions and limitations
19 |  |
      * under the License.
20 |  |
      */
21 |  |
22 |  |
23 |  |
     import java.util.ArrayList;
24 |  |
     import java.util.Iterator;
25 |  |
     import java.util.LinkedHashMap;
26 |  |
     import java.util.LinkedHashSet;
27 |  |
     import java.util.Map;
28 |  |
     import java.util.Set;
29 |  |
     import java.util.concurrent.ConcurrentHashMap;
30 |  |
     import java.util.concurrent.locks.ReentrantLock;
31 |  |
32 |  |
     import org.apache.commons.configuration.Configuration;
33 |  |
     import org.apache.commons.lang.StringUtils;
34 |  |
     import org.apache.commons.logging.Log;
35 |  |
     import org.apache.commons.logging.LogFactory;
36 |  |
37 |  |
     /**
38 |  |
      * A generic implementation of a <code>ServiceBroker</code> which
39 |  |
      * provides:
40 |  |
      *
41 |  |
      * <ul>
42 |  |
      * <li>Maintaining service name to class name mapping, allowing
43 |  |
      * pluggable service implementations.</li>
44 |  |
      * <li>Providing <code>Services</code> with a configuration based on
45 |  |
      * system wide configuration mechanism.</li>
46 |  |
      * <li>Integration of TurbineServiceProviders for looking up
47 |  |
      * non-local services</li>
48 |  |
      * </ul>
49 |  |
      *
50 |  |
      * @author <a href="mailto:burton@apache.org">Kevin Burton</a>
51 |  |
      * @author <a href="mailto:krzewski@e-point.pl">Rafal Krzewski</a>
52 |  |
      * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
53 |  |
      * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
54 |  |
      * @author <a href="mailto:mpoeschl@marmot.at">Martin Poeschl</a>
55 |  |
      * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
56 |  |
      * @version $Id: BaseServiceBroker.java 1820269 2018-01-05 08:33:52Z gk $
57 |  |
      */
58 |  |
     public abstract class BaseServiceBroker implements ServiceBroker
59 |  |
     {
60 |  |
         /**
61 |  |
          * Mapping of Service names to class names, keep order.
62 |  |
          */
63 | 34 |
         private final Map<String, Class<?>> mapping = new LinkedHashMap<String, Class<?>>();
64 |  |
65 |  |
         /**
66 |  |
          * A repository of Service instances.
67 |  |
          */
68 | 34 |
         private final ConcurrentHashMap<String, Service> services = new ConcurrentHashMap<String, Service>();
69 |  |
70 |  |
         /**
71 |  |
          * Lock access during service initialization
72 |  |
          */
73 | 34 |
         private final ReentrantLock serviceLock = new ReentrantLock();
74 |  |
75 |  |
         /**
76 |  |
          * Configuration for the services broker.
77 |  |
          * The configuration should be set by the application
78 |  |
          * in which the services framework is running.
79 |  |
          */
80 |  |
         private Configuration configuration;
81 |  |
82 |  |
         /**
83 |  |
          * A prefix for <code>Service</code> properties in
84 |  |
          * TurbineResource.properties.
85 |  |
          */
86 |  |
         public static final String SERVICE_PREFIX = "services.";
87 |  |
88 |  |
         /**
89 |  |
          * A <code>Service</code> property determining its implementing
90 |  |
          * class name .
91 |  |
          */
92 |  |
         public static final String CLASSNAME_SUFFIX = ".classname";
93 |  |
94 |  |
         /**
95 |  |
          * These are objects that the parent application
96 |  |
          * can provide so that application specific
97 |  |
          * services have a mechanism to retrieve specialized
98 |  |
          * information. For example, in Turbine there are services
99 |  |
          * that require the RunData object: these services can
100 |  |
          * retrieve the RunData object that Turbine has placed
101 |  |
          * in the service manager. This alleviates us of
102 |  |
          * the requirement of having init(Object) all
103 |  |
          * together.
104 |  |
          */
105 | 34 |
         private final ConcurrentHashMap<String, Object> serviceObjects = new ConcurrentHashMap<String, Object>();
106 |  |
107 |  |
         /** Logging */
108 | 34 |
         private static Log log = LogFactory.getLog(BaseServiceBroker.class);
109 |  |
110 |  |
         /**
111 |  |
          * Application root path as set by the
112 |  |
          * parent application.
113 |  |
          */
114 |  |
         private String applicationRoot;
115 |  |
116 |  |
         /**
117 |  |
          * mapping from service names to instances of TurbineServiceProviders
118 |  |
          */
119 | 34 |
         private final ConcurrentHashMap<String, Service> serviceProviderInstanceMap = new ConcurrentHashMap<String, Service>();
120 |  |
121 |  |
         /**
122 |  |
          * Default constructor, protected as to only be usable by subclasses.
123 |  |
          *
124 |  |
          * This constructor does nothing.
125 |  |
          */
126 |  |
         protected BaseServiceBroker()
127 | 34 |
         {
128 |  |
             // nothing to do
129 | 34 |
         }
130 |  |
131 |  |
         /**
132 |  |
          * Set the configuration object for the services broker.
133 |  |
          * This is the configuration that contains information
134 |  |
          * about all services in the care of this service
135 |  |
          * manager.
136 |  |
          *
137 |  |
          * @param configuration Broker configuration.
138 |  |
          */
139 |  |
         public void setConfiguration(Configuration configuration)
140 |  |
         {
141 | 44 |
             this.configuration = configuration;
142 | 44 |
         }
143 |  |
144 |  |
         /**
145 |  |
          * Get the configuration for this service manager.
146 |  |
          *
147 |  |
          * @return Broker configuration.
148 |  |
          */
149 |  |
         public Configuration getConfiguration()
150 |  |
         {
151 | 0 |
             return configuration;
152 |  |
         }
153 |  |
154 |  |
         /**
155 |  |
          * Initialize this service manager.
156 |  |
          * @throws InitializationException if the initialization fails
157 |  |
          */
158 |  |
         public void init() throws InitializationException
159 |  |
         {
160 |  |
             // Check:
161 |  |
             //
162 |  |
             // 1. The configuration has been set.
163 |  |
             // 2. Make sure the application root has been set.
164 |  |
165 |  |
             // FIXME: Make some service framework exceptions to throw in
166 |  |
             // the event these requirements aren't satisfied.
167 |  |
168 |  |
             // Create the mapping between service names
169 |  |
             // and their classes.
170 | 44 |
             initMapping();
171 |  |
172 |  |
             // Start services that have their 'earlyInit'
173 |  |
             // property set to 'true'.
174 | 44 |
             initServices(false);
175 | 44 |
         }
176 |  |
177 |  |
         /**
178 |  |
          * Set an application specific service object
179 |  |
          * that can be used by application specific
180 |  |
          * services.
181 |  |
          *
182 |  |
          * @param name name of service object
183 |  |
          * @param value value of service object
184 |  |
          */
185 |  |
         public void setServiceObject(String name, Object value)
186 |  |
         {
187 | 0 |
             serviceObjects.put(name, value);
188 | 0 |
         }
189 |  |
190 |  |
         /**
191 |  |
          * Get an application specific service object.
192 |  |
          *
193 |  |
          * @param name the name of the service object
194 |  |
          * @return Object application specific service object
195 |  |
          */
196 |  |
         public Object getServiceObject(String name)
197 |  |
         {
198 | 0 |
             return serviceObjects.get(name);
199 |  |
         }
200 |  |
201 |  |
         /**
202 |  |
          * Check recursively if the given checkIfc interface is among the implemented
203 |  |
          * interfaces
204 |  |
          *
205 |  |
          * @param checkIfc interface to check for
206 |  |
          * @param interfaces interfaces to scan
207 |  |
          * @return true if the interface is implemented
208 |  |
          */
209 |  |
         private boolean checkForInterface(Class<?> checkIfc, Class<?>[] interfaces)
210 |  |
         {
211 | 2147 |
             for (Class<?> ifc : interfaces)
212 |  |
             {
213 | 980 |
                 if (ifc == checkIfc)
214 |  |
                 {
215 | 34 |
                     return true;
216 |  |
                 }
217 |  |
218 | 946 |
                 Class<?>[] subInterfaces = ifc.getInterfaces();
219 | 946 |
                 if (checkForInterface(checkIfc, subInterfaces))
220 |  |
                 {
221 | 34 |
                     return true;
222 |  |
                 }
223 |  |
             }
224 |  |
225 | 1167 |
             return false;
226 |  |
         }
227 |  |
228 |  |
         /**
229 |  |
          * Creates a mapping between Service names and class names.
230 |  |
          *
231 |  |
          * The mapping is built according to settings present in
232 |  |
          * TurbineResources.properties.  The entries should have the
233 |  |
          * following form:
234 |  |
          *
235 |  |
          * <pre>
236 |  |
          * services.MyService.classname=com.mycompany.MyServiceImpl
237 |  |
          * services.MyOtherService.classname=com.mycompany.MyOtherServiceImpl
238 |  |
          * </pre>
239 |  |
          *
240 |  |
          * <br>
241 |  |
          *
242 |  |
          * Generic ServiceBroker provides no Services.
243 |  |
          * @throws InitializationException if a service class could not be found
244 |  |
          */
245 |  |
         protected void initMapping() throws InitializationException
246 |  |
         {
247 |  |
             // we need to temporarily store the earlyInit flags to avoid
248 |  |
             // ConcurrentModificationExceptions
249 | 44 |
             Map<String, String> earlyInitFlags = new LinkedHashMap<String, String>();
250 |  |
251 |  |
             /*
252 |  |
              * These keys returned in an order that corresponds
253 |  |
              * to the order the services are listed in
254 |  |
              * the TR.props.
255 |  |
              */
256 | 44 |
             for (Iterator<String> keys = configuration.getKeys(); keys.hasNext();)
257 |  |
             {
258 | 3139 |
                 String key = keys.next();
259 | 3139 |
                 String[] keyParts = StringUtils.split(key, ".");
260 |  |
261 | 3139 |
                 if ((keyParts.length == 3)
262 |  |
                         && (keyParts[0] + ".").equals(SERVICE_PREFIX)
263 |  |
                         && ("." + keyParts[2]).equals(CLASSNAME_SUFFIX))
264 |  |
                 {
265 | 362 |
                     String serviceKey = keyParts[1];
266 | 362 |
                     log.info("Added Mapping for Service: " + serviceKey);
267 |  |
268 | 362 |
                     if (!mapping.containsKey(serviceKey))
269 |  |
                     {
270 | 289 |
                         String className = configuration.getString(key);
271 |  |
                         try
272 |  |
                         {
273 | 289 |
                             Class<?> clazz = Class.forName(className);
274 | 289 |
                             mapping.put(serviceKey, clazz);
275 |  |
276 |  |
                             // detect TurbineServiceProviders
277 | 289 |
                             if (checkForInterface(TurbineServiceProvider.class, clazz.getInterfaces()))
278 |  |
                             {
279 | 34 |
                                 log.info("Found a TurbineServiceProvider: " + serviceKey + " - initializing it early");
280 | 34 |
                                 earlyInitFlags.put(SERVICE_PREFIX + serviceKey + ".earlyInit", "true");
281 |  |
                             }
282 |  |
                         }
283 |  |
                         // those two errors must be passed to the VM
284 | 0 |
                         catch (ThreadDeath t)
285 |  |
                         {
286 | 0 |
                             throw t;
287 |  |
                         }
288 | 0 |
                         catch (OutOfMemoryError t)
289 |  |
                         {
290 | 0 |
                             throw t;
291 |  |
                         }
292 | 0 |
                         catch (ClassNotFoundException e)
293 |  |
                         {
294 | 0 |
                             throw new InitializationException("Class " + className +
295 |  |
                                 " is unavailable. Check your jars and classes.", e);
296 |  |
                         }
297 | 0 |
                         catch (NoClassDefFoundError e)
298 |  |
                         {
299 | 0 |
                             throw new InitializationException("Class " + className +
300 |  |
                                 " is unavailable. Check your jars and classes.", e);
301 | 289 |
                         }
302 |  |
                     }
303 |  |
                 }
304 | 3139 |
             }
305 |  |
306 | 44 |
             for (Map.Entry<String, String> entry : earlyInitFlags.entrySet())
307 |  |
             {
308 | 34 |
                 configuration.setProperty(entry.getKey(), entry.getValue());
309 | 34 |
             }
310 | 44 |
         }
311 |  |
312 |  |
         /**
313 |  |
          * Determines whether a service is registered in the configured
314 |  |
          * <code>TurbineResources.properties</code>.
315 |  |
          *
316 |  |
          * @param serviceName The name of the service whose existence to check.
317 |  |
          * @return Registration predicate for the desired services.
318 |  |
          */
319 |  |
         @Override
320 |  |
         public boolean isRegistered(String serviceName)
321 |  |
         {
322 | 28 |
             return (services.get(serviceName) != null);
323 |  |
         }
324 |  |
325 |  |
         /**
326 |  |
          * Returns an Iterator over all known service names.
327 |  |
          *
328 |  |
          * @return An Iterator of service names.
329 |  |
          */
330 |  |
         public Iterator<String> getServiceNames()
331 |  |
         {
332 | 92 |
             return mapping.keySet().iterator();
333 |  |
         }
334 |  |
335 |  |
         /**
336 |  |
          * Returns an Iterator over all known service names beginning with
337 |  |
          * the provided prefix.
338 |  |
          *
339 |  |
          * @param prefix The prefix against which to test.
340 |  |
          * @return An Iterator of service names which match the prefix.
341 |  |
          */
342 |  |
         public Iterator<String> getServiceNames(String prefix)
343 |  |
         {
344 | 0 |
             Set<String> keys = new LinkedHashSet<String>(mapping.keySet());
345 | 0 |
             for(Iterator<String> key = keys.iterator(); key.hasNext();)
346 |  |
             {
347 | 0 |
                 if (!key.next().startsWith(prefix))
348 |  |
                 {
349 | 0 |
                     key.remove();
350 |  |
                 }
351 |  |
             }
352 |  |
353 | 0 |
             return keys.iterator();
354 |  |
         }
355 |  |
356 |  |
         /**
357 |  |
          * Performs early initialization of specified service.
358 |  |
          *
359 |  |
          * @param name The name of the service (generally the
360 |  |
          * <code>SERVICE_NAME</code> constant of the service's interface
361 |  |
          * definition).
362 |  |
          * @throws InitializationException Initialization of this
363 |  |
          * service was not successful.
364 |  |
          */
365 |  |
         @Override
366 |  |
         public synchronized void initService(String name)
367 |  |
                 throws InitializationException
368 |  |
         {
369 |  |
             // Calling getServiceInstance(name) assures that the Service
370 |  |
             // implementation has its name and broker reference set before
371 |  |
             // initialization.
372 | 118 |
             Service instance = getServiceInstance(name);
373 |  |
374 | 118 |
             if (!instance.getInit())
375 |  |
             {
376 |  |
                 // this call might result in an indirect recursion
377 | 90 |
                 instance.init();
378 |  |
             }
379 | 107 |
         }
380 |  |
381 |  |
         /**
382 |  |
          * Performs early initialization of all services.  Failed early
383 |  |
          * initialization of a Service may be non-fatal to the system,
384 |  |
          * thus any exceptions are logged and the initialization process
385 |  |
          * continues.
386 |  |
          */
387 |  |
         public void initServices()
388 |  |
         {
389 |  |
             try
390 |  |
             {
391 | 0 |
                 initServices(false);
392 |  |
             }
393 | 0 |
             catch (InstantiationException notThrown)
394 |  |
             {
395 | 0 |
                 log.debug("Caught non fatal exception", notThrown);
396 |  |
             }
397 | 0 |
             catch (InitializationException notThrown)
398 |  |
             {
399 | 0 |
                 log.debug("Caught non fatal exception", notThrown);
400 | 0 |
             }
401 | 0 |
         }
402 |  |
403 |  |
         /**
404 |  |
          * Performs early initialization of all services. You can decide
405 |  |
          * to handle failed initializations if you wish, but then
406 |  |
          * after one service fails, the other will not have the chance
407 |  |
          * to initialize.
408 |  |
          *
409 |  |
          * @param report <code>true</code> if you want exceptions thrown.
410 |  |
          * @throws InstantiationException if the service could not be instantiated
411 |  |
          * @throws InitializationException if the service could not be initialized
412 |  |
          */
413 |  |
         public void initServices(boolean report)
414 |  |
                 throws InstantiationException, InitializationException
415 |  |
         {
416 | 44 |
             if (report)
417 |  |
             {
418 |  |
                 // Throw exceptions
419 | 0 |
                 for (Iterator<String> names = getServiceNames(); names.hasNext();)
420 |  |
                 {
421 | 0 |
                     doInitService(names.next());
422 |  |
                 }
423 |  |
             }
424 |  |
             else
425 |  |
             {
426 |  |
                 // Eat exceptions
427 | 44 |
                 for (Iterator<String> names = getServiceNames(); names.hasNext();)
428 |  |
                 {
429 |  |
                     try
430 |  |
                     {
431 | 362 |
                         doInitService(names.next());
432 |  |
                     }
433 |  |
                             // In case of an exception, file an error message; the
434 |  |
                             // system may be still functional, though.
435 | 0 |
                     catch (InstantiationException e)
436 |  |
                     {
437 | 0 |
                         log.error(e);
438 |  |
                     }
439 | 11 |
                     catch (InitializationException e)
440 |  |
                     {
441 | 11 |
                         log.error(e);
442 | 362 |
                     }
443 |  |
                 }
444 |  |
             }
445 | 44 |
             log.info("Finished initializing all services!");
446 | 44 |
         }
447 |  |
448 |  |
         /**
449 |  |
          * Internal utility method for use in {@link #initServices(boolean)}
450 |  |
          * to prevent duplication of code.
451 |  |
          */
452 |  |
         private void doInitService(String name)
453 |  |
                 throws InstantiationException, InitializationException
454 |  |
         {
455 |  |
             // Only start up services that have their earlyInit flag set.
456 | 362 |
             if (getConfiguration(name).getBoolean("earlyInit", false))
457 |  |
             {
458 | 118 |
                 log.info("Start Initializing service (early): " + name);
459 | 118 |
                 initService(name);
460 | 107 |
                 log.info("Finish Initializing service (early): " + name);
461 |  |
             }
462 | 351 |
         }
463 |  |
464 |  |
         /**
465 |  |
          * Shuts down a <code>Service</code>, releasing resources
466 |  |
          * allocated by an <code>Service</code>, and returns it to its
467 |  |
          * initial (uninitialized) state.
468 |  |
          *
469 |  |
          * @param name The name of the <code>Service</code> to be
470 |  |
          * uninitialized.
471 |  |
          */
472 |  |
         @Override
473 |  |
         public synchronized void shutdownService(String name)
474 |  |
         {
475 |  |
             try
476 |  |
             {
477 | 400 |
                 Service service = getServiceInstance(name);
478 | 400 |
                 if (service != null && service.getInit())
479 |  |
                 {
480 | 309 |
                     service.shutdown();
481 |  |
482 | 309 |
                     if (service.getInit() && service instanceof BaseService)
483 |  |
                     {
484 |  |
                         // BaseService::shutdown() does this by default,
485 |  |
                         // but could've been overriden poorly.
486 | 16 |
                         ((BaseService) service).setInit(false);
487 |  |
                     }
488 |  |
                 }
489 |  |
             }
490 | 0 |
             catch (InstantiationException e)
491 |  |
             {
492 |  |
                 // Assuming harmless -- log the error and continue.
493 | 0 |
                 log.error("Shutdown of a nonexistent Service '"
494 |  |
                         + name + "' was requested", e);
495 | 400 |
             }
496 | 400 |
         }
497 |  |
498 |  |
         /**
499 |  |
          * Shuts down all Turbine services, releasing allocated resources and
500 |  |
          * returning them to their initial (uninitialized) state.
501 |  |
          */
502 |  |
         @Override
503 |  |
         public void shutdownServices()
504 |  |
         {
505 | 47 |
             log.info("Shutting down all services!");
506 |  |
507 | 47 |
             String serviceName = null;
508 |  |
509 |  |
             /*
510 |  |
              * Now we want to reverse the order of
511 |  |
              * this list. This functionality should be added to
512 |  |
              * the ExtendedProperties in the commons but
513 |  |
              * this will fix the problem for now.
514 |  |
              */
515 |  |
516 | 47 |
             ArrayList<String> reverseServicesList = new ArrayList<String>();
517 |  |
518 | 47 |
             for (Iterator<String> serviceNames = getServiceNames(); serviceNames.hasNext();)
519 |  |
             {
520 | 398 |
                 serviceName = serviceNames.next();
521 | 398 |
                 reverseServicesList.add(0, serviceName);
522 |  |
             }
523 |  |
524 | 47 |
             for (Iterator<String> serviceNames = reverseServicesList.iterator(); serviceNames.hasNext();)
525 |  |
             {
526 | 398 |
                 serviceName = serviceNames.next();
527 | 398 |
                 log.info("Shutting down service: " + serviceName);
528 | 398 |
                 shutdownService(serviceName);
529 |  |
             }
530 | 47 |
         }
531 |  |
532 |  |
         /**
533 |  |
          * Returns an instance of requested Service.
534 |  |
          *
535 |  |
          * @param name The name of the Service requested.
536 |  |
          * @return An instance of requested Service.
537 |  |
          * @throws InstantiationException if the service is unknown or
538 |  |
          * can't be initialized.
539 |  |
          */
540 |  |
         @Override
541 |  |
         public Object getService(String name) throws InstantiationException
542 |  |
         {
543 |  |
             Service service;
544 |  |
545 | 2529 |
             if (this.isLocalService(name))
546 |  |
             {
547 |  |
                     try
548 |  |
                     {
549 | 2007 |
                         service = getServiceInstance(name);
550 | 2007 |
                         if (!service.getInit())
551 |  |
                         {
552 | 226 |
                             synchronized (service.getClass())
553 |  |
                             {
554 | 226 |
                                 if (!service.getInit())
555 |  |
                                 {
556 | 226 |
                                     log.info("Start Initializing service (late): " + name);
557 | 226 |
                                     service.init();
558 | 222 |
                                     log.info("Finish Initializing service (late): " + name);
559 |  |
                                 }
560 | 222 |
                             }
561 |  |
                         }
562 | 2003 |
                         if (!service.getInit())
563 |  |
                         {
564 |  |
                             // this exception will be caught & rethrown by this very method.
565 |  |
                             // getInit() returning false indicates some initialization issue,
566 |  |
                             // which in turn prevents the InitableBroker from passing a
567 |  |
                             // reference to a working instance of the initable to the client.
568 | 0 |
                             throw new InitializationException(
569 |  |
                                     "init() failed to initialize service " + name);
570 |  |
                         }
571 | 2003 |
                         return service;
572 |  |
                     }
573 | 0 |
                     catch (InitializationException e)
574 |  |
                     {
575 | 0 |
                         throw new InstantiationException("Service " + name +
576 |  |
                                 " failed to initialize", e);
577 |  |
                     }
578 |  |
             }
579 | 522 |
             else if (this.isNonLocalService(name))
580 |  |
             {
581 | 506 |
                 return this.getNonLocalService(name);
582 |  |
             }
583 |  |
             else
584 |  |
             {
585 | 16 |
                 throw new InstantiationException(
586 |  |
                     "ServiceBroker: unknown service " + name
587 |  |
                     + " requested");
588 |  |
             }
589 |  |
         }
590 |  |
591 |  |
         /**
592 |  |
          * Retrieves an instance of a Service without triggering late
593 |  |
          * initialization.
594 |  |
          *
595 |  |
          * Early initialization of a Service can require access to Service
596 |  |
          * properties.  The Service must have its name and serviceBroker
597 |  |
          * set by then.  Therefore, before calling
598 |  |
          * Initable.initClass(Object), the class must be instantiated with
599 |  |
          * InitableBroker.getInitableInstance(), and
600 |  |
          * Service.setServiceBroker() and Service.setName() must be
601 |  |
          * called.  This calls for two - level accessing the Services
602 |  |
          * instances.
603 |  |
          *
604 |  |
          * @param name The name of the service requested.
605 |  |
          *
606 |  |
          * @return the Service instance
607 |  |
          *
608 |  |
          * @throws InstantiationException The service is unknown or
609 |  |
          * can't be initialized.
610 |  |
          */
611 |  |
         protected Service getServiceInstance(String name)
612 |  |
                 throws InstantiationException
613 |  |
         {
614 | 2525 |
             Service service = services.get(name);
615 |  |
616 | 2525 |
             if (service == null)
617 |  |
             {
618 | 289 |
                 serviceLock.lock();
619 |  |
620 |  |
                 try
621 |  |
                 {
622 |  |
                     // Double check
623 | 289 |
                     service = services.get(name);
624 |  |
625 | 289 |
                     if (service == null)
626 |  |
                     {
627 | 289 |
                         if (!this.isLocalService(name))
628 |  |
                         {
629 | 0 |
                             throw new InstantiationException(
630 |  |
                                     "ServiceBroker: unknown service " + name
631 |  |
                                     + " requested");
632 |  |
                         }
633 |  |
634 |  |
                         try
635 |  |
                         {
636 | 289 |
                             Class<?> clazz = mapping.get(name);
637 |  |
638 |  |
                             try
639 |  |
                             {
640 | 289 |
                                 service = (Service) clazz.newInstance();
641 |  |
642 |  |
                                 // check if the newly created service is also a
643 |  |
                                 // service provider - if so then remember it
644 | 289 |
                                 if (service instanceof TurbineServiceProvider)
645 |  |
                                 {
646 | 34 |
                                     Service _service = this.serviceProviderInstanceMap.putIfAbsent(name,service);
647 | 34 |
                                     if (_service != null)
648 |  |
                                     {
649 | 0 |
                                         service = _service;
650 |  |
                                     }
651 |  |
                                 }
652 |  |
                             }
653 |  |
                             // those two errors must be passed to the VM
654 | 0 |
                             catch (ClassCastException e)
655 |  |
                             {
656 | 0 |
                                 throw new InstantiationException("Class " + clazz +
657 |  |
                                         " doesn't implement the Service interface", e);
658 |  |
                             }
659 | 0 |
                             catch (ThreadDeath t)
660 |  |
                             {
661 | 0 |
                                 throw t;
662 |  |
                             }
663 | 0 |
                             catch (OutOfMemoryError t)
664 |  |
                             {
665 | 0 |
                                 throw t;
666 |  |
                             }
667 | 0 |
                             catch (Throwable t)
668 |  |
                             {
669 | 0 |
                                 throw new InstantiationException("Failed to instantiate " + clazz, t);
670 | 289 |
                             }
671 |  |
                         }
672 | 0 |
                         catch (InstantiationException e)
673 |  |
                         {
674 | 0 |
                             throw new InstantiationException(
675 |  |
                                     "Failed to instantiate service " + name, e);
676 | 289 |
                         }
677 | 289 |
                         service.setServiceBroker(this);
678 | 289 |
                         service.setName(name);
679 | 289 |
                         Service _service = services.putIfAbsent(name, service);
680 | 289 |
                         if (_service != null) // Unlikely
681 |  |
                         {
682 | 0 |
                             service = _service;
683 |  |
                         }
684 |  |
                     }
685 |  |
                 }
686 |  |
                 finally
687 |  |
                 {
688 | 289 |
                     serviceLock.unlock();
689 | 289 |
                 }
690 |  |
             }
691 |  |
692 | 2525 |
             return service;
693 |  |
         }
694 |  |
695 |  |
         /**
696 |  |
          * Returns the configuration for the specified service.
697 |  |
          *
698 |  |
          * @param name The name of the service.
699 |  |
          * @return Configuration of requested Service.
700 |  |
          */
701 |  |
         @Override
702 |  |
         public Configuration getConfiguration(String name)
703 |  |
         {
704 | 564 |
             return configuration.subset(SERVICE_PREFIX + name);
705 |  |
         }
706 |  |
707 |  |
         /**
708 |  |
          * Set the application root.
709 |  |
          *
710 |  |
          * @param applicationRoot application root
711 |  |
          */
712 |  |
         public void setApplicationRoot(String applicationRoot)
713 |  |
         {
714 | 44 |
             this.applicationRoot = applicationRoot;
715 | 44 |
         }
716 |  |
717 |  |
         /**
718 |  |
          * Get the application root as set by
719 |  |
          * the parent application.
720 |  |
          *
721 |  |
          * @return String application root
722 |  |
          */
723 |  |
         public String getApplicationRoot()
724 |  |
         {
725 | 0 |
             return applicationRoot;
726 |  |
         }
727 |  |
728 |  |
         /**
729 |  |
          * Determines if the requested service is managed by this
730 |  |
          * ServiceBroker.
731 |  |
          *
732 |  |
          * @param name The name of the Service requested.
733 |  |
          * @return true if the service is managed by the this ServiceBroker
734 |  |
          */
735 |  |
         protected boolean isLocalService(String name)
736 |  |
         {
737 | 2818 |
             return this.mapping.containsKey(name);
738 |  |
         }
739 |  |
740 |  |
         /**
741 |  |
          * Determines if the requested service is managed by an initialized
742 |  |
          * TurbineServiceProvider. We use the service names to lookup
743 |  |
          * the TurbineServiceProvider to ensure that we get a fully
744 |  |
          * initialized service.
745 |  |
          *
746 |  |
          * @param name The name of the Service requested.
747 |  |
          * @return true if the service is managed by a TurbineServiceProvider
748 |  |
          */
749 |  |
         protected boolean isNonLocalService(String name)
750 |  |
         {
751 | 522 |
             TurbineServiceProvider turbineServiceProvider = null;
752 |  |
753 | 522 |
             for (Map.Entry<String, Service> entry : this.serviceProviderInstanceMap.entrySet())
754 |  |
             {
755 | 522 |
                 turbineServiceProvider = (TurbineServiceProvider) this.getService(entry.getKey());
756 |  |
757 | 522 |
                 if (turbineServiceProvider.exists(name))
758 |  |
                 {
759 | 506 |
                     return true;
760 |  |
                 }
761 | 16 |
             }
762 |  |
763 | 16 |
             return false;
764 |  |
         }
765 |  |
766 |  |
         /**
767 |  |
          * Get a non-local service managed by a TurbineServiceProvider.
768 |  |
          *
769 |  |
          * @param name The name of the Service requested.
770 |  |
          * @return the requested service
771 |  |
          * @throws InstantiationException the service couldn't be instantiated
772 |  |
          */
773 |  |
         protected Object getNonLocalService(String name)
774 |  |
                 throws InstantiationException
775 |  |
         {
776 | 506 |
             TurbineServiceProvider turbineServiceProvider = null;
777 |  |
778 | 506 |
             for (Map.Entry<String, Service> entry : this.serviceProviderInstanceMap.entrySet())
779 |  |
             {
780 | 506 |
                 turbineServiceProvider = (TurbineServiceProvider) this.getService(entry.getKey());
781 |  |
782 | 506 |
                 if (turbineServiceProvider.exists(name))
783 |  |
                 {
784 | 506 |
                     return turbineServiceProvider.get(name);
785 |  |
                 }
786 | 0 |
             }
787 |  |
788 | 0 |
             throw new InstantiationException(
789 |  |
                 "ServiceBroker: unknown non-local service " + name
790 |  |
                 + " requested");
791 |  |
         }
792 |  |
     }