/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 * 
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package com.sun.jdo.spi.persistence.support.ejb.ejbc;

import com.sun.enterprise.deployment.EjbBundleDescriptor;
import com.sun.enterprise.deployment.ResourceReferenceDescriptor;
import com.sun.enterprise.deployment.io.DescriptorConstants;
import com.sun.enterprise.deployment.backend.DeploymentEventInfo;
import com.sun.enterprise.deployment.backend.DeploymentStatus;
import com.sun.enterprise.server.Constants;

import com.sun.jdo.spi.persistence.support.sqlstore.ejb.DeploymentHelper;
import com.sun.jdo.spi.persistence.support.sqlstore.ejb.EJBHelper;
import com.sun.jdo.api.persistence.support.JDOFatalUserException;
import com.sun.jdo.spi.persistence.utility.I18NHelper;

import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Iterator;

/**
 * If the application contains cmp 2.x beans process them. Check if
 * tables have to created or dropped depending on where we are called
 * in a deploy/undeploy case.
 * @author pramodg
 */
public class CMPProcessor
    extends BaseProcessor {
    
    /**
     * Creates a new instance of CMPProcessor
     * @param info the deployment info object.
     * @param create true if tables must be created as part of this event
     * @param cliCreateTables the cli string related to creating tables
     * at deployment time.
     * @param cliDropAndCreateTables the cli string that indicates that
     * old tables have to be dropped and new tables created.
     * @param cliDropTables the cli string to indicate that the tables
     * have to dropped at undeploy time.
     */
    public CMPProcessor(
            DeploymentEventInfo info, boolean create, 
            String cliCreateTables, String cliDropAndCreateTables, String cliDropTables) {
        super(info, create, cliCreateTables,
            cliDropAndCreateTables, cliDropTables);
    }
   
    /**
     * The entry point into this class. Process
     * any ejb bundle descriptors if defined 
     * for this application.
     */
    protected void processApplication() {        
        Collection bundleCollection = application.getEjbBundleDescriptors();
        if ( bundleCollection  == null) {
            return;
        }
        Iterator bundleItr = bundleCollection.iterator();

        // Process only those Bundle descriptors that contain CMP beans.
        while ( bundleItr.hasNext() ) {
             processAppBundle((EjbBundleDescriptor)bundleItr.next()); 
        } 
    } 
    
    /**
     * This method does all the work of checking
     * and processing each ejb bundle descriptor. 
     * @param bundle the ejb bundle descriptor that is being worked on.
     */
   private void processAppBundle(EjbBundleDescriptor bundle) {
           if (!bundle.containsCMPEntity()) {
                return;
            }

            ResourceReferenceDescriptor cmpResource = 
                    bundle.getCMPResourceReference();

            // If this bundle's beans are not created by Java2DB, then skip to
            // next bundle.
            if (!DeploymentHelper.isJavaToDatabase(
                    cmpResource.getSchemaGeneratorProperties())) {
                return;
            }
      
            boolean createTables = getCreateTablesValue(cmpResource) ;                   
            boolean dropTables = getDropTablesValue(cmpResource);

            if (debug) {                
                logger.fine("ejb.CMPProcessor.createanddroptables", //NOI18N
                    new Object[] {new Boolean(createTables), new Boolean(dropTables)});
            }

            if (!createTables && !dropTables) { 
                // Nothing to do.
                return;
            }
        
            // At this point of time we are sure that we would need to create 
            // the sql/jdbc files required to create or drop objects from the 
            // database. Hence setup the required directories from the info object.
            setApplicationLocation();
            setGeneratedLocation();
            
            constructJdbcFileNames(bundle);
            if (debug) {
                logger.fine("ejb.CMPProcessor.createanddropfilenames", 
                    createJdbcFileName, dropJdbcFileName); //NOI18N            
            }            

            String resourceName = cmpResource.getJndiName();
            if (dropTables) {
                executeStatements(dropJdbcFileName, resourceName);
            } else { 
                // else can only be createTables as otherwise we'll not reach here
                executeStatements(createJdbcFileName, resourceName);
            }
   }    

   /**
     * We need to create tables only on  deploy, and 
     * only if the CLI options cliCreateTables or 
     * cliDropAndCreateTables are not set to false. 
     * If those options are not set (UNDEFINED)
     * the value is taken from the 
     * create-tables-at-deploy element of the
     * sun-ejb-jar.xml 
     * (cmpResource.isCreateTablesAtDeploy()).
     * @param cmpResource the cmp resource reference descriptor.
     * @return true if tables have to created.
     */
    protected boolean getCreateTablesValue(
            ResourceReferenceDescriptor cmpResource) {
            boolean createTables = 
                create 
                    && (cliCreateTables.equals(Constants.TRUE) 
                        || (cmpResource.isCreateTablesAtDeploy()
                            && cliCreateTables.equals(Constants.UNDEFINED)));
            return createTables;
    }    

    /**
     *  We need to drop tables on undeploy and redeploy, 
     * if the corresponding CLI options cliDropAndCreateTables 
     * (for redeploy) or cliDropTables (for undeploy) are 
     * not set to false. 
     * If the corresponding option is not set (UNDEFINED)
     * the value is taken from the drop-tables-at-undeploy 
     * element of the sun-ejb-jar.xml 
     * (cmpResource.isDropTablesAtUndeploy()).
     * @param cmpResource the cmp resource reference descriptor.
     * @return true if the tables have to be dropped.
     */
    protected boolean getDropTablesValue(
            ResourceReferenceDescriptor cmpResource) {
        boolean dropTables = 
            (!create 
                && (cliDropAndCreateTables.equals(Constants.TRUE) 
                    || cliDropTables.equals(Constants.TRUE) 
                    || (cmpResource.isDropTablesAtUndeploy()
                        && cliDropAndCreateTables.equals(Constants.UNDEFINED)
                        && cliDropTables.equals(Constants.UNDEFINED))));
       return dropTables;
    } 
    
    /**
     * Construct the name of the create and 
     * drop jdbc ddl files that would be 
     * created. These name would be either
     * obtained from the persistence.xml file
     * (if the user has defined them) or we would
     * create default filenames
     * @param ejbBundle the ejb bundle descriptor being worked on.
     */
    private void  constructJdbcFileNames(EjbBundleDescriptor ejbBundle) {
        String filePrefix = EJBHelper.getDDLNamePrefix(ejbBundle);
        
        createJdbcFileName = filePrefix + CREATE_DDL_JDBC_FILE_SUFFIX;
        dropJdbcFileName = filePrefix + DROP_DDL_JDBC_FILE_SUFFIX;
    }
    
    /**
     * If the file is present, execute the corresponding statements
     * in the database.
     * @param fileName the name of the file to execute.
     * @param resourceName the jdbc resource name that would be used 
     * to get a connection to the database.
     */
    private void executeStatements(
            String fileName, String resourceName) {
        File file = getDDLFile(
                appGeneratedLocation + fileName, false);
        if (file.exists()) {
            executeDDLStatement(file, resourceName);
        } else {
            logI18NWarnMessage(
                 ((create)? "ejb.BaseProcessor.cannotcreatetables" //NOI18N
                 : "ejb.BaseProcessor.cannotdroptables"), //NOI18N
                appRegisteredName, file.getName(), null);
        }
    }
    
    /**
     * Get the ddl files eventually executed 
     * against the database. This method deals 
     * with both create and drop ddl files.
     * @param fileName  the create or drop jdbc ddl file.
     * @param resourceName the jdbc resource name that would be used 
     * to get a connection to the database.
     * @return true if the tables were successfully 
     *    created/dropped from the database. 
     */
    private boolean executeDDLStatement(File fileName, String resourceName) {
        boolean result = false;
        Connection conn = null;
        Statement sql = null;
        try {
            try {           
                    conn = DeploymentHelper.getConnection(resourceName);
                    sql = conn.createStatement();
                    result = true;
                } catch (SQLException ex) {
                    cannotConnect(resourceName, ex);
                } catch (JDOFatalUserException ex) {
                    cannotConnect(resourceName, ex);
                }
        
                if(result) {               
                    executeDDLs(fileName, sql);
                }
        } catch (IOException e) {
            fileIOError(application.getRegistrationName(), e);            
        } finally { 
            closeConn(conn);
        }
        return result;        
    }
        
}
