001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.xbean.kernel.standard;
018
019 import java.util.ArrayList;
020 import java.util.Arrays;
021 import java.util.HashMap;
022 import java.util.Iterator;
023 import java.util.LinkedHashSet;
024 import java.util.LinkedList;
025 import java.util.List;
026 import java.util.Map;
027 import java.util.Set;
028 import java.util.SortedSet;
029 import java.util.TreeSet;
030
031 import java.util.concurrent.ExecutionException;
032 import java.util.concurrent.atomic.AtomicLong;
033 import org.apache.xbean.kernel.IllegalServiceStateException;
034 import org.apache.xbean.kernel.KernelErrorsError;
035 import org.apache.xbean.kernel.KernelOperationInterruptedException;
036 import org.apache.xbean.kernel.ServiceAlreadyExistsException;
037 import org.apache.xbean.kernel.ServiceFactory;
038 import org.apache.xbean.kernel.ServiceName;
039 import org.apache.xbean.kernel.ServiceNotFoundException;
040 import org.apache.xbean.kernel.ServiceRegistrationException;
041 import org.apache.xbean.kernel.StopStrategies;
042 import org.apache.xbean.kernel.StopStrategy;
043 import org.apache.xbean.kernel.UnsatisfiedConditionsException;
044
045 /**
046 * The StandardServiceRegistry manages the registration of ServiceManagers for the kernel.
047 *
048 * @author Dain Sundstrom
049 * @version $Id$
050 * @since 2.0
051 */
052 public class ServiceManagerRegistry {
053 /**
054 * The sequence used for the serviceId assigned to service managers.
055 */
056 private final AtomicLong serviceId = new AtomicLong(1);
057
058 /**
059 * The factory used to create service managers.
060 */
061 private final ServiceManagerFactory serviceManagerFactory;
062
063 /**
064 * The registered service managers.
065 */
066 private final Map serviceManagers = new HashMap();
067
068 /**
069 * The service managers indexed by the service type. This map is populated when a service enters the running state.
070 */
071 private final Map serviceManagersByType = new HashMap();
072
073 /**
074 * Creates a ServiceManagerRegistry that uses the specified service manager factory to create new service managers.
075 *
076 * @param serviceManagerFactory the factory for new service managers
077 */
078 public ServiceManagerRegistry(ServiceManagerFactory serviceManagerFactory) {
079 this.serviceManagerFactory = serviceManagerFactory;
080 }
081
082 /**
083 * Stops and destroys all services service managers. This method will FORCE stop the services if necessary.
084 *
085 * @throws KernelErrorsError if any errors occur while stopping or destroying the service managers
086 */
087 public void destroy() throws KernelErrorsError {
088 // we gather all errors that occur during shutdown and throw them as on huge exception
089 List errors = new ArrayList();
090
091 List managerFutures;
092 synchronized (serviceManagers) {
093 managerFutures = new ArrayList(serviceManagers.values());
094 serviceManagers.clear();
095
096 }
097
098 List managers = new ArrayList(managerFutures.size());
099 for (Iterator iterator = managerFutures.iterator(); iterator.hasNext();) {
100 RegistryFutureTask registryFutureTask = (RegistryFutureTask) iterator.next();
101 try {
102 managers.add(registryFutureTask.get());
103 } catch (InterruptedException e) {
104 // ignore -- this should not happen
105 errors.add(new AssertionError(e));
106 } catch (ExecutionException e) {
107 // good -- one less manager to deal with
108 }
109 }
110
111 // Be nice and try to stop asynchronously
112 errors.addAll(stopAll(managers, StopStrategies.ASYNCHRONOUS));
113
114 // Be really nice and try to stop asynchronously again
115 errors.addAll(stopAll(managers, StopStrategies.ASYNCHRONOUS));
116
117 // We have been nice enough now nuke them
118 errors.addAll(stopAll(managers, StopStrategies.FORCE));
119
120 // All managers are gaurenteed to be destroyed now
121 for (Iterator iterator = managers.iterator(); iterator.hasNext();) {
122 ServiceManager serviceManager = (ServiceManager) iterator.next();
123 try {
124 serviceManager.destroy(StopStrategies.FORCE);
125 } catch (UnsatisfiedConditionsException e) {
126 // this should not happen, because we force stopped
127 errors.add(new AssertionError(e));
128 } catch (IllegalServiceStateException e) {
129 // this should not happen, because we force stopped
130 errors.add(new AssertionError(e));
131 } catch (RuntimeException e) {
132 errors.add(new AssertionError(e));
133 } catch (Error e) {
134 errors.add(new AssertionError(e));
135 }
136 }
137
138 if (!errors.isEmpty()) {
139 throw new KernelErrorsError(errors);
140 }
141 }
142
143 private List stopAll(List managers, StopStrategy stopStrategy) {
144 List errors = new ArrayList();
145 for (Iterator iterator = managers.iterator(); iterator.hasNext();) {
146 ServiceManager serviceManager = (ServiceManager) iterator.next();
147 try {
148 serviceManager.stop(stopStrategy);
149 } catch (UnsatisfiedConditionsException e) {
150 // this should not happen in with an asynchronous strategy
151 errors.add(new AssertionError(e));
152 } catch (RuntimeException e) {
153 errors.add(new AssertionError(e));
154 } catch (Error e) {
155 errors.add(new AssertionError(e));
156 }
157 }
158 return errors;
159 }
160
161 /**
162 * Determines if there is a service registered under the specified name.
163 *
164 * @param serviceName the unique name of the service
165 * @return true if there is a service registered with the specified name; false otherwise
166 */
167 public boolean isRegistered(ServiceName serviceName) {
168 if (serviceName == null) throw new NullPointerException("serviceName is null");
169
170 RegistryFutureTask registryFutureTask;
171 synchronized (serviceManagers) {
172 registryFutureTask = (RegistryFutureTask) serviceManagers.get(serviceName);
173 }
174 try {
175 // the service is registered if we have a non-null future value
176 return registryFutureTask != null && registryFutureTask.get() != null;
177 } catch (InterruptedException e) {
178 throw new KernelOperationInterruptedException(e, serviceName, "isRegistered");
179 } catch (ExecutionException e) {
180 return false;
181 }
182 }
183
184 /**
185 * Gets the service manager registered under the specified name.
186 *
187 * @param serviceName the unique name of the service
188 * @return the ServiceManager
189 * @throws ServiceNotFoundException if there is no service registered under the specified name
190 */
191 public ServiceManager getServiceManager(ServiceName serviceName) throws ServiceNotFoundException {
192 if (serviceName == null) throw new NullPointerException("serviceName is null");
193
194 RegistryFutureTask registryFutureTask;
195 synchronized (serviceManagers) {
196 registryFutureTask = (RegistryFutureTask) serviceManagers.get(serviceName);
197 }
198
199 // this service has no future
200 if (registryFutureTask == null) {
201 throw new ServiceNotFoundException(serviceName);
202 }
203
204 try {
205 ServiceManager serviceManager = (ServiceManager) registryFutureTask.get();
206 if (serviceManager == null) {
207 throw new ServiceNotFoundException(serviceName);
208 }
209 return serviceManager;
210 } catch (InterruptedException e) {
211 throw new KernelOperationInterruptedException(e, serviceName, "getServiceManager");
212 } catch (ExecutionException e) {
213 // registration threw an exception which means it didn't register
214 throw new ServiceNotFoundException(serviceName);
215 }
216 }
217
218 /**
219 * Gets the first registered service manager that creates an instance of the specified type, or null if no service
220 * managers create an instance of the specified type.
221 *
222 * @param type the of the desired service
223 * @return the first registered service manager that creates an instance of the specified type, or null if none found
224 */
225 public ServiceManager getServiceManager(Class type) {
226 SortedSet serviceManagerFutures = getServiceManagerFutures(type);
227 for (Iterator iterator = serviceManagerFutures.iterator(); iterator.hasNext();) {
228 RegistryFutureTask registryFutureTask = (RegistryFutureTask) iterator.next();
229 try {
230 ServiceManager serviceManager = (ServiceManager) registryFutureTask.get();
231 if (serviceManager != null) {
232 return serviceManager;
233 }
234 } catch (InterruptedException e) {
235 throw new KernelOperationInterruptedException(e, registryFutureTask.getServiceName(), "getServiceManagers(java.lang.Class)");
236 } catch (ExecutionException ignored) {
237 // registration threw an exception which means it didn't register
238 }
239 }
240 return null;
241 }
242
243 /**
244 * Gets all service managers that create an instances of the specified type, or an empty list if no service
245 * managers create an instance of the specified type.
246 *
247 * @param type the of the desired service managers
248 * @return all service managers that create an instances of the specified type, or an empty list if none found
249 */
250 public List getServiceManagers(Class type) {
251 SortedSet serviceManagerFutures = getServiceManagerFutures(type);
252 List serviceManagers = new ArrayList(serviceManagerFutures.size());
253 for (Iterator iterator = serviceManagerFutures.iterator(); iterator.hasNext();) {
254 RegistryFutureTask registryFutureTask = (RegistryFutureTask) iterator.next();
255 try {
256 ServiceManager serviceManager = (ServiceManager) registryFutureTask.get();
257 if (serviceManager != null) {
258 serviceManagers.add(serviceManager);
259 }
260 } catch (InterruptedException e) {
261 throw new KernelOperationInterruptedException(e, registryFutureTask.getServiceName(), "getServiceManagers(java.lang.Class)");
262 } catch (ExecutionException ignored) {
263 // registration threw an exception which means it didn't register
264 }
265 }
266 return serviceManagers;
267 }
268
269 /**
270 * Gets the first registed and running service that is an instance of the specified type, or null if no instances
271 * of the specified type are running.
272 *
273 * @param type the of the desired service
274 * @return the first registed and running service that is an instance of the specified type or null if none found
275 */
276 public synchronized Object getService(Class type) {
277 SortedSet serviceManagerFutures = getServiceManagerFutures(type);
278 for (Iterator iterator = serviceManagerFutures.iterator(); iterator.hasNext();) {
279 RegistryFutureTask registryFutureTask = (RegistryFutureTask) iterator.next();
280 try {
281 ServiceManager serviceManager = (ServiceManager) registryFutureTask.get();
282 if (serviceManager != null) {
283 Object service = serviceManager.getService();
284 if (service != null) {
285 return service;
286 }
287 }
288 } catch (InterruptedException e) {
289 throw new KernelOperationInterruptedException(e, registryFutureTask.getServiceName(), "getService(java.lang.Class)");
290 } catch (ExecutionException ignored) {
291 // registration threw an exception which means it didn't register
292 }
293 }
294 return null;
295 }
296
297 /**
298 * Gets the all of running service that are an instances of the specified type, or an empty list if no instances
299 * of the specified type are running.
300 *
301 * @param type the of the desired service
302 * @return the all of running service that are an instances of the specified type, or an empty list if none found
303 */
304 public synchronized List getServices(Class type) {
305 List serviceManagers = getServiceManagers(type);
306 List services = new ArrayList(serviceManagers.size());
307 for (Iterator iterator = serviceManagers.iterator(); iterator.hasNext();) {
308 ServiceManager serviceManager = (ServiceManager) iterator.next();
309 if (serviceManager != null) {
310 Object service = serviceManager.getService();
311 if (service != null) {
312 services.add(service);
313 }
314 }
315 }
316 return services;
317 }
318
319 private SortedSet getServiceManagerFutures(Class type) {
320 SortedSet serviceManagerFutures;
321 synchronized (serviceManagers) {
322 serviceManagerFutures = (SortedSet) serviceManagersByType.get(type);
323 if (serviceManagerFutures != null) {
324 serviceManagerFutures = new TreeSet(serviceManagerFutures);
325 } else {
326 serviceManagerFutures = new TreeSet();
327 }
328 }
329 return serviceManagerFutures;
330 }
331
332 /**
333 * Creates a ServiceManager and registers it under the specified name. If the service is restartable, it will
334 * enter the server in the STOPPED state. If a service is not restartable, the service manager will assure that all
335 * dependencies are satisfied and service will immediately enter in the RUNNING state. If a
336 * dependency for a non-restartable service is not immediately satisfiable, this method will throw a
337 * ServiceRegistrationException.
338 *
339 * @param serviceName the unique name of the service
340 * @param serviceFactory the factory used to create the service
341 * @throws ServiceAlreadyExistsException if service is already registered with the specified name
342 * @throws ServiceRegistrationException if the service is not restartable and an error occured while starting the service
343 */
344 public void registerService(ServiceName serviceName, ServiceFactory serviceFactory) throws ServiceAlreadyExistsException, ServiceRegistrationException {
345 if (serviceName == null) throw new NullPointerException("serviceName is null");
346 if (serviceFactory == null) throw new NullPointerException("serviceFactory is null");
347
348 if (!serviceFactory.isEnabled()) {
349 throw new ServiceRegistrationException(serviceName,
350 new IllegalServiceStateException("A disabled non-restartable service factory can not be registered", serviceName));
351 }
352
353 RegistryFutureTask registrationTask = null;
354
355 //
356 // This loop will continue until we put our registrationTask in the serviceManagers map. If at any point,
357 // we discover that there is already a service registered under the specified service name, we will throw
358 // a ServiceAlreadyExistsException exiting this method.
359 //
360 while (registrationTask == null) {
361 RegistryFutureTask existingRegistration;
362 synchronized (serviceManagers) {
363 existingRegistration = (RegistryFutureTask) serviceManagers.get(serviceName);
364
365 // if we do not have an existing registration or the existing registration task is complete
366 // we can create the new registration task; otherwise we need to wait for the existing registration to
367 // finish out side of the synchronized lock on serviceManagers.
368 if (existingRegistration == null || existingRegistration.isDone()) {
369 // if we have a valid existing registration, throw a ServiceAlreadyExistsException
370 if (existingRegistration != null) {
371 try {
372 boolean alreadyRegistered = (existingRegistration.get() != null);
373 if (alreadyRegistered) {
374 throw new ServiceAlreadyExistsException(serviceName);
375 }
376 } catch (InterruptedException e) {
377 throw new KernelOperationInterruptedException(e, serviceName, "registerService");
378 } catch (ExecutionException e) {
379 // the previous registration threw an exception.. we can continure as normal
380 }
381 }
382
383 // we are ready to register our serviceManager
384 existingRegistration = null;
385 ServiceManager serviceManager = serviceManagerFactory.createServiceManager(serviceId.getAndIncrement(),
386 serviceName,
387 serviceFactory);
388 registrationTask = RegistryFutureTask.createRegisterTask(serviceManager);
389 serviceManagers.put(serviceName, registrationTask);
390 addTypeIndex(serviceManager, registrationTask);
391 }
392 }
393
394 // If there is an unfinished exiting registration task, wait until it is done executing
395 if (existingRegistration != null) {
396 try {
397 existingRegistration.get();
398 // we don't throw an error here because we want to check in the synchronized block that this
399 // future is still registered in the serviceManagers map
400 } catch (InterruptedException e) {
401 throw new KernelOperationInterruptedException(e, serviceName, "registerService");
402 } catch (ExecutionException e) {
403 // good
404 }
405 }
406 }
407
408 // run our registration task and check the results
409 registrationTask.run();
410 try {
411 // if initialization completed successfully, this method will not throw an exception
412 registrationTask.get();
413 } catch (InterruptedException e) {
414 throw new KernelOperationInterruptedException(e, serviceName, "registerService");
415 } catch (ExecutionException e) {
416 // registration failed, remove our task
417 synchronized (serviceManagers) {
418 // make sure our task is still the registered one
419 if (serviceManagers.get(serviceName) == registrationTask) {
420 serviceManagers.remove(serviceName);
421 removeTypeIndex(registrationTask);
422 }
423 }
424 throw new ServiceRegistrationException(serviceName, e.getCause());
425 }
426 }
427
428 /**
429 * Stops and destorys the ServiceManager and then unregisters it. The ServiceManagerRegistry will attempt to stop
430 * the service using the specified stop strategy, but if the service can not be stopped a
431 * ServiceRegistrationException will be thrown containing either an UnsatisfiedConditionsException or an
432 * IllegalServiceStateException.
433 *
434 * @param serviceName the unique name of the service
435 * @param stopStrategy the strategy that determines how unsatisfied conditions are handled
436 * @throws ServiceNotFoundException if there is no service registered under the specified name
437 * @throws ServiceRegistrationException if the service could not be stopped
438 */
439 public void unregisterService(ServiceName serviceName, StopStrategy stopStrategy) throws ServiceNotFoundException, ServiceRegistrationException {
440 if (serviceName == null) throw new NullPointerException("serviceName is null");
441 if (stopStrategy == null) throw new NullPointerException("stopStrategy is null");
442
443 RegistryFutureTask unregistrationTask = null;
444
445 //
446 // This loop will continue until we put our unregistrationTask in the serviceManagers map. If at any point,
447 // we discover that there actually is not a service registered under the specified service name, we will throw
448 // a ServiceNotFoundException exiting this method.
449 //
450 while (unregistrationTask == null) {
451 RegistryFutureTask existingRegistration;
452 synchronized (serviceManagers) {
453 existingRegistration = (RegistryFutureTask) serviceManagers.get(serviceName);
454 if (existingRegistration == null) {
455 throw new ServiceNotFoundException(serviceName);
456 }
457
458 // if existing registration is done running, we can destroy it
459 if (existingRegistration.isDone()) {
460 ServiceManager serviceManager = null;
461 try {
462 serviceManager = (ServiceManager) existingRegistration.get();
463 } catch (InterruptedException e) {
464 throw new KernelOperationInterruptedException(e, serviceName, "unregisterService");
465 } catch (ExecutionException e) {
466 // good
467 }
468
469 // if there isn't a registered manager that is an exception
470 if (serviceManager == null) {
471 throw new ServiceNotFoundException(serviceName);
472 }
473
474 // we are ready to register our serviceManager
475 existingRegistration = null;
476 unregistrationTask = RegistryFutureTask.createUnregisterTask(serviceManager, stopStrategy);
477 serviceManagers.put(serviceName, unregistrationTask);
478 addTypeIndex(serviceManager, unregistrationTask);
479 }
480 }
481
482
483 // If there is an unfinished exiting registration task, wait until it is done executing
484 if (existingRegistration != null) {
485 try {
486 existingRegistration.get();
487 // we don't throw an error here because we want to check in the synchronized block that this
488 // future is still registered in the serviceManagers map
489 } catch (InterruptedException e) {
490 throw new KernelOperationInterruptedException(e, serviceName, "unregisterService");
491 } catch (ExecutionException e) {
492 // good
493 }
494 }
495 }
496
497 unregistrationTask.run();
498 try {
499 // if get returns any value other then null, the unregistration failed
500 if (unregistrationTask.get() == null) {
501 // unregistration was successful, remove the furuture object
502 synchronized (serviceManagers) {
503 // make sure our task is still the registered one
504 if (serviceManagers.get(serviceName) == unregistrationTask) {
505 serviceManagers.remove(serviceName);
506 removeTypeIndex(unregistrationTask);
507 }
508 }
509 } else {
510 synchronized (unregistrationTask) {
511 // the root exception is contained in the exception handle
512 throw new ServiceRegistrationException(serviceName, unregistrationTask.getThrowable());
513 }
514 }
515 } catch (InterruptedException e) {
516 throw new KernelOperationInterruptedException(e, serviceName, "unregisterService");
517 } catch (ExecutionException e) {
518 // this won't happen
519 throw new AssertionError(e);
520 }
521 }
522
523 private void addTypeIndex(ServiceManager serviceManager, RegistryFutureTask registryFutureTask) {
524 if (serviceManager == null) throw new NullPointerException("serviceManager is null");
525 if (registryFutureTask == null) throw new NullPointerException("serviceManagerFuture is null");
526
527 Set allTypes = new LinkedHashSet();
528 for (Iterator iterator = serviceManager.getServiceTypes().iterator(); iterator.hasNext();) {
529 Class serviceType = (Class) iterator.next();
530
531 if (serviceType.isArray()) {
532 throw new IllegalArgumentException("Service is an array: serviceName=" + serviceManager.getServiceName() +
533 ", serviceType=" + serviceManager.getServiceTypes());
534 }
535
536 allTypes.add(serviceType);
537 allTypes.addAll(getAllSuperClasses(serviceType));
538 allTypes.addAll(getAllInterfaces(serviceType));
539 }
540
541 synchronized (serviceManagers) {
542 for (Iterator iterator = allTypes.iterator(); iterator.hasNext();) {
543 Class type = (Class) iterator.next();
544 Set futureServiceManagers = (Set) serviceManagersByType.get(type);
545 if (futureServiceManagers == null) {
546 futureServiceManagers = new TreeSet();
547 serviceManagersByType.put(type, futureServiceManagers);
548 }
549 futureServiceManagers.add(registryFutureTask);
550 }
551 }
552 }
553
554 private void removeTypeIndex(RegistryFutureTask registryFutureTask) {
555 if (registryFutureTask == null) throw new NullPointerException("serviceManagerFuture is null");
556 synchronized (serviceManagers) {
557 for (Iterator iterator = serviceManagersByType.entrySet().iterator(); iterator.hasNext();) {
558 Map.Entry entry = (Map.Entry) iterator.next();
559 Set serviceManagers = (Set) entry.getValue();
560 serviceManagers.remove(registryFutureTask);
561 if (serviceManagers.isEmpty()) {
562 iterator.remove();
563 }
564 }
565 }
566 }
567
568 private static Set getAllSuperClasses(Class clazz) {
569 Set allSuperClasses = new LinkedHashSet();
570 for (Class superClass = clazz.getSuperclass(); superClass != null; superClass = superClass.getSuperclass()) {
571 allSuperClasses.add(superClass);
572 }
573 return allSuperClasses;
574 }
575
576 private static Set getAllInterfaces(Class clazz) {
577 Set allInterfaces = new LinkedHashSet();
578 LinkedList stack = new LinkedList();
579 stack.addAll(Arrays.asList(clazz.getInterfaces()));
580 while (!stack.isEmpty()) {
581 Class intf = (Class) stack.removeFirst();
582 if (!allInterfaces.contains(intf)) {
583 allInterfaces.add(intf);
584 stack.addAll(Arrays.asList(intf.getInterfaces()));
585 }
586 }
587 return allInterfaces;
588 }
589 }