/*
* JBoss, Home of Professional Open Source
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.ejb3;

import java.io.IOException;
import java.lang.reflect.Member;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import javax.annotation.EJBs;
import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.annotation.security.RolesReferenced;
import javax.ejb.Init;
import javax.ejb.Local;
import javax.ejb.MessageDriven;
import javax.ejb.Remote;
import javax.ejb.Remove;
import javax.ejb.Stateful;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.ejb.TransactionManagement;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.LinkRef;

import org.jboss.annotation.ejb.CallByValue;
import org.jboss.annotation.ejb.CallByValueImpl;
import org.jboss.annotation.ejb.Clustered;
import org.jboss.annotation.ejb.ClusteredImpl;
import org.jboss.annotation.ejb.LocalBinding;
import org.jboss.annotation.ejb.LocalBindingImpl;
import org.jboss.annotation.ejb.LocalHome;
import org.jboss.annotation.ejb.LocalHomeImpl;
import org.jboss.annotation.ejb.RemoteBinding;
import org.jboss.annotation.ejb.RemoteBindingImpl;
import org.jboss.annotation.ejb.RemoteHome;
import org.jboss.annotation.ejb.RemoteHomeImpl;
import org.jboss.annotation.security.RunAsPrincipalImpl;
import org.jboss.annotation.security.SecurityDomainImpl;
import org.jboss.aop.AspectManager;
import org.jboss.aop.advice.AdviceStack;
import org.jboss.aop.advice.AspectFactory;
import org.jboss.aop.advice.AspectDefinition;
import org.jboss.aop.advice.GenericAspectFactory;
import org.jboss.aop.advice.Scope;
import org.jboss.aop.advice.ScopedInterceptorFactory;
import org.jboss.aop.annotation.AnnotationRepository;
import org.jboss.ejb.ActivationConfigPropertyImpl;
import org.jboss.ejb.CallbackListenerImpl;
import org.jboss.ejb.DenyAllImpl;
import org.jboss.ejb.EJBImpl;
import org.jboss.ejb.EJBsImpl;
import org.jboss.ejb.InitImpl;
import org.jboss.ejb.InterceptorsImpl;
import org.jboss.ejb.LocalImpl;
import org.jboss.ejb.MessageDrivenImpl;
import org.jboss.ejb.PermitAllImpl;
import org.jboss.ejb.PostActivateImpl;
import org.jboss.ejb.PostConstructImpl;
import org.jboss.ejb.PreDestroyImpl;
import org.jboss.ejb.PrePassivateImpl;
import org.jboss.ejb.RemoteImpl;
import org.jboss.ejb.RemoveImpl;
import org.jboss.ejb.ResourceImpl;
import org.jboss.ejb.RolesAllowedImpl;
import org.jboss.ejb.RolesReferencedImpl;
import org.jboss.ejb.RunAsImpl;
import org.jboss.ejb.StatefulImpl;
import org.jboss.ejb.StatelessImpl;
import org.jboss.ejb.TransactionAttributeImpl;
import org.jboss.ejb.TransactionManagementImpl;
import org.jboss.ejb3.dd.AssemblyDescriptor;
import org.jboss.ejb3.dd.Callback;
import org.jboss.ejb3.dd.ClientBeanInterceptors;
import org.jboss.ejb3.dd.ClusterConfig;
import org.jboss.ejb3.dd.ContainerTransaction;
import org.jboss.ejb3.dd.EjbJarDD;
import org.jboss.ejb3.dd.EjbJarDDObjectFactory;
import org.jboss.ejb3.dd.EjbLocalRef;
import org.jboss.ejb3.dd.EjbRef;
import org.jboss.ejb3.dd.EnterpriseBean;
import org.jboss.ejb3.dd.EnterpriseBeans;
import org.jboss.ejb3.dd.EnvEntry;
import org.jboss.ejb3.dd.ExcludeList;
import org.jboss.ejb3.dd.InitList;
import org.jboss.ejb3.dd.Invoker;
import org.jboss.ejb3.dd.InvokerProxyBinding;
import org.jboss.ejb3.dd.JBossDDObjectFactory;
import org.jboss.ejb3.dd.LoaderRepository;
import org.jboss.ejb3.dd.MessageDestinationRef;
import org.jboss.ejb3.dd.MessageDrivenBean;
import org.jboss.ejb3.dd.MessageDrivenDestination;
import org.jboss.ejb3.dd.Method;
import org.jboss.ejb3.dd.MethodPermission;
import org.jboss.ejb3.dd.RemoveList;
import org.jboss.ejb3.dd.ResourceRef;
import org.jboss.ejb3.dd.SecurityIdentity;
import org.jboss.ejb3.dd.SecurityRole;
import org.jboss.ejb3.dd.SessionEnterpriseBean;
import org.jboss.ejb3.mdb.MDB;
import org.jboss.ejb3.stateful.StatefulContainer;
import org.jboss.ejb3.stateless.StatelessContainer;
import org.jnp.interfaces.NamingContext;
import org.jboss.logging.Logger;
import org.jboss.metadata.EnvEntryMetaData;
import org.jboss.naming.NonSerializableFactory;
import org.jboss.naming.Util;
import org.jboss.xb.binding.JBossXBException;
import org.jboss.xb.binding.ObjectModelFactory;
import org.jboss.xb.binding.Unmarshaller;
import org.jboss.xb.binding.UnmarshallerFactory;
import javassist.bytecode.ClassFile;

public class Ejb3DescriptorHandler extends Ejb3AnnotationHandler
{
   private static final Logger log = Logger
           .getLogger(Ejb3DescriptorHandler.class);

   protected EjbJarDD dd;

   protected List<EnterpriseBean> ejbs = new ArrayList<EnterpriseBean>();

   protected List<Collection> dependsList = new ArrayList<Collection>();
   

   public Ejb3DescriptorHandler(Ejb3Deployment.DeploymentUnit di, ClassFile cf, EjbJarDD dd, String defaultSLSB, String defaultSFSB, String defaultMDB)
   {
      super(di, cf, defaultSLSB, defaultSFSB,  defaultMDB);
      this.dd = dd;
   }

   public List<Collection> getAllDepends()
   {
      return dependsList;
   }

   public boolean isEjb()
   {

      if (super.isEjb()) return true;
      EnterpriseBeans enterpriseBeans = dd.getEnterpriseBeans();
      return enterpriseBeans.findEjbsByClass(cf
                                             .getName()).size() > 0;

   }

   private Collection getAllDepends(EnterpriseBean enterpriseBean)
   {
      ArrayList depends = new ArrayList();
      return getAllDepends(depends, enterpriseBean);
   }

   private Collection getAllDepends(Collection depends,
                                    EnterpriseBean enterpriseBean)
   {
      if (enterpriseBean != null)
      {
         Iterator ejbRefs = enterpriseBean.getEjbRefs().iterator();
         while (ejbRefs.hasNext())
         {
            String dependencyName = null;
            try
            {
               EjbRef ejbRef = (EjbRef) ejbRefs.next();

               String dependsRefName = ejbRef.getEjbLink();
               if (dependsRefName == null)
                  dependsRefName = ejbRef.getEjbRefName();

               dependencyName = "jboss.j2ee:service=EJB3,name="
                                + dependsRefName;
               ObjectName dependency = new ObjectName(dependencyName);
               depends.add(dependency);
            }
            catch (MalformedObjectNameException e)
            {
               throw new RuntimeException("'" + dependencyName
                                          + "' is not a valid object name");
            }
         }

         ejbRefs = enterpriseBean.getEjbLocalRefs().iterator();
         while (ejbRefs.hasNext())
         {
            String dependencyName = null;
            try
            {
               EjbLocalRef ejbRef = (EjbLocalRef) ejbRefs.next();
               String dependsRefName = ejbRef.getEjbLink();
               if (dependsRefName == null)
                  dependsRefName = ejbRef.getEjbRefName();

               dependencyName = "jboss.j2ee:service=EJB3,name="
                                + dependsRefName;
               ObjectName dependency = new ObjectName(dependencyName);
               depends.add(dependency);
            }
            catch (MalformedObjectNameException e)
            {
               throw new RuntimeException("'" + dependencyName
                                          + "' is not a valid object name");
            }
         }
      }

      return depends;
   }

   protected void populateBaseInfo() throws Exception
   {
      super.populateBaseInfo();

      EnterpriseBeans enterpriseBeans = dd.getEnterpriseBeans();

      List<EnterpriseBean> ejbsByClass = enterpriseBeans.findEjbsByClass(cf
                                                                         .getName());

      for (int i = 0; i < ejbNames.size(); ++i)
      {
         String ejbNameFromAnnotation = ejbNames.get(i);
         EnterpriseBean enterpriseBean = enterpriseBeans
                 .findEjbByEjbName(ejbNameFromAnnotation);
         ejbs.add(enterpriseBean);

         dependsList.add(getAllDepends(super.getAllDepends().get(i),
                                       enterpriseBean));

         boolean removed = false;
         int j = 0;
         while (!removed && j < ejbsByClass.size())
         {
            EnterpriseBean ejbByClass = ejbsByClass.get(j);
            if (ejbByClass.getEjbName().equals(ejbNameFromAnnotation))
               ejbsByClass.remove(j);
            else
               ++j;
         }
      }

      for (EnterpriseBean enterpriseBean : ejbsByClass)
      {
         String ejbName = enterpriseBean.getEjbName();

         ejbs.add(enterpriseBean);
         ejbNames.add(ejbName);

         if (enterpriseBean.isSessionBean())
         {
            if (((SessionEnterpriseBean) enterpriseBean).isStateless())
               ejbType = EJB_TYPE.STATELESS;
            else
               ejbType = EJB_TYPE.STATEFUL;
         }
         else if (enterpriseBean.isEntityBean())
            ejbType = EJB_TYPE.ENTITY;
         else if (enterpriseBean.isMessageDrivenBean())
            ejbType = EJB_TYPE.MESSAGE_DRIVEN;

         dependsList.add(getAllDepends(enterpriseBean));
      }
   }

   protected StatefulContainer getStatefulContainer(int ejbIndex)
           throws Exception
   {
      String ejbName = ejbNames.get(ejbIndex);

      EnterpriseBean enterpriseBean = ejbs.get(ejbIndex);

      StatefulContainer container = super.getStatefulContainer(ejbIndex);

      StatefulImpl annotation = new StatefulImpl(ejbName);
      if (enterpriseBean != null)
      {
         addClassAnnotation(container, Stateful.class, annotation);
      }

      addInterfaces(container, enterpriseBean);

      addDescriptorAnnotations(container, enterpriseBean, ejbName);

      return container;
   }

   private void addHomeAnnotations(EJBContainer container, EnterpriseBean enterpriseBean) throws Exception
   {
      if (enterpriseBean.getHome() != null)
      {
         RemoteHome annotation = new RemoteHomeImpl(di.getClassLoader().loadClass(enterpriseBean.getHome()));
         addClassAnnotation(container, annotation.annotationType(), annotation);
      }

      if (enterpriseBean.getLocalHome() != null)
      {
         LocalHome annotation = new LocalHomeImpl(di.getClassLoader().loadClass(enterpriseBean.getLocalHome()));
         addClassAnnotation(container, annotation.annotationType(), annotation);
      }
   }

   protected StatelessContainer getStatelessContainer(int ejbIndex)
           throws Exception
   {
      String ejbName = ejbNames.get(ejbIndex);

      EnterpriseBean enterpriseBean = ejbs.get(ejbIndex);

      StatelessContainer container = super.getStatelessContainer(ejbIndex);

      StatelessImpl annotation = new StatelessImpl(ejbName);
      if (enterpriseBean != null)
      {
         addClassAnnotation(container, Stateless.class, annotation);
      }

      addInterfaces(container, enterpriseBean);

      addDescriptorAnnotations(container, enterpriseBean, ejbName);

      return container;
   }

   protected MDB getMDB(int ejbIndex) throws Exception
   {
      String ejbName = ejbNames.get(ejbIndex);

      EnterpriseBean enterpriseBean = ejbs.get(ejbIndex);

      MDB container = super.getMDB(ejbIndex);

      if (enterpriseBean != null)
      {
         MessageDrivenBean mdb = (MessageDrivenBean) enterpriseBean;
         ArrayList properties = new ArrayList();
         if (mdb.getAcknowledgeMode() != null)
            properties.add(new ActivationConfigPropertyImpl("acknowledgeMode",
                                                            mdb.getAcknowledgeMode()));
         if (mdb.getMessageDrivenDestination() != null)
         {
            MessageDrivenDestination destination = mdb
                    .getMessageDrivenDestination();
            if (destination.getDestinationType() != null)
               properties.add(new ActivationConfigPropertyImpl("destinationType", destination.getDestinationType()));
            if (destination.getSubscriptionDurability() != null)
            {
               properties.add(new ActivationConfigPropertyImpl("durability",
                                                               destination.getSubscriptionDurability()));
               if (destination.getSubscriptionDurability().equals("Durable"))
                  properties.add(new ActivationConfigPropertyImpl("subscriptionName", "subscriptionName"));

            }
         }

         ActivationConfigPropertyImpl[] propsArray = new ActivationConfigPropertyImpl[properties
                 .size()];
         properties.toArray(propsArray);
         MessageDrivenImpl annotation = new MessageDrivenImpl(ejbName, propsArray);

         addClassAnnotation(container, MessageDriven.class, annotation);
      }

      addInterfaces(container, enterpriseBean);

      addDescriptorAnnotations(container, enterpriseBean, ejbName);

      return container;
   }

   private void addInterfaces(EJBContainer container, EnterpriseBean enterpriseBean)
           throws ClassNotFoundException
   {
      // TODO multiple remote interfaces
      if (enterpriseBean != null)
      {
         String local = enterpriseBean.getLocal();
         String remote = enterpriseBean.getRemote();

         if (remote != null)
         {
            StringTokenizer classes = new StringTokenizer(remote, ",");
            ArrayList<Class> remoteClasses = new ArrayList<Class>();
            while (classes.hasMoreTokens())
            {
               String token = classes.nextToken();
               String classname = token.trim();
               remoteClasses.add(di.getClassLoader().loadClass(classname));

            }
            Class[] intfs = new Class[remoteClasses.size()];
            intfs = remoteClasses.toArray(intfs);
            addClassAnnotation(container,
                               Remote.class, new RemoteImpl(intfs));
         }

         if (local != null)
         {
            StringTokenizer classes = new StringTokenizer(local, ",");
            ArrayList<Class> localClasses = new ArrayList<Class>();
            while (classes.hasMoreTokens())
            {
               String token = classes.nextToken();
               String classname = token.trim();
               localClasses.add(di.getClassLoader().loadClass(classname));

            }
            Class[] intfs = new Class[localClasses.size()];
            intfs = localClasses.toArray(intfs);
            addClassAnnotation(container, Local.class,
                               new LocalImpl(intfs));
         }
      }
   }

   public static EjbJarDD parseDescriptors(Ejb3Deployment.DeploymentUnit di) throws JBossXBException, IOException
   {
      URL ddResource = di.getEjbJarXml();
      log.debug("found ejb-jar.xml " + ddResource);

      if (ddResource == null)
         return null;

      ObjectModelFactory factory = new EjbJarDDObjectFactory();
      Unmarshaller unmarshaller = UnmarshallerFactory.newInstance()
              .newUnmarshaller();
      EjbJarDD dd = (EjbJarDD) unmarshaller.unmarshal(ddResource.openStream(), factory,
                                                      null);
      factory = new JBossDDObjectFactory(dd);
      ddResource = di.getJbossXml();
      if (ddResource != null)
      {
         unmarshaller = UnmarshallerFactory.newInstance().newUnmarshaller();
         dd = (EjbJarDD) unmarshaller.unmarshal(ddResource.openStream(),
                                                factory, null);
      }

      return dd;
   }

   private void addDescriptorAnnotations(EJBContainer container,
                                         EnterpriseBean enterpriseBean, String ejbName) throws Exception
   {
      addLoaderAnnotations(container);

      addAssemblyAnnotations(container, enterpriseBean, ejbName);

      addEjbAnnotations(container, enterpriseBean);

      addEjb21Annotations(container);
      
      addProxyBindingAnnotations(container, enterpriseBean);
   }

   private void addAopAnnotations(EJBContainer container, EnterpriseBean enterpriseBean)
           throws Exception
   {
      String aopDomainName = enterpriseBean.getAopDomainName();
      if (aopDomainName != null)
         log.info("Found aop-domain-name element for annotation " + aopDomainName);
   }

   private void addLoaderAnnotations(EJBContainer container)
           throws Exception
   {
      LoaderRepository loader = dd.getLoaderRepository();
      if (loader != null)
         log.info("Found load-repository element for annotation " + loader);
   }

   private void addEjb21Annotations(EJBContainer container)
           throws Exception
   {
      Class[] interfaces = ejbClass.getInterfaces();
      for (Class beanInterface : interfaces)
      {
         if (beanInterface.equals(javax.ejb.SessionBean.class))
         {
            Method method = new Method();
            method.setEjbName(container.getEjbName());

            Object annotation = new PostConstructImpl();
            Class annotationClass = javax.ejb.PostConstruct.class;
            method.setMethodName("ejbCreate");
            addAnnotations(annotationClass, annotation, container, method);

            annotation = new PostActivateImpl();
            annotationClass = javax.ejb.PostActivate.class;
            method.setMethodName("ejbActivate");
            addAnnotations(annotationClass, annotation, container, method);

            annotation = new PrePassivateImpl();
            annotationClass = javax.ejb.PrePassivate.class;
            method.setMethodName("ejbPassivate");
            addAnnotations(annotationClass, annotation, container, method);

            annotation = new PreDestroyImpl();
            annotationClass = javax.ejb.PreDestroy.class;
            method.setMethodName("ejbRemove");
            addAnnotations(annotationClass, annotation, container, method);
         }
      }
   }


   private void addAssemblyAnnotations(EJBContainer container, EnterpriseBean enterpriseBean, String ejbName)
           throws Exception
   {
      AssemblyDescriptor assembly = dd.getAssemblyDescriptor();
      if (assembly != null)
      {
         addTransactionAnnotations(container, enterpriseBean, assembly
                                                              .getContainerTransactions(), ejbName);

         addSecurityAnnotations(container, assembly.getMethodPermissions(),
                                assembly.getSecurityRoles(), enterpriseBean, ejbName);

         addExcludeAnnotations(container, assembly.getExcludeList(), ejbName);

         addInitAnnotations(container, assembly.getInitList(), ejbName);

         addRemoveAnnotations(container, assembly.getRemoveList(), ejbName);

         addCallbackAnnotations(container, assembly.getCallbacks(), ejbName);
      }
   }

   private void addExcludeAnnotations(EJBContainer container, ExcludeList list,
                                      String ejbName) throws ClassNotFoundException, NoSuchMethodException,
                                                             NoSuchFieldException
   {
      if (list != null)
      {
         Iterator methods = list.getMethods().iterator();
         while (methods.hasNext())
         {
            Method method = (Method) methods.next();
            if (method.getEjbName().equals(ejbName))
            {
               DenyAllImpl annotation = new DenyAllImpl();
               addAnnotations(DenyAll.class, annotation, container, method);
            }
         }
      }
   }

   private void addInitAnnotations(EJBContainer container, InitList list,
                                   String ejbName) throws ClassNotFoundException, NoSuchMethodException,
                                                          NoSuchFieldException
   {
      if (list != null)
      {
         Iterator methods = list.getMethods().iterator();
         while (methods.hasNext())
         {
            Method method = (Method) methods.next();
            if (method.getEjbName().equals(ejbName))
            {
               InitImpl annotation = new InitImpl();
               addAnnotations(Init.class, annotation, container, method);
            }
         }
      }
   }

   private void addRemoveAnnotations(EJBContainer container, RemoveList list,
                                     String ejbName) throws ClassNotFoundException, NoSuchMethodException,
                                                            NoSuchFieldException
   {
      if (list != null)
      {
         Iterator methods = list.getMethods().iterator();
         while (methods.hasNext())
         {
            Method method = (Method) methods.next();
            if (method.getEjbName().equals(ejbName))
            {
               RemoveImpl annotation = new RemoveImpl();
               addAnnotations(Remove.class, annotation, container, method);
            }
         }
      }
   }

   private void addSecurityAnnotations(EJBContainer container,
                                       List methodPermissions, List securityRoles, EnterpriseBean enterpriseBean, String ejbName) throws ClassNotFoundException,
                                                                                                                                         NoSuchMethodException, NoSuchFieldException
   {
      if (securityRoles.size() > 0)
      {
         ArrayList roleList = new ArrayList();
         Iterator roles = securityRoles.iterator();
         while (roles.hasNext())
         {
            SecurityRole role = (SecurityRole) roles.next();
            roleList.add(role.getRoleName());

         }
         RolesReferencedImpl annotation = new RolesReferencedImpl((String[]) roleList.toArray(new String[roleList.size()]));
         addClassAnnotation(container, RolesReferenced.class, annotation);
      }

      Iterator permissions = methodPermissions.iterator();
      while (permissions.hasNext())
      {
         MethodPermission permission = (MethodPermission) permissions.next();
         for (Method method : permission.getMethods())
         {
            if (method.getEjbName().equals(ejbName))
            {
               if (permission.isUnchecked())
               {
                  PermitAllImpl annotation = new PermitAllImpl();
                  addAnnotations(PermitAll.class, annotation, container,
                                 method);
               }
               else
               {
                  RolesAllowedImpl annotation = new RolesAllowedImpl();

                  Iterator roleNames = permission.getRoleNames().iterator();
                  while (roleNames.hasNext())
                  {
                     String roleName = (String) roleNames.next();
                     annotation.addValue(roleName);
                  }
                  addAnnotations(RolesAllowed.class, annotation, container,
                                 method);
               }
            }
         }
      }

      if (enterpriseBean != null && enterpriseBean.getSecurityDomain() != null)
      {
         String securityDomain = enterpriseBean.getSecurityDomain();

         SecurityDomainImpl annotation = new SecurityDomainImpl(securityDomain);
         addClassAnnotation(container,
                            annotation.annotationType(), annotation);
      }
   }

   private void addCallbackAnnotations(EJBContainer container,
                                       List callbackList, String ejbName) throws ClassNotFoundException,
                                                                                 NoSuchMethodException, NoSuchFieldException
   {
      Iterator callbacks = callbackList.iterator();
      while (callbacks.hasNext())
      {
         Callback callback = (Callback) callbacks.next();
         Iterator methods = callback.getMethods().iterator();
         while (methods.hasNext())
         {
            Method method = (Method) methods.next();
            if (method.getEjbName().equals(ejbName))
            {
               Class annotationClass = null;
               Object annotation = null;

               if (callback.getAnnotation().equals("PostActivate"))
               {
                  annotation = new PostActivateImpl();
                  annotationClass = javax.ejb.PostActivate.class;
               }
               else if (callback.getAnnotation().equals("PostConstruct"))
               {
                  annotation = new PostConstructImpl();
                  annotationClass = javax.ejb.PostConstruct.class;
               }
               else if (callback.getAnnotation().equals("PreDestroy"))
               {
                  annotation = new PreDestroyImpl();
                  annotationClass = javax.ejb.PreDestroy.class;
               }
               else if (callback.getAnnotation().equals("PrePassivate"))
               {
                  annotation = new PrePassivateImpl();
                  annotationClass = javax.ejb.PrePassivate.class;
               }

               if (annotation != null)
                  addAnnotations(annotationClass, annotation, container, method);
            }
         }
      }
   }

   private void addTransactionAnnotations(EJBContainer container, EnterpriseBean enterpriseBean,
                                          List containerTransactions, String ejbName)
           throws ClassNotFoundException, NoSuchMethodException,
                  NoSuchFieldException
   {
      if (enterpriseBean != null && enterpriseBean.getTransactionManagementType() != null)
      {
         TransactionManagementImpl annotation = new TransactionManagementImpl();
         annotation.setValue(enterpriseBean.getTransactionManagementType());
         addClassAnnotation(container, TransactionManagement.class, annotation);
      }

      Iterator transactions = containerTransactions.iterator();
      while (transactions.hasNext())
      {
         ContainerTransaction transaction = (ContainerTransaction) transactions
                 .next();
         if (transaction.getMethod().getEjbName().equals(ejbName))
         {
            String transAttribute = transaction.getTransAttribute();
            TransactionAttributeImpl annotation = new TransactionAttributeImpl();
            if (transAttribute.equals("Mandatory"))
               annotation.setType(TransactionAttributeType.MANDATORY);
            else if (transAttribute.equals("Required"))
               annotation.setType(TransactionAttributeType.REQUIRED);
            else if (transAttribute.equals("RequiresNew"))
               annotation.setType(TransactionAttributeType.REQUIRES_NEW);
            else if (transAttribute.equals("Supports"))
               annotation.setType(TransactionAttributeType.SUPPORTS);
            else if (transAttribute.equals("NotSupported"))
               annotation.setType(TransactionAttributeType.NOT_SUPPORTED);
            else if (transAttribute.equals("Never"))
               annotation.setType(TransactionAttributeType.NEVER);

            addAnnotations(TransactionAttribute.class, annotation, container,
                           transaction.getMethod());
         }
      }
   }
   
   private void addProxyBindingAnnotations(EJBContainer container, EnterpriseBean enterpriseBean) throws Exception
   {
      if (enterpriseBean != null)
      {
         Iterator invokers = enterpriseBean.getInvokers().iterator();
         while (invokers.hasNext())
         {
            Invoker invoker = (Invoker)invokers.next();
            String bindingName = invoker.getInvokerProxyBindingName();
            InvokerProxyBinding proxyBinding = dd.findInvokerProxyBinding(bindingName);
            if (proxyBinding != null)
            {
               RemoteBindingImpl annotation = (RemoteBindingImpl)container.getAnnotations().resolveClassAnnotation(RemoteBinding.class);
               if (annotation != null)
               {
                  String proxyFactory = proxyBinding.getProxyFactory();
                  if (proxyFactory != null)
                  {
                     Class factoryClass = container.classloader.loadClass(proxyFactory);
                     annotation.setFactory(factoryClass);
                  }
                  
                  ClientBeanInterceptors beanInterceptors = proxyBinding.getClientBeanInterceptors();
                  if (beanInterceptors != null && beanInterceptors.getInterceptors().size() > 0)
                  {
                     ArrayList factories = new ArrayList();
                     Iterator interceptors = beanInterceptors.getInterceptors().iterator();
                     while (interceptors.hasNext())
                     {
                        String interceptor = (String) interceptors.next();
                        AspectDefinition def = new AspectDefinition();
                        def.setName(interceptor);
                        def.setScope(Scope.PER_CLASS);
                        def.setFactory(new GenericAspectFactory(interceptor, null));
                        ScopedInterceptorFactory factory = new ScopedInterceptorFactory(def);
                        factories.add(factory);
                     }
                     AdviceStack stack = new AdviceStack(bindingName, factories);
                     AspectManager.instance().addAdviceStack(stack);
                     
                     annotation.setStack(bindingName);
                  }
               }
            }
         }
      }
   }

   private void addEjbAnnotations(EJBContainer container,
                                  EnterpriseBean enterpriseBean) throws Exception
   {
      if (enterpriseBean != null)
      {
         addHomeAnnotations(container, enterpriseBean);

         addAopAnnotations(container, enterpriseBean);

         addClusterAnnotations(container, enterpriseBean);

         addJndiAnnotations(container, enterpriseBean);

         addProxyAnnotations(container, enterpriseBean);

         addInterceptorAnnotations(container, enterpriseBean.getInterceptors());

         addEJBAnnotations(container, enterpriseBean.getEjbRefs());

         addEJBLocalAnnotations(container, enterpriseBean.getEjbLocalRefs());

         addSimpleResourceAnnotations(container, enterpriseBean.getEnvEntries());

         addResourceAnnotations(container, enterpriseBean.getResourceRefs());

         addMessageDestinationAnnotations(container, enterpriseBean
                                                     .getMessageDestinationRefs());

         addCallbackListenerAnnotations(container, enterpriseBean
                                                   .getCallbackListeners());

         addSecurityIdentityAnnotation(container, enterpriseBean
                                                  .getSecurityIdentity());
      }
   }

   private void addClusterAnnotations(EJBContainer container, EnterpriseBean enterpriseBean)
           throws Exception
   {
      if (enterpriseBean.isClustered())
      {
         ClusterConfig config = enterpriseBean.getClusterConfig();
         ClusteredImpl annotation = new ClusteredImpl();
         if (config.getLoadBalancePolicy() != null)
            annotation.setLoadBalancePolicy(di.getClassLoader().loadClass(config.getLoadBalancePolicy()));
         if (config.getPartition() != null)
            annotation.setPartition(config.getPartition());

         addClassAnnotation(container, Clustered.class, annotation);
      }
   }

   private void addProxyAnnotations(EJBContainer container, EnterpriseBean enterpriseBean)
   {
      if (enterpriseBean.isCallByValue())
      {
         CallByValueImpl annotation = new CallByValueImpl();
         addClassAnnotation(container, CallByValue.class, annotation);
      }
   }

   private void addJndiAnnotations(EJBContainer container, EnterpriseBean enterpriseBean)
   {
      String jndiName = enterpriseBean.getJndiName();

      if (jndiName != null)
      {
         RemoteBindingImpl annotation = new RemoteBindingImpl();
         annotation.setJndiBinding(jndiName);
         addClassAnnotation(container, RemoteBinding.class, annotation);
      }

      String localJndiName = enterpriseBean.getLocalJndiName();
      if (localJndiName != null)
      {
         LocalBindingImpl annotation = new LocalBindingImpl(localJndiName);
         addClassAnnotation(container, LocalBinding.class, annotation);
      }
   }

   private void addSimpleResourceAnnotations(EJBContainer container,
                                             List envEntryList) throws ClassNotFoundException,
                                                                       NoSuchMethodException, NoSuchFieldException, javax.naming.NamingException
   {
      Iterator refs = envEntryList.iterator();
      while (refs.hasNext())
      {
         EnvEntry entry = (EnvEntry) refs.next();
         String injectionTarget = entry.getInjectionTarget();
         if (injectionTarget != null)
         {
            ResourceImpl annotation = new ResourceImpl();
            annotation.setName(entry.getEnvEntryName());
            Method method = new Method();
            method.setMethodName(injectionTarget);
            addAnnotations(javax.annotation.Resource.class, annotation,
                           container, method);
         }
         
         java.lang.reflect.Field field = ejbClass.getDeclaredField(entry.getEnvEntryName());
         if (container.resolveAnnotation(field, javax.annotation.Resource.class) != null)
         {
            container.addEnvEntry(entry.getEnvEntryName(), entry.getEnvEntryType(), entry.getEnvEntryValue());
         }
      }
   }

   private void addMessageDestinationAnnotations(EJBContainer container,
                                                 List destinationRefList) throws ClassNotFoundException,
                                                                                 NoSuchMethodException, NoSuchFieldException
   {
      Iterator refs = destinationRefList.iterator();
      while (refs.hasNext())
      {
         MessageDestinationRef ref = (MessageDestinationRef) refs.next();
         String injectionTarget = ref.getInjectionTarget();
         if (injectionTarget != null)
         {
            ResourceImpl annotation = new ResourceImpl();
            annotation.setName(ref.getMessageDestinationRefName());
            Method method = new Method();
            method.setMethodName(injectionTarget);
            addAnnotations(javax.annotation.Resource.class, annotation,
                           container, method);
         }
      }
   }

   private void addResourceAnnotations(EJBContainer container,
                                       List resourceRefList) throws ClassNotFoundException,
                                                                    NoSuchMethodException, NoSuchFieldException
   {
      Iterator refs = resourceRefList.iterator();
      while (refs.hasNext())
      {
         ResourceRef ref = (ResourceRef) refs.next();

         ResourceImpl annotation = new ResourceImpl();
         annotation.setName(ref.getResRefName());
         annotation.setShareable(ref.isShareable());
         annotation.setAuthenticationType(ref.getAuthorizationType());
         String typeName = ref.getResType();
         if (typeName != null && !typeName.trim().equals(""))
         {
            Class type = di.getClassLoader().loadClass(typeName);
            annotation.setType(type);
         }

         String injectionTarget = ref.getInjectionTarget();
         if (injectionTarget != null)
         {
            Method method = new Method();
            method.setMethodName(injectionTarget);

            addAnnotations(javax.annotation.Resource.class, annotation, container,
                           method);
         }
         else
         {
            addClassAnnotation(container, javax.annotation.Resource.class, annotation);
         }
      }
   }

   private void addEJBAnnotations(EJBContainer container, List ejbRefs)
           throws ClassNotFoundException, NoSuchMethodException,
                  NoSuchFieldException
   {
      Iterator refs = ejbRefs.iterator();

      if (refs.hasNext())
      {
         EJBsImpl ejbsAnnotation = new EJBsImpl();

         while (refs.hasNext())
         {
            EjbRef ref = (EjbRef) refs.next();

            EJBImpl annotation = new EJBImpl();
            annotation.setName(ref.getEjbRefName());
            annotation.setBusinessInterface(di.getClassLoader().loadClass(ref.getRemote()));
            String link = (ref.getEjbLink() == null || ref.getEjbLink().equals("")) ? "" : ref.getEjbLink();
            annotation.setBeanName(link);

            String injectionTarget = ref.getInjectionTarget();
            if (injectionTarget != null)
            {
               Method method = new Method();
               method.setMethodName(injectionTarget);
               addAnnotations(javax.annotation.EJB.class, annotation, container,
                              method);
            }
            else
            {
               ejbsAnnotation.addEjb(annotation);
            }
         }

         if (ejbsAnnotation.value().length > 0)
            addClassAnnotation(container, EJBs.class, ejbsAnnotation);
      }
   }

   private void addEJBLocalAnnotations(EJBContainer container, List ejbRefs)
           throws ClassNotFoundException, NoSuchMethodException,
                  NoSuchFieldException
   {
      Iterator refs = ejbRefs.iterator();
      while (refs.hasNext())
      {
         EjbLocalRef ref = (EjbLocalRef) refs.next();
         String injectionTarget = ref.getInjectionTarget();
         if (injectionTarget != null)
         {
            EJBImpl annotation = new EJBImpl();
            annotation.setBeanName(ref.getEjbLink());
            annotation.setName(ref.getEjbRefName());
            Class interfaceClass = di.getClassLoader().loadClass(ref.getLocal());
            annotation.setBusinessInterface(interfaceClass);
            Method method = new Method();
            method.setMethodName(injectionTarget);
            addAnnotations(javax.annotation.EJB.class, annotation, container,
                           method);
         }
      }
   }

   private void addCallbackListenerAnnotations(EJBContainer container,
                                               List callbackList)
   {
      Iterator callbackNames = callbackList.iterator();
      while (callbackNames.hasNext())
      {
         try
         {
            String callbackName = (String) callbackNames.next();
            Class clazz = di.getClassLoader().loadClass(callbackName);
            CallbackListenerImpl annotation = new CallbackListenerImpl(clazz);
            addClassAnnotation(container,
                               annotation.annotationType(), annotation);
         }
         catch (ClassNotFoundException e)
         {
            throw new RuntimeException(e);
         }
      }
   }

   private void addInterceptorAnnotations(EJBContainer container,
                                          List interceptorList)
   {
      if (interceptorList.size() > 0)
      {
         InterceptorsImpl annotation = new InterceptorsImpl();
         Iterator interceptors = interceptorList.iterator();
         while (interceptors.hasNext())
         {
            String interceptor = (String) interceptors.next();
            try
            {
               Class clazz = di.getClassLoader().loadClass(interceptor);
               annotation.addValue(clazz);
            }
            catch (ClassNotFoundException e)
            {
               throw new RuntimeException(e);
            }
         }

         addClassAnnotation(container,
                            annotation.annotationType(), annotation);
      }
   }

   private void addSecurityIdentityAnnotation(EJBContainer container,
                                              SecurityIdentity identity)
   {
      if (identity != null)
      {
         String runAs = identity.getRunAs();
         if (runAs != null)
         {
            RunAsImpl annotation = new RunAsImpl(runAs);
            addClassAnnotation(container,
                               annotation.annotationType(), annotation);

            String runAsPrincipal = identity.getRunAsPrincipal();
            if (runAsPrincipal != null)
            {
               RunAsPrincipalImpl principalAnnotation = new RunAsPrincipalImpl(runAs);
               addClassAnnotation(container,
                                  principalAnnotation.annotationType(), principalAnnotation);
            }
         }
      }
   }

   protected void overrideAnnotations(EJBContainer container, Member m, String annotation, Object value)
   {
      AnnotationRepository annotations = container.getAnnotations();

      if (value instanceof javax.annotation.security.DenyAll)
      {
         annotations.disableAnnotation(m, javax.annotation.security.PermitAll.class.getName());
         annotations.disableAnnotation(m, javax.annotation.security.RolesAllowed.class.getName());
      }
      else if (value instanceof javax.annotation.security.PermitAll)
      {
         annotations.disableAnnotation(m, javax.annotation.security.DenyAll.class.getName());
         annotations.disableAnnotation(m, javax.annotation.security.RolesAllowed.class.getName());
      }
      else if (value instanceof javax.annotation.security.RolesAllowed)
      {
         annotations.disableAnnotation(m, javax.annotation.security.PermitAll.class.getName());
         annotations.disableAnnotation(m, javax.annotation.security.DenyAll.class.getName());
      }
   }

   private void addClassAnnotation(EJBContainer container, Class annotationClass, Object annotation)
   {
      log.info("adding class annotation " + annotationClass.getName() + " to "
               + ejbClass.getName() + " " + annotation);
      container.getAnnotations().addClassAnnotation(annotationClass, annotation);
   }

   private void addAnnotations(Class annotationClass, Object annotation,
                               EJBContainer container, Method method) throws ClassNotFoundException,
                                                                             NoSuchMethodException, NoSuchFieldException
   {
      String methodName = method.getMethodName();

      AnnotationRepository annotations = container.getAnnotations();
      if (methodName.equals("*"))
      {
         log.info("adding " + annotationClass.getName() + " annotation to "
                  + ejbClass.getName() + "." + methodName);

         for (java.lang.reflect.Method declaredMethod : ejbClass
                 .getDeclaredMethods())
         {
            annotations.addAnnotation(declaredMethod, annotationClass,
                                      annotation);
            overrideAnnotations(container, declaredMethod, annotationClass.getName(), annotation);
         }
      }
      else
      {
         List params = method.getMethodParams();
         if (params.size() == 0)
         {
            java.lang.reflect.Method[] methods = ejbClass.getDeclaredMethods();
            boolean foundMethod = false;
            for (int methodIndex = 0; methodIndex < methods.length; ++methodIndex)
            {
               if (methods[methodIndex].getName().equals(methodName))
               {
                  log.info("adding " + annotationClass.getName()
                           + " method annotation to " + ejbClass.getName() + "."
                           + methodName);
                  annotations.addAnnotation(methods[methodIndex],
                                            annotationClass, annotation);
                  overrideAnnotations(container, methods[methodIndex], annotationClass.getName(), annotation);
                  foundMethod = true;
               }
            }

            if (!foundMethod)
            {
               java.lang.reflect.Field member = ejbClass
                       .getDeclaredField(methodName);
               if (member != null)
               {
                  log.info("adding " + annotationClass.getName()
                           + " field annotation to " + ejbClass.getName() + "."
                           + methodName);
                  annotations
                          .addAnnotation(member, annotationClass, annotation);
                  overrideAnnotations(container, member, annotationClass.getName(), annotation);
               }
            }
         }
         else
         {
            Class[] methodSignature = new Class[params.size()];
            Iterator paramIterator = params.iterator();
            int paramIndex = 0;
            while (paramIterator.hasNext())
            {
               String param = (String) paramIterator.next();
               Class paramClass = null;
               if (param.equals("boolean"))
                  paramClass = boolean.class;
               else
                  paramClass = di.getClassLoader().loadClass(param);
               methodSignature[paramIndex++] = paramClass;
            }
            java.lang.reflect.Member member = ejbClass.getDeclaredMethod(methodName, methodSignature);
            log.info("adding " + annotationClass.getName()
                     + " method annotation to " + ejbClass.getName() + "."
                     + methodName);
            annotations.addAnnotation(member, annotationClass, annotation);
            overrideAnnotations(container, member, annotationClass.getName(), annotation);
         }
      }
   }
}
