

package gov.va.med.cds.testharness.sql;


import gov.va.med.cds.util.StreamUtil;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.Resource;

import java.io.IOException;
import java.io.InputStream;


/**
 * Runs PL/SQL scripts using external SqlPlus utility which must be installed in the execution path of the deployed
 * platform
 */
public class SqlPlusRunner
{
    private static Log logger = LogFactory.getLog( SqlPlusRunner.class );
    private static final String APPLICATION_NAME = "CDS";


    /**
     * Executes a PL/SQL script referenced by sqlResource
     * 
     * @param primaryConnectionString Oracle connection string
     * @param sqlResource Resource to the PL/SQL file
     * @throws ExecuteException
     */
    public void execute( String primaryConnectionString, Resource sqlResource )
        throws ExecuteException
    {
        try
        {
            execute( primaryConnectionString, sqlResource.getFile().getAbsolutePath() );
        }
        catch ( IOException e )
        {
            throw new ExecuteException( e );
        }
    }


    /**
     * Executes a PL/SQL script referenced by sqlFilePath
     * 
     * @param primaryConnectionString Oracle connection string
     * @param sqlFilePath File name of the PL/SQL file
     * @throws ExecuteException
     */
    public void execute( String primaryConnectionString, String sqlFilePath )
        throws ExecuteException
    {
        try
        {
            ProcessBuilder processBuilder = new ProcessBuilder( "sqlplus", "-s", primaryConnectionString, "@" + sqlFilePath );
            startProcessBuilder( processBuilder, sqlFilePath );

        }
        catch ( Exception io )
        {
            throw new ExecuteException( "Failed to execute sql with file path: " + sqlFilePath, io );
        }
    }


    /**
     * Executes a PL/SQL script referenced by sqlFilePath with parameter
     * 
     * @param primaryConnectionString Oracle connection string
     * @param sqlFilePath File name of the PL/SQL file
     * @throws ExecuteException
     */
    public void execute( String primaryConnectionString, String sqlFilePath, String parameter )
        throws ExecuteException
    {
        try
        {
            ProcessBuilder processBuilder = new ProcessBuilder( "sqlplus", "-s", primaryConnectionString, "@" + sqlFilePath, parameter );
            startProcessBuilder( processBuilder, sqlFilePath );
        }
        catch ( Exception io )
        {
            throw new ExecuteException( "Failed to execute sql with file path: " + sqlFilePath, io );
        }

    }


    private void startProcessBuilder( ProcessBuilder processBuilder, String sqlFilePath )
        throws ExecuteException
    {
        try
        {
            Process p = processBuilder.start();
            // Without grabing the streams before the p.waitFor() the p.waitFor() goes into deadlock.
            // This hack was copied from http://jira.public.thoughtworks.org/browse/CC-527
            // to address the deadlock
            p.getOutputStream().close();
            InputStream output = p.getInputStream();
            // p.waitFor();

            String msgString = StreamUtil.streamToString( output );

            if ( logger.isDebugEnabled() )
            {
                logger.debug( sqlFilePath );
                logger.debug( gov.va.med.cds.util.LogMessageUtil.buildMessage( null, null, APPLICATION_NAME, msgString ) );
            }

            if ( msgString.contains( "ORA-" ) )
            {
                throw new ExecuteException( msgString );
            }

        }
        catch ( IOException io )
        {
            throw new ExecuteException( "Failed to execute sql with file path: " + sqlFilePath, io );
        }
    }
}
