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 }