/* ==================================================================== * Limited Evaluation License: * * This software is open source, but licensed. The license with this package * is an evaluation license, which may not be used for productive systems. If * you want a full license, please contact us. * * The exclusive owner of this work is the OpenRate project. * This work, including all associated documents and components * is Copyright of the OpenRate project 2006-2015. * * The following restrictions apply unless they are expressly relaxed in a * contractual agreement between the license holder or one of its officially * assigned agents and you or your organisation: * * 1) This work may not be disclosed, either in full or in part, in any form * electronic or physical, to any third party. This includes both in the * form of source code and compiled modules. * 2) This work contains trade secrets in the form of architecture, algorithms * methods and technologies. These trade secrets may not be disclosed to * third parties in any form, either directly or in summary or paraphrased * form, nor may these trade secrets be used to construct products of a * similar or competing nature either by you or third parties. * 3) This work may not be included in full or in part in any application. * 4) You may not remove or alter any proprietary legends or notices contained * in or on this work. * 5) This software may not be reverse-engineered or otherwise decompiled, if * you received this work in a compiled form. * 6) This work is licensed, not sold. Possession of this software does not * imply or grant any right to you. * 7) You agree to disclose any changes to this work to the copyright holder * and that the copyright holder may include any such changes at its own * discretion into the work * 8) You agree not to derive other works from the trade secrets in this work, * and that any such derivation may make you liable to pay damages to the * copyright holder * 9) You agree to use this software exclusively for evaluation purposes, and * that you shall not use this software to derive commercial profit or * support your business or personal activities. * * This software is provided "as is" and any expressed or impled warranties, * including, but not limited to, the impled warranties of merchantability * and fitness for a particular purpose are disclaimed. In no event shall * The OpenRate Project or its officially assigned agents be liable to 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 any business interruption) however caused * and on 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 contains portions by The Apache Software Foundation, Robert * Half International. * ==================================================================== */ package OpenRate.adapter.jdbc; import OpenRate.CommonConfig; import OpenRate.adapter.AbstractTransactionalOutputAdapter; import OpenRate.configurationmanager.ClientManager; import OpenRate.db.DBUtil; import OpenRate.exception.InitializationException; import OpenRate.exception.ProcessingException; import OpenRate.logging.LogUtil; import OpenRate.record.DBRecord; import OpenRate.record.HeaderRecord; import OpenRate.record.IRecord; import OpenRate.utils.PropertyUtils; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Collection; import java.util.Iterator; /** * Please <a target='new' * href='http://www.open-rate.com/wiki/index.php?title=JDBC_Output_Adapter'>click * here</a> to go to wiki page. * * <p> * JDBC Output Adapter.<br> * * This module writes records into a table using JDBC. The processing needs to * be a little more complicated than is first obvious to allow for the case that * a transaction is rolled back. We therefore have to mark the records we are * inserting in some way to segregate them from the records from previous * transactions. Only when we reach the commit point do we make the new records * like all the others, and in the case that we encounter a rollback, we have to * remove the inserted records.<br> * * The management of the connection and statements is dynamic: When a connection * is needed, it is created out of the framework managed pool. When the * processing is finished, the connection is released.<br> * * This module performs as insert on every record received for processing, and * therefore does not offer very high performance. This can often lead to the * pipeline "backing up" during processing (because the performance bottleneck * is at the end of the processing chain. Faster alternatives are the * JDBCBatchOutputAdapter and DirectLoad adapters. However the faster types are * progressively trickier to use (because the detection of an error gets * progressively "further away" from the cause), therefore usually initial * development should happen on the most primitive adapter, and during the * hardening of the project for productive purposes, the right level of * performance tuning should be found. */ public abstract class JDBCOutputAdapter extends AbstractTransactionalOutputAdapter { /** * The query that is used to prepare the database for record insert */ protected String initQuery; /** * The insert query */ protected String insertQuery; /** * The commit query if the transaction ended correctly */ protected String commitQuery; /** * The rollback query if the transaction did not end correctly */ protected String rollbackQuery; /** * This is the name of the data source */ protected String dataSourceName; /** * This is the query that is used to make any preparation for the insert */ protected PreparedStatement stmtInitQuery; /** * This is the query that is used to make the record insert into the table */ protected PreparedStatement stmtInsertQuery; /** * This is the query that is used to commit the records in the table. */ protected PreparedStatement stmtCommitQuery; /** * This is the statement that is used to roll back records from a table. */ protected PreparedStatement stmtRollbackQuery; // this is the connection from the connection pool that we are using private static final String DATASOURCE_KEY = "DataSource"; // The SQL statements from the properties that are used to get the records private static final String INIT_QUERY_KEY = "InitStatement"; private static final String INSERT_QUERY_KEY = "RecordInsertStatement"; private static final String COMMIT_QUERY_KEY = "CommitStatement"; private static final String ROLLBACK_QUERY_KEY = "RollbackStatement"; // List of Services that this Client supports private final static String SERVICE_DATASOURCE_KEY = "DataSource"; private final static String SERVICE_INIT_QUERY_KEY = "InitStatement"; private final static String SERVICE_INSERT_QUERY_KEY = "RecordInsertStatement"; private final static String SERVICE_COMMIT_QUERY_KEY = "CommitStatement"; private final static String SERVICE_ROLLBACK_QUERY_KEY = "RollbackStatement"; private final static String SERVICE_STATUS_KEY = "PrintStatus"; // This tells us if we should look for new work or continue with something // that is going on at the moment private boolean OutputStreamOpen = false; // used for controlling if statements are used private boolean useInit; private boolean useCommit; private boolean useRollback; // Extended validation of the columns we are going to insert - number private Integer insertQueryParamCount = null; /** * This is our connection object */ protected Connection JDBCcon; /** * Default constructor */ public JDBCOutputAdapter() { super(); } /** * Initialise the module. Called during pipeline creation. Initialise the * Logger, and load the SQL statements. * * @param PipelineName The name of the pipeline this module is in * @param ModuleName The module symbolic name of this module * @throws OpenRate.exception.InitializationException */ @Override public void init(String PipelineName, String ModuleName) throws InitializationException { String ConfigHelper; super.init(PipelineName, ModuleName); registerClientManager(); // Register ourself with the client manager setSymbolicName(ModuleName); // get any initialisation SQL that we need to perform, such as marking // records for select and removal ConfigHelper = initInitQuery(); processControlEvent(SERVICE_INIT_QUERY_KEY, true, ConfigHelper); // this is the SQL that will tidy up after the select, and ensure that // they are not selected next time, in the case that the processing // was completed correctly ConfigHelper = initInsertQuery(); processControlEvent(SERVICE_INSERT_QUERY_KEY, true, ConfigHelper); // this is the SQL that will tidy up after the select, and ensure that // they are not selected next time, in the case that the processing // was completed correctly ConfigHelper = initCommitQuery(); processControlEvent(SERVICE_COMMIT_QUERY_KEY, true, ConfigHelper); // this is the SQL that will tidy up after the select, and ensure that // they are not selected next time, in the case that the processing // was completed correctly ConfigHelper = initRollbackQuery(); processControlEvent(SERVICE_ROLLBACK_QUERY_KEY, true, ConfigHelper); // The datasource property was added to allow database to database // JDBC adapters to work properly using 1 configuration file. ConfigHelper = initDataSourceName(); processControlEvent(SERVICE_DATASOURCE_KEY, true, ConfigHelper); // prepare the data source - this does not open a connection if (DBUtil.initDataSource(dataSourceName) == null) { message = "Could not initialise DB connection <" + dataSourceName + "> to in module <" + getSymbolicName() + ">."; getPipeLog().error(message); throw new InitializationException(message, getSymbolicName()); } // Set up the optional statements useInit = ((initQuery == null || initQuery.isEmpty()) == false); useCommit = ((commitQuery == null || commitQuery.isEmpty()) == false); useRollback = ((rollbackQuery == null || rollbackQuery.isEmpty()) == false); } /** * Process the stream header. Get the file base name and open the transaction. * * @param r The record we are working on * @return The processed record * @throws ProcessingException */ @Override public HeaderRecord procHeader(HeaderRecord r) throws ProcessingException { // perform any parent processing first super.procHeader(r); try { // get the connection we will be using for writing JDBCcon = DBUtil.getConnection(dataSourceName); } catch (InitializationException ex) { getExceptionHandler().reportException(new ProcessingException(ex, getSymbolicName())); } // prepare the statements used for writing try { prepareStatements(); } catch (SQLException Sex) { // Not good. Abort the transaction message = "Error preparing statements. message <" + Sex.getMessage() + ">. Aborting transaction."; getPipeLog().fatal(message); getExceptionHandler().reportException(new ProcessingException(message, Sex, getSymbolicName())); setTransactionAbort(getTransactionNumber()); } // Get the count of the insert params we are going to use, but only once if (insertQueryParamCount == null) { try { insertQueryParamCount = stmtInsertQuery.getParameterMetaData().getParameterCount(); } catch (SQLException Sex) { // Not good. Abort the transaction message = "Could not count the parameters for insert statement. message <" + Sex.getMessage() + ">. Aborting transaction."; getPipeLog().fatal(message); getExceptionHandler().reportException(new ProcessingException(message, Sex, getSymbolicName())); setTransactionAbort(getTransactionNumber()); } message = "Parameter count for insert statement in module <" + getSymbolicName() + "> is <" + insertQueryParamCount + ">"; getPipeLog().info(message); } // perform the Init (if defined) if (useInit) { try { stmtInitQuery.execute(); } catch (SQLException Sex) { // Not good. Abort the transaction message = "Error executing init statement. message <" + Sex.getMessage() + ">. Aborting transaction."; getPipeLog().fatal(message); getExceptionHandler().reportException(new ProcessingException(message, Sex, getSymbolicName())); setTransactionAbort(getTransactionNumber()); } } return r; } /** * Prepare good records for writing to the defined output stream. * * @param r The current record we are working on * @return The prepared record * @throws ProcessingException */ @Override public IRecord prepValidRecord(IRecord r) throws ProcessingException { int i; Collection<DBRecord> outRecCol = null; DBRecord outRec; Iterator<DBRecord> outRecIter; try { outRecCol = procValidRecord(r); } catch (ProcessingException pe) { // Pass the exception up message = "Processing exception preparing valid record in module <" + getSymbolicName() + ">. message <" + pe.getMessage() + ">. Aborting transaction."; getPipeLog().fatal(message); getExceptionHandler().reportException(new ProcessingException(message, pe, getSymbolicName())); setTransactionAbort(getTransactionNumber()); } catch (ArrayIndexOutOfBoundsException aiex) { // Not good. Abort the transaction message = "Column Index preparing valid record in module <" + getSymbolicName() + ">. message <" + aiex.getMessage() + ">. Aborting transaction."; getPipeLog().fatal(message); getExceptionHandler().reportException(new ProcessingException(message, aiex, getSymbolicName())); setTransactionAbort(getTransactionNumber()); } catch (Exception ex) { // Not good. Abort the transaction message = "Unexpected Exception preparing valid record in module <" + getSymbolicName() + ">. message <" + ex.getMessage() + ">. Aborting transaction."; getPipeLog().fatal(message); getExceptionHandler().reportException(new ProcessingException(message, ex, getSymbolicName())); setTransactionAbort(getTransactionNumber()); } // Null return means "do not bother to process" if (outRecCol != null && stmtInsertQuery != null) { outRecIter = outRecCol.iterator(); while (outRecIter.hasNext()) { outRec = outRecIter.next(); if (outRec.getOutputColumnCount() != insertQueryParamCount) { // columns we go does not match the expected columns message = "Column count in module <" + getSymbolicName() + "> does not match definition. Expected <" + insertQueryParamCount + ">, got <" + outRec.getOutputColumnCount() + ">"; getPipeLog().error(message); } else { try { // Prepare the parameter values stmtInsertQuery.clearParameters(); for (i = 0; i < outRec.getOutputColumnCount(); i++) { if (outRec.getOutputColumnType(i) == DBRecord.COL_TYPE_STRING) { // String value stmtInsertQuery.setString(i + 1, outRec.getOutputColumnValueString(i)); } else if (outRec.getOutputColumnType(i) == DBRecord.COL_TYPE_INTEGER) { // Integer value stmtInsertQuery.setInt(i + 1, outRec.getOutputColumnValueInt(i)); } else if (outRec.getOutputColumnType(i) == DBRecord.COL_TYPE_DOUBLE) { // Double value stmtInsertQuery.setDouble(i + 1, outRec.getOutputColumnValueDouble(i)); } else if (outRec.getOutputColumnType(i) == DBRecord.COL_TYPE_LONG) { // Long value stmtInsertQuery.setLong(i + 1, outRec.getOutputColumnValueLong(i)); } else if (outRec.getOutputColumnType(i) == DBRecord.COL_TYPE_DATE) { // Date value long DateValue = outRec.getOutputColumnValueLong(i); java.sql.Date DateToSet; DateToSet = new java.sql.Date(DateValue); stmtInsertQuery.setDate(i + 1, DateToSet); } else if (outRec.getOutputColumnType(i) == DBRecord.COL_TYPE_BOOL) { // Boolean value boolean value; value = outRec.getOutputColumnValueString(i).equals("1"); stmtInsertQuery.setBoolean(i + 1, value); } else if (outRec.getOutputColumnType(i) == DBRecord.COL_TYPE_TIME) { // Time value long DateValue = outRec.getOutputColumnValueLong(i); java.sql.Time TimeToSet; TimeToSet = new java.sql.Time(DateValue); stmtInsertQuery.setTime(i + 1, TimeToSet); } else if (outRec.getOutputColumnType(i) == DBRecord.COL_TYPE_BINARY) { // Binary value byte[] byteArray = outRec.getOutputColumnValueBytes(i); stmtInsertQuery.setBytes(i + 1, byteArray); } } stmtInsertQuery.execute(); } catch (SQLException Sex) { // Not good. Abort the transaction message = "SQL Exception inserting valid record in module <" + getSymbolicName() + ">. message <" + Sex.getMessage() + ">. Aborting transaction."; getPipeLog().fatal(message); getExceptionHandler().reportException(new ProcessingException(message, Sex, getSymbolicName())); setTransactionAbort(getTransactionNumber()); } catch (ArrayIndexOutOfBoundsException aiex) { // Not good. Abort the transaction message = "Column Index inserting valid record in module <" + getSymbolicName() + ">. message <" + aiex.getMessage() + ">. Aborting transaction."; getPipeLog().fatal(message); getExceptionHandler().reportException(new ProcessingException(message, aiex, getSymbolicName())); setTransactionAbort(getTransactionNumber()); } catch (NumberFormatException nfe) { // Not good. Abort the transaction message = "Number format inserting valid record in module <" + getSymbolicName() + ">. message <" + nfe.getMessage() + ">. Aborting transaction."; getPipeLog().fatal(message); getExceptionHandler().reportException(new ProcessingException(message, nfe, getSymbolicName())); setTransactionAbort(getTransactionNumber()); } catch (Exception ex) { // Not good. Abort the transaction message = "Unknown Exception inserting valid record in module <" + getSymbolicName() + ">. message <" + ex.getMessage() + ">. Aborting transaction."; getPipeLog().fatal(message); getExceptionHandler().reportException(new ProcessingException(message, ex, getSymbolicName())); setTransactionAbort(getTransactionNumber()); } } } } return r; } /** * Prepare bad records for writing to the defined output stream. * * @param r The current record we are working on * @return The prepared record * @throws ProcessingException */ @Override public IRecord prepErrorRecord(IRecord r) throws ProcessingException { int i; Collection<DBRecord> outRecCol = null; DBRecord outRec; Iterator<DBRecord> outRecIter; try { outRecCol = procErrorRecord(r); } catch (ProcessingException pe) { // Pass the exception up message = "Processing exception preparing error record in module <" + getSymbolicName() + ">. message <" + pe.getMessage() + ">. Aborting transaction."; getPipeLog().fatal(message); getExceptionHandler().reportException(new ProcessingException(message, pe, getSymbolicName())); setTransactionAbort(getTransactionNumber()); } catch (ArrayIndexOutOfBoundsException aiex) { // Not good. Abort the transaction message = "Column Index preparing error record in module <" + getSymbolicName() + ">. message <" + aiex.getMessage() + ">. Aborting transaction."; getPipeLog().fatal(message); getExceptionHandler().reportException(new ProcessingException(message, aiex, getSymbolicName())); setTransactionAbort(getTransactionNumber()); } catch (Exception ex) { // Not good. Abort the transaction message = "Unknown Exception preparing error record in module <" + getSymbolicName() + ">. message <" + ex.getMessage() + ">. Aborting transaction."; getPipeLog().fatal(message); getExceptionHandler().reportException(new ProcessingException(message, ex, getSymbolicName())); setTransactionAbort(getTransactionNumber()); } // Null return means "do not bother to process" if (outRecCol != null && stmtInsertQuery != null) { outRecIter = outRecCol.iterator(); while (outRecIter.hasNext()) { outRec = outRecIter.next(); try { // Prepare the parameter values stmtInsertQuery.clearParameters(); for (i = 0; i < outRec.getOutputColumnCount(); i++) { if (outRec.getOutputColumnType(i) == DBRecord.COL_TYPE_STRING) { // String value stmtInsertQuery.setString(i + 1, outRec.getOutputColumnValueString(i)); } else if (outRec.getOutputColumnType(i) == DBRecord.COL_TYPE_INTEGER) { // Integer value stmtInsertQuery.setInt(i + 1, outRec.getOutputColumnValueInt(i)); } else if (outRec.getOutputColumnType(i) == DBRecord.COL_TYPE_DOUBLE) { // Double value stmtInsertQuery.setDouble(i + 1, outRec.getOutputColumnValueDouble(i)); } else if (outRec.getOutputColumnType(i) == DBRecord.COL_TYPE_LONG) { // Long value stmtInsertQuery.setLong(i + 1, outRec.getOutputColumnValueLong(i)); } else if (outRec.getOutputColumnType(i) == DBRecord.COL_TYPE_DATE) { // Date value long DateValue = outRec.getOutputColumnValueLong(i); java.sql.Date DateToSet; DateToSet = new java.sql.Date(DateValue); stmtInsertQuery.setDate(i + 1, DateToSet); } else if (outRec.getOutputColumnType(i) == DBRecord.COL_TYPE_BOOL) { // Boolean value boolean value; value = outRec.getOutputColumnValueString(i).equals("1"); stmtInsertQuery.setBoolean(i + 1, value); } else if (outRec.getOutputColumnType(i) == DBRecord.COL_TYPE_TIME) { // Time value long DateValue = outRec.getOutputColumnValueLong(i); java.sql.Time TimeToSet; TimeToSet = new java.sql.Time(DateValue); stmtInsertQuery.setTime(i + 1, TimeToSet); } else if (outRec.getOutputColumnType(i) == DBRecord.COL_TYPE_BINARY) { // Binary value byte[] byteArray = outRec.getOutputColumnValueBytes(i); stmtInsertQuery.setBytes(i + 1, byteArray); } } stmtInsertQuery.execute(); } catch (SQLException Sex) { // Not good. Abort the transaction message = "SQL Exception inserting error record in module <" + getSymbolicName() + ">. message <" + Sex.getMessage() + ">. Aborting transaction."; getPipeLog().fatal(message); getExceptionHandler().reportException(new ProcessingException(message, Sex, getSymbolicName())); setTransactionAbort(getTransactionNumber()); } catch (ArrayIndexOutOfBoundsException aiex) { // Not good. Abort the transaction message = "Column Index inserting error record in module <" + getSymbolicName() + ">. message <" + aiex.getMessage() + ">. Aborting transaction."; getPipeLog().fatal(message); getExceptionHandler().reportException(new ProcessingException(message, aiex, getSymbolicName())); setTransactionAbort(getTransactionNumber()); } catch (NumberFormatException nfe) { // Not good. Abort the transaction message = "Number format inserting error record in module <" + getSymbolicName() + ">. message <" + nfe.getMessage() + ">. Aborting transaction."; getPipeLog().fatal(message); getExceptionHandler().reportException(new ProcessingException(message, nfe, getSymbolicName())); setTransactionAbort(getTransactionNumber()); } catch (Exception ex) { // Not good. Abort the transaction message = "Unknown Exception inserting error record in module <" + getSymbolicName() + ">. message <" + ex.getMessage() + ">. Aborting transaction."; getPipeLog().fatal(message); getExceptionHandler().reportException(new ProcessingException(message, ex, getSymbolicName())); setTransactionAbort(getTransactionNumber()); } } } return r; } /** * This is called when a data record is encountered. You should do any normal * processing here. Note that the result is a collection for the case that we * have to re-expand after a record compression input adapter has done * compression on the input stream. * * @param r The record we are working on * @return The collection of processed records * @throws ProcessingException */ public abstract Collection<DBRecord> procValidRecord(IRecord r) throws ProcessingException; /** * This is called when a data record with errors is encountered. You should do * any processing here that you have to do for error records, e.g. statistics, * special handling, even error correction! * * @param r The record we are working on * @return The collection of processed records * @throws ProcessingException */ public abstract Collection<DBRecord> procErrorRecord(IRecord r) throws ProcessingException; // ----------------------------------------------------------------------------- // ------------------ Custom connection management functions ------------------- // ----------------------------------------------------------------------------- /** * PrepareStatements creates the statements from the SQL expressions so that * they can be run as needed. */ private void prepareStatements() throws SQLException { // prepare the SQL for the Insert statement if (insertQuery == null || insertQuery.isEmpty()) { stmtInsertQuery = null; } else { stmtInsertQuery = JDBCcon.prepareStatement(insertQuery, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); } // prepare the SQL for the Commit Statement if (useCommit) { stmtCommitQuery = JDBCcon.prepareStatement(commitQuery, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); } if (useRollback) { // prepare the SQL for the Rollback Statement stmtRollbackQuery = JDBCcon.prepareStatement(rollbackQuery, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); } if (useInit) { // prepare the SQL for the Rollback Statement stmtInitQuery = JDBCcon.prepareStatement(initQuery, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); } } /* * closeStream() is called by the pipeline when no more information comes * down it. We must perform a transaction state change here to FLUSHED */ @Override public void closeStream(int TransactionNumber) { if (OutputStreamOpen) { setTransactionFlushed(TransactionNumber); OutputStreamOpen = false; } } /** * perform the final commit statement. This will only be performed if we have * defined a commot statement. Not all types of procesing require a commit. * * @param TransactionNumber The transaction we are commiting for */ public void finaliseOutputCommit(int TransactionNumber) { // commit the changes if (useCommit) { try { // Open the init statement getPipeLog().debug("Adapter <" + getSymbolicName() + "> performing commit."); stmtCommitQuery.execute(); } catch (SQLException Sex) { message = "JDBCOutputAdapter Error performing commit <" + Sex.getMessage() + ">"; getPipeLog().fatal(message); getExceptionHandler().reportException(new ProcessingException(message, Sex, getSymbolicName())); setTransactionAbort(getTransactionNumber()); } } } /** * perform the rollback statement, optionally removing records * * @param TransactionNumber The transaction number that is rolling back */ public void finaliseOutputRollback(int TransactionNumber) { if (useRollback) { // rollback the changes try { // Open the init statement getPipeLog().debug("Adapter <" + getSymbolicName() + "> performing rollback."); stmtRollbackQuery.execute(); } catch (SQLException Sex) { message = "JDBCOutputAdapter Error performing rollback <" + Sex.getMessage() + ">"; getPipeLog().fatal(message); getExceptionHandler().reportException(new ProcessingException(message, Sex, getSymbolicName())); setTransactionAbort(getTransactionNumber()); } } } /** * Used to skip to the end of the stream in the case that the transaction is * aborted. * * @return True if the rest of the transaction was skipped otherwise false */ @Override public boolean SkipRestOfStream() { return getTransactionAborted(getTransactionNumber()); } // ----------------------------------------------------------------------------- // ------------- Start of inherited IEventInterface functions ------------------ // ----------------------------------------------------------------------------- /** * processControlEvent is the event processing hook for the External Control * Interface (ECI). This allows interaction with the external world, for * example turning the dumping on and off. * * @param Command The command that we are to work on * @param Init True if the pipeline is currently being constructed * @param Parameter The parameter value for the command * @return The result message of the operation */ @Override public String processControlEvent(String Command, boolean Init, String Parameter) { int ResultCode = -1; if (Command.equalsIgnoreCase(SERVICE_DATASOURCE_KEY)) { if (Init) { dataSourceName = Parameter; ResultCode = 0; } else { if (Parameter.equals("")) { return dataSourceName; } else { return CommonConfig.NON_DYNAMIC_PARAM; } } } if (Command.equalsIgnoreCase(SERVICE_INIT_QUERY_KEY)) { if (Init) { initQuery = Parameter; ResultCode = 0; } else { if (Parameter.equals("")) { return initQuery; } else { return CommonConfig.NON_DYNAMIC_PARAM; } } } if (Command.equalsIgnoreCase(SERVICE_INSERT_QUERY_KEY)) { if (Init) { insertQuery = Parameter; ResultCode = 0; } else { if (Parameter.equals("")) { return insertQuery; } else { return CommonConfig.NON_DYNAMIC_PARAM; } } } if (Command.equalsIgnoreCase(SERVICE_COMMIT_QUERY_KEY)) { if (Init) { commitQuery = Parameter; ResultCode = 0; } else { if (Parameter.equals("")) { return commitQuery; } else { return CommonConfig.NON_DYNAMIC_PARAM; } } } if (Command.equalsIgnoreCase(SERVICE_ROLLBACK_QUERY_KEY)) { if (Init) { rollbackQuery = Parameter; ResultCode = 0; } else { if (Parameter.equals("")) { return rollbackQuery; } else { return CommonConfig.NON_DYNAMIC_PARAM; } } } if (Command.equalsIgnoreCase(SERVICE_STATUS_KEY)) { return "OK"; } if (ResultCode == 0) { getPipeLog().debug(LogUtil.LogECIPipeCommand(getSymbolicName(), getPipeName(), Command, Parameter)); return "OK"; } else { // This is not our event, pass it up the stack return super.processControlEvent(Command, Init, Parameter); } } /** * registerClientManager registers this class as a client of the ECI listener * and publishes the commands that the plug in understands. The listener is * responsible for delivering only these commands to the plug in. * * @throws OpenRate.exception.InitializationException */ @Override public void registerClientManager() throws InitializationException { // Set the client reference and the base services first super.registerClientManager(); //Register services for this Client ClientManager.getClientManager().registerClientService(getSymbolicName(), SERVICE_DATASOURCE_KEY, ClientManager.PARAM_MANDATORY); ClientManager.getClientManager().registerClientService(getSymbolicName(), SERVICE_INIT_QUERY_KEY, ClientManager.PARAM_NONE); ClientManager.getClientManager().registerClientService(getSymbolicName(), SERVICE_INSERT_QUERY_KEY, ClientManager.PARAM_MANDATORY); ClientManager.getClientManager().registerClientService(getSymbolicName(), SERVICE_COMMIT_QUERY_KEY, ClientManager.PARAM_NONE); ClientManager.getClientManager().registerClientService(getSymbolicName(), SERVICE_ROLLBACK_QUERY_KEY, ClientManager.PARAM_NONE); ClientManager.getClientManager().registerClientService(getSymbolicName(), SERVICE_STATUS_KEY, ClientManager.PARAM_DYNAMIC); } // ----------------------------------------------------------------------------- // --------------- Start of transactional layer functions ---------------------- // ----------------------------------------------------------------------------- /** * When a transaction is started, the transactional layer calls this method to * see if we have any reson to stop the transaction being started, and to do * any preparation work that may be necessary before we start. * * @param transactionNumber The transaction to start * @return that we can start the transaction */ @Override public int startTransaction(int transactionNumber) { // We do not have any reason to inhibit the transaction start, so return // the OK flag return 0; } /** * Perform any processing that needs to be done when we are flushing the * transaction; * * @param transactionNumber The transaction to flush * @return that we can flush the transaction */ @Override public int flushTransaction(int transactionNumber) { // close the input stream closeStream(transactionNumber); return 0; } /** * Perform any processing that needs to be done when we are committing the * transaction; * * @param transactionNumber The transaction to commit */ @Override public void commitTransaction(int transactionNumber) { finaliseOutputCommit(transactionNumber); } /** * Perform any processing that needs to be done when we are rolling back the * transaction; * * @param transactionNumber The transaction to rollback */ @Override public void rollbackTransaction(int transactionNumber) { finaliseOutputRollback(transactionNumber); } /** * Close Transaction is the trigger to clean up transaction related * information such as variables, status etc. * * Close down the statements we opened. Because the commit and rollback * statements are optional, we check if they have been defined before we ry to * close them. * * @param transactionNumber The transaction we are working on */ @Override public void closeTransaction(int transactionNumber) { // Close the insert statement DBUtil.close(stmtInsertQuery); // Close the commit statement if (useCommit) { DBUtil.close(stmtCommitQuery); } // Close the rollback statement if (useRollback) { DBUtil.close(stmtRollbackQuery); } // Close the connection DBUtil.close(JDBCcon); } // ----------------------------------------------------------------------------- // --------------- Start of custom initialisation functions --------------------- // ----------------------------------------------------------------------------- /** * The initQuery is the query that will be executed at the beginning of a new * stream of data. This is executed once, and should be used to prepare data * for extraction. * * @return The query string * @throws OpenRate.exception.InitializationException */ public String initInitQuery() throws InitializationException { String query; // Get the init statement from the properties query = PropertyUtils.getPropertyUtils().getBatchOutputAdapterPropertyValueDef(getPipeName(), getSymbolicName(), INIT_QUERY_KEY, "None"); if ((query == null) || query.equalsIgnoreCase("None")) { message = "Output <" + getSymbolicName() + "> - Initialisation statement not found from <" + INIT_QUERY_KEY + ">"; getPipeLog().error(message); throw new InitializationException(message, getSymbolicName()); } return query; } /** * The insertQuery is the query that will be executed to actually handle the * data writing. * * @return The query string * @throws OpenRate.exception.InitializationException */ public String initInsertQuery() throws InitializationException { String query; // Get the init statement from the properties query = PropertyUtils.getPropertyUtils().getBatchOutputAdapterPropertyValueDef(getPipeName(), getSymbolicName(), INSERT_QUERY_KEY, "None"); if ((query == null) || query.equalsIgnoreCase("None")) { message = "Output <" + getSymbolicName() + "> - Initialisation statement not found from <" + INSERT_QUERY_KEY + ">"; getPipeLog().error(message); throw new InitializationException(message, getSymbolicName()); } return query; } /** * The commitQuery is used to finally persist the records to the target table * and "fix" them into the table. * * @return The query string * @throws OpenRate.exception.InitializationException */ public String initCommitQuery() throws InitializationException { String query; // Get the init statement from the properties query = PropertyUtils.getPropertyUtils().getBatchOutputAdapterPropertyValueDef(getPipeName(), getSymbolicName(), COMMIT_QUERY_KEY, "None"); if ((query == null) || query.equalsIgnoreCase("None")) { message = "Output <" + getSymbolicName() + "> - Initialisation statement not found from <" + COMMIT_QUERY_KEY + ">"; getPipeLog().error(message); throw new InitializationException(message, getSymbolicName()); } return query; } /** * The rollbackQuery is used to remove any records that have been written to * the target table, but which have not yet been "fixed". * * @return The query string * @throws OpenRate.exception.InitializationException */ public String initRollbackQuery() throws InitializationException { String query; // Get the init statement from the properties query = PropertyUtils.getPropertyUtils().getBatchOutputAdapterPropertyValueDef(getPipeName(), getSymbolicName(), ROLLBACK_QUERY_KEY, "None"); if ((query == null) || query.equalsIgnoreCase("None")) { message = "Output <" + getSymbolicName() + "> - Initialisation statement not found from <" + ROLLBACK_QUERY_KEY + ">"; getPipeLog().error(message); throw new InitializationException(message, getSymbolicName()); } return query; } /** * Get the data source name from the properties * * @return The query string * @throws OpenRate.exception.InitializationException */ public String initDataSourceName() throws InitializationException { String DSN; DSN = PropertyUtils.getPropertyUtils().getBatchOutputAdapterPropertyValueDef(getPipeName(), getSymbolicName(), DATASOURCE_KEY, "None"); if ((DSN == null) || DSN.equalsIgnoreCase("None")) { message = "Output <" + getSymbolicName() + "> - Datasource name not found from <" + DATASOURCE_KEY + ">"; getPipeLog().error(message); throw new InitializationException(message, getSymbolicName()); } return DSN; } }