/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2000 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

package org.apache.tools.ant.taskdefs.optional.ejb;

import java.io.*;
import java.util.*;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
import org.xml.sax.InputSource;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Commandline;
import org.apache.tools.ant.taskdefs.Java;

public class WeblogicDeploymentTool extends GenericDeploymentTool {
    
    protected static final String WL_DD = "weblogic-ejb-jar.xml";
    protected final static int SUN            = 0;
    protected final static int WEBLOGIC       = 1;
    
    /** DTDs that can be found on this file system **/
    protected static String[] FILE_DTD  = 
      {"/weblogic/ejb/deployment/xml/ejb-jar.dtd", "/weblogic/ejb/deployment/xml/weblogic-ejb-jar.dtd"};
    
    /** DTDs that can be found else where **/
    protected static String[] HTTP_DTD  =   
      {"http://java.sun.com/j2ee/dtds/ejb-jar_1_1.dtd", "http://www.bea.com/servers/wls510/dtd/weblogic-ejb-jar.dtd"};
    
    /** Public IDs for Sun and Bea **/
    protected static String[] PUBLIC_ID = 
      {"-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN", "-//BEA Systems, Inc.//DTD WebLogic 5.1.0 EJB//EN"};
    
    /** Instance variable that stores the location of the Sun, Bea DTD files. */
    protected static String[] DTDS = new String[2];  

    /** Instance variable that stores the suffix for the weblogic jarfile. */
    private String jarSuffix = ".jar";

    private Path classpath;

    /** Instance variable that determines whether generic ejb jars are kept. */
    private boolean keepgenerated = false;

    private String additionalArgs = "";

    private boolean keepGeneric = false;

    private String compiler = null;
    
    /**
     * Set the classpath to be used for this compilation.
     */
    public void setClasspath(Path classpath) {
        this.classpath = classpath;
    }

    /**
     * The compiler (switch <code>-compiler</code>) to use
     */
    public void setCompiler(String compiler) 
    {
        this.compiler = compiler;
    }
    

    /**
     * Setter used to store the suffix for the generated weblogic jar file.
     * @param inString the string to use as the suffix.
     */
    public void setSuffix(String inString) {
        this.jarSuffix = inString;
    }

    /**
     * Setter used to store the value of keepGeneric
     * @param inValue a string, either 'true' or 'false'.
     */
    public void setKeepgeneric(boolean inValue) {
        this.keepGeneric = inValue;
    }

    /**
     * Sets whether -keepgenerated is passed to ejbc (that is,
     * the .java source files are kept).
     * @param inValue either 'true' or 'false'
     */
    public void setKeepgenerated(String inValue) 
    {
        this.keepgenerated = Boolean.valueOf(inValue).booleanValue();
    }

    /**
     * sets some additional args to send to ejbc.
     */
    public void setArgs(String args) 
    {
        this.additionalArgs = args;
    }
    
    
    /**
     * Setter used to store the location of the weblogic DTD. This can be a file on the system 
     * or a resource on the classpath. 
     * @param inString the string to use as the DTD location.
     */
    public void setWeblogicdtd(String inString) {
        this.DTDS[WEBLOGIC] = inString;
    }
    
    /**
     * Setter used to store the location of the Sun's Generic EJB DTD. 
     * This can be a file on the system or a resource on the classpath. 
     * @param inString the string to use as the DTD location.
     */
    public void setEJBdtd(String inString) {
        this.DTDS[SUN] = inString;
    }

    
    /**
     * Factory Method used to create typed DescriptorHandler objects 
     * @param srcDir a file object representing the directory where the descriptor can be located
     * @param fileType the type of dtd to be parsed (i.e. 0-(ejb-jar.xml) 1-(weblogic-ejb-jar.xml))
     */
    protected DescriptorHandler getDescriptorHandler(File srcDir, int fileType){
      
      DescriptorHandler handler = null;
      //return the appropriate DescriptorHandler based on the Descriptor type
      switch(fileType){
        case SUN:
          handler = new DescriptorHandler(srcDir);
          break;
        case WEBLOGIC:
          handler = getWeblogicDescriptorHandler(srcDir);
          break;
      }
      registerDTDs(handler, fileType);
      return handler;
    }
    
    /**
     * Registers the DTD with the document handler
     * @param handler Typed DocumentHandler used to parse the descriptor
     * @param fileType the type of dtd to be parsed (i.e. 0-(ejb-jar.xml) 1-(weblogic-ejb-jar.xml))
     */
    protected void registerDTDs(DescriptorHandler handler, int fileType){
        
        if (DTDS[fileType] != null) {
            // is the DTD a local file?
            File dtdFile = new File(DTDS[fileType]);
            if (dtdFile.exists()) {
                handler.registerFileDTD(PUBLIC_ID[fileType], dtdFile);
            } else {
                handler.registerResourceDTD(PUBLIC_ID[fileType], DTDS[fileType]);
            }                                            
        } else {
            handler.registerResourceDTD(PUBLIC_ID[fileType], FILE_DTD[fileType]);
        }
    }
    
    
    /**
     * Returns the Default DescriptorHandler (Handles ejb-jar.xml files)
     * @param srcDir the directory where the file can be found
     */
    protected DescriptorHandler getDescriptorHandler(File srcDir) {
      return getDescriptorHandler(srcDir, SUN);
    }
    
    /**
    * Creates a subclass of DescriptorHandler that parses Weblogic EJB Descriptors.
    * The DescriptorHandler searches for CMP related descriptors that can be found
    * by checking for the "type-storage" element in the Weblogic XML Descriptor.
    */
    
    protected DescriptorHandler getWeblogicDescriptorHandler(File srcDir)
    {
      DescriptorHandler handler = 
        new DescriptorHandler(srcDir) {        
          public void endElement(String name) throws SAXException {
            processElement();
            currentText = "";
            this.currentElement = "";
          }
        
          private void processElement() {
            if (currentElement.equals("type-storage")) {
              // Get the filename of vendor specific descriptor
              String fileNameWithMETA = currentText;
              //trim the META_INF\ off of the file name
              String fileName = fileNameWithMETA.substring(META_DIR.length(), 
                                                           fileNameWithMETA.length() );
              File descriptorFile = new File(this.srcDir, fileName);
              ejbFiles.put(fileNameWithMETA, descriptorFile);
            }
          }
      };
      return handler;
    }
    

    /**
     * Add any vendor specific files which should be included in the 
     * EJB Jar.
     */
    protected void addVendorFiles(Hashtable ejbFiles, String baseName) {
        String ddPrefix = (usingBaseJarName() ? "" : baseName + getBaseNameTerminator());
        
        File weblogicDD = new File(getDescriptorDir(), ddPrefix + WL_DD);
        if (weblogicDD.exists()) {
            ejbFiles.put(META_DIR + WL_DD,
                         weblogicDD);
        }
        else {
            return;
        }
        
        /** 
        * now that we have the weblogic descriptor, we parse the file
        * to find other descriptors needed to deploy the bean.
        * this could be the weblogic-cmp-rdbms.xml or any other O/R
        * mapping tool descriptors.
        */
        try
        {
          File ejbDescriptor = (File)ejbFiles.get(META_DIR + EJB_DD);
          SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
          saxParserFactory.setValidating(true);
          SAXParser saxParser = saxParserFactory.newSAXParser();
          DescriptorHandler handler = getDescriptorHandler(ejbDescriptor.getParentFile(), WEBLOGIC);
          saxParser.parse(new InputSource
                            (new FileInputStream
                            (weblogicDD)),
                            handler);
                            
          Hashtable ht = handler.getFiles();
          Enumeration e = ht.keys();
          while(e.hasMoreElements()){
            String key = (String)e.nextElement();
            ejbFiles.put(key, ht.get(key));
          }
        }
        catch(Exception e)
        { 
            String msg = "Exception while adding Vendor specific files" + e.toString();
            throw new BuildException(msg, e);
        }
    }
    
    /**
     * Get the vendor specific name of the Jar that will be output. The modification date
     * of this jar will be checked against the dependent bean classes.
     */
    File getVendorOutputJarFile(String baseName) {
        return new File(getDestDir(), baseName + jarSuffix);
    }

    /**
     * Helper method invoked by execute() for each WebLogic jar to be built.
     * Encapsulates the logic of constructing a java task for calling
     * weblogic.ejbc and executing it.
     * @param sourceJar java.io.File representing the source (EJB1.1) jarfile.
     * @param destJar java.io.File representing the destination, WebLogic
     *        jarfile.
     */
    private void buildWeblogicJar(File sourceJar, File destJar) {
        org.apache.tools.ant.taskdefs.Java javaTask = null;
        
        try {
            String args = additionalArgs;
            if (keepgenerated) {
                args += " -keepgenerated";
            }
            
            if (compiler != null) {
                args += " -compiler " + compiler;
            }
            
            args += " -noexit " + sourceJar.getPath() + " " + destJar.getPath();
            
            javaTask = (Java) getTask().getProject().createTask("java");
            javaTask.setClassname("weblogic.ejbc");
            Commandline.Argument arguments = javaTask.createArg();
            arguments.setLine(args);
            if (classpath != null) {
                javaTask.setClasspath(classpath);
                javaTask.setFork(true);
            }
            else {
                javaTask.setFork(false);
            }
            

            getTask().log("Calling weblogic.ejbc for " + sourceJar.toString(),
                          Project.MSG_VERBOSE);

            javaTask.execute();
        }
        catch (Exception e) {
            // Have to catch this because of the semantics of calling main()
            String msg = "Exception while calling ejbc. Details: " + e.toString();
            throw new BuildException(msg, e);
        }
    }

    /**
     * Method used to encapsulate the writing of the JAR file. Iterates over the
     * filenames/java.io.Files in the Hashtable stored on the instance variable
     * ejbFiles.
     */
    protected void writeJar(String baseName, File jarFile, Hashtable files) throws BuildException {
        // need to create a generic jar first.
        File genericJarFile = super.getVendorOutputJarFile(baseName);
        super.writeJar(baseName, genericJarFile, files);
        
        buildWeblogicJar(genericJarFile, jarFile);
        if (!keepGeneric) {
             getTask().log("deleting generic jar " + genericJarFile.toString(),
                           Project.MSG_VERBOSE);
             genericJarFile.delete();
        }
    }

    /**
     * Called to validate that the tool parameters have been configured.
     *
     */
    public void validateConfigured() throws BuildException {
        super.validateConfigured();
    }
}
