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    }