/*
 * JBoss, Home of Professional Open Source
 * Copyright 2007, Red Hat Middleware LLC, and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software 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. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jboss.deployment;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.jboss.deployers.spi.DeploymentException;
import org.jboss.deployers.spi.deployer.DeploymentStages;
import org.jboss.deployers.spi.deployer.helpers.AbstractRealDeployer;
import org.jboss.deployers.structure.spi.DeploymentUnit;
import org.jboss.deployment.dependency.ContainerDependencyMetaData;
import org.jboss.deployment.dependency.JndiDependencyMetaData;
import org.jboss.deployment.plugin.MappedDeploymentEndpointResolver;
import org.jboss.deployment.spi.DeploymentEndpointResolver;
import org.jboss.deployment.spi.EndpointInfo;
import org.jboss.deployment.spi.EndpointType;
import org.jboss.logging.Logger;
import org.jboss.metadata.client.jboss.JBossClientMetaData;
import org.jboss.metadata.ejb.jboss.JBossEnterpriseBeanMetaData;
import org.jboss.metadata.ejb.jboss.JBossEnterpriseBeansMetaData;
import org.jboss.metadata.ejb.jboss.JBossEntityBeanMetaData;
import org.jboss.metadata.ejb.jboss.JBossGenericBeanMetaData;
import org.jboss.metadata.ejb.jboss.JBossMetaData;
import org.jboss.metadata.ejb.jboss.JBossSessionBean31MetaData;
import org.jboss.metadata.ejb.jboss.JBossSessionBeanMetaData;
import org.jboss.metadata.ejb.jboss.jndi.resolver.impl.JNDIPolicyBasedJNDINameResolverFactory;
import org.jboss.metadata.ejb.jboss.jndi.resolver.spi.EntityBeanJNDINameResolver;
import org.jboss.metadata.ejb.jboss.jndi.resolver.spi.SessionBean31JNDINameResolver;
import org.jboss.metadata.ejb.jboss.jndi.resolver.spi.SessionBeanJNDINameResolver;
import org.jboss.metadata.ejb.jboss.jndipolicy.plugins.DefaultJNDIBindingPolicyFactory;
import org.jboss.metadata.ejb.jboss.jndipolicy.spi.DefaultJndiBindingPolicy;
import org.jboss.metadata.ejb.jboss.jndipolicy.spi.DeploymentSummary;
import org.jboss.metadata.ejb.jboss.jndipolicy.spi.EjbDeploymentSummary;
import org.jboss.metadata.ejb.jboss.jndipolicy.spi.PackagingType;
import org.jboss.metadata.ejb.spec.BusinessLocalsMetaData;
import org.jboss.metadata.ejb.spec.BusinessRemotesMetaData;
import org.jboss.metadata.javaee.spec.AnnotatedEJBReferenceMetaData;
import org.jboss.metadata.javaee.spec.AnnotatedEJBReferencesMetaData;
import org.jboss.metadata.javaee.spec.EJBLocalReferenceMetaData;
import org.jboss.metadata.javaee.spec.EJBLocalReferencesMetaData;
import org.jboss.metadata.javaee.spec.EJBReferenceMetaData;
import org.jboss.metadata.javaee.spec.EJBReferencesMetaData;
import org.jboss.metadata.javaee.spec.Environment;
import org.jboss.metadata.javaee.spec.MessageDestinationMetaData;
import org.jboss.metadata.javaee.spec.MessageDestinationReferenceMetaData;
import org.jboss.metadata.javaee.spec.MessageDestinationReferencesMetaData;
import org.jboss.metadata.javaee.spec.MessageDestinationsMetaData;
import org.jboss.metadata.web.jboss.JBossWebMetaData;

/**
 * A deployer which resolves references for parsing deployers using
 * a deployment map of all endpoints.
 *
 * @author Scott.Stark@jboss.org
 * @version $Revision: 99498 $
 */
public class MappedReferenceMetaDataResolverDeployer extends AbstractRealDeployer
{
   /** Key for Map<String, ContainerDependencyMetaData> attachment */
   public static final String ENDPOINT_MAP_KEY = "MappedReferenceMetaDataResolverDeployer.endpointMap";
   /** Key for Set<ContainerDependencyMetaData> attachment */
   public static final String DEPENDS_SET_KEY = "MappedReferenceMetaDataResolverDeployer.dependsSet";
   /** Key for Map<String, String> of alternate endpoint resolution to the endpointMap key */
   public static final String ALTERNATE_MAP_KEY = "MappedReferenceMetaDataResolverDeployer.endpointAlternateMap";
   private static Logger log = Logger.getLogger(MappedReferenceMetaDataResolverDeployer.class);
   /** */
   private boolean failOnUnresolvedRefs;
   
   /**
    * Default jndi binding policy to be used for resolving
    * jndi names for EJBs
    */
   private DefaultJndiBindingPolicy defaultJNDIBindingPolicy = DefaultJNDIBindingPolicyFactory.getDefaultJNDIBindingPolicy();

   public MappedReferenceMetaDataResolverDeployer()
   {
      setStage(DeploymentStages.POST_CLASSLOADER);
      HashSet<String> inputs = new HashSet<String>();
      inputs.add(JBossClientMetaData.class.getName());
      inputs.add(JBossMetaData.class.getName());
      inputs.add(JBossWebMetaData.class.getName());
      super.setInputs(inputs);
      super.setOutput(DeploymentEndpointResolver.class);
   }

   /**
    * We want to process the parent last
    */
   @Override
   public boolean isParentFirst()
   {
      return false;
   }

   public boolean isFailOnUnresolvedRefs()
   {
      return failOnUnresolvedRefs;
   }

   public void setFailOnUnresolvedRefs(boolean failOnUnresolvedRefs)
   {
      this.failOnUnresolvedRefs = failOnUnresolvedRefs;
   }

   /**
    * Look for ejb, web or client metadata to resolve references without
    * mapped names.
    */
   public void internalDeploy(DeploymentUnit unit)
      throws DeploymentException
   {
      JBossMetaData ejbMetaData = unit.getAttachment(JBossMetaData.class);
      JBossWebMetaData webMetaData = unit.getAttachment(JBossWebMetaData.class);
      JBossClientMetaData clientMetaData = unit.getAttachment(JBossClientMetaData.class);
      if(ejbMetaData == null && webMetaData == null && clientMetaData == null)
         return;

      // Create a map of the reference endpoints if it does not exist in the top unit
      DeploymentUnit top = unit.getTopLevel();
      Map<String, ContainerDependencyMetaData> endpointMap = top.getAttachment(ENDPOINT_MAP_KEY, Map.class);
      Map<String, String> endpointAlternateMap = top.getAttachment(ALTERNATE_MAP_KEY, Map.class);
      if(endpointMap == null)
      {
         endpointMap = new ConcurrentHashMap<String, ContainerDependencyMetaData>();
         endpointAlternateMap = new ConcurrentHashMap<String, String>();
         mapEndpoints(top, endpointMap, endpointAlternateMap);
         top.addAttachment(ENDPOINT_MAP_KEY, endpointMap, Map.class);
         top.addAttachment(ALTERNATE_MAP_KEY, endpointAlternateMap);
         DeploymentEndpointResolver resolver = new MappedDeploymentEndpointResolver(endpointMap, endpointAlternateMap, unit.getRelativePath());
         top.addAttachment(DeploymentEndpointResolver.class, resolver);
      }

      DeploymentEndpointResolver resolver = new MappedDeploymentEndpointResolver(
            endpointMap, endpointAlternateMap, unit.getRelativePath());

      List<String> unresolvedPaths = new ArrayList<String>();
      if(ejbMetaData != null)
      {
         JBossEnterpriseBeansMetaData beans = ejbMetaData.getEnterpriseBeans();
         // Process ejb references
         try
         {
            resolve(unit, endpointMap, beans, resolver, unresolvedPaths);
         }
         catch (Exception e)
         {
            throw new DeploymentException(e);
         }
         if(unresolvedPaths.size() > 0)
            log.warn("Unresolved references exist in JBossMetaData:"+unresolvedPaths);
      }
      if(webMetaData != null)
      {
         // Process web app references
         ContainerDependencyMetaData webAppCDMD = new ContainerDependencyMetaData(unit.getSimpleName(), "web-app", unit.getRelativePath());
         try
         {
            resolve(webAppCDMD, unit, endpointMap, webMetaData.getJndiEnvironmentRefsGroup(), resolver, unresolvedPaths);
         }
         catch (Exception e)
         {
            throw new DeploymentException(e);
         }
         if(unresolvedPaths.size() > 0)
            log.warn("Unresolved references exist in JBossWebMetaData:"+unresolvedPaths);
      }
      if(clientMetaData != null)
      {
         // Process client app references
         ContainerDependencyMetaData clientCDMD = new ContainerDependencyMetaData(unit.getSimpleName(), "client", unit.getRelativePath());
         try
         {
            resolve(clientCDMD, unit, endpointMap, clientMetaData.getJndiEnvironmentRefsGroup(), resolver, unresolvedPaths);
         }
         catch (Exception e)
         {
            throw new DeploymentException(e);
         }
         if(unresolvedPaths.size() > 0)
            log.warn("Unresolved references exist in JBossClientMetaData: "+unresolvedPaths);
      }
      // Add the unique set of ContainerDependencyMetaData
      Set<ContainerDependencyMetaData> depends = new HashSet<ContainerDependencyMetaData>();
      for(ContainerDependencyMetaData cdmd : endpointMap.values())
      {
         depends.add(cdmd);
      }
      top.addAttachment(DEPENDS_SET_KEY, depends, Set.class);

      unit.addAttachment(DeploymentEndpointResolver.class, resolver);
      dump(unit);
   }

   /**
    * Map the ejb and message-destination endpoints.
    *
    * @param unit - the deployment top level unit to start the mapping from
    * @param endpointMap - the endpoint
    */
   protected void mapEndpoints(DeploymentUnit unit, Map<String, ContainerDependencyMetaData> endpointMap,
         Map<String, String> endpointAlternateMap)
   {
      boolean trace = log.isTraceEnabled();
      // First map the ejbs
      mapEjbs(unit, endpointMap, endpointAlternateMap, trace);
      // Map all sources of message-destinations
      mapMessageDestinations(unit, endpointMap, endpointAlternateMap, trace);
      // Map persistence units
      mapPersistenceUnits(unit, endpointMap, trace);
      // Display the endpoint map for debugging
      displayEndpoints(unit.getName(), endpointMap);
   }

   /**
    * @param unit
    * @param endpointMap
    * @param trace
    */
   protected void mapPersistenceUnits(DeploymentUnit unit,
         Map<String, ContainerDependencyMetaData> endpointMap, boolean trace)
   {
      // TODO Auto-generated method stub
      String vfsPath = unit.getRelativePath();
      //PersistenceUnitsMetaData ejbMetaData = unit.getAttachment(PersistenceUnitsMetaData.class);

   }

   protected void mapMessageDestinations(DeploymentUnit unit,
         Map<String, ContainerDependencyMetaData> endpointMap,
         Map<String, String> endpointAlternateMap,
         boolean trace)
   {
      String vfsPath = unit.getRelativePath();
      JBossMetaData ejbMetaData = unit.getAttachment(JBossMetaData.class);
      MessageDestinationsMetaData msgDestinations = null;
      ClassLoader loader = unit.getClassLoader();
      if(ejbMetaData != null)
      {
         msgDestinations = ejbMetaData.getAssemblyDescriptor().getMessageDestinations();
         mapMessageDestinations(vfsPath, msgDestinations, endpointMap, endpointAlternateMap, loader, trace);
      }
      JBossWebMetaData webMetaData = unit.getAttachment(JBossWebMetaData.class);
      if(webMetaData != null)
      {
         msgDestinations = webMetaData.getMessageDestinations();
         mapMessageDestinations(vfsPath, msgDestinations, endpointMap, endpointAlternateMap, loader, trace);
      }
      JBossClientMetaData clientMetaData = unit.getAttachment(JBossClientMetaData.class);
      if(clientMetaData != null)
      {
         msgDestinations = clientMetaData.getMessageDestinations();
         mapMessageDestinations(vfsPath, msgDestinations, endpointMap, endpointAlternateMap, loader, trace);
      }

      // Process children
      List<DeploymentUnit> children = unit.getChildren();
      if(children != null)
      {
         for(DeploymentUnit child : children)
            mapMessageDestinations(child, endpointMap, endpointAlternateMap, trace);
      }
   }

   protected void mapMessageDestinations(String vfsPath, MessageDestinationsMetaData msgDestinations,
         Map<String, ContainerDependencyMetaData> endpointMap,
         Map<String, String> endpointAlternateMap,
         ClassLoader loader, boolean trace)
   {
      if(msgDestinations == null || msgDestinations.size() == 0)
         return;
      String prefix = "message-destination/" + vfsPath;
      for(MessageDestinationMetaData dest : msgDestinations)
      {
         String mappedName = dest.getMappedName();
         String destName = dest.getMessageDestinationName();
         if(mappedName == null || mappedName.length() == 0)
         {
            log.debug("Message-destination: "+destName+" has no mappedName");
            continue;
         }
         String destPath = prefix + "#" + destName;
         ContainerDependencyMetaData destMD = new ContainerDependencyMetaData(mappedName, destName, vfsPath);
         endpointMap.put(destPath, destMD);
         if(trace)
            log.trace("mapMessageDestinations: "+destPath+", mappedName: "+mappedName);
         
         // Create global message-destination/destName alt-mappings
         String destKey = EndpointType.MessageDestination  + "/" + destName;
         if(endpointAlternateMap.containsKey(destKey) == false)
         {
            endpointAlternateMap.put(destKey, destPath);
            if(trace)
               log.trace("mapMessageDestinations, added alternate root message-destination: "+destPath);
         }
         else
         {
            log.debug("Duplicate root ejb-name: "+destKey+" from: "+destPath);
         }

     }
   }

   /**
    * Creates a mapping of the unit relative path + ejb-name to the bean mapped name.
    * For beans with business locals/remotes, an entry of the form:
    * unit relative path / interface name to the ejb-name
    *
    * @param unit
    * @param endpointMap
    */
   protected void mapEjbs(DeploymentUnit unit, Map<String, ContainerDependencyMetaData> endpointMap,
         Map<String, String> endpointAlternateMap, boolean trace)
   {
      JBossMetaData ejbMetaData = unit.getAttachment(JBossMetaData.class);
      JBossEnterpriseBeansMetaData beans = null;
      if(ejbMetaData != null)
      {
         if(ejbMetaData.getDeploymentSummary() == null)
         {
            DeploymentSummary deploymentSummary = getDeploymentSummary(unit);
            ejbMetaData.setDeploymentSummary(deploymentSummary);
         }
         beans = ejbMetaData.getEnterpriseBeans();
      }

      if(beans != null)
      {
         String vfsPath = unit.getRelativePath();
         String prefix = "ejb/" + vfsPath;
         for(JBossEnterpriseBeanMetaData bean : beans)
         {
            // The unique id for this ejb in the deployment
            String ejbCompID = prefix + "#" + bean.getEjbName();
            //LegacyEjb3JndiPolicy policy = new LegacyEjb3JndiPolicy();
            String jndiName = bean.determineJndiName();
            ContainerDependencyMetaData cdmd = new ContainerDependencyMetaData(jndiName, bean.getEjbName(), vfsPath);
            cdmd.setEjb3X(ejbMetaData.isEJB3x());
            // TODO, this is a mess that should be simply from the metadata
            //ClassLoader loader = unit.getClassLoader();
            EjbDeploymentSummary unitSummary = getEjbDeploymentSummary(unit, bean);
            cdmd.setUnitSummary(unitSummary);

            endpointMap.put(ejbCompID, cdmd);
            // debug info for the mappings each ejb has
            ArrayList<String> mappings = new ArrayList<String>();
            // Alternate mappings
            String ejbNameKey = "ejb/" + bean.getEjbName();
            if(endpointAlternateMap.containsKey(ejbNameKey) == false)
            {
               endpointAlternateMap.put(ejbNameKey, ejbCompID);
               mappings.add(ejbNameKey);
               if(trace)
                  log.trace("mapEjbs, added alternate root ejb-name: "+ejbNameKey);
            }
            else
            {
               log.debug("Duplicate root ejb-name: "+ejbNameKey+" from: "+ejbCompID);
            }
            // Create mappings based on the bean business interfaces
            if(bean instanceof JBossSessionBeanMetaData)
            {
               JBossSessionBeanMetaData sbean = (JBossSessionBeanMetaData) bean;
               String ejbName = sbean.getEjbName();
               SessionBeanJNDINameResolver sessionbeanJNDINameResolver = JNDIPolicyBasedJNDINameResolverFactory.getJNDINameResolver(sbean, this.defaultJNDIBindingPolicy);
               // home
               if(sbean.getHome() != null && sbean.getHome().length() > 0)
               {
                  String remoteHomeJNDIName = sessionbeanJNDINameResolver.resolveRemoteHomeJNDIName(sbean);
                  cdmd.addJndiName(remoteHomeJNDIName);
                  // Add ejb/vfsPath@iface
                  String home = sbean.getHome();
                  String ifacePath = prefix + "@" + home;
                  if(endpointAlternateMap.containsKey(ifacePath))
                     log.debug(ejbName+" duplicates home: "+home+", existing: "+endpointAlternateMap.get(ifacePath));
                  else
                  {
                     endpointAlternateMap.put(ifacePath, ejbCompID);
                     mappings.add(ifacePath);
                     if(trace)
                        log.trace("mapEjbs, local-home: "+ifacePath+", ejbName: "+ejbName);
                  }
                  // Add ejb/iface
                  ifacePath = "ejb@" + home;
                  if(endpointMap.containsKey(ifacePath))
                  {
                     // TODO: may need to track the duplicates to create an error
                     log.debug(ejbName+" duplicates home: "+home+", existing: "+endpointMap.get(ifacePath));
                  }
                  else
                  {
                     endpointAlternateMap.put(ifacePath, ejbCompID);
                     mappings.add(ifacePath);
                     if(trace)
                        log.trace("mapEjbs, home: "+ifacePath+", ejbName: "+ejbName);
                  }
               }
               // remote
               if(sbean.getRemote() != null && sbean.getRemote().length() > 0)
               {
                  // Add ejb/vfsPath@iface
                  String remote = sbean.getRemote();
                  String remoteJndiName = sessionbeanJNDINameResolver.resolveJNDIName(sbean, remote);
                  cdmd.addJndiName(remoteJndiName);
                  String ifacePath = prefix + "@" + remote;
                  if(endpointAlternateMap.containsKey(ifacePath))
                     log.debug(ejbName+" duplicates remote: "+remote+", existing: "+endpointAlternateMap.get(ifacePath));
                  else
                  {
                     endpointAlternateMap.put(ifacePath, ejbCompID);
                     mappings.add(ifacePath);
                     if(trace)
                        log.trace("mapEjbs, remote: "+ifacePath+", ejbName: "+ejbName);
                  }
                  // Add ejb/iface
                  ifacePath = "ejb@" + remote;
                  if(endpointMap.containsKey(ifacePath))
                  {
                     // TODO: may need to track the duplicates to create an error
                     log.debug(ejbName+" duplicates remote: "+remote+", existing: "+endpointMap.get(ifacePath));
                  }
                  else
                  {
                     endpointAlternateMap.put(ifacePath, ejbCompID);
                     mappings.add(ifacePath);
                     if(trace)
                        log.trace("mapEjbs, local: "+ifacePath+", ejbName: "+ejbName);
                  }
               }
               // local-home
               if(sbean.getLocalHome() != null && sbean.getLocalHome().length() > 0)
               {
                  String localHomeJNDIName = sessionbeanJNDINameResolver.resolveLocalHomeJNDIName(sbean);
                  cdmd.addJndiName(localHomeJNDIName);
                  // Add ejb/vfsPath@iface
                  String local = sbean.getLocalHome();
                  String ifacePath = prefix + "@" + local;
                  if(endpointAlternateMap.containsKey(ifacePath))
                     log.debug(ejbName+" duplicates local-home: "+local+", existing: "+endpointAlternateMap.get(ifacePath));
                  else
                  {
                     endpointAlternateMap.put(ifacePath, ejbCompID);
                     mappings.add(ifacePath);
                     if(trace)
                        log.trace("mapEjbs, local-home: "+ifacePath+", ejbName: "+ejbName);
                  }
                  // Add ejb/iface
                  ifacePath = "ejb@" + local;
                  if(endpointMap.containsKey(ifacePath))
                  {
                     // TODO: may need to track the duplicates to create an error
                     log.debug(ejbName+" duplicates local-home: "+local+", existing: "+endpointMap.get(ifacePath));
                  }
                  else
                  {
                     endpointAlternateMap.put(ifacePath, ejbCompID);
                     mappings.add(ifacePath);
                     if(trace)
                        log.trace("mapEjbs, local-home: "+ifacePath+", ejbName: "+ejbName);
                  }
               }
               // local
               if(sbean.getLocal() != null && sbean.getLocal().length() > 0)
               {
                  // Add ejb/vfsPath@iface
                  String local = sbean.getLocal();
                  String localJndiName = sessionbeanJNDINameResolver.resolveJNDIName(sbean, local);
                  cdmd.addJndiName(localJndiName);
                  String ifacePath = prefix + "@" + local;
                  if(endpointAlternateMap.containsKey(ifacePath))
                     log.debug(ejbName+" duplicates local: "+local+", existing: "+endpointAlternateMap.get(ifacePath));
                  else
                  {
                     endpointAlternateMap.put(ifacePath, ejbCompID);
                     mappings.add(ifacePath);
                     if(trace)
                        log.trace("mapEjbs, local: "+ifacePath+", ejbName: "+ejbName);
                  }
                  // Add ejb/iface
                  ifacePath = "ejb@" + local;
                  if(endpointMap.containsKey(ifacePath))
                  {
                     // TODO: may need to track the duplicates to create an error
                     log.debug(ejbName+" duplicates local: "+local+", existing: "+endpointMap.get(ifacePath));
                  }
                  else
                  {
                     endpointAlternateMap.put(ifacePath, ejbCompID);
                     mappings.add(ifacePath);
                     if(trace)
                        log.trace("mapEjbs, local: "+ifacePath+", ejbName: "+ejbName);
                  }
               }

               BusinessLocalsMetaData locals = sbean.getBusinessLocals();
               if(locals != null && locals.size() > 0)
               {
                  String defaultLocalBusinessJNDIName = sessionbeanJNDINameResolver.resolveLocalBusinessDefaultJNDIName(sbean);
                  cdmd.addJndiName(defaultLocalBusinessJNDIName);
                  for(String local : locals)
                  {
                     // Add a targeted jndi name
                     String localBusinessJNDIName = sessionbeanJNDINameResolver.resolveJNDIName(sbean, local);
                     cdmd.addJndiName(localBusinessJNDIName);
                     // Add ejb/vfsPath@iface
                     String ifacePath = prefix + "@" + local;
                     if(endpointAlternateMap.containsKey(ifacePath))
                        log.debug(ejbName+" duplicates business local: "+local+", existing: "+endpointAlternateMap.get(ifacePath));
                     else
                     {
                        endpointAlternateMap.put(ifacePath, ejbCompID);
                        mappings.add(ifacePath);
                        if(trace)
                           log.trace("mapEjbs, business-local: "+ifacePath+", ejbName: "+ejbName);
                     }
                     // Add ejb/iface
                     ifacePath = "ejb@" + local;
                     if(endpointMap.containsKey(ifacePath))
                     {
                        // TODO: may need to track the duplicates to create an error
                        log.debug(ejbName+" duplicates business-local: "+local+", existing: "+endpointMap.get(ifacePath));
                     }
                     else
                     {
                        endpointAlternateMap.put(ifacePath, ejbCompID);
                        mappings.add(ifacePath);
                        if(trace)
                           log.trace("mapEjbs, business-local: "+ifacePath+", ejbName: "+ejbName);
                     }
                  }
               }
               BusinessRemotesMetaData remotes = sbean.getBusinessRemotes();
               if(remotes != null && remotes.size() > 0)
               {
                  String defaultRemoteBusinessJNDIName = sessionbeanJNDINameResolver.resolveRemoteBusinessDefaultJNDIName(sbean);
                  cdmd.addJndiName(defaultRemoteBusinessJNDIName);
                  for(String remote : remotes)
                  {
                     // Add a targeted jndi name
                     String remoteBusinessJNDIName = sessionbeanJNDINameResolver.resolveJNDIName(sbean, remote);
                     cdmd.addJndiName(remoteBusinessJNDIName);
                     // Add ejb/vfsPath/iface
                     String ifacePath = prefix + "@" + remote;
                     if(endpointAlternateMap.containsKey(ifacePath))
                        log.debug(ejbName+" duplicates business remote: "+remote+", existing: "+endpointAlternateMap.get(ifacePath));
                     else
                     {
                        endpointAlternateMap.put(ifacePath, ejbCompID);
                        mappings.add(ifacePath);
                        if(trace)
                           log.trace("mapEjbs, business-remote: "+ifacePath+", ejbName: "+ejbName);
                     }
                     // Add ejb/iface
                     ifacePath = "ejb@" + remote;
                     if(endpointMap.containsKey(ifacePath))
                        log.debug(ejbName+" duplicates business-remote: "+remote+", existing: "+endpointMap.get(ifacePath));
                     else
                     {
                        endpointAlternateMap.put(ifacePath, ejbCompID);
                        mappings.add(ifacePath);
                        if(trace)
                           log.trace("mapEjbs, business-remote: "+ifacePath+", ejbName: "+ejbName);
                     }
                  }
               }
               // Map no-interface view of EJB3.1 bean
               if (sbean instanceof JBossSessionBean31MetaData)
               {
                  JBossSessionBean31MetaData sessionBean31 = (JBossSessionBean31MetaData) sbean;
                  if (sessionBean31.isNoInterfaceBean())
                  {
                     SessionBean31JNDINameResolver sessionBean31JNDINameResolver = (SessionBean31JNDINameResolver) sessionbeanJNDINameResolver;
                     String noInterfaceJNDIName = sessionBean31JNDINameResolver.resolveNoInterfaceJNDIName(sessionBean31);
                     // add it to container metadata
                     cdmd.addJndiName(noInterfaceJNDIName);
                     // TODO: The rest of the stuff is way too ugly, but that's what is being
                     // done for maintaining per business interface endpoint mappings. So let's 
                     // for the time being, do the same for no-interface view endpoint mappings.
                     
                     String ejbClassName = sessionBean31.getEjbClass();
                     // add to end point mappings
                     String ifacePath = prefix + "@" + ejbClassName;
                     if(endpointAlternateMap.containsKey(ifacePath))
                     {
                        log.debug(ejbName + " duplicates nointerface bean: " + ejbClassName
                              + ", existing: " + endpointAlternateMap.get(ifacePath));
                     }
                     else
                     {
                        endpointAlternateMap.put(ifacePath, ejbCompID);
                        mappings.add(ifacePath);
                        if(trace)
                        {
                           log.trace("mapEjbs, nointerface bean: " + ifacePath + ", ejbName: " + ejbName);
                        }
                     }
                     // Add ejb/iface
                     ifacePath = "ejb@" + ejbClassName;
                     if(endpointMap.containsKey(ifacePath))
                     {
                        // TODO: may need to track the duplicates to create an error
                        log.debug(ejbName + " duplicates nointerface bean : " + ejbClassName + ", existing: "
                              + endpointMap.get(ifacePath));
                     }
                     else
                     {
                        endpointAlternateMap.put(ifacePath, ejbCompID);
                        mappings.add(ifacePath);
                        if(trace)
                           log.trace("mapEjbs, nointerface bean : " + ifacePath + ", ejbName: " + ejbName);
                     }
                     
                  }
               }
               if(trace)
                  log.trace("mapEjbs: "+ejbCompID+", mappings: "+mappings);
            }
            else if(bean instanceof JBossEntityBeanMetaData)
            {
               JBossEntityBeanMetaData ebean = (JBossEntityBeanMetaData) bean;
               EntityBeanJNDINameResolver entityBeanJNDINameResolver = JNDIPolicyBasedJNDINameResolverFactory.getJNDINameResolver(ebean, this.defaultJNDIBindingPolicy);
               String ejbName = ebean.getEjbName();
               // home
               if(ebean.getHome() != null && ebean.getHome().length() > 0)
               {
                  String homeJNDIName = entityBeanJNDINameResolver.resolveRemoteHomeJNDIName(ebean);
                  cdmd.addJndiName(homeJNDIName);
                  // Add ejb/vfsPath@iface
                  String home = ebean.getHome();
                  String ifacePath = prefix + "@" + home;
                  if(endpointAlternateMap.containsKey(ifacePath))
                     log.debug(ejbName+" duplicates home: "+home+", existing: "+endpointAlternateMap.get(ifacePath));
                  else
                  {
                     endpointAlternateMap.put(ifacePath, ejbCompID);
                     mappings.add(ifacePath);
                     if(trace)
                        log.trace("mapEjbs, local-home: "+ifacePath+", ejbName: "+ejbName);
                  }
                  // Add ejb/iface
                  ifacePath = "ejb@" + home;
                  if(endpointMap.containsKey(ifacePath))
                  {
                     // TODO: may need to track the duplicates to create an error
                     log.debug(ejbName+" duplicates home: "+home+", existing: "+endpointMap.get(ifacePath));
                  }
                  else
                  {
                     endpointAlternateMap.put(ifacePath, ejbCompID);
                     mappings.add(ifacePath);
                     if(trace)
                        log.trace("mapEjbs, home: "+ifacePath+", ejbName: "+ejbName);
                  }
               }
               // remote
               if(ebean.getRemote() != null && ebean.getRemote().length() > 0)
               {
                  // Add ejb/vfsPath@iface
                  String remote = ebean.getRemote();
                  String ifacePath = prefix + "@" + remote;
                  if(endpointAlternateMap.containsKey(ifacePath))
                     log.debug(ejbName+" duplicates remote: "+remote+", existing: "+endpointAlternateMap.get(ifacePath));
                  else
                  {
                     endpointAlternateMap.put(ifacePath, ejbCompID);
                     mappings.add(ifacePath);
                     if(trace)
                        log.trace("mapEjbs, remote: "+ifacePath+", ejbName: "+ejbName);
                  }
                  // Add ejb/iface
                  ifacePath = "ejb@" + remote;
                  if(endpointMap.containsKey(ifacePath))
                  {
                     // TODO: may need to track the duplicates to create an error
                     log.debug(ejbName+" duplicates remote: "+remote+", existing: "+endpointMap.get(ifacePath));
                  }
                  else
                  {
                     endpointAlternateMap.put(ifacePath, ejbCompID);
                     mappings.add(ifacePath);
                     if(trace)
                        log.trace("mapEjbs, local: "+ifacePath+", ejbName: "+ejbName);
                  }
               }
               // local-home
               if(ebean.getLocalHome() != null && ebean.getLocalHome().length() > 0)
               {
                  String localHomeJNDIName = entityBeanJNDINameResolver.resolveLocalHomeJNDIName(ebean);
                  cdmd.addJndiName(localHomeJNDIName);
                  // Add ejb/vfsPath@iface
                  String local = ebean.getLocalHome();
                  String ifacePath = prefix + "@" + local;
                  if(endpointAlternateMap.containsKey(ifacePath))
                     log.debug(ejbName+" duplicates local-home: "+local+", existing: "+endpointAlternateMap.get(ifacePath));
                  else
                  {
                     endpointAlternateMap.put(ifacePath, ejbCompID);
                     mappings.add(ifacePath);
                     if(trace)
                        log.trace("mapEjbs, local-home: "+ifacePath+", ejbName: "+ejbName);
                  }
                  // Add ejb/iface
                  ifacePath = "ejb@" + local;
                  if(endpointMap.containsKey(ifacePath))
                  {
                     // TODO: may need to track the duplicates to create an error
                     log.debug(ejbName+" duplicates local-home: "+local+", existing: "+endpointMap.get(ifacePath));
                  }
                  else
                  {
                     endpointAlternateMap.put(ifacePath, ejbCompID);
                     mappings.add(ifacePath);
                     if(trace)
                        log.trace("mapEjbs, local-home: "+ifacePath+", ejbName: "+ejbName);
                  }
               }
               // local
               if(ebean.getLocal() != null && ebean.getLocal().length() > 0)
               {
                  // Add ejb/vfsPath@iface
                  String local = ebean.getLocal();
                  String ifacePath = prefix + "@" + local;
                  if(endpointAlternateMap.containsKey(ifacePath))
                     log.debug(ejbName+" duplicates local: "+local+", existing: "+endpointAlternateMap.get(ifacePath));
                  else
                  {
                     endpointAlternateMap.put(ifacePath, ejbCompID);
                     mappings.add(ifacePath);
                     if(trace)
                        log.trace("mapEjbs, local: "+ifacePath+", ejbName: "+ejbName);
                  }
                  // Add ejb/iface
                  ifacePath = "ejb@" + local;
                  if(endpointMap.containsKey(ifacePath))
                  {
                     // TODO: may need to track the duplicates to create an error
                     log.debug(ejbName+" duplicates local: "+local+", existing: "+endpointMap.get(ifacePath));
                  }
                  else
                  {
                     endpointAlternateMap.put(ifacePath, ejbCompID);
                     mappings.add(ifacePath);
                     if(trace)
                        log.trace("mapEjbs, local: "+ifacePath+", ejbName: "+ejbName);
                  }
               }
            }
            else if(bean instanceof JBossGenericBeanMetaData)
            {
               log.warn("JBossGenericBeanMetaData seen for: "+bean.getEjbName());
            }
         }
      }

      // Process children
      List<DeploymentUnit> children = unit.getChildren();
      if(children != null)
      {
         for(DeploymentUnit child : children)
            mapEjbs(child, endpointMap, endpointAlternateMap, trace);
      }
   }

   /**
    * 
    * @param cdmd
    * @param unit
    * @param endpointMap
    * @param env
    * @param resolver
    * @param unresolvedRefs
    * @throws Exception
    */
   protected void resolve(ContainerDependencyMetaData cdmd, DeploymentUnit unit,
         Map<String, ContainerDependencyMetaData> endpointMap,
         Environment env,
         DeploymentEndpointResolver resolver,
         List<String> unresolvedRefs)
      throws Exception
   {
      if(env == null)
         return;

      AnnotatedEJBReferencesMetaData annotatedRefs = env.getAnnotatedEjbReferences();
      resolveEjbAnnotatedRefs(cdmd, unit, endpointMap, annotatedRefs, resolver, unresolvedRefs);
      EJBLocalReferencesMetaData localRefs = env.getEjbLocalReferences();
      resolveEjbLocalRefs(cdmd, unit, endpointMap, localRefs, resolver, unresolvedRefs);
      EJBReferencesMetaData ejbRefs = env.getEjbReferences();
      resolveEjbRefs(cdmd, unit, endpointMap, ejbRefs, resolver, unresolvedRefs);
      MessageDestinationReferencesMetaData msgRefs = env.getMessageDestinationReferences();
      resolveMsgRefs(cdmd, unit, endpointMap, msgRefs, resolver, unresolvedRefs);
      // TODO, other references
   }
   protected void resolve(DeploymentUnit unit, Map<String, ContainerDependencyMetaData> endpointMap,
         JBossEnterpriseBeansMetaData beans,
         DeploymentEndpointResolver resolver,
         List<String> unresolvedPaths)
      throws Exception
   {
      if(beans == null || beans.size() == 0)
         return;

      String vfsPath = unit.getRelativePath();
      for(JBossEnterpriseBeanMetaData bean : beans)
      {
         // Find the container dependency metadata
         String ejbCompID = "ejb/" + vfsPath + "#" + bean.getEjbName();
         ContainerDependencyMetaData cdmd = endpointMap.get(ejbCompID);
         if(cdmd == null)
            throw new IllegalStateException("Failed to find ContainerDependencyMetaData for: "+ejbCompID);
         Environment env = bean.getJndiEnvironmentRefsGroup();
         resolve(cdmd, unit, endpointMap, env, resolver,  unresolvedPaths);
      }
   }

   protected void resolveEjbAnnotatedRefs(ContainerDependencyMetaData cdmd, DeploymentUnit unit,
      Map<String, ContainerDependencyMetaData> endpointMap,
      AnnotatedEJBReferencesMetaData annotatedRefs,
      DeploymentEndpointResolver resolver,
      List<String> unresolvedRefs)
   {
      if(annotatedRefs == null)
         return;

      String vfsContext = unit.getRelativePath();
      for(AnnotatedEJBReferenceMetaData ref : annotatedRefs)
      {
         Class iface = ref.getBeanInterface();
         String link = ref.getLink();
         EndpointInfo info = null;
         if(link != null)
            info = resolver.getEndpointInfo(link, EndpointType.EJB, vfsContext);
         if(info == null && iface != null)
            info = resolver.getEndpointInfo(iface, EndpointType.EJB, vfsContext);

         if(info != null)
         {
            ContainerDependencyMetaData target = endpointMap.get(info.getComponentKey());
            if(target != null)
            {
               cdmd.addDependency(target);
               // Determine the jndi name for the reference interface
               String ifaceName = iface != null ? iface.getName() : null;
               
               //LegacyEjb3JndiPolicy policy = new LegacyEjb3JndiPolicy();
               JBossEnterpriseBeanMetaData beanMD = target.getBeanMetaData();
               String containerJndiName = null;
               if (beanMD.isSession() || beanMD.isService())
               {
                  SessionBeanJNDINameResolver jndiNameResolver = JNDIPolicyBasedJNDINameResolverFactory.getJNDINameResolver((JBossSessionBeanMetaData) beanMD, this.defaultJNDIBindingPolicy);
                  containerJndiName = jndiNameResolver.resolveJNDIName((JBossSessionBeanMetaData) beanMD, ifaceName);
               }
               else if (beanMD.isEntity())
               {
                  EntityBeanJNDINameResolver jndiNameResolver = JNDIPolicyBasedJNDINameResolverFactory.getJNDINameResolver((JBossEntityBeanMetaData) beanMD, this.defaultJNDIBindingPolicy);
                  containerJndiName = jndiNameResolver.resolveJNDIName((JBossEntityBeanMetaData) beanMD, ifaceName);
               }
               if(containerJndiName != null)
                  ref.setResolvedJndiName(containerJndiName);
            }
            else
            {
               unresolvedRefs.add(cdmd.getComponentID()+":"+ref);
            }
         }
         else
         {
            unresolvedRefs.add(cdmd.getComponentID()+":"+ref);
         }
      }
   }

   /**
    *
    * @param unit
    * @param localRefs
    * @param unresolvedRefs
    */
   protected void resolveEjbLocalRefs(ContainerDependencyMetaData cdmd, DeploymentUnit unit,
         Map<String, ContainerDependencyMetaData> endpointMap,
         EJBLocalReferencesMetaData localRefs,
         DeploymentEndpointResolver resolver,
         List<String> unresolvedRefs)
      throws Exception
   {
      if(localRefs == null)
         return;

      String vfsContext = unit.getRelativePath();
      ClassLoader loader = unit.getClassLoader();
      for(EJBLocalReferenceMetaData ref : localRefs)
      {
         if (ref.getIgnoreDependency() != null)
         {
            log.debug("IGNORING <ejb-ref> DEPENDENCY: " + ref);
            return;
         }

         String link = ref.getLink();
         String mappedName = ref.getMappedName();
         // Use mapped name first
         if(mappedName == null || mappedName.length() == 0)
         {
            ContainerDependencyMetaData target = null;
            if(link != null)
            {
               EndpointInfo info = resolver.getEndpointInfo(link, EndpointType.EJB, vfsContext);
               if(info != null)
               {
                  target = endpointMap.get(info.getComponentKey());
               }
               else
               {
                  /* A non-local link without a # jar target. This is allowed
                     for java ee clients so we have to search all ejb deployments.
                     First get the vfspaths of the ejb deploymens.
                  */
                  List<String> ejbPaths = getEjbDeploymentPaths(unit);
                  for(String path : ejbPaths)
                  {
                     EndpointInfo altInfo = resolver.getEndpointInfo(link, EndpointType.EJB, path);
                     if(altInfo != null)
                        target = endpointMap.get(altInfo.getComponentKey());
                     if(target != null)
                        break;
                  }
               }
            }
            if(target == null && ref.getLocal() != null)
            {
               // Try the local interface type
               target = resolveEjbInterface(ref.getLocal(), unit,
                     endpointMap, resolver);
            }
            if(target == null)
               unresolvedRefs.add(cdmd.getComponentID()+":"+ref);
            else
            {
               
               // Need to look at the local jndi name
               String localInterface = ref.getLocal();
               JBossEnterpriseBeanMetaData md = target.getBeanMetaData();
               
               /*
                * If for a Session bean we've got a reference to an EJB2.x
                * Local Component interface, stop processing because these
                * are not bound in JNDI (only accessible via LocalHome.create()
                */
               
               // Session EJB?
               boolean useDefaultProxy = false;
               if(md.isSession())
               {
                  // Cast
                  JBossSessionBeanMetaData smd = (JBossSessionBeanMetaData)md;
                  
                  // Get the name of the Component Local Interface
                  String ejb2xLocalInterface = smd.getLocal();
                  
                  // If the ejb-ref is to a EJB2.x Local Component Interface
                  if(localInterface.equals(ejb2xLocalInterface))
                  {
                     // Use the default proxy
                     useDefaultProxy = true;
                  }
               }
                
               // Get ejb-jar Metadata
               JBossMetaData ejbJarMd = md.getEnterpriseBeansMetaData().getEjbJarMetaData();
               
               // Resolve a local JNDI Name based on Spec type
               String localJndiName = null;
               if (ejbJarMd.isEJB3x())
               {
                  if (md.isSession() || md.isService())
                  {
                     SessionBeanJNDINameResolver sessionBeanJNDINameResolver = JNDIPolicyBasedJNDINameResolverFactory.getJNDINameResolver((JBossSessionBeanMetaData) md, this.defaultJNDIBindingPolicy);
                     if (useDefaultProxy)
                     {
                        localJndiName = sessionBeanJNDINameResolver
                              .resolveLocalBusinessDefaultJNDIName((JBossSessionBeanMetaData) md);   
                     }
                     else
                     {
                        localJndiName = sessionBeanJNDINameResolver.resolveJNDIName((JBossSessionBeanMetaData) md, localInterface);
                     }
                     
                  }
                  else if (md.isEntity())
                  {
                     EntityBeanJNDINameResolver entityBeanJNDINameResolver = JNDIPolicyBasedJNDINameResolverFactory.getJNDINameResolver((JBossEntityBeanMetaData) md, this.defaultJNDIBindingPolicy);
                     localJndiName = entityBeanJNDINameResolver.resolveJNDIName((JBossEntityBeanMetaData) md, localInterface);
                  }
               }
               else
               {
                  localJndiName = md.determineLocalJndiName();
               }

               // If we've got a resolved JNDI Name 
               if (localJndiName != null)
               {
                  // Set it and forget it! 
                  // http://en.wikipedia.org/wiki/Ron_Popeil
                  ref.setResolvedJndiName(localJndiName);
               }
               
               // Add the dependency
               cdmd.addDependency(target);
            }
         }
         else
         {
            // Create a JNDI dependency
            ref.setResolvedJndiName(mappedName);
            JndiDependencyMetaData jdmd = new JndiDependencyMetaData(mappedName, loader);
            cdmd.addJndiDependency(jdmd);
         }
      }

   }

   protected void resolveEjbRefs(ContainerDependencyMetaData cdmd, DeploymentUnit unit,
         Map<String, ContainerDependencyMetaData> endpointMap,
         EJBReferencesMetaData ejbRefs,
         DeploymentEndpointResolver resolver,
         List<String> unresolvedRefs)
      throws Exception
   {
      if(ejbRefs == null)
         return;

      String vfsContext = unit.getRelativePath();
      ClassLoader loader = unit.getClassLoader();
      for(EJBReferenceMetaData ref : ejbRefs)
      {
         if (ref.getIgnoreDependency() != null)
         {
            log.debug("IGNORING <ejb-ref> DEPENDENCY: " + ref);
            return;
         }

         String link = ref.getLink();
         String mappedName = ref.getMappedName();
         // Use mapped name first
         if(mappedName == null || mappedName.length() == 0)
         {
            ContainerDependencyMetaData target = null;
            if(link != null)
            {
               EndpointInfo info = resolver.getEndpointInfo(link, EndpointType.EJB, vfsContext);
               if(info != null)
               {
                  target = endpointMap.get(info.getComponentKey());
               }
               else
               {
                  /* A non-local link without a # jar target. This is allowed
                     for java ee clients so we have to search all ejb deployments.
                     First get the vfspaths of the ejb deploymens.
                  */
                  List<String> ejbPaths = getEjbDeploymentPaths(unit);
                  for(String path : ejbPaths)
                  {
                     EndpointInfo altInfo = resolver.getEndpointInfo(link, EndpointType.EJB, path);
                     if(altInfo != null)
                        target = endpointMap.get(altInfo.getComponentKey());
                     if(target != null)
                        break;
                  }
               }
               if(target == null)
               {
                  unresolvedRefs.add(cdmd.getComponentID()+":"+ref);
                  continue;
               }
            }
            if(target == null && ref.getRemote() != null)
            {
               // Try the local interface type
               target = resolveEjbInterface(ref.getRemote(), unit,
                     endpointMap, resolver);
            }

            if(target == null)
               unresolvedRefs.add(cdmd.getComponentID()+":"+ref);
            else
            {
               
               // Obtain remote interface name
               String remoteInterface = ref.getRemote();
               
               // Get Metadata
               JBossEnterpriseBeanMetaData md = target.getBeanMetaData();
               
               /*
                * If for a Session bean we've got a reference to an EJB2.x
                * Remote Component interface, stop processing because these
                * are not bound in JNDI (only accessible via Home.create()
                */
               
               // Session EJB?
               boolean useDefaultProxy = false;
               if(md.isSession())
               {
                  // Cast
                  JBossSessionBeanMetaData smd = (JBossSessionBeanMetaData)md;
                  
                  // Get the name of the Component Remote Interface
                  String ejb2xRemoteInterface = smd.getRemote();
                  
                  // If the ejb-ref is to a EJB2.x Remote Component Interface
                  if(remoteInterface.equals(ejb2xRemoteInterface))
                  {
                     // Use the default proxy
                     useDefaultProxy = true;
                  }
               }
               
               // Get ejb-jar metadata
               JBossMetaData ejbMarMd = md.getEnterpriseBeansMetaData().getEjbJarMetaData();
               
               // Resolve a JNDI name
               String remoteJNDIName = null;
               if (ejbMarMd.isEJB3x())
               {
                  if (md.isSession() || md.isService())
                  {
                     SessionBeanJNDINameResolver sessionBeanJNDINameResolver = JNDIPolicyBasedJNDINameResolverFactory.getJNDINameResolver((JBossSessionBeanMetaData) md, this.defaultJNDIBindingPolicy);
                     if (useDefaultProxy)
                     {
                        remoteJNDIName = sessionBeanJNDINameResolver.resolveRemoteBusinessDefaultJNDIName((JBossSessionBeanMetaData) md);   
                     }
                     else
                     {
                        remoteJNDIName = sessionBeanJNDINameResolver.resolveJNDIName((JBossSessionBeanMetaData) md, remoteInterface);
                     }
                     
                  }
                  else if (md.isEntity())
                  {
                     EntityBeanJNDINameResolver entityBeanJNDINameResolver = JNDIPolicyBasedJNDINameResolverFactory.getJNDINameResolver((JBossEntityBeanMetaData) md, this.defaultJNDIBindingPolicy);
                     remoteJNDIName = entityBeanJNDINameResolver.resolveJNDIName((JBossEntityBeanMetaData) md, remoteInterface);
                  }
                  
               }
               else
               {
                  remoteJNDIName = md.determineJndiName();
               }
               
               // If we've got a resolved name
               if(remoteJNDIName != null)
               {
                  // Set it
                  ref.setResolvedJndiName(remoteJNDIName);
               }
               
               // Add the dependency
               cdmd.addDependency(target);
            }
         }
         else
         {
            // Create a JNDI dependency
            ref.setResolvedJndiName(mappedName);
            JndiDependencyMetaData jdmd = new JndiDependencyMetaData(mappedName, loader);
            cdmd.addJndiDependency(jdmd);
         }
      }
   }

   protected String getInterface(EJBReferenceMetaData ref)
   {
      String iface = ref.getHome();
      if(iface == null)
         iface = ref.getRemote();
      return iface;
   }

   protected void resolveMsgRefs(ContainerDependencyMetaData cdmd, DeploymentUnit unit,
         Map<String, ContainerDependencyMetaData> endpointMap,
         MessageDestinationReferencesMetaData msgRefs,
         DeploymentEndpointResolver resolver,
         List<String> unresolvedRefs)
   {
      if(msgRefs == null)
         return;

      ClassLoader loader = unit.getClassLoader();
      String vfsContext = unit.getRelativePath();
      for(MessageDestinationReferenceMetaData ref : msgRefs)
      {
         String mappedName = ref.getMappedName();
         if(mappedName == null || mappedName.length() == 0)
         {
            String link = ref.getLink();
            ContainerDependencyMetaData target = null;
            if(link != null)
            {
               EndpointInfo info = resolver.getEndpointInfo(link, EndpointType.MessageDestination, vfsContext);
               if(info != null)
               {
                  target = endpointMap.get(info.getComponentKey());
               }
            }
            if(target == null)
               unresolvedRefs.add(cdmd.getComponentID()+":"+ref);
            else
            {
               cdmd.addDependency(target);
               String containerJndiName = target.getContainerName();
               if(containerJndiName != null)
                  ref.setResolvedJndiName(containerJndiName);
            }
         }
         else
         {
            // Create a JNDI dependency
            ref.setResolvedJndiName(mappedName);
            JndiDependencyMetaData jdmd = new JndiDependencyMetaData(mappedName, loader);
            cdmd.addJndiDependency(jdmd);
         }
      }
   }

   protected ContainerDependencyMetaData resolveEjbInterface(String iface,
         DeploymentUnit unit, Map<String, ContainerDependencyMetaData> endpointMap,
         DeploymentEndpointResolver resolver)
      throws Exception
   {
      ClassLoader loader = unit.getClassLoader();
      Class<?> ifaceClass = loader.loadClass(iface);
      String vfsContext = unit.getRelativePath();
      EndpointInfo info = resolver.getEndpointInfo(ifaceClass, EndpointType.EJB, vfsContext);
      if(info == null)
         throw new IllegalStateException("Failed to find ContainerDependencyMetaData for interface: "+ iface);
      ContainerDependencyMetaData cdmd = endpointMap.get(info.getComponentKey());
      return cdmd;
   }

   private List<String> getEjbDeploymentPaths(DeploymentUnit unit)
   {
      ArrayList<String> paths = new ArrayList<String>();
      DeploymentUnit root = unit.getTopLevel();
      getEjbDeploymentPaths(root, paths);
      return paths;
   }
   
   private void getEjbDeploymentPaths(DeploymentUnit unit,
         ArrayList<String> paths)
   {
      if(unit.isAttachmentPresent(JBossMetaData.class))
         paths.add(unit.getRelativePath());
      // Process children
      List<DeploymentUnit> children = unit.getChildren();
      if(children != null)
      {
         for(DeploymentUnit child : children)
            getEjbDeploymentPaths(child, paths);
      }
   }

   private static <T> void getAllAttachments(DeploymentUnit unit, Class<T> type, ArrayList<T> attachments)
   {
      T attachment = unit.getAttachment(type);
      if(attachment != null)
         attachments.add(attachment);
      List<DeploymentUnit> children = unit.getChildren();
      if(children != null)
         for(DeploymentUnit child : children)
            getAllAttachments(child, type, attachments);
   }

   /*
    * Unused
    */
//   private static String getJndiName(JBossEnterpriseBeanMetaData beanMD, boolean isLocal,
//         DeploymentUnit unit)
//   {
//      String jndiName = beanMD.getMappedName();
//      if (isLocal && jndiName == null)
//      {
//         // Validate that there is a local home associated with this bean
//         if(jndiName == null)
//            jndiName = beanMD.determineLocalJndiName();
//         if (jndiName == null)
//         {
//            log.warn("LocalHome jndi name requested for: '" +beanMD.getEjbName() + "' but there is no LocalHome class");
//         }
//      }
//      else if(jndiName == null)
//      {
//         // TODO, this is a mess that should be simply from the metadata
//         ClassLoader loader = unit.getClassLoader();
//         EjbDeploymentSummary unitSummary = getUnitSummary(unit, beanMD);
//         Class<? extends DefaultJndiBindingPolicy> defaultPolicyClass = null;
//         try
//         {
//            if(beanMD.getJBossMetaData().isEJB3x())
//            {
//               String policyClassName = "org.jboss.ejb3.jndipolicy.impl.PackagingBasedJndiBindingPolicy";
//               defaultPolicyClass = (Class<? extends DefaultJndiBindingPolicy>) loader.loadClass(policyClassName);
//            }
//            else if(beanMD.isSession())
//               defaultPolicyClass = SessionJndiBindingPolicy.class;
//            DefaultJndiBindingPolicy policy = beanMD.createPolicy(loader, defaultPolicyClass);
//            // Run through the options for which jndi name
//            if(beanMD.isSession())
//            {
//               JBossSessionBeanMetaData sbeanMD = (JBossSessionBeanMetaData) beanMD;
//               jndiName = sbeanMD.getJndiName();
//               if(jndiName == null)
//               {
//                  if(sbeanMD.getBusinessRemotes() != null && sbeanMD.getBusinessRemotes().size() > 0)
//                     jndiName = policy.getDefaultRemoteJndiName(unitSummary);
//                  else if(sbeanMD.getHome() != null)
//                     jndiName = policy.getDefaultRemoteHomeJndiName(unitSummary);
//                  else if(sbeanMD.getRemote() != null)
//                     jndiName = policy.getDefaultRemoteJndiName(unitSummary);
//                  else if(sbeanMD.getLocalHome() != null)
//                     jndiName = policy.getDefaultLocalHomeJndiName(unitSummary);
//                  else if(sbeanMD.getLocal() != null)
//                     jndiName = policy.getDefaultLocalJndiName(unitSummary);
//                  else
//                     jndiName = policy.getJndiName(unitSummary);
//               }
//            }
//            else if(policy != null)
//            {
//               jndiName = policy.getJndiName(unitSummary);
//            }
//            else if(beanMD.isEntity())
//            {
//               JBossEntityBeanMetaData ebeanMD = (JBossEntityBeanMetaData) beanMD;
//               jndiName = ebeanMD.determineJndiName();
//            }
//         }
//         catch(Exception e)
//         {
//            log.warn("Failed to obtain jndi name for bean: "+beanMD.getEjbName(), e);
//            jndiName = beanMD.getEjbName();
//         }
//      }
//      return jndiName;
//   }

   private static EjbDeploymentSummary getEjbDeploymentSummary(DeploymentUnit unit, JBossEnterpriseBeanMetaData beanMD)
   {
      DeploymentSummary dSummary = getDeploymentSummary(unit);
      
      EjbDeploymentSummary summary = new EjbDeploymentSummary(beanMD,dSummary);
      return summary;
   }
   
   private static DeploymentSummary getDeploymentSummary(DeploymentUnit unit)
   {
      DeploymentSummary dSummary = new DeploymentSummary();
      dSummary.setDeploymentName(unit.getSimpleName());
      String baseName = unit.getTopLevel().getSimpleName();
      int dot = baseName.lastIndexOf('.');
      if(dot > 0)
      {
         baseName = baseName.substring(0, dot);
      }
      dSummary.setDeploymentScopeBaseName(baseName);
      
      /*
       * Determine the packaging type (JAR or EAR, Standalone File not 
       * supported by this deployer) 
       */
      
      // Initialize to JAR
      PackagingType packagingType = PackagingType.JAR;
      
      // Determine if EAR
      boolean isEar = unit != unit.getTopLevel();
      if(isEar)
      {
         packagingType = PackagingType.EAR;
      }
      
      // Set type
      dSummary.setPackagingType(packagingType);
      
      // Return
      return dSummary;
   }
   
   private void dump(DeploymentUnit unit)
   {
      DeploymentUnit top = unit.getTopLevel();
      StringBuffer tmp = new StringBuffer();
      dump(top, tmp, 0);
      log.debug("Processing unit="+unit.getSimpleName()+", structure:\n"+tmp);
   }
   private void dump(DeploymentUnit unit, StringBuffer tmp, int depth)
   {
      for(int n = 0; n < depth; n ++)
         tmp.append('+');
      if(depth == 0)
         tmp.append(unit.getSimpleName());
      else
         tmp.append(unit.getRelativePath());
      tmp.append('\n');
      JBossMetaData metaData = unit.getAttachment(JBossMetaData.class);
      if(metaData != null)
      {
         JBossEnterpriseBeansMetaData beans = metaData.getEnterpriseBeans();
         if(beans != null)
         {
            for(JBossEnterpriseBeanMetaData bean : beans)
            {
               for(int n = 0; n < depth+1; n ++)
                  tmp.append('+');
               tmp.append("EjbEndpoint:ejbName=");
               tmp.append(bean.getEjbName());
               if(bean.getEjbClass() != null)
               {
                  tmp.append(",ejbClass=");
                  tmp.append(bean.getEjbClass());
               }
               if(bean instanceof JBossSessionBeanMetaData)
               {
                  JBossSessionBeanMetaData sbean = (JBossSessionBeanMetaData) bean;
                  if(sbean.getHome() != null)
                  {
                     tmp.append(",home=");
                     tmp.append(sbean.getHome());
                  }
                  if(sbean.getRemote() != null)
                  {
                     tmp.append(",remote=");
                     tmp.append(sbean.getRemote());
                  }
                  BusinessLocalsMetaData locals = sbean.getBusinessLocals();
                  if(locals != null)
                  {
                     tmp.append(",BusinessLocals: ");
                     tmp.append(locals);
                  }
                  BusinessRemotesMetaData remotes = sbean.getBusinessRemotes();
                  if(remotes != null)
                  {
                     tmp.append(",BusinessRemotes: ");
                     tmp.append(remotes);
                  }
                  dumpEnv(bean.getJndiEnvironmentRefsGroup(), tmp, depth+1);
               }
               tmp.append('\n');
            }
         }
      }
      else if(unit.isAttachmentPresent(JBossWebMetaData.class))
      {
         JBossWebMetaData webMD = unit.getAttachment(JBossWebMetaData.class);
         dumpEnv(webMD.getJndiEnvironmentRefsGroup(), tmp, depth++);        
      }
      else if(unit.isAttachmentPresent(JBossClientMetaData.class))
      {
         JBossClientMetaData clientMD = unit.getAttachment(JBossClientMetaData.class);
         dumpEnv(clientMD.getJndiEnvironmentRefsGroup(), tmp, depth++);
      }
      tmp.append('\n');
      List<DeploymentUnit> children = unit.getChildren();
      if(children != null)
      {
         for(DeploymentUnit child : children)
            dump(child, tmp, depth+1);
      }
   }
   private void dumpEnv(Environment env, StringBuffer tmp, int depth)
   {
      if(env == null)
         return;

      AnnotatedEJBReferencesMetaData annotatedRefs = env.getAnnotatedEjbReferences();
      if(annotatedRefs != null)
      {
         for(AnnotatedEJBReferenceMetaData ref : annotatedRefs)
         {
            for(int n = 0; n < depth+1; n ++)
               tmp.append('+');
            tmp.append("@EJB(");
            tmp.append(ref.getEjbRefName());
            tmp.append(") -> mappedName=");
            tmp.append(ref.getMappedName());
            tmp.append("| resolvedJndiName=");
            tmp.append(ref.getResolvedJndiName());
            tmp.append('\n');
         }
      }
      EJBLocalReferencesMetaData localRefs = env.getEjbLocalReferences();
      if(localRefs != null)
      {
         for(EJBLocalReferenceMetaData ref : localRefs)
         {
            for(int n = 0; n < depth+1; n ++)
               tmp.append('+');
            tmp.append("ejb-local-ref(");
            tmp.append(ref.getEjbRefName());
            tmp.append(") -> mappedName=");
            tmp.append(ref.getMappedName());
            tmp.append("| resolvedJndiName=");
            tmp.append(ref.getResolvedJndiName());
            tmp.append('\n');
         }
      }
      EJBReferencesMetaData ejbRefs = env.getEjbReferences();
      if(ejbRefs != null)
      {
         for(EJBReferenceMetaData ref : ejbRefs)
         {
            for(int n = 0; n < depth+1; n ++)
               tmp.append('+');
            tmp.append("ejb-ref(");
            tmp.append(ref.getEjbRefName());
            tmp.append(") -> mappedName=");
            tmp.append(ref.getMappedName());
            tmp.append("| resolvedJndiName=");
            tmp.append(ref.getResolvedJndiName());
            tmp.append('\n');
         }
      }
      MessageDestinationReferencesMetaData msgRefs = env.getMessageDestinationReferences();
   }
   private void displayEndpoints(String unitName, Map<String, ContainerDependencyMetaData> endpointMap)
   {
      StringBuilder tmp = new StringBuilder(unitName+" endpoint mappings:\n");
      for(Map.Entry<String, ContainerDependencyMetaData> entry : endpointMap.entrySet())
      {
         tmp.append("  + "+entry.getKey()+" -> "+entry.getValue());
         tmp.append('\n');
      }
      log.debug(tmp.toString());
   }
}
