Project: ant4eclipse
/**********************************************************************
 * Copyright (c) 2005-2009 ant4eclipse project team. 
 * 
 * All rights reserved. This program and the accompanying materials 
 * are made available under the terms of the Eclipse Public License v1.0 
 * which accompanies this distribution, and is available at 
 * http://www.eclipse.org/legal/epl-v10.html 
 * 
 * Contributors: 
 *     Nils Hartmann, Daniel Kasmeroglu, Gerd Wuetherich 
 **********************************************************************/
package org.ant4eclipse.lib.core.service; 
 
import org.ant4eclipse.lib.core.Assure; 
import org.ant4eclipse.lib.core.CoreExceptionCode; 
import org.ant4eclipse.lib.core.Lifecycle; 
import org.ant4eclipse.lib.core.exception.Ant4EclipseException; 
 
import java.util.HashMap; 
import java.util.Iterator; 
import java.util.LinkedList; 
import java.util.List; 
import java.util.Map; 
 
/**
 * <p> 
 * The {@link ServiceRegistry} implements a singleton that can be used to register and retrieve services. A service is 
 * an instance of any kind of class. 
 * </p> 
 * <p> 
 * The service registry must be configured before it can be used. To configure the registry a 
 * {@link ServiceRegistryConfiguration} must be implemented: <code><pre> 
 * final ServiceRegistryConfiguration configuration = new ServiceRegistryConfiguration() { 
 *   public void configure(final ConfigurationContext context) { 
 *     // register service 
 *     context.registerService(new MyServiceImpl(), MyService.class); 
 *   } 
 * }; </pre></code> 
 * <p> 
 * An instance of type {@link ServiceRegistryConfiguration} must be set through the configure() method: <code><pre> 
 * ServiceRegistry.configure(configuration);</pre></code> 
 * </p> 
 * <p> 
 * After configuring the registry, services can be requested. 
 * </p> 
 *  
 * @author Gerd Wütherich ([email protected]
 *  
 * @todo [10-Dec-2009:KASI] The static reference should be removed so that classloading won't cleanup existing data 
 *       (happens within a taskdef if ant). 
 */
 
public class ServiceRegistry { 
 
  /** the service map **/ 
  private Map<String, Object> _serviceMap; 
 
  /** list that contains the ordering of the services **/ 
  private List<Object>        _serviceOrdering; 
 
  /** indicates whether the registry instance is initialized **/ 
  private boolean             _isInitialized = false
 
  /**
   * <p> 
   * Returns <code>true</code> if a service with the identifier <code>serviceType.getName()</code> is registered. 
   * </p> 
   *  
   * @param <T> 
   *          The type of the service class. 
   * @param serviceType 
   *          The service class itself. 
   *  
   * @return <code>true</code> <=> A service is registered using the supplied type. 
   */
 
  public <T> boolean hasService(Class<T> serviceType) { 
    return hasService(serviceType.getName()); 
  } 
 
  /**
   * <p> 
   * Returns <code>true</code> if a service with the supplied identifier could be found. 
   * </p> 
   *  
   * @param serviceIdentifier 
   *          The identifier of the service class used to look for. 
   *  
   * @return <code>true</code> <=> A service is registered using the supplied identifier. 
   */
 
  public final boolean hasService(String serviceIdentifier) { 
    return this._serviceMap.containsKey(serviceIdentifier); 
  } 
 
  /**
   * <p> 
   * Returns the service with the identifier <code>serviceType.getName()</code>. 
   * </p> 
   *  
   * @param <T> 
   *          The type of the service class. 
   * @param serviceType 
   *          The service class itself. 
   *  
   * @return The instance of the service class. 
   */
 
  @SuppressWarnings("unchecked"
  public <T> T getService(Class<T> serviceType) { 
    Object result = this._serviceMap.get(serviceType.getName()); 
    if (result == null) { 
      throw new Ant4EclipseException(CoreExceptionCode.SERVICE_NOT_AVAILABLE, serviceType.getName()); 
    } 
    return (T) result; 
  } 
 
  /**
   * Returns a list of all currently registered services. 
   *  
   * @return A list of all currently registered services. Not <code>null</code>. 
   */
 
  public Object[] getAllServices() { 
    Object[] result = new Object[this._serviceMap.size()]; 
    this._serviceMap.values().toArray(result); 
    return result; 
  } 
 
  /**
   * <p> 
   * </p> 
   *  
   * @return 
   */
 
  private final boolean isInitialized() { 
    return this._isInitialized; 
  } 
 
  /**
   * <p> 
   * </p> 
   */
 
  void initialize() { 
 
    Assure.assertTrue(!isInitialized(), "Service registry already has been initialized!"); 
 
    Iterator<Object> iterator = this._serviceOrdering.iterator(); 
 
    while (iterator.hasNext()) { 
      Object service = iterator.next(); 
 
      if (service instanceof Lifecycle) { 
        try { 
          ((Lifecycle) service).initialize(); 
        } catch (Exception e) { 
          throw new Ant4EclipseException(e, CoreExceptionCode.SERVICE_COULD_NOT_BE_INITIALIZED, service.getClass() 
              .getName()); 
        } 
      } 
    } 
 
    setInitialized(true); 
  } 
 
  /**
   * <p> 
   * </p> 
   */
 
  void dispose() { 
 
    Assure.assertTrue(isInitialized(), "Service registry is not initialized."); 
 
    Iterator<Object> iterator = this._serviceOrdering.iterator(); 
 
    while (iterator.hasNext()) { 
      Object service = iterator.next(); 
      if (service instanceof Lifecycle) { 
        try { 
          ((Lifecycle) service).dispose(); 
        } catch (Exception e) { 
          // no need to do anything here... 
          System.err.printf(CoreExceptionCode.SERVICE_COULD_NOT_BE_DISPOSED.getMessage(), service); 
        } 
      } 
    } 
 
    setInitialized(false); 
  } 
 
  /**
   * @param b 
   */
 
  private void setInitialized(boolean b) { 
    this._isInitialized = b; 
  } 
 
  /**
   * <p> 
   * Creates an instance of type {@link ServiceRegistry}. 
   * </p> 
   *  
   * @param configuration 
   *          the service registry configuration 
   */
 
  ServiceRegistry(ServiceRegistryConfiguration configuration) { 
    this._serviceMap = new HashMap<String, Object>(); 
    this._serviceOrdering = new LinkedList<Object>(); 
    configuration.configure(new ConfigurationContextImpl()); 
  } 
 
  /**
   * ConfigurationContextImpl -- 
   *  
   * @author Gerd Wütherich ([email protected]
   */
 
  private class ConfigurationContextImpl implements ConfigurationContext { 
 
    /**
     * {@inheritDoc} 
     */
 
    public void registerService(Object service, String serviceIdentifier) { 
      Assure.assertTrue(!ServiceRegistry.this._isInitialized, "ServiceRegistry.this._isInitialized!"); 
      Assure.notNull("service", service); 
      Assure.notNull("serviceIdentifier", serviceIdentifier); 
 
      if (!ServiceRegistry.this._serviceMap.containsKey(serviceIdentifier)) { 
        ServiceRegistry.this._serviceMap.put(serviceIdentifier, service); 
        ServiceRegistry.this._serviceOrdering.add(service); 
      } else { 
        throw new Ant4EclipseException(CoreExceptionCode.SERVICE_IDENTIFIER_IS_NOT_UNIQUE, serviceIdentifier); 
      } 
    } 
 
    /**
     * {@inheritDoc} 
     */
 
    public void registerService(Object service, String[] serviceIdentifier) { 
      Assure.assertTrue(!ServiceRegistry.this._isInitialized, "ServiceRegistry.this._isInitialized!"); 
      Assure.notNull("service", service); 
      Assure.notNull("serviceIdentifier", serviceIdentifier); 
      Assure.assertTrue(serviceIdentifier.length > 0"serviceIdentifier.length = 0!"); 
 
      for (int i = 0; i < serviceIdentifier.length; i++) { 
        Assure.assertTrue(serviceIdentifier[i] != null"Parameter serviceIdentifier[" + i + "] has to be set!"); 
      } 
 
      for (String object : serviceIdentifier) { 
        if (ServiceRegistry.this._serviceMap.containsKey(object)) { 
          throw new Ant4EclipseException(CoreExceptionCode.SERVICE_IDENTIFIER_IS_NOT_UNIQUE, (Object) serviceIdentifier); 
        } 
      } 
 
      for (String object : serviceIdentifier) { 
        ServiceRegistry.this._serviceMap.put(object, service); 
        ServiceRegistry.this._serviceOrdering.add(service); 
      } 
    } 
 
  } /* ENDCLASS */ 
 
/* ENDCLASS */