/*
 * Copyright (c) 1998-2012 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Resin Open Source is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *
 *   Free Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.config.inject;

import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.annotation.Resource;
import javax.annotation.sql.DataSourceDefinition;
import javax.annotation.sql.DataSourceDefinitions;
import javax.ejb.EJB;
import javax.ejb.EJBs;
import javax.ejb.Stateful;
import javax.el.ELResolver;
import javax.el.ExpressionFactory;
import javax.enterprise.context.ContextNotActiveException;
import javax.enterprise.context.Conversation;
import javax.enterprise.context.ConversationScoped;
import javax.enterprise.context.Dependent;
import javax.enterprise.context.NormalScope;
import javax.enterprise.context.spi.Context;
import javax.enterprise.context.spi.Contextual;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.event.Event;
import javax.enterprise.inject.Alternative;
import javax.enterprise.inject.AmbiguousResolutionException;
import javax.enterprise.inject.IllegalProductException;
import javax.enterprise.inject.InjectionException;
import javax.enterprise.inject.Instance;
import javax.enterprise.inject.New;
import javax.enterprise.inject.Specializes;
import javax.enterprise.inject.Stereotype;
import javax.enterprise.inject.UnsatisfiedResolutionException;
import javax.enterprise.inject.spi.Annotated;
import javax.enterprise.inject.spi.AnnotatedField;
import javax.enterprise.inject.spi.AnnotatedMethod;
import javax.enterprise.inject.spi.AnnotatedType;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.Decorator;
import javax.enterprise.inject.spi.Extension;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.enterprise.inject.spi.InjectionTarget;
import javax.enterprise.inject.spi.InterceptionType;
import javax.enterprise.inject.spi.Interceptor;
import javax.enterprise.inject.spi.ObserverMethod;
import javax.enterprise.inject.spi.PassivationCapable;
import javax.enterprise.inject.spi.ProcessBean;
import javax.enterprise.inject.spi.Producer;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
import javax.inject.Qualifier;
import javax.inject.Scope;
import javax.interceptor.InterceptorBinding;
import javax.naming.InitialContext;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceUnit;

import com.caucho.config.CauchoDeployment;
import com.caucho.config.ConfigException;
import com.caucho.config.Configured;
import com.caucho.config.ContextDependent;
import com.caucho.config.LineConfigException;
import com.caucho.config.ModulePrivate;
import com.caucho.config.ModulePrivateLiteral;
import com.caucho.config.bytecode.ScopeAdapter;
import com.caucho.config.el.CandiElResolver;
import com.caucho.config.el.CandiExpressionFactory;
import com.caucho.config.event.EventBeanImpl;
import com.caucho.config.event.EventManager;
import com.caucho.config.extension.ExtensionManager;
import com.caucho.config.j2ee.DataSourceDefinitionHandler;
import com.caucho.config.j2ee.EjbHandler;
import com.caucho.config.j2ee.PersistenceContextHandler;
import com.caucho.config.j2ee.PersistenceUnitHandler;
import com.caucho.config.j2ee.ResourceHandler;
import com.caucho.config.program.ConfigProgram;
import com.caucho.config.program.ResourceProgramManager;
import com.caucho.config.reflect.AnnotatedTypeUtil;
import com.caucho.config.reflect.BaseType;
import com.caucho.config.reflect.BaseTypeFactory;
import com.caucho.config.reflect.ReflectionAnnotatedFactory;
import com.caucho.config.scope.ApplicationContext;
import com.caucho.config.scope.DependentContext;
import com.caucho.config.scope.ErrorContext;
import com.caucho.config.scope.SingletonScope;
import com.caucho.config.xml.XmlCookie;
import com.caucho.config.xml.XmlCookieLiteral;
import com.caucho.config.xml.XmlStandardPlugin;
import com.caucho.inject.Module;
import com.caucho.inject.RequestContext;
import com.caucho.loader.DynamicClassLoader;
import com.caucho.loader.Environment;
import com.caucho.loader.EnvironmentApply;
import com.caucho.loader.EnvironmentClassLoader;
import com.caucho.loader.EnvironmentListener;
import com.caucho.loader.EnvironmentLocal;
import com.caucho.util.CurrentTime;
import com.caucho.util.IoUtil;
import com.caucho.util.L10N;
import com.caucho.vfs.Path;
import com.caucho.vfs.ReadStream;
import com.caucho.vfs.Vfs;

/**
 * The CDI container for a given environment.
 */
@ModulePrivate
@SuppressWarnings("serial")
public final class InjectManager
  implements BeanManager, EnvironmentListener,
             Serializable, HandleAware
{
  private static final L10N L = new L10N(InjectManager.class);
  private static final Logger log
    = Logger.getLogger(InjectManager.class.getName());

  private static final EnvironmentLocal<InjectManager> _localContainer
    = new EnvironmentLocal<InjectManager>();

  private static final int DEFAULT_PRIORITY = 1;

  private static final Annotation []DEFAULT_ANN
    = DefaultLiteral.DEFAULT_ANN_LIST;

  private static final String []FORBIDDEN_ANNOTATIONS = {
    "javax.persistence.Entity",
    /*
    "javax.ejb.Stateful",
    "javax.ejb.Stateless",
    "javax.ejb.Singleton",
    "javax.ejb.MessageDriven"
    */
  };

  private static final String []FORBIDDEN_CLASSES = {
    "javax.servlet.Servlet",
    "javax.servlet.Filter",
    "javax.servlet.ServletContextListener",
    "javax.servlet.http.HttpSessionListener",
    "javax.servlet.ServletRequestListener",
    "javax.ejb.EnterpriseBean",
    "javax.faces.component.UIComponent",
    "javax.enterprise.inject.spi.Extension",
  };

  private static final Class<? extends Annotation> []_forbiddenAnnotations;
  private static final Class<?> []_forbiddenClasses;
  
  private static final ClassLoader _systemClassLoader;

  private final String _id;

  private final InjectManager _parent;

  private EnvironmentClassLoader _classLoader;
  private ClassLoader _jndiClassLoader;
  private boolean _isChildManager;
  
  private final InjectScanManager _scanManager;
  private final ExtensionManager _extensionManager;
  private EventManager _eventManager = new EventManager(this);
  
  private AtomicLong _version = new AtomicLong();

  private HashMap<Class<?>,Class<?>> _specializedMap
    = new HashMap<Class<?>,Class<?>>();

  private HashMap<Class<?>,Integer> _deploymentMap
    = new HashMap<Class<?>,Integer>();

  private BaseTypeFactory _baseTypeFactory = new BaseTypeFactory();

  private HashMap<Class<?>,InjectionPointHandler> _injectionMap
    = new HashMap<Class<?>,InjectionPointHandler>();
  
  private ResourceProgramManager _resourceManager
    = new ResourceProgramManager();

  //
  // self configuration
  //

  private ConcurrentHashMap<Class<?>,ArrayList<TypedBean>> _selfBeanMap
    = new ConcurrentHashMap<Class<?>,ArrayList<TypedBean>>();

  private ConcurrentHashMap<String,ArrayList<Bean<?>>> _selfNamedBeanMap
    = new ConcurrentHashMap<String,ArrayList<Bean<?>>>();

  private HashMap<String,Bean<?>> _selfPassivationBeanMap
    = new HashMap<String,Bean<?>>();

  //
  // combined visibility configuration
  //

  private HashMap<String,WebComponent> _beanMap
    = new HashMap<String,WebComponent>();

  private ConcurrentHashMap<String,ArrayList<Bean<?>>> _namedBeanMap
    = new ConcurrentHashMap<String,ArrayList<Bean<?>>>();

  private HashMap<Type,Bean<?>> _newBeanMap
    = new HashMap<Type,Bean<?>>();
  
  private HashSet<Class<? extends Annotation>> _qualifierSet
    = new HashSet<Class<? extends Annotation>>();
  
  private HashSet<Class<? extends Annotation>> _scopeTypeSet
    = new HashSet<Class<? extends Annotation>>();
  
  private HashSet<Class<? extends Annotation>> _normalScopeSet
    = new HashSet<Class<? extends Annotation>>();
  
  private HashSet<Class<? extends Annotation>> _passivatingScopeSet
    = new HashSet<Class<? extends Annotation>>();
  
  private HashMap<Class<? extends Annotation>, Set<Annotation>> _stereotypeMap
    = new HashMap<Class<? extends Annotation>, Set<Annotation>>();

  private HashMap<Class<?>,Context> _contextMap
    = new HashMap<Class<?>,Context>();

  private ArrayList<Class<?>> _interceptorClassList
    = new ArrayList<Class<?>>();

  private ArrayList<InterceptorEntry<?>> _interceptorList
    = new ArrayList<InterceptorEntry<?>>();

  private ArrayList<Class<?>> _decoratorClassList
    = new ArrayList<Class<?>>();

  private ArrayList<DecoratorEntry<?>> _decoratorList
    = new ArrayList<DecoratorEntry<?>>();

  private HashSet<Bean<?>> _beanSet = new HashSet<Bean<?>>();

  private boolean _isEnableAutoUpdate = true;
  private boolean _isUpdateNeeded = true;
  private boolean _isAfterValidationNeeded = true;

  private ConcurrentHashMap<Path, List<Path>> _beansXMLOverrides 
    = new ConcurrentHashMap<Path, List<Path>>();
  
  private ArrayList<AnnotatedType<?>> _pendingAnnotatedTypes
    = new ArrayList<AnnotatedType<?>>();

  private ArrayList<AbstractBean<?>> _pendingBindList
    = new ArrayList<AbstractBean<?>>();
  
  private ArrayList<Bean<?>> _pendingValidationBeans
    = new ArrayList<Bean<?>>();

  private ArrayList<Bean<?>> _pendingServiceList
    = new ArrayList<Bean<?>>();
  
  private ArrayList<ConfigProgram> _globalProgram
    = new ArrayList<ConfigProgram>();
  
  private ConcurrentHashMap<Bean<?>,ReferenceFactory<?>> _refFactoryMap
    = new ConcurrentHashMap<Bean<?>,ReferenceFactory<?>>();
  
  private ConcurrentHashMap<String,ReferenceFactory<?>> _namedRefFactoryMap
  = new ConcurrentHashMap<String,ReferenceFactory<?>>();
  
  private ConcurrentHashMap<Member,AtomicBoolean> _staticMemberMap
  = new ConcurrentHashMap<Member,AtomicBoolean>();

  private ThreadLocal<CreationalContextImpl<?>> _proxyThreadLocal
    = new ThreadLocal<CreationalContextImpl<?>>();
  
  private ConcurrentHashMap<Class<?>,ManagedBeanImpl<?>> _transientMap
    = new ConcurrentHashMap<Class<?>,ManagedBeanImpl<?>>();
  
  private ConcurrentHashMap<Long,InjectionTarget<?>> _xmlTargetMap
    = new ConcurrentHashMap<Long,InjectionTarget<?>>();

  private boolean _isBeforeBeanDiscoveryComplete;
  private boolean _isBeforeBeanDiscoverFired;
  private boolean _isAfterBeanDiscoveryComplete;
  
  private final AtomicLong _xmlCookieSequence
    = new AtomicLong(CurrentTime.getCurrentTime());

  // XXX: needs to be a local resolver
  private ELResolver _elResolver = new CandiElResolver(this);

  private final DependentContext _dependentContext = new DependentContext();
  private SingletonScope _singletonScope;
  private ApplicationContext _applicationScope;
  private final XmlStandardPlugin _xmlExtension;

  private RuntimeException _configException;

  private Object _serializationHandle;

  private InjectManager(String id,
                        InjectManager parent,
                        EnvironmentClassLoader loader,
                        boolean isSetLocal)
  {
    _id = id;
    
    _classLoader = loader;
    
    _parent = parent;
    
    _extensionManager = new ExtensionManager(this);
    _scanManager = new InjectScanManager(this);
    _xmlExtension = new XmlStandardPlugin(this);

    Thread thread = Thread.currentThread();
    ClassLoader oldLoader = thread.getContextClassLoader();

    try {
      thread.setContextClassLoader(_classLoader);

      if (isSetLocal) {
        _localContainer.set(this, _classLoader);

        if (_parent == null) {
          _localContainer.setGlobal(this);
        }
      }

      if (_classLoader != null) {
        _classLoader.getNewTempClassLoader();
      }
    } finally {
      thread.setContextClassLoader(oldLoader);
    }
  }

  private void init(boolean isSetLocal)
  {
    Thread thread = Thread.currentThread();
    ClassLoader oldLoader = thread.getContextClassLoader();

    try {
      thread.setContextClassLoader(_classLoader);

      try {
        InitialContext ic = new InitialContext();
        ic.rebind("java:comp/BeanManager", new WebBeansJndiProxy());
      } catch (Throwable e) {
        log.log(Level.FINEST, e.toString(), e);
      }

      _singletonScope = new SingletonScope();
      _applicationScope = new ApplicationContext();
      
      addContext(new RequestContext());
      addContext("com.caucho.server.webbeans.SessionScopeImpl");
      addContext("com.caucho.server.webbeans.ConversationContext");
      addContext("com.caucho.server.webbeans.TransactionScope");
      addContext(_applicationScope);
      addContext(_singletonScope);
      addContext(_dependentContext);

      _injectionMap.put(PersistenceContext.class,
                        new PersistenceContextHandler(this));
      _injectionMap.put(PersistenceUnit.class,
                        new PersistenceUnitHandler(this));
      _injectionMap.put(Resource.class,
                        new ResourceHandler(this));
      _injectionMap.put(EJB.class,
                        new EjbHandler(this));
      _injectionMap.put(EJBs.class,
                        new EjbHandler(this));
      _injectionMap.put(DataSourceDefinition.class,
                        new DataSourceDefinitionHandler(this));
      _injectionMap.put(DataSourceDefinitions.class,
                        new DataSourceDefinitionHandler(this));

      _deploymentMap.put(CauchoDeployment.class, 0);
      // DEFAULT_PRIORITY
      _deploymentMap.put(Configured.class, 2);

      BeanBuilder<InjectManager> factory = createBeanFactory(InjectManager.class);
      // factory.deployment(Standard.class);
      factory.type(InjectManager.class);
      factory.type(BeanManager.class);
      factory.annotation(ModulePrivateLiteral.create());
      addBean(factory.singleton(this));
      
      // ioc/0162
      addBean(new InjectionPointStandardBean());

      addExtension(_xmlExtension);
      _extensionManager.createExtension("com.caucho.server.webbeans.ResinStandardPlugin");

      if (_classLoader != null && isSetLocal) {
        // _classLoader.addScanListener(this);
        _classLoader.addScanListener(_scanManager);
      }

      Environment.addEnvironmentListener(this, _classLoader);
    } finally {
      thread.setContextClassLoader(oldLoader);
    }
  }

  /**
   * Returns the modification version.
   */
  public long getVersion()
  {
    return _version.get();
  }
  
  public InjectScanManager getScanManager()
  {
    return _scanManager;
  }
  
  public void setIsCustomExtension(boolean isCustom)
  {
    getScanManager().setIsCustomExtension(isCustom);
  }
  
  @Module
  public EventManager getEventManager()
  {
    return _eventManager;
  }
  
  @Module
  public ExtensionManager getExtensionManager()
  {
    return _extensionManager;
  }

  private void addContext(String contextClassName)
  {
    try {
      Class<?> cl = Class.forName(contextClassName);
      Context context = (Context) cl.newInstance();

      addContext(context);
    } catch (ClassNotFoundException e) {
      log.log(Level.FINER, e.toString(), e);
    } catch (NoClassDefFoundError e) {
      log.log(Level.FINER, e.toString(), e);
    } catch (Exception e) {
      throw ConfigException.create(e);
    }
  }

  /**
   * Returns the local container.
   */
  public static InjectManager getCurrent()
  {
    return getCurrent(Thread.currentThread().getContextClassLoader());
  }

  /**
   * Returns the current environment container.
   */
  public static InjectManager getCurrent(ClassLoader loader)
  {
    return _localContainer.get(loader);
  }

  /**
   * Returns the current active container.
   */
  public static InjectManager create()
  {
    return create(Thread.currentThread().getContextClassLoader());
  }

  /**
   * Returns the current active container.
   */
  public static InjectManager create(ClassLoader loader)
  {
    if (loader == null)
      loader = _systemClassLoader;
    
    InjectManager manager = null;

    manager = _localContainer.getLevel(loader);
    
    if (manager != null)
      return manager;
      
    EnvironmentClassLoader envLoader
      = Environment.getEnvironmentClassLoader(loader);

    // ejb doesn't create a new InjectManager even though it's a new
    // environment
    // XXX: yes it does, because of the SessionContext
    // ejb/2016 vs ejb/12h0
    /*
    if (envLoader != null
        && Boolean.FALSE.equals(envLoader.getAttribute("caucho.inject"))) {
      manager = create(envLoader.getParent());
      
      if (manager != null)
        return manager;
    }
    */

    String id;

    if (envLoader != null)
      id = envLoader.getId();
    else
      id = "";

    InjectManager parent = null;

    if (envLoader != null && envLoader != _systemClassLoader)
      parent = create(envLoader.getParent());

    synchronized (_localContainer) {
      manager = _localContainer.getLevel(envLoader);
        
      if (manager != null)
        return manager;
        
      manager = new InjectManager(id, parent, envLoader, true);
    }
      
    manager.init(true);
    
    return manager;
  }

  /**
   * Returns the current active container.
   */
  /*
  public InjectManager createParent(String prefix)
  {
    _parent = new InjectManager(prefix + _id,
                                _parent,
                                _classLoader,
                                false);
    _parent.init(false);

    return _parent;
  }
  */

  public ClassLoader getClassLoader()
  {
    return _classLoader;
  }
  
  public ClassLoader getJndiClassLoader()
  {
    return _jndiClassLoader;
  }
  
  public boolean isChildManager()
  {
    return _isChildManager;
  }
  
  public void setJndiClassLoader(ClassLoader loader)
  {
    if (_parent == null)
      throw new IllegalStateException();
    
    _isChildManager = true;
    _jndiClassLoader = loader;
  }

  public InjectManager getParent()
  {
    return _parent;
  }

  public ApplicationContext getApplicationScope()
  {
    return _applicationScope;
  }

  /*
  public void setParent(InjectManager parent)
  {
    _parent = parent;
  }
  */

  public void addXmlPath(Path path)
  {
    _isUpdateNeeded = true;

    _xmlExtension.addXmlPath(path);
  }

  public void addBeansXmlOverride(Path path, Path beansXmlPath)
  {
    if (path == null)
      throw new NullPointerException();
    
    List<Path> beansXmlPaths = _beansXMLOverrides.get(path);

    if (beansXmlPaths == null) {
      beansXmlPaths = new ArrayList<Path>();
    }

    beansXmlPaths.add(beansXmlPath);

    _beansXMLOverrides.put(path, beansXmlPaths);
  }
  
  public List<Path> getBeansXmlOverride(Path path)
  {
    return _beansXMLOverrides.get(path);
  }
  
  public void setEnableAutoUpdate(boolean isEnable)
  {
    _isEnableAutoUpdate = isEnable;
  }
  
  public void setDeploymentTypes(ArrayList<Class<?>> deploymentList)
  {
    _deploymentMap.clear();

    _deploymentMap.put(CauchoDeployment.class, 0);
    // DEFAULT_PRIORITY

    int priority = DEFAULT_PRIORITY + 1;

    if (! deploymentList.contains(Configured.class)) {
      _deploymentMap.put(Configured.class, priority);
    }

    for (int i = deploymentList.size() - 1; i >= 0; i--) {
      _deploymentMap.put(deploymentList.get(i), priority);
    }
  }

  /**
   * Adds the bean to the named bean map.
   */
  private void addBeanByName(String name, Bean<?> bean)
  {
    ArrayList<Bean<?>> beanList = _selfNamedBeanMap.get(name);

    if (beanList == null) {
      beanList = new ArrayList<Bean<?>>();
      _selfNamedBeanMap.put(name, beanList);
    }
    else if (bean.isAlternative()) {
    }
    else if (_specializedMap.get(bean.getBeanClass()) != null) {
    }
    else {
      // ioc/0n18 vs ioc/0g30
      for (Bean<?> testBean : beanList) {
        if (testBean.isAlternative()) {
        }
        else if (bean.isAlternative()) {
        }
        else if (bean.getBeanClass().isAnnotationPresent(Specializes.class)
                 && testBean.getBeanClass().isAssignableFrom(bean.getBeanClass())) {
        }
        else if (testBean.getBeanClass().isAnnotationPresent(Specializes.class)
                  && bean.getBeanClass().isAssignableFrom(testBean.getBeanClass())) {
        }
        else if ((bean instanceof AbstractIntrospectedBean<?>)
                 && ((AbstractIntrospectedBean<?>) bean).getAnnotated().isAnnotationPresent(Specializes.class)) {
          // ioc/07a2
        }
        else if ((testBean instanceof AbstractIntrospectedBean<?>)
                 && ((AbstractIntrospectedBean<?>) testBean).getAnnotated().isAnnotationPresent(Specializes.class)) {
        }
        else {
          throw new ConfigException(L.l("@Named('{0}') is a duplicate name for\n  {1}\n  {2}",
                                        name, bean, testBean));
        }
      }
    }

    beanList.add(bean);

    _namedBeanMap.remove(name);
    
    // ioc/0g31
    int p = name.indexOf('.');
    if (p > 0) {
      addBeanByName(name.substring(0, p), bean);
    }
  }

  /**
   * Adds a bean by the interface type
   *
   * @param type the interface type to expose the component
   * @param bean the component to register
   */
  private void addBeanByType(Type type,
                             Annotated annotated,
                             Bean<?> bean)
  {
    if (type == null)
      return;
    
    BaseType baseType = createSourceBaseType(type);
    
    addBeanByType(baseType, annotated, bean);
  }

  private void addBeanByType(BaseType type,
                             Annotated annotated,
                             Bean<?> bean)
  {
    if (type == null)
      return;
    
    if (isSpecialized(bean.getBeanClass())) {
      return;
    }

    if (log.isLoggable(Level.FINEST))
      log.finest(bean + "(" + type + ") added to " + this);

    Class<?> rawType = type.getRawClass();

    ArrayList<TypedBean> beanSet = _selfBeanMap.get(rawType);

    if (beanSet == null) {
      beanSet = new ArrayList<TypedBean>();
      _selfBeanMap.put(rawType, beanSet);
    }
    _beanMap.remove(rawType);

    TypedBean typedBean = new TypedBean(type, annotated, bean);
    
    if (! beanSet.contains(typedBean)) {
      beanSet.add(typedBean);
    }
  }

  /**
   * Finds a component by its component name.
   */
  protected ArrayList<Bean<?>> findByName(String name)
  {
    // #3334 - shutdown timing issues
    ConcurrentHashMap<String,ArrayList<Bean<?>>> namedBeanMap = _namedBeanMap;

    if (namedBeanMap == null)
      return null;

    ArrayList<Bean<?>> beanList = _namedBeanMap.get(name);

    if (beanList == null) {
      beanList = new ArrayList<Bean<?>>();

      if (_classLoader != null)
        _classLoader.applyVisibleModules(new FillByName(name, beanList));

      // ioc/0680 
      /*
      for (int i = beanList.size() - 1; i >= 0; i--) {
        if (getDeploymentPriority(beanList.get(i)) < 0) {
          beanList.remove(i);
        }
      }
      */

      _namedBeanMap.put(name, beanList);
    }

    return beanList;
  }

  private void fillByName(String name, ArrayList<Bean<?>> beanList)
  {
    ArrayList<Bean<?>> localBeans = _selfNamedBeanMap.get(name);

    if (localBeans != null) {
      for (Bean<?> bean : localBeans) {
        /*
        if (getDeploymentPriority(bean) < 0)
          continue;
          */
        
        // ioc/0g20
        if (bean.isAlternative() && ! isEnabled(bean))
          continue;
        
        if (_specializedMap.containsKey(bean.getBeanClass()))
          continue;
        
        if (! beanList.contains(bean))
          beanList.add(bean);
      }
    }
  }

  //
  // javax.enterprise.context.Conversation
  //

  public Conversation createConversation()
  {
    return (Conversation) _contextMap.get(ConversationScoped.class);
  }

  /**
   * Creates an object, but does not register the
   * component with webbeans.
   */
  public <T> T createTransientObject(Class<T> type)
  {
    ManagedBeanImpl<T> bean = createCachedManagedBean(type);

    validate(bean);
    
    // server/10gn
    //return factory.create(new ConfigContext());
    InjectionTarget<T> injectionTarget = bean.getInjectionTarget();

    CreationalContext<T> env = new OwnerCreationalContext<T>(bean);

    T instance = injectionTarget.produce(env);
    injectionTarget.inject(instance, env);
    // jsp/1o60
    injectionTarget.postConstruct(instance);

    return instance;
  }

  /**
   * Creates an object, but does not register the
   * component with webbeans.
   */
  private <T> ManagedBeanImpl<T> createCachedManagedBean(Class<T> type)
  {
    ManagedBeanImpl<T> bean = (ManagedBeanImpl<T>) _transientMap.get(type);
    
    if (bean == null) {
      bean = createManagedBean(type);

      validate(bean);
      
      _transientMap.putIfAbsent(type, bean);
      
      bean = (ManagedBeanImpl<T>) _transientMap.get(type);
    }
    
    return bean;
  }

  /**
   * Returns a new instance for a class, but does not register the
   * component with webbeans.
   */
  public <T> BeanBuilder<T> createBeanFactory(ManagedBeanImpl<T> managedBean)
  {
    return new BeanBuilder<T>(managedBean);
  }

  /**
   * Returns a new instance for a class, but does not register the
   * component with webbeans.
   */
  public <T> BeanBuilder<T> createBeanFactory(Class<T> type)
  {
    ManagedBeanImpl<T> managedBean = createManagedBean(type);
    
    if (managedBean != null)
      return createBeanFactory(managedBean);
    else
      return null;
  }

  /**
   * Returns a new instance for a class, but does not register the
   * component with CDI.
   */
  public <T> BeanBuilder<T> createBeanFactory(AnnotatedType<T> type)
  {
    return createBeanFactory(createManagedBean(type));
  }
  
  public <T> Bean<T> addSingleton(T obj)
  {
    BeanBuilder<T> builder = createBeanFactory((Class<T>) obj.getClass());
    
    Bean<T> bean = builder.singleton(obj);
    
    addBeanDiscover(bean);
    
    return bean;
  }

  //
  // enabled deployment types, scopes, and qualifiers
  //

  @Module
  public void addScope(Class<? extends Annotation> scopeType,
                       boolean isNormal,
                       boolean isPassivating)
  {
    if (isPassivating && ! isNormal)
      throw new ConfigException(L.l("@{0} must be 'normal' because it's using 'passivating'",
                                    scopeType.getName()));

    _scopeTypeSet.add(scopeType);
    
    if (isNormal)
      _normalScopeSet.add(scopeType);
    
    if (isPassivating)
      _passivatingScopeSet.add(scopeType);
    
    if (isNormal) {
      // TCK - force validation of all methods
      _scanManager.setIsCustomExtension(true);
    }
  }

  /**
   * Tests if an annotation is an enabled scope type
   */
  @Override
  public boolean isScope(Class<? extends Annotation> annotationType)
  {
    return (annotationType.isAnnotationPresent(Scope.class)
            || annotationType.isAnnotationPresent(NormalScope.class)
            || _scopeTypeSet.contains(annotationType));
  }

  /**
   * Tests if an annotation is an enabled scope type
   */
  @Override
  public boolean isNormalScope(Class<? extends Annotation> annotationType)
  {
    return (annotationType.isAnnotationPresent(NormalScope.class)
            || _normalScopeSet.contains(annotationType));
  }

  /**
   * Tests if an annotation is an enabled scope type
   */
  @Override
  public boolean isPassivatingScope(Class<? extends Annotation> annotationType)
  {
    NormalScope scope = annotationType.getAnnotation(NormalScope.class);

    if (scope != null)
      return scope.passivating();
    
    return _passivatingScopeSet.contains(annotationType);
  }
  
  @Module
  public void addQualifier(Class<? extends Annotation> qualifier)
  {
    _qualifierSet.add(qualifier);
  }

  /**
   * Tests if an annotation is an enabled binding type
   */
  @Override
  public boolean isQualifier(Class<? extends Annotation> annotationType)
  {
    return (annotationType.isAnnotationPresent(Qualifier.class)
            || _qualifierSet.contains(annotationType));
  }
  
  /**
   * Tests if an annotation is an enabled interceptor binding type
   */
  @Override
  public boolean isInterceptorBinding(Class<? extends Annotation> annotationType)
  {
    return annotationType.isAnnotationPresent(InterceptorBinding.class);
  }

  /**
   * Returns the bindings for an interceptor binding type
   */
  @Override
  public Set<Annotation> getInterceptorBindingDefinition(Class<? extends Annotation> bindingType)
  {
    LinkedHashSet<Annotation> annSet = new LinkedHashSet<Annotation>();
    
    for (Annotation ann : bindingType.getAnnotations()) {
      annSet.add(ann);
    }
    
    return annSet;
  }

  @Module
  public void addStereotype(Class<? extends Annotation> annotationType,
                            Annotation []annotations)
  {
    LinkedHashSet<Annotation> annSet = new LinkedHashSet<Annotation>();
    
    for (Annotation ann : annotations)
      annSet.add(ann);
    
    _stereotypeMap.put(annotationType, annSet);
  }
  
  /**
   * Tests if an annotation is an enabled stereotype.
   */
  @Override
  public boolean isStereotype(Class<? extends Annotation> annotationType)
  {
    return (annotationType.isAnnotationPresent(Stereotype.class)
            || _stereotypeMap.get(annotationType) != null);
  }

  /**
   * Returns the annotations associated with a stereotype
   */
  @Override
  public Set<Annotation> getStereotypeDefinition(Class<? extends Annotation> stereotype)
  {
    Set<Annotation> mapAnnSet = _stereotypeMap.get(stereotype);
    
    if (mapAnnSet != null)
      return mapAnnSet;
    
    if (! stereotype.isAnnotationPresent(Stereotype.class))
      return null;
    
    LinkedHashMap<Class<?>, Annotation> annMap
      = new LinkedHashMap<Class<?>, Annotation>();
    
    addStereotypeDefinitions(annMap, stereotype);
    
    mapAnnSet = new LinkedHashSet<Annotation>(annMap.values());
    
    _stereotypeMap.put(stereotype, mapAnnSet);
    
    return mapAnnSet;
  }
  
  private void addStereotypeDefinitions(Map<Class<?>,Annotation> annMap, 
                                        Class<? extends Annotation> stereotype)
  {
    for (Annotation ann : stereotype.getAnnotations()) {
      if (annMap.get(ann.annotationType()) == null)
        annMap.put(ann.annotationType(), ann);
    }
    
    for (Annotation ann : stereotype.getAnnotations()) {
      Class<? extends Annotation> annType = ann.annotationType();
      
      if (annType.isAnnotationPresent(Stereotype.class)) {
        addStereotypeDefinitions(annMap, annType);
      }
    }
  }

  //
  // bean resolution and instantiation
  //

  /**
   * Creates a BaseType from a Type used as a target, for example 
   * an injection point.
   */
  public BaseType createTargetBaseType(Type type)
  {
    return _baseTypeFactory.createForTarget(type);
  }

  /**
   * Creates a BaseType from a Type used as a source, for example a Bean.
   */
  public BaseType createSourceBaseType(Type type)
  {
    return _baseTypeFactory.createForSource(type);
  }

  /**
   * Creates an annotated type.
   */
  @Override
  public <T> AnnotatedType<T> createAnnotatedType(Class<T> cl)
  {
    if (cl == null)
      throw new NullPointerException();
    
    AnnotatedType<T> annType = ReflectionAnnotatedFactory.introspectType(cl);
    
    // TCK:
    // return getExtensionManager().processAnnotatedType(annType);
    
    return annType;
  }

  /**
   * Creates an injection target
   */
  @Override
  public <T> InjectionTarget<T> createInjectionTarget(AnnotatedType<T> type)
  {
    InjectionTarget<T> target = new InjectionTargetBuilder<T>(this, type);

    // ioc/0p14 - createInjectionTarget doesn't trigger the event, the
    // initial scan does
    // return getExtensionManager().processInjectionTarget(target, type);

    return target;
  }

  /**
   * Creates an injection target
   */
  public <T> InjectionTarget<T> discoverInjectionTarget(AnnotatedType<T> type)
  {
    InjectionTarget<T> target = createInjectionTarget(type);

    return getExtensionManager().processInjectionTarget(target, type);
  }

  /**
   * Creates a managed bean.
   */
  public <T> InjectionTarget<T> createInjectionTarget(Class<T> type)
  {
    // ioc/0062 (vs discover)
    try {
      AnnotatedType<T> annType = ReflectionAnnotatedFactory.introspectType(type);
      // special call from servlet, etc.
      return createInjectionTarget(annType);
    } catch (Exception e) {
      throw ConfigException.createConfig(e);
    }
  }

  /**
   * Creates a managed bean.
   */
  public <T> InjectionTarget<T> discoverInjectionTarget(Class<T> type)
  {
    fireBeforeBeanDiscovery();
    
    try {
      AnnotatedType<T> annType = ReflectionAnnotatedFactory.introspectType(type);
      
      AnnotatedType<T> enhAnnType
        = getExtensionManager().processAnnotatedType(annType);
      
      if (enhAnnType != null)
        return discoverInjectionTarget(enhAnnType);
      else {
        // special call from servlet, etc.
        return discoverInjectionTarget(annType);
      }
    } catch (Exception e) {
      throw ConfigException.createConfig(e);
    }
  }

  public <T,X> void addObserver(ObserverMethod<T> observer,
                                AnnotatedMethod<X> method)
  {
    _extensionManager.processObserver(observer, method);
    
    getEventManager().addObserver(observer);
  }

  /**
   * Creates a managed bean.
   */
  public <T> ManagedBeanImpl<T> createManagedBean(AnnotatedType<T> type)
  {
    if (type == null)
      throw new NullPointerException();
    
    ManagedBeanImpl<T> bean
      = new ManagedBeanImpl<T>(this, type, false);
    bean.introspect();

    return bean;
  }

  /**
   * Creates a managed bean.
   */
  public <T> ManagedBeanImpl<T> createManagedBean(Class<T> cl)
  {
    if (cl == null)
      throw new NullPointerException();
    
    AnnotatedType<T> type = createAnnotatedType(cl);
    
    return createManagedBean(type);
  }

  /**
   * Creates a managed bean.
   */
  public <T> ManagedBeanImpl<T> discoverManagedBean(Class<T> cl)
  {
    fireBeforeBeanDiscovery();
    
    if (cl == null)
      throw new NullPointerException();
    
    AnnotatedType<T> type = createAnnotatedType(cl);
    
    AnnotatedType<T> extType = getExtensionManager().processAnnotatedType(type);
    
    if (extType != null)
      return createManagedBean(extType);
    else
      return createManagedBean(type);
  }

  /**
   * Processes the discovered bean
   */
  public <T> void addBeanDiscover(Bean<T> bean)
  {
    if (bean == null)
      throw new NullPointerException(L.l("null bean passed to addBean"));
    
    addBeanDiscover(bean, (Annotated) null);
  }

  /**
   * Processes the discovered bean
   */
  public <T> void addBean(Bean<T> bean)
  {
    if (bean == null)
      throw new NullPointerException(L.l("null bean passed to addBean"));
    
    addBean(bean, (Annotated) null);
  }
  
  /**
   * Processes the discovered bean
   */
  @Module
  public <T> void addBeanDiscover(Bean<T> bean, Annotated ann)
  {
    fireBeforeBeanDiscovery();
    
    if (ann != null) {
    }
    else if (bean instanceof AbstractBean<?>) {
      ann = ((AbstractBean<T>) bean).getAnnotatedType();
    }
    else if (bean instanceof InjectionPointStandardBean) {
      ann = ((InjectionPointStandardBean) bean).getAnnotated();
    }
    else if (bean instanceof InterceptorBean) {
      ann = ((InterceptorBean)bean).getAnnotatedType();
    }

    if (bean instanceof ManagedBeanImpl<?>) {
      ManagedBeanImpl<T> managedBean = (ManagedBeanImpl<T>) bean;
      
      bean = getExtensionManager().processManagedBean(managedBean, ann);
    }
    else if (bean instanceof ProducesMethodBean<?,?>) {
      ProducesMethodBean<?,T> methodBean = (ProducesMethodBean<?,T>) bean;
      
      bean = getExtensionManager().processProducerMethod(methodBean);
    }
    else if (bean instanceof ProducesFieldBean<?,?>) {
      ProducesFieldBean<?,T> fieldBean = (ProducesFieldBean<?,T>) bean;
      
      bean = getExtensionManager().processProducerField(fieldBean);
    }
    else
      bean = getExtensionManager().processBean(bean, ann);

    addBean(bean, ann);
  }
  
  @Module
  public <T> void addBean(Bean<T> bean, Annotated ann)
  {
    addBeanImpl(bean, ann);
  }
  
  /**
   * Adds a new bean definition to the manager
   */
  public <T> void addBean(Bean<T> bean, ProcessBean<T> process)
  {
    bean = getExtensionManager().processBean(bean, process);

    if (bean != null)
      addBeanImpl(bean, process.getAnnotated());
  }

  /**
   * Adds a new bean definition to the manager
   */
  public <T> void addBeanImpl(Bean<T> bean, Annotated ann)
  {
    if (bean == null)
      return;
    
    if (_specializedMap.containsKey(bean.getBeanClass())) {
      return;
    }

    if (log.isLoggable(Level.FINER))
      log.finer(this + " add bean " + bean);
    
    _isAfterValidationNeeded = true;

    _version.incrementAndGet();
    
    if (bean instanceof Interceptor<?>) {
      addInterceptor((Interceptor<?>) bean);
      return;
    }
    else if (bean instanceof Decorator<?>) {
      addDecorator((Decorator<?>) bean);
      return;
    }

    // bean = new InjectBean<T>(bean, this);

    _beanSet.add(bean);

    for (Type type : bean.getTypes()) {
      addBeanByType(type, ann, bean);
    }

    if (bean.getName() != null) {
      addBeanByName(bean.getName(), bean);
    }
    
    // XXX: required for TCK, although we use lazily
    boolean isNullable = bean.isNullable();

    if (bean instanceof PassivationCapable) {
      PassivationCapable pass = (PassivationCapable) bean;

      if (pass.getId() != null)
        _selfPassivationBeanMap.put(pass.getId(), bean);
    }
    
    // server/1aj1
    clearBeanCache();

    registerJmx(bean);
  }
  
  public ResourceProgramManager getResourceManager()
  {
    return _resourceManager;
  }

  private void registerJmx(Bean<?> bean)
  {
    Thread thread = Thread.currentThread();
    ClassLoader oldLoader = thread.getContextClassLoader();
    try {
      thread.setContextClassLoader(_classLoader);

      /*
      WebBeanAdmin admin = new WebBeanAdmin(bean, _beanId);

      admin.register();
      */
    } finally {
      thread.setContextClassLoader(oldLoader);
    }
  }
  
  void addGlobalProgram(ConfigProgram program)
  {
    if (program != null) {
      if (_isChildManager) {
        _parent.addGlobalProgram(program);
      }
      else { 
        _globalProgram.add(program);
      }
    }
  }

  /**
   * Returns the bean definitions matching a given name
   *
   * @param name the name of the bean to match
   */
  @Override
  public Set<Bean<?>> getBeans(String name)
  {
    ArrayList<Bean<?>> beanList = findByName(name);

    if (beanList != null)
      return new LinkedHashSet<Bean<?>>(beanList);
    else
      return new LinkedHashSet<Bean<?>>();
  }
  
  @Module
  public AtomicBoolean getStaticMemberBoolean(Member member)
  {
    AtomicBoolean flag = _staticMemberMap.get(member);
    
    if (flag == null) {
      flag = new AtomicBoolean();
      
      _staticMemberMap.putIfAbsent(member, flag);
      
      flag = _staticMemberMap.get(member);
    }
    
    return flag;
  }
  
  @Module
  public ReferenceFactory<?> getReferenceFactory(String name)
  {
    // ioc/23n3
    update();
    
    ReferenceFactory<?> refFactory = _namedRefFactoryMap.get(name);
    
    if (refFactory == null) {
      Set<Bean<?>> beanSet = getBeans(name);
      
      if (beanSet != null && beanSet.size() > 0) {
        Bean<?> bean = resolve(beanSet);

        // server/10sx
        if (name.equals(bean.getName())) {
          refFactory = getReferenceFactory(bean);
        
          // ioc/0301
          if (refFactory instanceof DependentReferenceFactoryImpl<?>)
            refFactory = new DependentElReferenceFactoryImpl((ManagedBeanImpl<?>) bean);
        }
      }
      
      if (refFactory == null) {
        refFactory = new UnresolvedReferenceFactory();
      }
      
      _namedRefFactoryMap.put(name, refFactory);
    }
    
    return refFactory;
  }

  /**
   * Returns the beans matching a class and annotation set
   *
   * @param type the bean's class
   * @param qualifiers required @Qualifier annotations
   */
  @Override
  public Set<Bean<?>> getBeans(Type type,
                               Annotation... qualifiers)
  {
    if (qualifiers != null) {
      for (int i = 0; i < qualifiers.length; i++) {
        for (int j = i + 1; j < qualifiers.length; j++) {
          if (qualifiers[i].annotationType() == qualifiers[j].annotationType())
            throw new IllegalArgumentException(L.l("getBeans may not have a duplicate qualifier '{0}'",
                                          qualifiers[i]));
        }
      }
    }
    
    Set<Bean<?>> set = resolve(type, qualifiers, null);

    if (set != null)
      return (Set<Bean<?>>) set;
    else
      return new HashSet<Bean<?>>();
  }

  /**
   * Returns the beans matching a class and annotation set
   *
   * @param type the bean's class
   * @param qualifierSet required @Qualifier annotations
   */
  private Set<Bean<?>> getBeans(Type type,
                                Set<Annotation> qualifierSet)
  {
    Annotation []qualifiers = new Annotation[qualifierSet.size()];
    qualifierSet.toArray(qualifiers);
    
    return getBeans(type, qualifiers);
  }

  /**
   * Returns the web beans component with a given binding list.
   */
  private Set<Bean<?>> resolve(Type type, 
                               Annotation []bindings,
                               InjectionPoint ip)
  {
    if (type == null)
      throw new NullPointerException();
    
    if (bindings == null || bindings.length == 0) {
      if (Object.class.equals(type))
        return resolveAllBeans();

      bindings = DEFAULT_ANN;
    }

    BaseType baseType = createTargetBaseType(type);
    
    // ioc/024n
    /*
    if (baseType.isGeneric())
      throw new IllegalArgumentException(L.l("'{0}' is an invalid getBeans type because it's generic.",
                                    baseType));
                                    */
    
    // ioc/02b1
    if (baseType.isVariable())
      throw new IllegalArgumentException(L.l("'{0}' is an invalid getBeans type because it's a type variable.",
                                             baseType));

    return resolveRec(baseType, bindings, ip);
  }

  /**
   * Returns the web beans component with a given binding list.
   */
  private Set<Bean<?>> resolveRec(BaseType baseType,
                                  Annotation []qualifiers,
                                  InjectionPoint ip)
  {
    WebComponent component = getWebComponent(baseType);

    if (component != null) {
      Set<Bean<?>> beans = component.resolve(baseType, qualifiers);

      if (beans != null && beans.size() > 0) {
        if (log.isLoggable(Level.FINEST))
          log.finest(this + " bind(" + baseType.getSimpleName()
                     + "," + toList(qualifiers) + ") -> " + beans);

        return beans;
      }
    }
    
    if (New.class.equals(qualifiers[0].annotationType())) {
      // ioc/0721
      New newQualifier = (New) qualifiers[0];
      HashSet<Bean<?>> set = new HashSet<Bean<?>>();
      Class<?> newClass = newQualifier.value();
      
      if (newClass == null || newClass.equals(void.class))
        newClass = baseType.getRawClass();
      
      AnnotatedType<?> ann = ReflectionAnnotatedFactory.introspectType(newClass);
      NewBean<?> newBean = new NewBean(this, baseType.getRawClass(), ann);
      newBean.introspect();
      
      if (component != null) {
        component.addComponent(baseType, null, newBean);
      }

      set.add(newBean);

      return set;
    }

    Class<?> rawType = baseType.getRawClass();

    if (Instance.class.equals(rawType)
        || Provider.class.equals(rawType)) {
      BaseType []param = baseType.getParameters();

      Type beanType;
      if (param.length > 0)
        beanType = param[0].getRawClass();
      else
        beanType = Object.class;

      HashSet<Bean<?>> set = new HashSet<Bean<?>>();
      set.add(new InstanceBeanImpl(this, beanType, qualifiers, ip));
      return set;
    }
    else if (Event.class.equals(rawType)) {
      if (baseType.isGenericRaw())
        throw new InjectionException(L.l("Event must have parameters because a non-parameterized Event would observe no events."));
                                      
      BaseType []param = baseType.getParameters();

      Type beanType;
      if (param.length > 0)
        beanType = param[0].getRawClass();
      else
        beanType = Object.class;
      
      HashSet<Annotation> qualifierSet = new LinkedHashSet<Annotation>();
      
      for (Annotation ann : qualifiers) {
        qualifierSet.add(ann);
      }
      
      qualifierSet.add(AnyLiteral.ANY);

      HashSet<Bean<?>> set = new HashSet<Bean<?>>();
      set.add(new EventBeanImpl(this, beanType, qualifierSet));
      return set;
    }

    if (_parent != null) {
      return _parent.resolveRec(baseType, qualifiers, ip);
    }

    for (Annotation ann : qualifiers) {
      if (! ann.annotationType().isAnnotationPresent(Qualifier.class)) {
        throw new IllegalArgumentException(L.l("'{0}' is an invalid binding annotation because it does not have a @Qualifier meta-annotation",
                                               ann));
      }
    }

    if (log.isLoggable(Level.FINEST)) {
      log.finest(this + " bind(" + baseType.getSimpleName()
                + "," + toList(qualifiers) + ") -> none");
    }

    return null;
  }


  /**
   * Returns the web beans component with a given binding list.
   */
  public Set<Bean<?>> resolveAllByType(Class<?> type)
  {
    Annotation []bindings = new Annotation[0];

    WebComponent component = getWebComponent(createTargetBaseType(type));

    if (component != null) {
      Set<Bean<?>> beans = component.resolve(type, bindings);

      if (log.isLoggable(Level.FINEST))
        log.finest(this + " bind(" + getSimpleName(type)
                  + "," + toList(bindings) + ") -> " + beans);

      if (beans != null && beans.size() > 0)
        return beans;
    }

    if (_parent != null) {
      return _parent.resolveAllByType(type);
    }

    return null;
  }

  private WebComponent getWebComponent(BaseType baseType)
  {
    if (_beanMap == null)
      return null;

    Class<?> rawClass = baseType.getRawClass();
    String className = rawClass.getName();
    
    WebComponent beanSet = _beanMap.get(className);

    if (beanSet == null) {
      HashSet<TypedBean> typedBeans = new HashSet<TypedBean>();

      if (_classLoader != null) {
        FillByType fillByType = new FillByType(baseType, typedBeans, this);

        _classLoader.applyVisibleModules(fillByType);
      }
      
      
      beanSet = new WebComponent(this, className);
      _beanMap.put(className, beanSet);
      
      for (TypedBean typedBean : typedBeans) {
        if (getDeploymentPriority(typedBean.getBean()) < 0) {
          continue;
        }
        
        _pendingValidationBeans.add(typedBean.getBean());
        
        beanSet.addComponent(typedBean.getType(),
                             typedBean.getAnnotated(),
                             typedBean.getBean());
      }
    }

    return beanSet;
  }
  
  private void clearBeanCache()
  {
    _namedRefFactoryMap.clear();
    _beanMap.clear();
  }

  private void fillByType(BaseType baseType,
                          HashSet<TypedBean> beanSet,
                          InjectManager beanManager)
  {
    Class<?> rawClass = baseType.getRawClass();
    
    InjectScanClass scanClass
      = _scanManager.getScanClass(rawClass.getName());

    if (scanClass != null && ! scanClass.isRegistered()) {
      discoverScanClass(scanClass);
      processPendingAnnotatedTypes();
    }
      
    ArrayList<TypedBean> localBeans = _selfBeanMap.get(rawClass);
    
    if (localBeans != null) {
      // ioc/0k00, ioc/0400 - XXX: not exactly right.  want local beans to have
      // priority if type and binding match
      /*
      if (this == beanManager)
        beanSet.clear();
      else if (beanSet.size() > 0) {
        return;
      }
      */

      for (TypedBean bean : localBeans) {
        if (getDeploymentPriority(bean.getBean()) < 0)
          continue;

        if (bean.isModulePrivate() && this != beanManager)
          continue;

        beanSet.add(bean);
      }
    }
  }

  //@Override
  public <X> Bean<X> getMostSpecializedBean(Bean<X> bean)
  {
    throw new UnsupportedOperationException();
    /*
    Bean<?> special = _specializedMap.get(bean.getBeanClass());
    
    if (special != null)
      return (Bean<X>) special;
    else
      return bean;
      */
  }
  
  @Module
  public boolean isSpecialized(Class<?> beanClass)
  {
    return _specializedMap.get(beanClass) != null;
  }

  @Override
  public <X> Bean<? extends X> resolve(Set<Bean<? extends X>> beans)
  {
    Bean<? extends X> bestBean = null;
    Bean<? extends X> secondBean = null;
    
    int bestPriority = -1;
    boolean isSpecializes = false;

    for (Bean<? extends X> bean : beans) {
      if (_specializedMap.get(bean.getBeanClass()) != null)
        continue;
      
      if ((bean instanceof AbstractIntrospectedBean<?>)
          && ((AbstractIntrospectedBean<?>) bean).getAnnotated().isAnnotationPresent(Specializes.class)) {
        if (! isSpecializes) {
          // ioc/07a3
          
          bestPriority = -1;
          bestBean = null;
          secondBean = null;
          isSpecializes = true;
        }
      }
      else if (isSpecializes) {
        continue;
      }
      
      int priority = getDeploymentPriority(bean);

      if (priority < 0) {
        // alternatives
      }
      else if (bestPriority < priority) {
        bestBean = bean;
        secondBean = null;
        
        bestPriority = priority;
      }
      else if (bestPriority == priority) {
        secondBean = bean;

        // TCK: ProducerFieldDefinitionTest
        boolean isFirstProduces = (bestBean instanceof ProducesMethodBean<?,?>
                                   || bestBean instanceof ProducesFieldBean<?,?>);
        boolean isSecondProduces = (secondBean instanceof ProducesMethodBean<?,?>
                                    || secondBean instanceof ProducesFieldBean<?,?>);
        
        // ioc/02b0
        if (isFirstProduces && ! isSecondProduces) {
          secondBean = null;
        }
        else if (isSecondProduces && ! isFirstProduces) {
          bestBean = bean;
          secondBean = null;
        }
      }
    }

    if (secondBean == null)
      return bestBean;
    else {
      throw ambiguousException(beans, bestPriority);
    }
  }
  
  private void validate(Type type)
  {
    BaseType baseType = createTargetBaseType(type);
    
    WebComponent comp = getWebComponent(baseType);
  }

  private void validate(Bean<?> bean)
  {
    if (bean.isAlternative() && ! isEnabled(bean))
      return;
    
    boolean isPassivating = isPassivatingScope(bean.getScope());
    
    if (bean instanceof InjectEnvironmentBean) {
      InjectEnvironmentBean envBean = (InjectEnvironmentBean) bean;
      
      if (envBean.getCdiManager() != this) {
        envBean.getCdiManager().validate(bean);
        return;
      }
    }
    
    if (bean instanceof CdiStatefulBean)
      isPassivating = true;
    
    if (bean instanceof ManagedBeanImpl
        && ((ManagedBeanImpl) bean).validate()) {
    }

    for (InjectionPoint ip : bean.getInjectionPoints()) {
      ReferenceFactory<?> factory = validateInjectionPoint(ip);

      if (ip.isDelegate() && ! (bean instanceof Decorator))
        throw new ConfigException(L.l("'{0}' is an invalid delegate because {1} is not a Decorator.",
                                      ip.getMember().getName(),
                                      bean));

      RuntimeException exn = validatePassivation(ip);

      if (exn != null && ! factory.isProducer())
        throw exn;
    }

    if (isNormalScope(bean.getScope())) {
      validateNormal(bean);
    }
  }
  
  private RuntimeException validatePassivation(InjectionPoint ip)
  {
    Bean<?> bean = ip.getBean();
    
    if (bean == null)
      return null;
    
    boolean isPassivating = isPassivatingScope(bean.getScope());
    
    if (bean instanceof CdiStatefulBean
        || bean.getBeanClass().isAnnotationPresent(Stateful.class)) {
      isPassivating = true;
    }
    
    if (isPassivating && ! ip.isTransient()) {
      Class<?> cl = getRawClass(ip.getType());
      
      Bean<?> prodBean = resolve(getBeans(ip.getType(), ip.getQualifiers()));
    
      // TCK conflict
      if (! cl.isInterface()
          && ! cl.isPrimitive()
          && ! Serializable.class.isAssignableFrom(cl)
          && ! isPassivationCapable(prodBean)) {
        RuntimeException exn;

        if (isProduct(prodBean))
          exn = new IllegalProductException(L.l("'{0}' is an invalid injection point of type {1} because it's not serializable for {2}",
                                                ip.getMember().getName(),
                                                ip.getType(),
                                                bean));
        else
          exn = new ConfigException(L.l("'{0}.{1}' is an invalid injection point of type {2} ({3}) because it's not serializable for {4}",
                                        bean.getBeanClass().getName(),
                                        ip.getMember().getName(),
                                        ip.getType(),
                                        prodBean,
                                        bean));
        
        return exn;
      }
    }
    
    return null;
  }
  
  private boolean isPassivationCapable(Bean<?> bean)
  {
    if (isNormalScope(bean.getScope()))
      return true;
    
    // ioc/05e2
    if (bean instanceof PassivationCapable
        && ((PassivationCapable) bean).getId() != null) {
      return true;
    }
    
    return false;
  }
  
  private boolean isProduct(Bean<?> bean)
  {
    return ((bean instanceof ProducesFieldBean<?,?>)
            || (bean instanceof ProducesMethodBean<?,?>));
  }
  
  private Class<?> getRawClass(Type type)
  {
    if (type instanceof Class<?>)
      return (Class<?>) type;
    
    BaseType baseType = createSourceBaseType(type);
    
    return baseType.getRawClass();
  }
  
  private void validateNormal(Bean<?> bean)
  {
    Annotated ann = null;
    
    if (bean instanceof AbstractBean) {
      AbstractBean absBean = (AbstractBean) bean;
      
      ann = absBean.getAnnotated();
    }
    
    if (ann == null)
      return;
    
    Type baseType = ann.getBaseType();
    
    Class<?> cl = createTargetBaseType(baseType).getRawClass();
    
    if (cl.isInterface())
      return;
    
    int modifiers = cl.getModifiers();
    
    if (Modifier.isFinal(modifiers)) {
      throw new ConfigException(L.l("'{0}' is an invalid @{1} bean because it's a final class, for {2}.",
                                    cl.getSimpleName(), bean.getScope().getSimpleName(),
                                    bean));
    }
    
    Constructor<?> ctor = null;
    
    for (Constructor<?> ctorPtr : cl.getDeclaredConstructors()) {
      if (ctorPtr.getParameterTypes().length > 0
          && ! ctorPtr.isAnnotationPresent(Inject.class)) {
        // ioc/05am
        continue;
      }
      
      if (Modifier.isPrivate(ctorPtr.getModifiers())) {
        throw new ConfigException(L.l("'{0}' is an invalid @{1} bean because its constructor is private for {2}.",
                                      cl.getSimpleName(), bean.getScope().getSimpleName(),
                                      bean));

      }
      
      ctor = ctorPtr;
    }
    
    if (ctor == null) {
      throw new ConfigException(L.l("'{0}' is an invalid @{1} bean because it doesn't have a zero-arg constructor for {2}.",
                                    cl.getName(), bean.getScope().getSimpleName(),
                                    bean));

    }
    
    
    for (Method method : cl.getMethods()) {
      if (method.getDeclaringClass() == Object.class)
        continue;
      
      if (Modifier.isFinal(method.getModifiers())) {
        throw new ConfigException(L.l("'{0}' is an invalid @{1} bean because {2} is a final method for {3}.",
                                      cl.getSimpleName(), bean.getScope().getSimpleName(),
                                      method.getName(),
                                      bean));
      
      }
    }
    
    for (Field field : cl.getFields()) {
      if (Modifier.isStatic(field.getModifiers()))
        continue;
      
      if (Modifier.isPublic(field.getModifiers())) {
        throw new ConfigException(L.l("'{0}' is an invalid @{1} bean because {2} is a public field for {3}.",
                                      cl.getSimpleName(), bean.getScope().getSimpleName(),
                                      field.getName(),
                                      bean));
      }
    }
    
    for (InjectionPoint ip : bean.getInjectionPoints()) {
      if (ip.getType().equals(InjectionPoint.class))
        throw new ConfigException(L.l("'{0}' is an invalid @{1} bean because '{2}' injects an InjectionPoint for {3}.",
                                      cl.getSimpleName(), bean.getScope().getSimpleName(),
                                      ip.getMember().getName(),
                                      bean));
      
    }
  }
  
  @Override
  public void validate(InjectionPoint ij)
  {
    validateInjectionPoint(ij);
  }
  
  public ReferenceFactory<?> validateInjectionPoint(InjectionPoint ij)
  {
    try {
      if (ij.isDelegate()) {
        if (! (ij.getBean() instanceof Decorator<?>))
          throw new ConfigException(L.l("'{0}' is an invalid @Delegate because {1} is not a decorator",
                                        ij.getMember().getName(), ij.getBean()));
      }
      else {
        return getReferenceFactory(ij);
      }
    } catch (AmbiguousResolutionException e) {
      throw new AmbiguousResolutionException(L.l("{0}.{1}: {2}",
                                       ij.getMember().getDeclaringClass().getName(),
                                       ij.getMember().getName(),
                                       e.getMessage()),
                                   e);
    } catch (UnsatisfiedResolutionException e) {
      throw new UnsatisfiedResolutionException(L.l("{0}.{1}: {2}",
                                                   ij.getMember().getDeclaringClass().getName(),
                                                   ij.getMember().getName(),
                                                   e.getMessage()),
                                   e);
    } catch (IllegalProductException e) {
      throw new IllegalProductException(L.l("{0}.{1}: {2}",
                                            ij.getMember().getDeclaringClass().getName(),
                                            ij.getMember().getName(),
                                            e.getMessage()),
                                   e);
    } catch (Exception e) {
      throw new InjectionException(L.l("{0}.{1}: {2}",
                                       ij.getMember().getDeclaringClass().getName(),
                                       ij.getMember().getName(),
                                       e.getMessage()),
                                   e);
    }
    
    return null;
  }

  public int getDeploymentPriority(Bean<?> bean)
  {
    int priority = DEFAULT_PRIORITY;

    if (bean.isAlternative()) {
      priority = -1;
      
      Integer value = getPriority(bean.getBeanClass());

      if (value != null)
        priority = value;
    }

    Set<Class<? extends Annotation>> stereotypes = bean.getStereotypes();
    
    if (stereotypes != null) {
      for (Class<? extends Annotation> annType : stereotypes) {
        Integer value = _deploymentMap.get(annType);
                                           
        if (value != null) {
          if (priority < value)
            priority = value;
        }
        else if (annType.isAnnotationPresent(Alternative.class)
                 && priority == DEFAULT_PRIORITY)
          priority = -1;
      }
    }

    if (priority < 0)
      return priority;
    else if (bean instanceof AbstractBean<?>) {
      // ioc/0213
      AbstractBean<?> absBean = (AbstractBean<?>) bean;

      if (absBean.getBeanManager() == this)
        priority += 1000000;
    }
    else {
      priority += 1000000;
    }

    return priority;
  }
  
  private Integer getPriority(Class<?> cl)
  {
    Integer value = _deploymentMap.get(cl);
    
    if (value != null)
      return value;
    else if (_parent != null)
      return _parent.getPriority(cl);
    else
      return null;
  }
  
  private Set<Bean<?>> resolveAllBeans()
  {
    LinkedHashSet<Bean<?>> beans = new LinkedHashSet<Bean<?>>();

    for (ArrayList<TypedBean> comp : _selfBeanMap.values()) {
      for (TypedBean typedBean : comp) {
        beans.add(typedBean.getBean());
      }
    }

    return beans;
  }

  @Override
  public <T> CreationalContext<T> createCreationalContext(Contextual<T> bean)
  {
    return new OwnerCreationalContext<T>(bean);
  }

  /**
   * Convenience-class for Resin.
   */
  public <T> T getReference(Class<T> type, Annotation... qualifiers)
  {
    Set<Bean<?>> beans = getBeans(type, qualifiers);
    Bean<T> bean = (Bean<T>) resolve(beans);

    if (bean == null)
      return null;

    return getReference(bean);
  }

  /**
   * Convenience for Resin.
   */
  public <T> T getReference(Bean<T> bean)
  {
    ReferenceFactory<T> factory = getReferenceFactory(bean);
    
    if (factory != null)
      return factory.create(null, null, null);
    else
      return null;
  }

  /**
   * Convenience for Resin.
   */
  public <T> T findReference(Bean<T> bean)
  {
    Context context = getContext(bean.getScope());
    
    if (context != null)
      return context.get(bean);
    else
      return null;
  }

  /**
   * Convenience for Resin.
   */
  public <T> T getReference(Bean<T> bean, CreationalContextImpl<?> parentEnv)
  {
    ReferenceFactory<T> factory = getReferenceFactory(bean);
    
    return factory.create(null, parentEnv, null);
  }

  /**
   * Convenience-class for Resin.
   */
  public <T> T getReference(String name)
  {
    Set<Bean<?>> beans = getBeans(name);
    Bean<T> bean = (Bean<T>) resolve(beans);

    if (bean == null)
      return null;

    ReferenceFactory<T> factory = getReferenceFactory(bean);
    
    return factory.create(null, null, null);
  }

  /**
   * Convenience-class for Resin.
   */
  public <T> T getReference(String name, CreationalContextImpl parentEnv)
  {
    Set<Bean<?>> beans = getBeans(name);
    Bean<T> bean = (Bean<T>) resolve(beans);

    if (bean == null)
      return null;

    ReferenceFactory<T> factory = getReferenceFactory(bean);

    return factory.create(null, parentEnv, null);
  }

  /**
   * Returns an instance for the given bean.  This method will obey
   * the scope of the bean, so a singleton will return the single bean.
   *
   * @param bean the metadata for the bean
   *
   * @return an instance of the bean obeying scope
   */
  @Override
  public Object getReference(Bean<?> bean,
                             Type type,
                             CreationalContext<?> createContext)
  {
    Thread thread = Thread.currentThread();
    ClassLoader oldLoader = thread.getContextClassLoader();
    
    try {
      thread.setContextClassLoader(getClassLoader());
      
      ReferenceFactory factory = getReferenceFactory(bean);
      
      if (factory == null) {
        throw new IllegalStateException(L.l("{0} is an uninstantiable bean",
                                            bean));
      }

      if (createContext instanceof CreationalContextImpl<?>)
        return factory.create((CreationalContextImpl) createContext, null, null);
      else
        return factory.create(null, null, null);
    } finally {
      thread.setContextClassLoader(oldLoader);
    }
  }
  
  /**
   * Used by ScopeProxy
   */
  private <T> T getInstanceForProxy(Bean<T> bean)
  {
    CreationalContextImpl<?> oldEnv = _proxyThreadLocal.get();
  
    T value;
    
    if (oldEnv != null) {
      value = oldEnv.get(bean);
      
      if (value != null)
        return value;
    }
    
    try {
      CreationalContextImpl<T> env = new OwnerCreationalContext(bean, oldEnv);
      
      _proxyThreadLocal.set(env);

      value = bean.create(env);
      
      return value;
    } finally {
      _proxyThreadLocal.set(oldEnv);
    }
  }

  public <T> ReferenceFactory<T> getReferenceFactory(Bean<T> bean)
  {
    if (bean == null)
      return null;
    
    ReferenceFactory<T> factory = (ReferenceFactory<T>) _refFactoryMap.get(bean);
    
    if (factory == null) {
      factory = createReferenceFactory(bean);
      _refFactoryMap.put(bean, factory);
      factory.validate();
    }
    
    return factory;
  }
  
  private <T> ReferenceFactory<T> createReferenceFactory(Bean<T> bean)
  {
    Class<? extends Annotation> scopeType = bean.getScope();
    
    if (InjectionPoint.class.equals(bean.getBeanClass())) {
      return (ReferenceFactory) new InjectionPointReferenceFactory();
    }

    if (Dependent.class == scopeType) {
      if (bean instanceof ManagedBeanImpl<?>)
        return new DependentReferenceFactoryImpl<T>((ManagedBeanImpl<T>) bean);
      else
        return new DependentReferenceFactory<T>(bean);
    }

    if (scopeType == null) {
      throw new IllegalStateException("Unknown scope for " + bean);
    }

    InjectManager ownerManager;

    if (bean instanceof AbstractBean<?>)
      ownerManager = ((AbstractBean<?>) bean).getBeanManager();
    else
      ownerManager = this;

    Context context = ownerManager.getContextImpl(scopeType);

    /*
    if (context == null)
      return null;
      */
    if (context == null)
      throw new InjectionException(L.l("Bean has an unknown scope '{0}' for bean {1}",
                                       scopeType, bean));
    
    if (isNormalScope(scopeType) && bean instanceof ScopeAdapterBean<?>) {
      ScopeAdapterBean<T> scopeAdapterBean = (ScopeAdapterBean<T>) bean;
      
      return new NormalContextReferenceFactory<T>(bean, scopeAdapterBean, context);
    }
    else
      return new ContextReferenceFactory<T>(bean, context);
  }
  
  public <T> ReferenceFactory<T> createNormalInstanceFactory(Bean<T> bean)
  {
    Class<? extends Annotation> scopeType = bean.getScope();

    if (! isNormalScope(scopeType)) {
      throw new IllegalStateException(L.l("{0} is an invalid normal scope for {1}",
                                          scopeType, bean));
    }

    InjectManager ownerManager;

    if (bean instanceof AbstractBean<?>)
      ownerManager = ((AbstractBean<?>) bean).getBeanManager();
    else
      ownerManager = this;

    Context context = ownerManager.getContextImpl(scopeType);

    if (context == null)
      throw new InjectionException(L.l("Bean has an unknown scope '{0}' for bean {1}",
                                       scopeType, bean));

    return new NormalInstanceReferenceFactory<T>(bean, context);
  }

  public RuntimeException unsatisfiedException(Type type,
                                               Annotation []qualifiers)
  {
    WebComponent component = getWebComponent(createTargetBaseType(type));

    if (component == null) {
      throw new UnsatisfiedResolutionException(L.l("Can't find a bean for '{0}' because no beans implementing that class have been registered with the injection manager {1}.",
                                                   type, this));
    }
    else {
      ArrayList<Bean<?>> enabledList = component.getEnabledBeanList();

      if (enabledList.size() == 0) {
        throw new UnsatisfiedResolutionException(L.l("Can't find a bean for '{0}' because no beans implementing that class have been registered with the injection manager {1}.",
                                                     type, this));
      }
      else {
        return new UnsatisfiedResolutionException(L.l("Can't find a bean for '{0}' because no beans match the type and qualifiers {1}.\nBeans:{2}",
                                                      type,
                                                      toList(qualifiers),
                                                      listToLines(enabledList)));
      }
    }
  }

  private String listToLines(List<?> list)
  {
    StringBuilder sb = new StringBuilder();

    ArrayList<String> lines = new ArrayList<String>();

    for (int i = 0; i < list.size(); i++) {
      lines.add(list.get(i).toString());
    }

    Collections.sort(lines);

    for (String line : lines) {
      for (String split : line.split("\n")) {
        sb.append("\n    ").append(split);
      }
    }

    return sb.toString();
  }

  /**
   * Convert an annotation array to a list for debugging purposes
   */
  private ArrayList<Annotation> toList(Annotation []annList)
  {
    ArrayList<Annotation> list = new ArrayList<Annotation>();

    if (annList != null) {
      for (Annotation ann : annList) {
        list.add(ann);
      }
    }

    return list;
  }
  
  InjectionPointHandler getInjectionPointHandler(AnnotatedField<?> field)
  {
    // InjectIntrospector.introspect(_injectProgramList, field);

    for (Annotation ann : field.getAnnotations()) {
      Class<? extends Annotation> annType = ann.annotationType();

      InjectionPointHandler handler = _injectionMap.get(annType);

      if (handler != null) {
        return handler;
      }
    }

    return null;
  }

  InjectionPointHandler getInjectionPointHandler(AnnotatedMethod<?> method)
  {
    // InjectIntrospector.introspect(_injectProgramList, field);

    for (Annotation ann : method.getAnnotations()) {
      Class<? extends Annotation> annType = ann.annotationType();

      InjectionPointHandler handler = _injectionMap.get(annType);

      if (handler != null) {
        return handler;
      }
    }

    return null;
  }
  
  public InjectionPointHandler 
  getInjectionPointHandler(Class<? extends Annotation> annType)
  {
    return _injectionMap.get(annType);
  }

  /**
   * Internal callback during creation to get a new injection instance.
   */
  @Override
  public Object getInjectableReference(InjectionPoint ij,
                                       CreationalContext<?> parentCxt)
  {
    CreationalContextImpl<?> parentEnv = null;
    
    if (parentCxt instanceof CreationalContextImpl<?>)
      parentEnv = (CreationalContextImpl<?>) parentCxt;
    
    if (InjectionPoint.class.equals(ij.getType())) {
      if (parentEnv != null) {
        return parentEnv.findInjectionPoint();
      }
    }
    
    ReferenceFactory<?> factory = getReferenceFactory(ij);
    
    return factory.create(null, parentEnv, ij);
  }

  public ReferenceFactory<?> getReferenceFactory(InjectionPoint ij)
  {
    if (ij.isDelegate())
      return new DelegateReferenceFactory();
    else if (ij.getType().equals(InjectionPoint.class))
      return new InjectionPointReferenceFactory();
    
    Type type = ij.getType();
    Set<Annotation> qualifiers = ij.getQualifiers();

    ReferenceFactory<?> factory = getReferenceFactory(type, qualifiers, ij);

    RuntimeException exn = validatePassivation(ij);
    
    if (exn != null) {
      if (factory.isProducer())
        return new ErrorReferenceFactory(exn);
      else
        throw exn;
    }
    
    return factory;
  }

  public ReferenceFactory<?> getReferenceFactory(Type type,
                                                 Set<Annotation> qualifiers,
                                                 InjectionPoint ij)
  {
    if (ij != null && ij.isDelegate())
      return new DelegateReferenceFactory();
    
    Bean<?> bean = resolveByInjectionPoint(type, qualifiers, ij);
    
    return getReferenceFactory(bean);
  }

  private Bean<?> resolveByInjectionPoint(Type type,
                                          Set<Annotation> qualifierSet,
                                          InjectionPoint ij)
  {
    Annotation []qualifiers;

    if (qualifierSet != null && qualifierSet.size() > 0) {
      qualifiers = new Annotation[qualifierSet.size()];
      qualifierSet.toArray(qualifiers);

      if (qualifiers.length == 1
          && qualifiers[0].annotationType().equals(New.class)) {
        New newQualifier = (New) qualifiers[0];
        
        return createNewBean(type, newQualifier);
      }
    }
    else
      qualifiers = new Annotation[] { DefaultLiteral.DEFAULT };
    
    BaseType baseType = createTargetBaseType(type);
    
    /*
    if (baseType.isGeneric())
      throw new InjectionException(L.l("'{0}' is an invalid type for injection because it's generic. {1}",
                                       baseType, ij));
                                       */
    if (baseType.isGenericVariable())
      throw new InjectionException(L.l("'{0}' is an invalid type for injection because it's a variable generic type.\n  {1}",
                                       baseType, ij));

    Set<Bean<?>> set = resolveRec(baseType, qualifiers, ij);
    
    if (set == null || set.size() == 0) {
      if (InjectionPoint.class.equals(type))
        return new InjectionPointBean(this, ij);
      
      throw unsatisfiedException(type, qualifiers);
    }

    Bean<?> bean = resolve(set);

    if (bean != null 
        && type instanceof Class<?>
        && ((Class<?>) type).isPrimitive()
        && bean.isNullable()) {
      throw new InjectionException(L.l("'{0}' cannot be injected because it's a primitive with {1}",
                                       type, bean));                               
    }
    
    return bean;

    /*
    else if (set.size() == 1) {
      Iterator iter = set.iterator();

      if (iter.hasNext()) {
        Bean bean = (Bean) iter.next();

        return bean;
      }
    }
    else {
      throw new AmbiguousResolutionException(L.l("'{0}' with binding {1} matches too many configured beans{2}",
                                                 BaseType.create(type, null),
                                                 bindingSet,
                                                 toLineList(set)));
    }

    return null;
*/
  }

  private <T> Bean<?> createNewBean(Type type, New newQualifier)
  {
    Class<?> newClass = newQualifier.value();
    
    if (newClass == null 
        || void.class.equals(newClass)
        || New.class.equals(newClass)) {
      BaseType baseType = createTargetBaseType(type);
      newClass = (Class<T>) baseType.getRawClass();
    }
      
    Bean<?> bean = _newBeanMap.get(newClass);

    if (bean == null) {
      AnnotatedType<T> annType = (AnnotatedType<T>) ReflectionAnnotatedFactory.introspectType(newClass);
      
      BaseType newType = createSourceBaseType(type);

      NewBean<T> newBean = new NewBean<T>(this, newType.getRawClass(), annType);
      newBean.introspect();

      _newBeanMap.put(type, bean);
      bean = newBean;
    }

    return bean;
  }

  private <X> AmbiguousResolutionException
    ambiguousException(Set<Bean<? extends X>> beanSet, 
                       int bestPriority)
  {
    // ArrayList<Bean<?>> matchBeans = new ArrayList<Bean<?>>();
    ArrayList<String> matchBeans = new ArrayList<String>();

    for (Bean<?> bean : beanSet) {
      int priority = getDeploymentPriority(bean);

      if (priority == bestPriority) {
        matchBeans.add(toDisplayString(bean));
      }
    }

    return new AmbiguousResolutionException(L.l("Too many beans match, because they all have equal precedence.  Beans:{0}\nfor {1}. You may need to use the @Alternative or <alternatives> to select one.",
                                                listToLines(matchBeans), this));
  }
  
  private String toDisplayString(Bean<?> bean)
  {
    if (bean instanceof AbstractBean<?>) {
      AbstractBean<?> absBean = (AbstractBean<?>) bean;
      
      return absBean.toDisplayString();
    }
    else
      return String.valueOf(bean);
  }

  @Override
  public ELResolver getELResolver()
  {
    return _elResolver;
  }

  @Override
  public ExpressionFactory
    wrapExpressionFactory(ExpressionFactory expressionFactory)
  {
    return new CandiExpressionFactory(expressionFactory);
  }

  //
  // scopes
  //

  /**
   * Adds a new scope context
   */
  public void addContext(Context context)
  {
    Class<? extends Annotation> scopeType = context.getScope();
    
    Context oldContext = _contextMap.get(scopeType);
    
    if (oldContext == null) {
      _contextMap.put(context.getScope(), context);
    }
    else {
      // ioc/0p41 - CDI TCK
      
      RuntimeException exn
        = new IllegalStateException(L.l("{0} is an invalid new context because @{1} is already registered as a scope",
                                        context, scopeType.getName()));
                                        
      _contextMap.put(context.getScope(), new ErrorContext(exn, context));
    }
  }
  
  public void replaceContext(Context context)
  {
    _contextMap.put(context.getScope(), context);
  }

  /**
   * Returns the scope context for the given type
   */
  @Override
  public Context getContext(Class<? extends Annotation> scopeType)
  {
    Context context = _contextMap.get(scopeType);

    if (context != null && context.isActive()) {
      return context;
    }
    
    if (context instanceof ErrorContext) {
      ErrorContext cxt = (ErrorContext) context;
      
      throw cxt.getException();
    }
    
    /*
    if (! isScope(scopeType)) {
      throw new IllegalStateException(L.l("'@{0}' is not a valid scope because it does not have a @Scope annotation",
                                          scopeType));
    }
    */
    
    throw new ContextNotActiveException(L.l("'@{0}' is not an active Java Injection context.",
                                            scopeType.getName()));
  }

  /**
   * Required for TCK. Returns the scope context for the given type.
   */
  public Context getContextImpl(Class<? extends Annotation> scopeType)
  {
    return _contextMap.get(scopeType);
  }

  /**
   * Returns the bean for the given passivation id.
   */
  public Bean<?> getPassivationCapableBean(String id)
  {
    return _selfPassivationBeanMap.get(id);
  }

  public Annotation []getQualifiers(Set<Annotation> annotations)
  {
    ArrayList<Annotation> bindingList = new ArrayList<Annotation>();

    for (Annotation ann : annotations) {
      if (ann.annotationType().isAnnotationPresent(Qualifier.class))
        bindingList.add(ann);
    }

    Annotation []bindings = new Annotation[bindingList.size()];
    bindingList.toArray(bindings);

    return bindings;
  }

  public Annotation []getQualifiers(Annotation []annotations)
  {
    ArrayList<Annotation> bindingList = new ArrayList<Annotation>();

    for (Annotation ann : annotations) {
      if (ann.annotationType().isAnnotationPresent(Qualifier.class))
        bindingList.add(ann);
    }

    Annotation []bindings = new Annotation[bindingList.size()];
    bindingList.toArray(bindings);

    return bindings;
  }

  /**
   * Sends the specified event to any observer instances in the scope
   */
  @Override
  public void fireEvent(Object event, Annotation... qualifiers)
  {
    if (log.isLoggable(Level.FINEST))
      log.finest(this + " fireEvent " + event);

    getEventManager().fireEvent(event, qualifiers);
  }

  /**
   * Returns the observers listening for an event
   *
   * @param event to resolve
   * @param qualifiers the binding set for the event
   */
  @Override
  public <T> Set<ObserverMethod<? super T>>
  resolveObserverMethods(T event, Annotation... qualifiers)
  {
    return getEventManager().resolveObserverMethods(event, qualifiers);
  }

  //
  // interceptor support
  //

  /**
   * Adds a new decorator class
   */
  public <X> BeanManager addInterceptorClass(Class<?> interceptorClass)
  {
    _interceptorClassList.add(interceptorClass);

    return this;
  }

  /**
   * Adds a new interceptor to the manager
   */
  private <X> InterceptorEntry<X> addInterceptor(Interceptor<X> interceptor)
  {
    InterceptorEntry<X> entry = new InterceptorEntry<X>(interceptor);
    
    _interceptorList.add(entry);
    
    return entry;
  }

  /**
   * Resolves the interceptors for a given interceptor type
   *
   * @param type the main interception type
   * @param qualifiers qualifying bindings
   *
   * @return the matching interceptors
   */
  @Override
  public List<Interceptor<?>> resolveInterceptors(InterceptionType type,
                                                  Annotation... qualifiers)
  {
    if (qualifiers == null || qualifiers.length == 0)
      throw new IllegalArgumentException(L.l("resolveInterceptors requires at least one @InterceptorBinding"));
    
    for (int i = 0; i < qualifiers.length; i++) {
      Class<? extends Annotation> annType = qualifiers[i].annotationType();
      
      if (! annType.isAnnotationPresent(InterceptorBinding.class))
        throw new IllegalArgumentException(L.l("Annotation must be an @InterceptorBinding at '{0}' in resolveInterceptors",
                                               qualifiers[i]));
        
      for (int j = i + 1; j < qualifiers.length; j++) {
        if (annType.equals(qualifiers[j].annotationType()))
          throw new IllegalArgumentException(L.l("Duplicate binding '{0}' is not allowed in resolveInterceptors",
                                                 qualifiers[i]));
      }
    }

    ArrayList<Interceptor<?>> interceptorList
      = new ArrayList<Interceptor<?>>();

    for (InterceptorEntry<?> entry : _interceptorList) {
      Interceptor<?> interceptor = entry.getInterceptor();

      if (! interceptor.intercepts(type)) {
        continue;
      }

      if (entry.isMatch(qualifiers)) {
        interceptorList.add(interceptor);
      }
    }

    return interceptorList;
  }

  //
  // decorator
  //

  /**
   * Adds a new decorator
   */
  private <X> DecoratorEntry<X> addDecorator(Decorator<X> decorator)
  {
    BaseType baseType = createTargetBaseType(decorator.getDelegateType());

    DecoratorEntry<X> entry = new DecoratorEntry<X>(this, decorator, baseType);
    
    _decoratorList.add(entry);
    
    for (Type type : decorator.getDecoratedTypes()) {
      if (type instanceof Class<?>) {
        Class<?> cl = (Class<?>) type;
      
        if (Object.class.equals(cl)
            || Serializable.class.equals(cl)) {
          continue;
        }
        
        String javaClassName = cl.getName();
      
        InjectScanClass scanClass = getScanManager().getScanClass(javaClassName);

        if (scanClass != null && ! scanClass.isRegistered()) {
          discoverScanClass(scanClass);
        }
      }
    }
    processPendingAnnotatedTypes();

    return entry;
  }

  /**
   * Adds a new decorator class
   */
  public <X> BeanManager addDecoratorClass(Class<?> decoratorClass)
  {
    _decoratorClassList.add(decoratorClass);

    return this;
  }
  
  /**
   * Called by the generated code.
   */
  public List<Decorator<?>> resolveDecorators(Class<?> type)
  {
    HashSet<Type> types = new HashSet<Type>();
    types.add(type);

    ArrayList<Annotation> bindingList = new ArrayList<Annotation>();

    boolean isQualifier = false;

    for (Annotation ann : type.getAnnotations()) {
      if (isQualifier(ann.annotationType())) {
        bindingList.add(ann);

        if (! Named.class.equals(ann.annotationType())) {
          isQualifier = true;
        }
      }
    }

    if (! isQualifier)
      bindingList.add(DefaultLiteral.DEFAULT);
    bindingList.add(AnyLiteral.ANY);

    Annotation []bindings = new Annotation[bindingList.size()];
    bindingList.toArray(bindings);

    List<Decorator<?>> decorators = resolveDecorators(types, bindings);
    
    // XXX: 4.0.7
    // log.info("DECORATORS: " + decorators + " " + types + " " + this);
    
    return decorators;
  }

  /**
   * Resolves the decorators for a given set of types
   *
   * @param types the types to match for the decorator
   * @param qualifiers qualifying bindings
   *
   * @return the matching interceptors
   */
  @Override
  public List<Decorator<?>> resolveDecorators(Set<Type> types,
                                              Annotation... qualifiers)
  {
    if (types.size() == 0)
      throw new IllegalArgumentException(L.l("type set must contain at least one type"));
    
    if (qualifiers != null) {
      for (int i = 0; i < qualifiers.length; i++) {
        for (int j = i + 1; j < qualifiers.length; j++) {
          if (qualifiers[i].annotationType() == qualifiers[j].annotationType())
            throw new IllegalArgumentException(L.l("resolveDecorators may not have a duplicate qualifier '{0}'",
                                          qualifiers[i]));
        }
      }
    }

    ArrayList<Decorator<?>> decorators = new ArrayList<Decorator<?>>();

    if (qualifiers == null || qualifiers.length == 0)
      qualifiers = DEFAULT_ANN;

    if (_decoratorList == null)
      return decorators;
    
    for (Annotation ann : qualifiers) {
      if (! isQualifier(ann.annotationType()))
        throw new IllegalArgumentException(L.l("@{0} must be a qualifier", ann.annotationType()));
    }
    
    ArrayList<BaseType> targetTypes = new ArrayList<BaseType>();
    
    for (Type type : types) {
      targetTypes.add(createSourceBaseType(type));
    }

    for (DecoratorEntry<?> entry : _decoratorList) {
      Decorator<?> decorator = entry.getDecorator();
      
      // XXX: delegateTypes
      if (isDelegateAssignableFrom(entry.getDelegateType(), targetTypes)
          && entry.isMatch(qualifiers)) {
        decorators.add(decorator);
      }
    }

    return decorators;
  }

  private boolean isDelegateAssignableFrom(BaseType delegateType,
                                           ArrayList<BaseType> sourceTypes)
  {
    for (BaseType sourceType : sourceTypes) {
      if (delegateType.isAssignableFrom(sourceType)) {
        return true;
      }
    }

    return false;
  }

  public void addConfiguredClass(String className)
  {
    _xmlExtension.addConfiguredBean(className);
//    _configuredClasses.add(className);
  }
  
  public XmlCookie generateXmlCookie()
  {
    return new XmlCookieLiteral(_xmlCookieSequence.incrementAndGet());
  }

  public void addLoader()
  {
    _isUpdateNeeded = true;
  }

  public void update()
  {
    // ioc/0044
    if (! _isEnableAutoUpdate) {
      return;
    }

    if (! _isUpdateNeeded 
        && ! _scanManager.isPending()
        && _pendingAnnotatedTypes.size() == 0
        && ! _xmlExtension.isPending()) {
      return;
    }

    _isUpdateNeeded = false;

    Thread thread = Thread.currentThread();
    ClassLoader oldLoader = thread.getContextClassLoader();

    try {
      thread.setContextClassLoader(_classLoader);

      _extensionManager.updateExtensions();

      ArrayList<ScanRootContext> rootContextList
        = _scanManager.getPendingScanRootList();

      for (ScanRootContext context : rootContextList) {
        _xmlExtension.addRoot(context.getRoot());
      }

      _isBeforeBeanDiscoveryComplete = true;

      fireBeforeBeanDiscovery();

      _xmlExtension.processRoots();

      processPendingAnnotatedTypes();
    } catch (ConfigException e) {
      if (_configException == null)
        _configException = e;

      throw e;
    } finally {
      thread.setContextClassLoader(oldLoader);
    }
  }
  
  private void fireBeforeBeanDiscovery()
  {
    if (_isBeforeBeanDiscoverFired) {
      return;
    }
    
    Thread thread = Thread.currentThread();
    ClassLoader oldLoader = thread.getContextClassLoader();

    try {
      thread.setContextClassLoader(_classLoader);

      if (_classLoader != null) {
        _classLoader.updateScan();
      }

      _extensionManager.updateExtensions();

      if (! _isBeforeBeanDiscoverFired) {
        getExtensionManager().fireBeforeBeanDiscovery();

        _isBeforeBeanDiscoverFired = true;
      }
    } catch (ConfigException e) {
      if (_configException == null)
        _configException = e;

      throw e;
    } finally {
      thread.setContextClassLoader(oldLoader);
    }
  
  }
  
  public void updateResources()
  {
  }
  
  private void addPendingAnnotatedType(AnnotatedType<?> annType)
  {
    _pendingAnnotatedTypes.add(annType);
  }

  public void processPendingAnnotatedTypes()
  {
    _scanManager.discover();
    
    ArrayList<AnnotatedType<?>> types = new ArrayList<AnnotatedType<?>>(_pendingAnnotatedTypes);
    _pendingAnnotatedTypes.clear();
    
    Collections.sort(types, new AnnotatedTypeComparator());

    for (AnnotatedType<?> beanType : types) {
      AnnotatedType<?> type = getExtensionManager().processAnnotatedType(beanType);

      if (type != null) {
        discoverBeanImpl(type);
      }
    }
    
    _extensionManager.processPendingEvents();
  }

  void discoverScanClass(InjectScanClass scanClass)
  {
    scanClass.register();
    
    // processPendingAnnotatedTypes();
  }
  
  void discoverBean(InjectScanClass scanClass)
  {
    AnnotatedType<?> type = createDiscoveredType(scanClass.getClassName());
    
    if (type != null)
      discoverBean(type);
  }
  
  void discoverBean(String className)
  {
    AnnotatedType<?> type = createDiscoveredType(className);
    
    if (type != null)
      discoverBean(type);
  }
  
  private AnnotatedType<?> createDiscoveredType(String className)
  {
    try {
      Class<?> cl;

      cl = Class.forName(className, false, _classLoader);

      /*
      if (! isValidSimpleBean(cl))
        return;
        */

      if (cl.getDeclaringClass() != null
          && ! Modifier.isStatic(cl.getModifiers()))
        return null;

      for (Class<? extends Annotation> forbiddenAnnotation : _forbiddenAnnotations) {
        if (cl.isAnnotationPresent(forbiddenAnnotation))
          return null;
      }

      for (Class<?> forbiddenClass : _forbiddenClasses) {
        if (forbiddenClass.isAssignableFrom(cl))
          return null;
      }

      // ioc/0619
      /*
      if (isDisabled(cl))
        return;
        */
      
      if (cl.isInterface()) {
        if (Annotation.class.isAssignableFrom(cl)
            && cl.isAnnotationPresent(Qualifier.class)) {
          // validateQualifier(cl);
          QualifierBinding.validateQualifier(cl, null);
        }
      }

      return createAnnotatedType(cl);
    } catch (ClassNotFoundException e) {
      log.log(Level.FINER, e.toString(), e);
    } catch (NoClassDefFoundError e) {
      log.log(Level.FINER, e.toString(), e);
    }
    
    return null;
  }
  
  public <X> void discoverBean(AnnotatedType<X> beanType)
  {
    Class<X> cl;

    // ioc/07fb
    cl = beanType.getJavaClass();

    if (cl.isAnnotationPresent(Specializes.class)) {
      Class<?> parent = cl.getSuperclass();

      if (parent != null) {
        addSpecialize(cl, parent);
      }
    }
    
    addPendingAnnotatedType(beanType);
  }
  
  private void addSpecialize(Class<?> specializedType, Class<?> parentType)
  {
    Class<?> oldSpecialized = _specializedMap.get(parentType);
    
    if (oldSpecialized != null)
      throw new ConfigException(L.l("@Specialized on '{0}' is invalid because it conflicts with an older specialized '{1}'",
                                    specializedType.getName(),
                                    oldSpecialized.getName()));
    
    if (! isValidSimpleBean(parentType))
      throw new ConfigException(L.l("@Specialized on '{0}' is invalid because its parent '{1}' is not a managed bean.",
                                    specializedType.getName(),
                                    parentType.getName()));
    
    _specializedMap.put(parentType, specializedType);
  }

  boolean isEnabled(Bean<?> bean)
  {
    if (! bean.isAlternative())
      return true;
    
    if (_deploymentMap.containsKey(bean.getBeanClass()))
      return true;
    
    for (Class<?> stereotype : bean.getStereotypes()) {
      if (_deploymentMap.containsKey(stereotype))
        return true;
    }
    
    return false;
  }

  boolean isIntrospectObservers(AnnotatedType<?> type)
  {
    if (type.isAnnotationPresent(Specializes.class))
      return true;
    
    String javaClassName = type.getJavaClass().getName();
    
    InjectScanClass scanClass = getScanManager().getScanClass(javaClassName);
    
    if (scanClass == null)
      return true;
    
    return scanClass.isObserves();
  }
  
  private boolean isValidSimpleBean(Class<?> type)
  {
    if (type.isInterface())
      return false;
    else if (type.isAnonymousClass())
      return false;
    /*
    else if (type.isMemberClass())
      return false;
      */
    
    if (Modifier.isAbstract(type.getModifiers()))
      return false;

    /* XXX: ioc/024d */
    // ioc/070c, ioc/0j0g
    /*
    if (type.getTypeParameters() != null
        && type.getTypeParameters().length > 0) {
      return false;
    }
    */
    
    if (! isValidConstructor(type))
      return false;

    return true;
  }

  private boolean isValidSimpleBean(AnnotatedType<?> type)
  {
    if (type.isAnnotationPresent(XmlCookie.class)) {
      // ioc/04d0
      return true;
    }
      
    return isValidSimpleBean(type.getJavaClass());
  }

  private boolean isValidConstructor(Class<?> type)
  {
    for (Constructor<?> ctor : type.getDeclaredConstructors()) {
      if (ctor.getParameterTypes().length == 0)
        return true;

      if (ctor.isAnnotationPresent(Inject.class))
        return true;
    }

    return false;
  }

  private <T> void discoverBeanImpl(AnnotatedType<T> type)
  {
    // ioc/0n18
    /*
    if (_specializedMap.get(type.getJavaClass()) != null)
      return;
      */
    
    // XXX: not sure this is correct.
    if (Throwable.class.isAssignableFrom(type.getJavaClass()))
      return;
    
    if (! isValidSimpleBean(type)) {
      return;
    }
    
    ManagedBeanImpl<T> bean = new ManagedBeanImpl<T>(this, type, false);
    
    InjectionTarget<T> target = bean.getInjectionTarget(); //createInjectionTarget(type);

    if (target instanceof InjectionTargetBuilder<?>) {
      InjectionTargetBuilder<?> targetImpl = (InjectionTargetBuilder<?>) target;

      targetImpl.setGenerateInterception(true);
    }

    target = processInjectionTarget(target, type);

    if (target == null)
      return;

    if (target instanceof InjectionTargetBuilder<?>) {
      InjectionTargetBuilder<T> targetImpl = (InjectionTargetBuilder<T>) target;

      targetImpl.setBean(bean);
    }
    
    bean.setInjectionTarget(target);

    bean.introspect();

    AnnotatedType<T> annType = bean.getAnnotatedType();

    // ioc/0i04
    if (annType.isAnnotationPresent(javax.decorator.Decorator.class)) {
      if (annType.isAnnotationPresent(javax.interceptor.Interceptor.class))
        throw new ConfigException(L.l("'{0}' bean may not have both a @Decorator and @Interceptor annotation.",
                                      annType.getJavaClass()));
      // ioc/0c92
      DecoratorBean decoratorBean = new DecoratorBean(this, annType.getJavaClass());
      
      // addBean(decoratorBean);
    
      return;
    }
    // ioc/0c1a
    if (annType.isAnnotationPresent(javax.interceptor.Interceptor.class)) {
      InterceptorBean interceptorBean = new InterceptorBean(this, annType.getJavaClass());
      
      addBeanDiscover(interceptorBean);
      return;
    }
    
    addDiscoveredBean(bean);
    
    fillProducerBeans(bean);

    // beans.addScannedClass(cl);
  }
  
  public <T> InjectionTarget<T> processInjectionTarget(InjectionTarget<T> target,
                                                       AnnotatedType<T> ann)
  {
    return getExtensionManager().processInjectionTarget(target, ann);
  }
  
  private void fillProducerBeans(ManagedBeanImpl<?> bean)
  {
  }

  private <X> void addDiscoveredBean(ManagedBeanImpl<X> managedBean)
  {
    /*
     // ioc/04d0
    if (! isValidSimpleBean(managedBean.getBeanClass()))
      return;
      */
    
    // ioc/0680
    if (! managedBean.isAlternative() || isEnabled(managedBean)) {
      // ioc/0680
      addBeanDiscover(managedBean);

      // ioc/0b0f
      if (! _specializedMap.containsKey(managedBean.getBeanClass())) {
        managedBean.introspectObservers();
      }
    }

    // ioc/07d2
    if (! _specializedMap.containsKey(managedBean.getBeanClass())
        && isEnabled(managedBean)) {
      managedBean.introspectProduces();
    }
  }
  
  public <X> void addProduces(Bean<X> bean, AnnotatedType<X> beanType)
  {
    ProducesBuilder builder = new ProducesBuilder(this);
    
    builder.introspectProduces(bean, beanType);
  }
  
  public <X> void addManagedProduces(Bean<X> bean, AnnotatedType<X> beanType)
  {
    ProducesBuilder builder = new ManagedProducesBuilder(this);
    
    builder.introspectProduces(bean, beanType);
  }
  
  public <X,T> void addProducesBean(ProducesMethodBean<X,T> bean)
  {
    AnnotatedMethod<X> producesMethod
    = (AnnotatedMethod<X>) bean.getProducesMethod();
    
    Producer<T> producer = bean.getProducer();
    
    producer = getExtensionManager().processProducer(producesMethod, producer);
    
    bean.setProducer(producer);

    //addBean(bean, producesMethod);
     addBeanDiscover(bean, producesMethod);
  }
  
  public <X,T> void addProducesFieldBean(ProducesFieldBean<X,T> bean)
  {
    AnnotatedField<X> producesField
      = (AnnotatedField<X>) bean.getField();
    
    Producer<T> producer = bean.getProducer();
    
    producer = getExtensionManager().processProducer(producesField, producer);
    
    bean.setProducer(producer);
    
    //addBean(bean, producesField);
    addBeanDiscover(bean, producesField);
  }

  public <X> void addManagedBeanDiscover(ManagedBeanImpl<X> managedBean)
  {
    addBeanDiscover(managedBean);
    
    managedBean.introspectProduces();
  }

  public <X> void addManagedBean(ManagedBeanImpl<X> managedBean)
  {
    addBean(managedBean);
    
    managedBean.introspectProduces();
  }

  public <T> ArrayList<T> loadServices(Class<T> serviceClass)
  {
    return loadServices(serviceClass, new HashSet<URL>(), false);
  }

  public <T> ArrayList<T> loadLocalServices(Class<T> serviceClass)
  {
    return loadServices(serviceClass, new HashSet<URL>(), true);
  }

  private <T> ArrayList<T> loadServices(Class<T> serviceApiClass,
                                        HashSet<URL> serviceSet,
                                        boolean isLocal)
  {
    ArrayList<T> services = new ArrayList<T>();

    try {
      DynamicClassLoader loader = _classLoader;

      if (loader == null)
        return services;
      
      String serviceName = "META-INF/services/" + serviceApiClass.getName();
      
      Enumeration<URL> e;
      
      if (isLocal) {
        e = loader.findResources(serviceName);
      }
      else {
        e = loader.getResources(serviceName);
      }
      
      HashSet<Class<T>> classSet = new HashSet<Class<T>>();

      while (e.hasMoreElements()) {
        URL url = e.nextElement();

        if (serviceSet.contains(url))
          continue;

        serviceSet.add(url);

        InputStream is = null;
        try {
          is = url.openStream();
          ReadStream in = Vfs.openRead(is);

          String line;

          while ((line = in.readLine()) != null) {
            int p = line.indexOf('#');
            if (p >= 0)
              line = line.substring(0, p);
            line = line.trim();

            if (line.length() > 0) {
              Class<T> cl = loadServiceClass(serviceApiClass, line);

              if (cl != null) {
                classSet.add(cl);
              }
            }
          }

          in.close();
        } catch (IOException e1) {
          log.log(Level.WARNING, e1.toString(), e1);
        } finally {
          IoUtil.close(is);
        }
      }
      
      for (Class<T> cl : classSet) {
        services.add(createTransientObject(cl));
      }
    } catch (Exception e) {
      log.log(Level.WARNING, e.toString(), e);
    }

    return services;
  }

  private <T> Class<T> loadServiceClass(Class<T> serviceApi,
                                        String className)
  {
    try {
      ClassLoader loader = Thread.currentThread().getContextClassLoader();

      Class<?> serviceClass = Class.forName(className, false, loader);

      if (! serviceApi.isAssignableFrom(serviceClass))
        throw new InjectionException(L.l("'{0}' is not a valid servicebecause it does not implement {1}",
                                         serviceClass, serviceApi.getName()));

      return (Class<T>) serviceClass;
    } catch (Exception e) {
      log.log(Level.WARNING, e.toString(), e);

      return null;
    }
  }

  public void addExtension(Extension extension)
  {
    _extensionManager.addExtension(extension);
  }

  /**
   * Starts the bind phase
   */
  public void bind()
  {
    Thread thread = Thread.currentThread();
    ClassLoader oldLoader = thread.getContextClassLoader();
    boolean isBind = false;

    try {
      thread.setContextClassLoader(_classLoader);
      
      processPendingAnnotatedTypes();
      
      if (_pendingBindList != null) {
        ArrayList<AbstractBean<?>> bindList
          = new ArrayList<AbstractBean<?>>(_pendingBindList);

        _pendingBindList.clear();

        if (bindList.size() > 0)
          isBind = true;
      }
      
      if (! _isAfterBeanDiscoveryComplete)
        isBind = true;

      if (isBind) {
        _isAfterBeanDiscoveryComplete = true;

        getExtensionManager().fireAfterBeanDiscovery();
      }

      if (_configException != null)
        throw _configException;

      /*
      for (AbstractBean comp : bindList) {
        if (_deploymentMap.get(comp.getDeploymentType()) != null)
          comp.bind();
      }
      */
      
      addDecorators();
      addInterceptors();
      
      bindGlobals();

      validate();
      
      /*
      if (isBind) {
        getExtensionManager().fireAfterDeploymentValidation();
      }
      */
    } catch (RuntimeException e) {
      if (_configException == null)
        _configException = e;
      else {
        log.log(Level.WARNING, e.toString(), e);
      }

      throw e;
    } finally {
      thread.setContextClassLoader(oldLoader);
    }
  }
  
  public void bindGlobals()
  {
    if (_globalProgram.size() > 0) {
      ArrayList<ConfigProgram> programList
        = new ArrayList<ConfigProgram>(_globalProgram);
      _globalProgram.clear();

      for (ConfigProgram program : programList) {
        program.inject(this, null);
      }
    }
  }
  
  private void addInterceptors()
  {
    if (_interceptorClassList.size() == 0)
      return;
    
    ArrayList<Class<?>> interceptorClassList
      = new ArrayList<Class<?>>(_interceptorClassList);
    _interceptorClassList.clear();
    
    for (Class<?> interceptorClass : interceptorClassList) {
      for (InterceptorEntry<?> entry : _interceptorList) {
        if (entry.getInterceptor().getBeanClass().equals(interceptorClass)) {
          entry.setEnabled(true);
          return;
        }
      }
      
      if (! interceptorClass.isAnnotationPresent(javax.interceptor.Interceptor.class))
        throw new ConfigException(L.l("'{0}' is an invalid interceptor because it does not have an @Interceptor.",
                                      interceptorClass.getName()));
        
      
      InterceptorBean<?> bean = new InterceptorBean(this, interceptorClass);
      
      InterceptorEntry<?> entry = addInterceptor(bean);
      entry.setEnabled(true);
    }
  }
  
  private void addDecorators()
  {
    if (_decoratorClassList.size() == 0)
      return;
    
    ArrayList<Class<?>> decoratorClassList
      = new ArrayList<Class<?>>(_decoratorClassList);
    _decoratorClassList.clear();
    
    for (Class<?> decoratorClass : decoratorClassList) {
      for (DecoratorEntry<?> entry : _decoratorList) {
        if (entry.getDecorator().getBeanClass().equals(decoratorClass)) {
          entry.setEnabled(true);
          return;
        }
      }
      
      DecoratorBean<?> bean = new DecoratorBean(this, decoratorClass);
      
      DecoratorEntry<?> entry = addDecorator(bean);
      entry.setEnabled(true);
    }
    
    // ioc/0i57 - validation must be early
    for (DecoratorEntry<?> entry : _decoratorList) {
      if (entry.isEnabled()) {
        for (Type type : entry.getDelegateType().getTypeClosure(this)) {
          validate(type);
        }
      }
    }
  }
  
  private void validate()
  {
    ArrayList<ArrayList<TypedBean>> typeValues
      = new ArrayList<ArrayList<TypedBean>>(_selfBeanMap.values());
    
    for (int i = typeValues.size() - 1; i >= 0; i--) {
      ArrayList<TypedBean> beans = typeValues.get(i);
      
      validateSpecializes(beans);
    }
    
    for (int i = typeValues.size() - 1; i >= 0; i--) {
      ArrayList<TypedBean> beans = typeValues.get(i);
      
      if (beans == null)
        continue;

      for (int j = beans.size() - 1; j >= 0; j--) {
        TypedBean typedBean = beans.get(j);
        
        typedBean.validate();
      }
    }
  }
  
  private void validateSpecializes(ArrayList<TypedBean> beans)
  {
    if (beans == null)
      return;
    
    for (int i = beans.size() - 1; i >= 0; i--) {
      TypedBean bean = beans.get(i);
      
      Annotated ann = bean.getAnnotated();
      
      if (ann == null || ! ann.isAnnotationPresent(Specializes.class))
        continue;

      for (int j = beans.size() - 1; j >= 0; j--) {
        if (i == j)
          continue;
        
        TypedBean bean2 = beans.get(j);
        
        // XXX:
        
        Annotated ann2 = bean.getAnnotated();
        
        if (ann2 == null)
          continue;
        
        if (isSpecializes(ann, ann2) && isMatchInject(bean, bean2)) {
          beans.remove(j);
          i = 0;
        }
      }
    }
  }
  
  private boolean isMatchInject(TypedBean typedBeanA, TypedBean typedBeanB)
  {
    Bean<?> beanA = typedBeanA.getBean();
    Bean<?> beanB = typedBeanB.getBean();
    
    return (beanA.getTypes().equals(beanB.getTypes())
            && beanA.getQualifiers().equals(beanB.getQualifiers()));
  }
  
  private boolean isSpecializes(Annotated childAnn, Annotated parentAnn)
  {
    if (childAnn instanceof AnnotatedMethod<?>
        && parentAnn instanceof AnnotatedMethod<?>) {
      Method childMethod = ((AnnotatedMethod<?>) childAnn).getJavaMember();
      Method parentMethod = ((AnnotatedMethod<?>) parentAnn).getJavaMember();
      
      if (! AnnotatedTypeUtil.isMatch(childMethod, parentMethod)) {
        return false;
      }
      
      Class<?> childClass = childMethod.getDeclaringClass();
      Class<?> parentClass = parentMethod.getDeclaringClass();
       
      if (parentClass.isAssignableFrom(childClass))
        return true;
    }
    
    return false;
  }

  /**
   * Handles the case the environment config phase
   */
  public void environmentConfigure(EnvironmentClassLoader loader)
  {
    initialize();
  }

  /**
   * Handles the case the environment config phase
   */
  @Override
  public void environmentBind(EnvironmentClassLoader loader)
  {
    initialize();
    bind();
  }

  /**
   * Handles the case where the environment is starting (after init).
   */
  @Override
  public void environmentStart(EnvironmentClassLoader loader)
  {
    start();
  }

  public void initialize()
  {
    update();

    /*
    if (_lifecycle.toInit()) {
      fireEvent(this, new AnnotationLiteral<Initialized>() {});
    }
    */
  }

  public void start()
  {
    initialize();

    bind();

    startServices();

    if (_configException != null) {
      // ioc/0p91
      throw _configException;
    }
    
    notifyStart();
  }

  public void notifyStart()
  {

    Thread thread = Thread.currentThread();
    ClassLoader oldLoader = thread.getContextClassLoader();

    try {
      thread.setContextClassLoader(_classLoader);

      update();
      
      // cloud/0300
      if (_isAfterValidationNeeded) {
        _isAfterValidationNeeded = false;
        getExtensionManager().fireAfterDeploymentValidation();
      }
    } catch (ConfigException e) {
      if (_configException == null)
        _configException = e;

      throw e;
    } finally {
      thread.setContextClassLoader(oldLoader);
    }
    
    // ioc/0p30
    if (_configException != null)
      throw _configException;
  }

  public void addDefinitionError(Throwable t)
  {
    if (_configException != null) {
      log.log(Level.WARNING, t.toString(), t);
    }
    else if (t instanceof RuntimeException) {
      _configException = (RuntimeException) t;
    }
    else {
      _configException = ConfigException.create(t);
    }
  }
  
  public RuntimeException getConfigException()
  {
    return _configException;
  }

  public void addConfiguredBean(String className)
  {
    _xmlExtension.addConfiguredBean(className);
  }

  public void addXmlInjectionTarget(long cookie, InjectionTarget<?> target)
  {
    _xmlTargetMap.put(cookie, target);
  }
  
  public InjectionTarget<?> getXmlInjectionTarget(long cookie)
  {
    return _xmlTargetMap.get(cookie);
  }
  
  void addService(Bean<?> bean)
  {
    _pendingServiceList.add(bean);
  }

  /**
   * Initialize all the services
   */
  private void startServices()
  {
    ArrayList<Bean<?>> services;
    // ArrayList<ManagedBean> registerServices;

    synchronized (_pendingServiceList) {
      services = new ArrayList<Bean<?>>(_pendingServiceList);
      _pendingServiceList.clear();
    }

    for (Bean<?> bean : services) {
      CreationalContext<?> env = createCreationalContext(bean);

      getReference(bean, bean.getBeanClass(), env);
    }

    /*
    for (ManagedBean bean : registerServices) {
      startRegistration(bean);
    }
    */
  }

  /**
   * Handles the case where the environment is stopping
   */
  @Override
  public void environmentStop(EnvironmentClassLoader loader)
  {
    destroy();
  }

  public void destroy()
  {
    _singletonScope.closeContext();
    
    // _parent = null;
    _classLoader = null;
    _deploymentMap = null;

    _selfBeanMap = null;
    _selfNamedBeanMap = null;
    _beanMap = null;
    _namedBeanMap = null;
    _contextMap = null;

    _interceptorList = null;
    _decoratorList = null;
    _pendingBindList = null;
    _pendingServiceList = null;
    
    _eventManager = null;
  }

  public static ConfigException injectError(AccessibleObject prop, String msg)
  {
    String location = "";

    if (prop instanceof Field) {
      Field field = (Field) prop;
      String className = field.getDeclaringClass().getName();

      location = className + "." + field.getName() + ": ";
    }
    else if (prop instanceof Method) {
      Method method = (Method) prop;
      String className = method.getDeclaringClass().getName();

      location = className + "." + method.getName() + ": ";
    }

    return new ConfigException(location + msg);
  }

  public static String location(Field field)
  {
    return field.getDeclaringClass().getName() + "." + field.getName() + ": ";
  }

  public static String location(Method method)
  {
    return LineConfigException.loc(method);
  }

  public static ConfigException error(Method method, String msg)
  {
    return new ConfigException(location(method) + msg);
  }

  public void setSerializationHandle(Object handle)
  {
    _serializationHandle = handle;
  }

  /**
   * Serialization rewriting
   */
  public Object writeReplace()
  {
    return _serializationHandle;
  }

  public void checkActive()
  {
  }

  /**
   * @return
   */
  public boolean isClosed()
  {
    return _beanMap == null;
  }

  public String toString()
  {
    if (_classLoader != null)
      return getClass().getSimpleName() + "[" + _classLoader.getId() + "]";
    else
      return getClass().getSimpleName() + "[" + _id + "]";
  }

  static String getSimpleName(Type type)
  {
    if (type instanceof Class<?>)
      return ((Class<?>) type).getSimpleName();
    else
      return String.valueOf(type);
  }

  class TypedBean {
    private final BaseType _type;
    private final Annotated _annotated;
    private final Bean<?> _bean;
    private final boolean _isModulePrivate;
    private boolean _isValidated;

    TypedBean(BaseType type, Annotated annotated, Bean<?> bean)
    {
      _type = type;
      _annotated = annotated;
      _bean = bean;
      
      _isModulePrivate = isModulePrivate(bean) || bean.isAlternative();
    }
    
    public Annotated getAnnotated()
    {
      return _annotated;
    }

    /**
     * 
     */
    public void validate()
    {
      if (! _isValidated) {
        _isValidated = true;
    
        InjectManager.this.validate(_bean);
        /*
        for (InjectionPoint ip : _bean.getInjectionPoints()) {
          InjectManager.this.validate(ip);
        }
        */
      }
    }

    boolean isModulePrivate()
    {
      return _isModulePrivate;
    }

    BaseType getType()
    {
      return _type;
    }

    Bean<?> getBean()
    {
      return _bean;
    }

    boolean isModulePrivate(Bean<?> bean)
    {
      if (! (bean instanceof AnnotatedBean))
        return false;

      Annotated annotated = ((AnnotatedBean) bean).getAnnotated();

      if (annotated == null)
        return false;

      for (Annotation ann : annotated.getAnnotations()) {
        Class<?> annType = ann.annotationType();

        if (annType.equals(ModulePrivate.class)
            || annType.isAnnotationPresent(ModulePrivate.class)
            || annType.equals(Module.class)
            || annType.isAnnotationPresent(Module.class)) {
          return true;
        }
      }

      return false;
    }

    @Override
    public int hashCode()
    {
      return 65521 * _type.hashCode() + _bean.hashCode();
    }

    @Override
    public boolean equals(Object o)
    {
      if (! (o instanceof TypedBean))
        return false;

      TypedBean bean = (TypedBean) o;

      return _type.equals(bean._type) && _bean.equals(bean._bean);
    }

    public String toString()
    {
      return getClass().getSimpleName() + "[" + _type + "," + _bean + "]";
    }
  }

  static class FillByName implements EnvironmentApply
  {
    private String _name;
    private ArrayList<Bean<?>> _beanList;

    FillByName(String name, ArrayList<Bean<?>> beanList)
    {
      _name = name;
      _beanList = beanList;
    }

    public void apply(EnvironmentClassLoader loader)
    {
      InjectManager beanManager = InjectManager.getCurrent(loader);

      beanManager.fillByName(_name, _beanList);
    }
  }

  static class FillByType implements EnvironmentApply
  {
    private BaseType _baseType;
    private HashSet<TypedBean> _beanSet;
    private InjectManager _manager;

    FillByType(BaseType baseType,
               HashSet<TypedBean> beanSet,
               InjectManager manager)
    {
      _baseType = baseType;
      _beanSet = beanSet;
      _manager = manager;
    }

    @Override
    public void apply(EnvironmentClassLoader loader)
    {
      InjectManager beanManager = InjectManager.getCurrent(loader);

      beanManager.fillByType(_baseType, _beanSet, _manager);
    }
  }

  static class FactoryBinding {
    private static final Annotation []NULL = new Annotation[0];

    private final Type _type;
    private final Annotation []_ann;

    FactoryBinding(Type type, Annotation []ann)
    {
      _type = type;

      if (ann != null)
        _ann = ann;
      else
        _ann = NULL;
    }

    @Override
    public int hashCode()
    {
      int hash = _type.hashCode();

      for (Annotation ann : _ann)
        hash = 65521 * hash + ann.hashCode();

      return hash;
    }

    @Override
    public boolean equals(Object obj)
    {
      if (! (obj instanceof FactoryBinding))
        return false;

      FactoryBinding binding = (FactoryBinding) obj;

      if (_type != binding._type)
        return false;

      if (_ann.length != binding._ann.length)
        return false;

      for (int i = 0; i < _ann.length; i++) {
        if (! _ann[i].equals(binding._ann[i]))
          return false;
      }

      return true;
    }
  }

  static class InjectBean<X> extends BeanWrapper<X>
    implements PassivationCapable, ScopeAdapterBean<X>
  {
    private ClassLoader _loader;

    InjectBean(Bean<X> bean, InjectManager beanManager)
    {
      super(beanManager, bean);

      _loader = Thread.currentThread().getContextClassLoader();

      if (bean instanceof AbstractBean) {
        AbstractBean<X> absBean = (AbstractBean<X>) bean;
        Annotated annotated = absBean.getAnnotated();

        if (annotated != null
            && annotated.isAnnotationPresent(ContextDependent.class)) {
          // ioc/0e17
          _loader = null;
        }
      }
    }

    public String getId()
    {
      Bean<?> bean = getBean();

      if (bean instanceof PassivationCapable)
        return ((PassivationCapable) bean).getId();
      else
        return null;
    }

    public X getScopeAdapter(Bean<?> topBean, CreationalContextImpl<X> cxt)
    {
      Bean<?> bean = getBean();

      if (bean instanceof ScopeAdapterBean<?>)
        return (X) ((ScopeAdapterBean) bean).getScopeAdapter(topBean, cxt);
      else
        return null;
    }

    @Override
    public X create(CreationalContext<X> env)
    {
      Thread thread = Thread.currentThread();
      ClassLoader oldLoader = thread.getContextClassLoader();

      try {
        if (_loader != null) {
          // ioc/0e17
          thread.setContextClassLoader(_loader);
        }

        X value = getBean().create(env);
        
        return value;
      } finally {
        thread.setContextClassLoader(oldLoader);
      }
    }

    @Override
    public int hashCode()
    {
      return getBean().hashCode();
    }

    public boolean equals(Object o)
    {
      if (! (o instanceof InjectBean<?>))
        return false;

      InjectBean<?> bean = (InjectBean<?>) o;

      return getBean().equals(bean.getBean());
    }

    public String toString()
    {
      return getClass().getSimpleName() + "[" + getBean() + "]";
    }
  }
  
  abstract public class ReferenceFactory<T> {
    public Bean<T> getBean()
    {
      return null;
    }
    
    protected void validate()
    {
    }
    
    public final T create()
    {
      return create(null, null, null);
    }
    
    public boolean isResolved()
    {
      return true;
    }
    
    public boolean isProducer()
    {
      Bean<T> bean = getBean();
      
      return ((bean instanceof ProducesMethodBean<?,?>)
              || (bean instanceof ProducesFieldBean<?,?>));
    }
    
    abstract public T create(CreationalContextImpl<T> env,
                             CreationalContextImpl<?> parentEnv,
                             InjectionPoint ip);
  }
  
  public class DependentReferenceFactory<T> extends ReferenceFactory<T> {
    private Bean<T> _bean;
    
    DependentReferenceFactory(Bean<T> bean)
    {
      _bean = bean;
    }
    
    @Override
    public Bean<T> getBean()
    {
      return _bean;
    }
   
    @Override
    public T create(CreationalContextImpl<T> env,
                    CreationalContextImpl<?> parentEnv,
                    InjectionPoint ip)
    {
      Bean<T> bean = _bean;
      
      T instance = CreationalContextImpl.find(parentEnv, bean);
      
      if (instance != null)
        return instance;
      
      if (env == null) {
        if (parentEnv != null)
          env = new DependentCreationalContext<T>(bean, parentEnv, ip);
        else {
          env = new OwnerCreationalContext<T>(bean);
          
          if (ip != null)
            env = new DependentCreationalContext<T>(bean, env, ip);
        }
      }
      
      instance = bean.create(env);
      
      env.push(instance);
      
      /*
      if (env.isTop() && ! (bean instanceof CdiStatefulBean)) {
        bean.destroy(instance, env);
      }
      */
      
      return instance;
    }
    
    @Override
    public String toString()
    {
      return getClass().getSimpleName() + "[" + _bean + "]";
    }
  }
  
  public class DependentReferenceFactoryImpl<T> extends ReferenceFactory<T> {
    private ManagedBeanImpl<T> _bean;
    
    DependentReferenceFactoryImpl(ManagedBeanImpl<T> bean)
    {
      _bean = bean;
    }
    
    @Override
    public Bean<T> getBean()
    {
      return _bean;
    }
   
    @Override
    public T create(CreationalContextImpl<T> env,
                    CreationalContextImpl<?> parentEnv,
                    InjectionPoint ip)
    {
      ManagedBeanImpl<T> bean = _bean;
      
      T instance = CreationalContextImpl.find(parentEnv, bean);
      
      if (instance != null)
        return instance;
      
      if (env == null) {
        if (parentEnv != null)
          env = new DependentCreationalContext<T>(bean, parentEnv, ip);
        else
          env = new OwnerCreationalContext<T>(bean);
      }
      
      instance = bean.createDependent(env);

      // ioc/0k13
      /*
      if (env.isTop() && ! (bean instanceof CdiStatefulBean)) {
        bean.destroy(instance, env);
      }
      */
      
      return instance;
    }
  }
  
  public class DependentElReferenceFactoryImpl<T> extends ReferenceFactory<T> {
    private ManagedBeanImpl<T> _bean;
    
    DependentElReferenceFactoryImpl(ManagedBeanImpl<T> bean)
    {
      _bean = bean;
    }
    
    @Override
    public Bean<T> getBean()
    {
      return _bean;
    }
   
    @Override
    public T create(CreationalContextImpl<T> env,
                    CreationalContextImpl<?> parentEnv,
                    InjectionPoint ip)
    {
      ManagedBeanImpl<T> bean = _bean;
      
      T instance = CreationalContextImpl.findAny(parentEnv, bean);
      
      if (instance != null)
        return instance;
      
      if (env == null) {
        if (parentEnv != null)
          env = new DependentCreationalContext<T>(bean, parentEnv, ip);
        else
          env = new OwnerCreationalContext<T>(bean);
      }
      
      instance = bean.createDependent(env);

      if (env.isTop() && ! (bean instanceof CdiStatefulBean)) {
        bean.destroy(instance, env);
      }
      
      return instance;
    }
  }
  
  public class ContextReferenceFactory<T> extends ReferenceFactory<T> {
    private Bean<T> _bean;
    private Context _context;
    
    ContextReferenceFactory(Bean<T> bean,
                            Context context)
    {
      _bean = bean;
      _context = context;
    }
    
    @Override
    public Bean<T> getBean()
    {
      return _bean;
    }
   
    @Override
    public T create(CreationalContextImpl<T> env,
                    CreationalContextImpl<?> parentEnv,
                    InjectionPoint ip)
    {
      Bean<T> bean = _bean;
      
      T instance = CreationalContextImpl.find(parentEnv, bean);
      
      if (instance != null)
        return instance;
      
      if (env == null)
        env = new OwnerCreationalContext<T>(bean, parentEnv);

      instance = _context.get(bean, env);
        
      if (instance == null)
        throw new NullPointerException(L.l("null instance returned by '{0}' for bean '{1}'",
                                           _context, bean));
        
      return instance;
    }
  }
  
  public class NormalInstanceReferenceFactory<T> extends ReferenceFactory<T> {
    private ThreadLocal<CreationalContextImpl<T>> _threadLocal
      = new ThreadLocal<CreationalContextImpl<T>>();
    
    private Bean<T> _bean;
    private Context _context;
    
    NormalInstanceReferenceFactory(Bean<T> bean,
                                   Context context)
    {
      _bean = bean;
      _context = context;
    }
    
    @Override
    public Bean<T> getBean()
    {
      return _bean;
    }
   
    @Override
    public T create(CreationalContextImpl<T> env,
                    CreationalContextImpl<?> parentEnv,
                    InjectionPoint ip)
    {
      Bean<T> bean = _bean;
      
      // ioc/0155
      // XXX: possibly restrict to NormalScope adapter
      CreationalContextImpl<T> oldEnv = _threadLocal.get();
      
      try {
        T instance = CreationalContextImpl.find(oldEnv, bean);
        
        if (instance != null) {
          return instance;
        }

        env = new OwnerCreationalContext<T>(bean, oldEnv);
          
        _threadLocal.set(env);
      
        instance = _context.get(bean, env);
        
        if (instance == null)
          throw new NullPointerException(L.l("null instance returned by '{0}' for bean '{1}'",
                                             _context, bean));
        
        return instance;
      } finally {
        _threadLocal.set(oldEnv);
      }
    }
  }

  public class NormalContextReferenceFactory<T> extends ReferenceFactory<T> {
    private Bean<T> _bean;
    private ScopeAdapterBean<T> _scopeAdapterBean;
    private Context _context;
    private T _scopeAdapter;
    
    NormalContextReferenceFactory(Bean<T> bean,
                                  ScopeAdapterBean<T> scopeAdapterBean,
                                  Context context)
    {
      _bean = bean;
      _scopeAdapterBean = scopeAdapterBean;
      
      _context = context;
      
      ScopeAdapter scopeAdapter = ScopeAdapter.create(bean);
      _scopeAdapter = scopeAdapter.wrap(createNormalInstanceFactory(bean));
    }
    
    protected void validate()
    {
      validateNormal(_bean);
    }
    
    @Override
    public Bean<T> getBean()
    {
      return _bean;
    }
   
    @Override
    public T create(CreationalContextImpl<T> env,
                    CreationalContextImpl<?> parentEnv,
                    InjectionPoint ip)
    {
      return _scopeAdapter;
    }
  }
  
  public class DelegateReferenceFactory<T> extends ReferenceFactory<T> {
    DelegateReferenceFactory()
    {
    }
   
    @Override
    public T create(CreationalContextImpl<T> env,
                    CreationalContextImpl<?> parentEnv,
                    InjectionPoint ip)
    {
      return (T) parentEnv.getDelegate();
    }
  }
  
  public class ErrorReferenceFactory<T> extends ReferenceFactory<T> {
    private RuntimeException _exn;
    
    ErrorReferenceFactory(RuntimeException e)
    {
      _exn = e;
    }
    
    @Override
    public boolean isProducer()
    {
      return true;
    }
   
    @Override
    public T create(CreationalContextImpl<T> env,
                    CreationalContextImpl<?> parentEnv,
                    InjectionPoint ip)
    {
      throw _exn;
    }
  }
  
  public class InjectionPointReferenceFactory 
    extends ReferenceFactory<InjectionPoint> {
    InjectionPointReferenceFactory()
    {
    }
   
    @Override
    public InjectionPoint create(CreationalContextImpl<InjectionPoint> env,
                                 CreationalContextImpl<?> parentEnv,
                                 InjectionPoint ip)
    {
      InjectionPoint ip2 =  parentEnv.findInjectionPoint();
      
      if (ip2 != null)
        return ip2;
      
      throw new InjectionException(L.l("no injection point available in this context {0}",
                                       ip));
    }
  }
  
  public class UnresolvedReferenceFactory extends ReferenceFactory<Object> {
    private InjectionException _exn;
    
    UnresolvedReferenceFactory()
    {
      _exn = new InjectionException("unresolved injection");
    }
    
    @Override
    public boolean isResolved()
    {
      return false;
    }
   
    @Override
    public Object create(CreationalContextImpl<Object> env,
                         CreationalContextImpl<?> parentEnv,
                         InjectionPoint ip)
    {
      throw _exn;
    }
  }
  
  private static class AnnotatedTypeComparator 
    implements Comparator<AnnotatedType<?>>
  {
    @Override
    public int compare(AnnotatedType<?> a, AnnotatedType<?> b)
    {
      return a.toString().compareTo(b.toString());
    }
  }

  static {
    ArrayList<Class<?>> forbiddenAnnotations = new ArrayList<Class<?>>();
    ArrayList<Class<?>> forbiddenClasses = new ArrayList<Class<?>>();

    for (String className : FORBIDDEN_ANNOTATIONS) {
      try {
        Class<?> cl = Class.forName(className);

        if (cl != null)
          forbiddenAnnotations.add(cl);
      } catch (Throwable e) {
        log.log(Level.FINEST, e.toString(), e);
      }
    }

    for (String className : FORBIDDEN_CLASSES) {
      try {
        Class<?> cl = Class.forName(className);

        if (cl != null)
          forbiddenClasses.add(cl);
      } catch (Throwable e) {
        log.log(Level.FINEST, e.toString(), e);
      }
    }

    _forbiddenAnnotations = new Class[forbiddenAnnotations.size()];
    forbiddenAnnotations.toArray(_forbiddenAnnotations);

    _forbiddenClasses = new Class[forbiddenClasses.size()];
    forbiddenClasses.toArray(_forbiddenClasses);

    ClassLoader systemClassLoader = null;

    try {
      systemClassLoader = ClassLoader.getSystemClassLoader();
    } catch (Throwable e) {
      // a security manager may not allow this call

      log.log(Level.FINEST, e.toString(), e);
    }

    _systemClassLoader = systemClassLoader;
  }
}
