/* JFox, the OpenSource J2EE Application Server
 *
 * Distributable under GNU LGPL license by gun.org
 * more details please visit http://www.huihoo.org/jfox
 */
package org.jfox.deployment.application;

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.jfox.deployment.J2EEClassLoader;
import org.jfox.deployment.J2EEModule;
import org.jfox.deployment.ejb.NestedEJBModule;
import org.jfox.deployment.web.NestedWebModule;
import org.jfox.deployment.web.WebDescriptor;
import org.jfox.ejb.meta.EJBDescriptionException;
import org.jfox.ejb.meta.EJBDescriptor;
import org.jfox.ejb.meta.EJBReferenceDescriptor;
import org.jfox.ejb.meta.XmlUtil;
import org.jfox.framework.deploy.DeployException;
import org.jfox.framework.deploy.Deployer;
import org.jfox.framework.util.Files;
import org.jfox.framework.util.JarUtils;
import org.jfox.system.JFoxMeta;

/**
 * @author <a href="mailto:young_yy@hotmail.com">Young Yang</a>
 */

public class EARModule extends J2EEModule {
    private File earFile;
    private J2EEClassLoader loader = new J2EEClassLoader(new URL[]{}, EARModule.class.getClassLoader());

    private EARDescriptor earDescriptor;

    // directory relative to the earFile directory
    private String baseDeployDirectory = JFoxMeta.getJFoxHome() + "/" + File.separator + "temp" + File.separator;

    private List nestedEjbs = new ArrayList();
    private List nestedWebs = new ArrayList();

    public EARModule(File earFile) throws DeployException {
        this.earFile = earFile;
        init();
    }

    private void init() throws DeployException {
        try {
            JarUtils.extract(getModuleFile(), getTempDeployDirectory());
            String[] cpArray = JarUtils.getClassPathFromManifest(earFile);

            // move the jar file to lib directory , and add it to ClassLoader
            for(int i = 0; i < cpArray.length; i++) {
                File jarLib = new File(getTempDeployDirectory(), cpArray[i]);
                if(jarLib.exists()) {
                    File file = Files.move(jarLib, new File(getTempDeployDirectory(), "lib"));
                    loader.addURL(file.toURL());
                }
            }

            // parse application.xml
            earDescriptor = new EARDescriptor(this);
            URL url = getDescriptorURL();
            earDescriptor.processXML(XmlUtil.getDocument(url).getDocumentElement());

            // set the ejb reference
            initEjbReference();
        }
        catch(Exception e) {
            throw new DeployException(e);
        }

    }

    private void initEjbReference() throws EJBDescriptionException {
        for(Iterator it = nestedWebs.iterator(); it.hasNext();) {
            NestedWebModule nestedWebModule = (NestedWebModule) it.next();
            WebDescriptor webDescriptor = nestedWebModule.getWebDescriptor();
            EJBReferenceDescriptor[] ejbRefs = webDescriptor.getEjbReferences();
            for(int i = 0; i < ejbRefs.length; i++) {
                EJBReferenceDescriptor ejbRef = (EJBReferenceDescriptor) ejbRefs[i];

                String linkedEjbName = ejbRef.getEjbName();
                if(linkedEjbName == null || linkedEjbName.trim().equals("")) {
                    // if not specify ejb-link, just use ejb-ref
                    ejbRef.setJndiName(ejbRef.getRefName());
                }
                else {
                    String ejbJndiName = findEjbJndiNameByEjbName(linkedEjbName);
                    if(ejbJndiName == null) {
                        throw new EJBDescriptionException("not find the ejb name for ejb-link: " + linkedEjbName + " in ear module: " + earFile.getName());
                    }
                    ejbRef.setJndiName(ejbJndiName);
                }
            }
        }
    }

    private String findEjbJndiNameByEjbName(String ejbName) {
        EJBDescriptor ejbDescriptor = null;
        for(Iterator it = nestedEjbs.iterator(); it.hasNext();) {
            NestedEJBModule nestedEjbModule = (NestedEJBModule) it.next();
            ejbDescriptor = nestedEjbModule.getEJBBundle().getEjb(ejbName);
            if(ejbDescriptor != null) break;
        }
        return ejbDescriptor == null ? null : ejbDescriptor.getJndiName();
    }

    public URL getDescriptorURL() {
        try {
            return new File(getTempDeployDirectory(), "META-INF/application.xml").toURL();
        }
        catch(Exception e) {
            return null;
        }
    }

    public File getModuleFile() {
        return earFile;
    }

    public URLClassLoader getClassLoader() {
        return loader;
    }

    public File getTempDeployDirectory() {
        File dir = new File(earFile.getParentFile(), baseDeployDirectory + earFile.getName());
        if(!dir.exists()) {
            dir.mkdirs();
        }
        return dir;
    }

    public NestedEJBModule[] getNestedEJBModules() {
        return (NestedEJBModule[]) nestedEjbs.toArray(new NestedEJBModule[nestedEjbs.size()]);
    }

    public NestedWebModule[] getNestedWebModules() {
        return (NestedWebModule[]) nestedWebs.toArray(new NestedWebModule[nestedWebs.size()]);
    }

    void addNestedEJBModule(NestedEJBModule nestedEjb) {
        nestedEjbs.add(nestedEjb);
    }

    void addNestedWebModule(NestedWebModule nestedWeb) {
        nestedWebs.add(nestedWeb);
    }

    public synchronized EARDescriptor getEARDescriptor() throws EJBDescriptionException {
        return earDescriptor;
    }

    public void deploy(Deployer deployer) throws DeployException {
        if(deployer instanceof EARDeployer) {
            deployer.deploy(this);
        }
        else {
            throw new DeployException("can use deployer " + deployer + " to deploy component " + this);
        }
    }

    public static void main(String[] args) {

    }

}

