/*
 * The contents of this file are subject to the terms 
 * of the Common Development and Distribution License 
 * (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/CDDLv1.0.html or
 * glassfish/bootstrap/legal/CDDLv1.0.txt.
 * See the License for the specific language governing 
 * permissions and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL 
 * Header Notice in each file and include the License file 
 * at glassfish/bootstrap/legal/CDDLv1.0.txt.  
 * If applicable, add the following below the CDDL Header, 
 * with the fields enclosed by brackets [] replaced by
 * you own identifying information: 
 * "Portions Copyrighted [year] [name of copyright owner]"
 * 
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 */

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            
            }            

            // These calls will be a no-op if it's not a corresponding event.
            dropTablesFromDB(dropTables, cmpResource.getJndiName());
            createTablesInDB(createTables, cmpResource.getJndiName());
       
   }    

   /**
     * 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 drop file is present, drop the tables
     * from the database.
     * @param dropTables true if the table need to be dropped.
     * @param resourceName the cmp resource string.
     * @return true if tables were successfully dropped.
     */
    private boolean dropTablesFromDB(
            boolean dropTables, String resourceName) {
         boolean dropResult = false;
        File dropFile = null;
        
        if(dropTables) {
            dropFile = getDDLFile(
                    appGeneratedLocation + dropJdbcFileName, false);
            if (dropFile.exists()) {
                dropResult = executeDDLStatement(dropFile, resourceName);
            } else {
                if (debug) {
                    logger.fine("ejb.BaseProcessor.cannotdroptables", dropFile.getName()); //NOI18N          
                }
            }
        }
        return dropResult;
    }
    
    /**
     * Get the create jdbc ddl file and execute it
     * against the database
     * @param createTables true if tables have to be created.
     * @param resourceName the cmp resource name
     * @return true if tables were successfully created.
     */
    private boolean createTablesInDB
            (boolean createTables, String resourceName) {
        boolean createResult = false;
        File createFile = null;
        
        if(createTables) {
            createFile = getDDLFile(
                    appGeneratedLocation + createJdbcFileName, true);
            if(createFile.exists()) {
                createResult = executeDDLStatement(createFile, resourceName);    
            }  else {
                if (debug) {
                    logger.fine("ejb.BaseProcessor.cannotcreatetables", createFile.getName()); //NOI18N          
                }
            }
        }
        return createResult;        
    }
    
    /**
     * 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 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;        
    }
        
}
