/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-2004 Bull S.A.
 * Contact: jonas-team@ow2.org
 *
 * This library 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 any later version.
 *
 * This library 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 library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.deployment.ejb;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import org.ow2.jonas.deployment.common.DeploymentDescException;
import org.ow2.jonas.deployment.common.xml.JLinkedList;
import org.ow2.jonas.deployment.ejb.xml.AssemblyDescriptor;
import org.ow2.jonas.deployment.ejb.xml.EjbJar;
import org.ow2.jonas.deployment.ejb.xml.EjbRelation;
import org.ow2.jonas.deployment.ejb.xml.Entity;
import org.ow2.jonas.deployment.ejb.xml.JonasEjbJar;
import org.ow2.jonas.deployment.ejb.xml.JonasEjbRelation;
import org.ow2.jonas.deployment.ejb.xml.JonasEntity;
import org.ow2.jonas.deployment.ejb.xml.JonasMessageDriven;
import org.ow2.jonas.deployment.ejb.xml.MessageDriven;

import org.objectweb.util.monolog.api.Logger;
import org.objectweb.util.monolog.api.BasicLevel;

/**
 * Class to hold meta-information related to the deployment of an ejb-jar
 * This subclass is for specification EJB 2.x, i.e. CMP2 persistance and MDB.
 * It is also responsible for loading the Jorm Meta Information.
 * @author S.Chassande-Barrioz : Initial developer
 * @author Christophe Ney [cney@batisseurs.com]
 * @author Philippe Durieux (new Jorm mapping)
 */
public class DeploymentDescEjb2 extends DeploymentDesc {

    /**
     * List of relations (EjbRelationDesc) defined in this Descriptor
     */
    private ArrayList ejbRelations = new ArrayList();

    /**
     * Build the Meta-Information from the XML data binding trees
     * containing the EJB and JOnAS deployment descriptors.
     * @param classLoader The Class Loader to be used
     * @param ejbJar The EjbJar information, from standard deployment descriptor.
     * @param jonasEjbJar The JonasEjbJar information, from JOnAS specific deployment descriptor.
     * @param l The logger to be used for tracing
     * @param FileName deployment desc's jar or directory name
     * @throws DeploymentDescException Error when building the Deployment Descriptor
     */
    public DeploymentDescEjb2(ClassLoader classLoader, EjbJar ejbJar,
                              JonasEjbJar jonasEjbJar, Logger l,
                              String fileName)
        throws DeploymentDescException {

        // Session and Entity bean descriptors are built here.
        // This part is common with EJB1.1 spec (no CMP2, no MDB)
        super(classLoader, ejbJar, jonasEjbJar, l, fileName);

        // HashMap of jonas-message-driven
        HashMap jonasMessageDriven = new HashMap();
        for (Iterator i = jonasEjbJar.getJonasMessageDrivenList().iterator(); i.hasNext(); ) {
            JonasMessageDriven jMd = (JonasMessageDriven) i.next();
            jonasMessageDriven.put(jMd.getEjbName(), jMd);
        }

        // message-driven beans
        if (ejbJar.getEnterpriseBeans() != null) {
            for (Iterator i = ejbJar.getEnterpriseBeans().getMessageDrivenList().iterator(); i.hasNext(); ) {
                BeanDesc bd = null;
                MessageDriven md = (MessageDriven) i.next();
                /*
                 * The Standard DTD allows a message-driven-bean without ejb-name
                 * Problem to associate infos to this bean (trans-attribute, jonas-specific infos)
                 * So the ejb-name must be specify and we check this
                 */
                if (md.getEjbName() == null) {
                    throw new DeploymentDescException("ejb-name missing for a message driven bean");
                }
                // find corresponding jonas message-driven
                JonasMessageDriven jMd = (JonasMessageDriven) jonasMessageDriven.get(md.getEjbName());
                if (jMd == null) {
                    throw new DeploymentDescException("jonas-message-driven-bean missing for bean " + md.getEjbName());
                }

                bd = new MessageDrivenDesc(classLoader, md, asd, jMd,
                        jonasEjbJar.getJonasMessageDestinationList(), fileName);

                bd.setDeploymentDesc(this);
                bd.check();
                beanDesc.put(bd.getEjbName(), bd);
            }
        }

        // Relations on Entity beans CMP2
        if (ejbJar.getRelationships() != null) {

            // ArrayList of standard-relation names (Strings)
            ArrayList stdRelations = new ArrayList();

            // HashMap of jonas-relation
            HashMap jonasRelations = new HashMap();
            for (Iterator i = jonasEjbJar.getJonasEjbRelationList().iterator(); i.hasNext(); ) {
                JonasEjbRelation jer = (JonasEjbRelation) i.next();
                String jerName = jer.getEjbRelationName();
                //if (!stdRelations.contains(jerName)) {
                //    throw new DeploymentDescException("ejb-relation missing in ejb-jar.xml for the relation " + jerName);
                //}
                jonasRelations.put(jerName, jer);
            }

            // ejb-relation
            for (Iterator i = ejbJar.getRelationships().getEjbRelationList().iterator(); i.hasNext();) {
                EjbRelation er = (EjbRelation) i.next();

                // build the descriptors for the relation.
                EjbRelationDesc erd = new EjbRelationDesc(er, logger);
                ejbRelations.add(erd);

                // Keep a list of relation names.
                stdRelations.add(erd.getName());

                // find corresponding jonas relation (may not exist)
                JonasEjbRelation jer = (JonasEjbRelation) jonasRelations.get(erd.getName());
                erd.setJonasInfo(jer);

                // makes the links between entity beans and relationship roles
                EjbRelationshipRoleDesc rsd1 = erd.getRelationshipRole1();
                EjbRelationshipRoleDesc rsd2 = erd.getRelationshipRole2();
                EntityCmp2Desc ed1 = (EntityCmp2Desc) beanDesc.get(rsd1.getSourceBeanName());
                EntityCmp2Desc ed2 = (EntityCmp2Desc) beanDesc.get(rsd2.getSourceBeanName());
                // Check the sources beans names are correct
                if ((ed1 == null) || (ed2 == null)) {
                    throw new DeploymentDescException("Invalid ejb-name for a relation-ship-role-source for the relation '"
                                                      + erd.getName()
                                                      + "' ('"
                                                      + rsd1.getSourceBeanName()
                                                      + "' or '"
                                                      + rsd2.getSourceBeanName()
                                                      + "' invalid)");
                }
                // Check if the source beans have local interfaces
                if ((ed1.getLocalHomeClass() == null) || (ed1.getLocalClass() == null)) {
                    throw new DeploymentDescException("The entity bean '" + ed1.getEjbName()
                                                      + "' involved in the relationship '"
                                                      + erd.getName()
                                                      + "' must have local interfaces");
                }
                if ((ed2.getLocalHomeClass() == null) || (ed2.getLocalClass() == null)) {
                    throw new DeploymentDescException("The entity bean '" + ed2.getEjbName()
                                                      + "' involved in the relationship '"
                                                      + erd.getName()
                                                      + "' must have local interfaces");
                }
                ed1.addEjbRelationshipRoleDesc(rsd1);
                ed2.addEjbRelationshipRoleDesc(rsd2);
                rsd1.setTargetBean(ed2);
                rsd1.setSourceBean(ed1);
                rsd2.setTargetBean(ed1);
                rsd2.setSourceBean(ed2);

                // Fill the mapping information with the values defined in jonas DD
                erd.fillMappingInfo();

                // Fill the mapping information for the relation with default values
                // if some mapping information is missing
                erd.fillMappingInfoWithDefault();
            }

            for (Iterator i = jonasEjbJar.getJonasEjbRelationList().iterator(); i.hasNext(); ) {
                JonasEjbRelation jer = (JonasEjbRelation) i.next();
                String jerName = jer.getEjbRelationName();
                if (!stdRelations.contains(jerName)) {
                    throw new DeploymentDescException("ejb-relation missing in ejb-jar.xml for the relation " + jerName);
                }
            }
        }

        // Trace
        if (logger.getCurrentIntLevel() == BasicLevel.DEBUG) {
            logger.log(BasicLevel.DEBUG, "DEPLOYMENT DESCRIPTOR = \n(" + this.toString() + "\n)");
        }
    }

    /**
     * In case of beans with old CMP1 persistance, we need to instanciate the old class,
     * as if we were in an old Deployment Descriptor.
     * Default is CMP2.x for entity beans with a EJB2.0 DD.
     * @param classLoader The ClassLoader to be used
     * @param ent Entity MetaInformation from XML files
     * @param asd AssemblyDescriptor MetaInformation from XML files
     * @param jEnt JonasEntity MetaInformation from XML files
     * @return The Entity Bean Descriptor, for the good CMP version.
     * @throws DeploymentDescException Cannot build Entity Descriptor
     */
    protected BeanDesc newEntityBeanDesc(ClassLoader classLoader,
                                         Entity ent,
                                         AssemblyDescriptor asd,
                                         JonasEntity jEnt,
                                         JLinkedList jMDRList)
        throws DeploymentDescException {

        if (ent.getCmpVersion() == null || ent.getCmpVersion().equals("2.x")) {
            return new EntityJdbcCmp2Desc(classLoader, ent, asd, jEnt, this, jMDRList, fileName);
        } else {
            return new EntityJdbcCmp1Desc(classLoader, ent, asd, jEnt, jMDRList, fileName);
        }
    }

    /**
     * Get iterator of meta-info for all defined relations
     * @return an iterator of EjbRelationDesc
     */
    public Iterator getEjbRelationDescIterator() {
        return ejbRelations.iterator();
    }

    /**
     * String representation of the object for test purpose
     * @return String representation of this object
     */
    public String toString() {
        StringBuffer ret = new StringBuffer();
        ret.append(super.toString());
        for (Iterator i = getEjbRelationDescIterator(); i.hasNext();) {
            ret.append("\nejbRelationDesc[]=" + i.next());
        }
        return ret.toString();
    }

}
