package org.limewire.lifecycle; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.limewire.concurrent.ThreadExecutor; import org.limewire.inject.EagerSingleton; import org.limewire.logging.Log; import org.limewire.logging.LogFactory; import org.limewire.util.ExceptionUtils; import org.limewire.util.Stopwatch; @EagerSingleton class ServiceRegistryImpl implements ServiceRegistry { private static final Log LOG = LogFactory.getLog(ServiceRegistryImpl.class); private final List<StagedRegisterBuilderImpl> builders = new ArrayList<StagedRegisterBuilderImpl>(); private final Map<Object, List<ServiceHolder>> services = new HashMap<Object, List<ServiceHolder>>(); private final List<ServiceHolder> startedServices = new ArrayList<ServiceHolder>(); private final List<ServiceRegistryListener> registryListeners = new ArrayList<ServiceRegistryListener>(); public void initialize() { // Remove builders & assign services. for(Iterator<StagedRegisterBuilderImpl> iter = builders.iterator(); iter.hasNext(); ) { StagedRegisterBuilderImpl builder = iter.next(); Object stage = builder.getCustomStage(); if(stage == null) stage = builder.getStage(); List<ServiceHolder> servicesInStage = services.get(stage); if(servicesInStage == null) { servicesInStage = new ArrayList<ServiceHolder>(); services.put(stage, servicesInStage); } servicesInStage.add(new ServiceHolder(builder.getService())); iter.remove(); } // Do the actual initialization. // First go through built-in stages. for(ServiceStage stage : getStagesInOrder()) { if(services.get(stage) != null) { for(ServiceHolder service : services.get(stage)) { service.init(); } } } // Then go through custom stages. for(Map.Entry<Object, List<ServiceHolder>> entry : services.entrySet()) { if(entry.getKey().getClass() != ServiceStage.class) { if(entry.getValue() != null) { for(ServiceHolder service : entry.getValue()) { service.init(); } } } } } public void start(Object stage) { initialize(); startStage(stage); } public void start() { initialize(); Stopwatch stopwatch = new Stopwatch(LOG); for(ServiceStage stage : getStagesInOrder()) { startStage(stage); } stopwatch.resetAndLog("started ServiceRegistry"); } private void startStage(Object stage) { Stopwatch stopwatch = new Stopwatch(LOG); List<ServiceHolder> servicedStages = services.get(stage); if(servicedStages != null) { for(Iterator<ServiceHolder> iter = servicedStages.iterator(); iter.hasNext(); ) { ServiceHolder service = iter.next(); try { service.start(); startedServices.add(service); } catch (Throwable e) { //catching exception to potentially allow other services to //startup if there is an error starting any other services. ExceptionUtils.reportOrReturn(e); } iter.remove(); } for (ServiceHolder startedService : startedServices) { try { startedService.join(); } catch (InterruptedException e) { e.printStackTrace(); // TODO log, throw? } } } if(LOG.isTraceEnabled()) { stopwatch.resetAndLog("started stage " + stage.toString()); } } public void stop() { Stopwatch stopwatch = new Stopwatch(LOG); for(int i = startedServices.size()-1; i >= 0; i--) { startedServices.get(i).stop(); } for(int i = startedServices.size()-1; i >= 0; i--) { try { startedServices.get(i).join(); } catch (InterruptedException e) { e.printStackTrace(); // TODO log, throw? } startedServices.remove(i); } if(LOG.isTraceEnabled()) { stopwatch.resetAndLog("stopped ServiceRegistry"); } } public StagedRegisterBuilder register(Service service) { StagedRegisterBuilderImpl builder = new StagedRegisterBuilderImpl(service); builders.add(builder); return builder; } public void addListener(ServiceRegistryListener serviceRegistryListener) { registryListeners.add(serviceRegistryListener); } ServiceStage[] getStagesInOrder() { return new ServiceStage[] { ServiceStage.EARLY, ServiceStage.NORMAL, ServiceStage.LATE, ServiceStage.VERY_LATE }; } private class ServiceHolder { private final AnnotatedService service; private boolean initted; private boolean started; private boolean stopped; public ServiceHolder(Service service) { this.service = new AnnotatedService(service); } void init() { if(!initted) { initted = true; for(ServiceRegistryListener listener : registryListeners) { listener.initializing(service); } service.initialize(); } } void start() { if(!started) { started = true; for(ServiceRegistryListener listener : registryListeners) { listener.starting(service); } service.start(); } } void stop() { if(!stopped) { stopped = true; for(ServiceRegistryListener listener : registryListeners) { listener.stopping(service); } service.stop(); } } void join() throws InterruptedException { service.join(); } private class AnnotatedService implements Service { private final Service service; private volatile Thread serviceExecutor; private volatile Asynchronous asynchronous; AnnotatedService(Service service) { this.service = service; } void join() throws InterruptedException { if(asynchronous != null) { asynchronous.join().join(serviceExecutor, asynchronous.timeout()); } } public void initialize() { service.initialize(); } public String getServiceName() { return service.getServiceName(); } public void start() { asynchronous = getAsynchronousAnnotation("start"); if(asynchronous != null) { serviceExecutor = asyncStart(); } else { Stopwatch stopwatch = new Stopwatch(LOG); service.start(); if(LOG.isTraceEnabled()) { stopwatch.resetAndLog("started " + service.getClass()); } } } private Thread asyncStart() { Thread startThread = ThreadExecutor.newManagedThread(new Runnable() { public void run() { Stopwatch stopwatch = new Stopwatch(LOG); service.start(); if(LOG.isTraceEnabled()) { stopwatch.resetAndLog("started " + service.getClass()); } } }, "ServiceRegistry-start-" + service.getServiceName()); startThread.setDaemon(asynchronous.daemon()); startThread.start(); return startThread; } public void stop() { joinOnStart(); asynchronous = getAsynchronousAnnotation("stop"); if(asynchronous != null) { serviceExecutor = asyncStop(); } else { Stopwatch stopwatch = new Stopwatch(LOG); service.stop(); if(LOG.isTraceEnabled()) { stopwatch.resetAndLog("stopped " + service.getClass()); } } } private void joinOnStart() { if(asynchronous != null) { // annotation on the "start" method try { serviceExecutor.join(); } catch (InterruptedException e) { LOG.debug("interrupted while join()'ing on start: ", e); } } } private Thread asyncStop() { Thread stopThread = ThreadExecutor.newManagedThread(new Runnable() { public void run() { Stopwatch stopwatch = new Stopwatch(LOG); service.stop(); if(LOG.isTraceEnabled()) { stopwatch.resetAndLog("stopped " + service.getClass()); } } }, "ServiceRegistry-stop-" + service.getServiceName()); stopThread.setDaemon(asynchronous.daemon()); stopThread.start(); return stopThread; } private Asynchronous getAsynchronousAnnotation(String methodName){ try { return service.getClass().getMethod(methodName).getAnnotation(Asynchronous.class); } catch (NoSuchMethodException e) { throw new IllegalStateException(e); } } } } }