/******************************************************************************* * Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Oracle - initial API and implementation from Oracle TopLink ******************************************************************************/ package org.eclipse.persistence.eis; import javax.resource.ResourceException; import javax.resource.cci.Connection; import javax.resource.cci.ConnectionMetaData; import javax.resource.cci.Interaction; import javax.resource.cci.InteractionSpec; import javax.resource.cci.Record; import javax.resource.cci.RecordFactory; import org.eclipse.persistence.eis.interactions.EISInteraction; import org.eclipse.persistence.exceptions.DatabaseException; import org.eclipse.persistence.exceptions.QueryException; import org.eclipse.persistence.internal.databaseaccess.DatasourceAccessor; import org.eclipse.persistence.internal.helper.Helper; import org.eclipse.persistence.internal.sessions.AbstractRecord; import org.eclipse.persistence.internal.sessions.AbstractSession; import org.eclipse.persistence.logging.SessionLog; import org.eclipse.persistence.queries.Call; import org.eclipse.persistence.sessions.SessionProfiler; /** * <p><code>EISAccessor</code> is an implementation of the <code>Accessor</code> * interface. It is responsible for: * <ul> * <li>Connecting via connection factory * <li>Local transactions * <li>Interaction execution * <li>Record translation * </ul> * * @see EISInteraction * @see EISLogin * * @author James * @since OracleAS TopLink 10<i>g</i> (10.0.3) */ public class EISAccessor extends DatasourceAccessor { protected Connection cciConnection; protected RecordFactory recordFactory; /** * Default Constructor. */ public EISAccessor() { super(); } /** * Begin a local transaction. */ @Override protected void basicBeginTransaction(AbstractSession session) throws EISException { try { if (getEISPlatform().supportsLocalTransactions()) { getCCIConnection().getLocalTransaction().begin(); } } catch (ResourceException exception) { throw EISException.resourceException(exception, this, session); } } /** * Close the connection. */ @Override protected void closeDatasourceConnection() { try { getCCIConnection().close(); } catch (ResourceException exception) { throw EISException.resourceException(exception, this, null); } } /** * Commit the local transaction. */ @Override protected void basicCommitTransaction(AbstractSession session) throws EISException { try { if (getEISPlatform().supportsLocalTransactions()) { getCCIConnection().getLocalTransaction().commit(); } } catch (ResourceException exception) { throw EISException.resourceException(exception, this, session); } } /** * If logging is turned on and the CCI implementation supports meta data then display connection info. */ @Override protected void buildConnectLog(AbstractSession session) { try { // Log connection information. if (session.shouldLog(SessionLog.CONFIG, SessionLog.CONNECTION)) {// Avoid printing if no logging required. ConnectionMetaData metaData = getCCIConnection().getMetaData(); Object[] args = { metaData.getUserName(), metaData.getEISProductName(), metaData.getEISProductVersion(), Helper.cr(), "\t" }; session.log(SessionLog.CONFIG, SessionLog.CONNECTION, "connected_user_database", args, this); } } catch (ResourceException exception) { // Some databases do not support metadata, ignore exception. session.warning("JDBC_driver_does_not_support_meta_data", SessionLog.CONNECTION); } } /** * Avoid super to have logging occur after possible manual auto-commit. */ @Override public Object executeCall(Call call, AbstractRecord translationRow, AbstractSession session) throws DatabaseException { return basicExecuteCall(call, translationRow, session); } /** * Execute the interaction. * The execution can differ slightly depending on the type of interaction. * The call may be parameterized where the arguments are in the translation row. * The row will be empty if there are no parameters. * @return depending of the type either the row count, row or vector of rows. */ @Override public Object basicExecuteCall(Call call, AbstractRecord translationRow, AbstractSession session) throws DatabaseException { // If the login is null, then this accessor has never been connected. if (getLogin() == null) { throw DatabaseException.databaseAccessorNotConnected(); } Interaction interaction = null; Object result = null; EISInteraction eisCall = null; try { eisCall = (EISInteraction)call; } catch (ClassCastException e) { throw QueryException.invalidDatabaseCall(call); } // Record and check if auto-commit is required. // Some platforms may require this (AQ). boolean autoCommit = (!isInTransaction()) && getEISPlatform().requiresAutoCommit(); if (autoCommit) { beginTransaction(session); } try { if (session.shouldLog(SessionLog.FINE, SessionLog.SQL)) {// pre-check to improve performance session.log(SessionLog.FINE, SessionLog.SQL, call.getLogString(this), (Object[])null, this, false); } incrementCallCount(session); session.startOperationProfile(SessionProfiler.SqlPrepare, eisCall.getQuery(), SessionProfiler.ALL); Record input = null; Record output = null; try { interaction = getCCIConnection().createInteraction(); input = getEISPlatform().createInputRecord(eisCall, this); output = getEISPlatform().createOutputRecord(eisCall, translationRow, this); } finally { session.endOperationProfile(SessionProfiler.SqlPrepare, eisCall.getQuery(), SessionProfiler.ALL); } session.startOperationProfile(SessionProfiler.StatementExecute, eisCall.getQuery(), SessionProfiler.ALL); try { boolean success = true; InteractionSpec interactionSpec = getEISPlatform().buildInteractionSpec(eisCall); if (output == null) { output = interaction.execute(interactionSpec, input); } else { success = interaction.execute(interactionSpec, input, output); } session.log(SessionLog.FINEST, SessionLog.QUERY, "adapter_result", output); if (eisCall.isNothingReturned()) { if (success) { result = Integer.valueOf(1); } else { result = Integer.valueOf(0); } // Fire the output parameter row to allow app to handle return value. if (output != null) { AbstractRecord outputRow = getEISPlatform().buildRow(output, eisCall, this); if (outputRow != null) { eisCall.getQuery().setProperty("output", outputRow); if (session.hasEventManager()) { session.getEventManager().outputParametersDetected(outputRow, eisCall); } } } } else if (eisCall.isOneRowReturned()) { result = getEISPlatform().buildRow(output, eisCall, this); } else { result = getEISPlatform().buildRows(output, eisCall, this); } session.log(SessionLog.FINEST, SessionLog.QUERY, "data_access_result", output); } finally { session.endOperationProfile(SessionProfiler.StatementExecute, eisCall.getQuery(), SessionProfiler.ALL); } } catch (ResourceException exception) { // Ensure each resource is released, but still ensure that the real exception is thrown. if (interaction != null) { try { interaction.close(); } catch (Exception closeException) { // Ignore error to avoid masking real exception. } } try { decrementCallCount(); } catch (Exception closeException) { // Ignore error to avoid masking real exception. } try { if (autoCommit) { commitTransaction(session); } } catch (Exception closeException) { // Ignore error to avoid masking real exception. } throw EISException.resourceException(exception, call, this, session); } catch (RuntimeException exception) { try {// Ensure that the statement is closed, but still ensure that the real exception is thrown. try { if (interaction != null) { interaction.close(); } } finally { if (autoCommit) { commitTransaction(session); } } } catch (Exception closeException) { } throw exception; } boolean transactionCommitted = false; boolean countDecremented = false; // This is in separate try block to ensure that the real exception is not masked by the close exception. try { interaction.close(); if (autoCommit) { commitTransaction(session); } transactionCommitted = true; decrementCallCount(); countDecremented = true; } catch (ResourceException exception) { try { if (!transactionCommitted) { if (autoCommit) { commitTransaction(session); } } } catch (Exception ignore) { // Ignore error to avoid masking real exception. } try { if (!countDecremented) { decrementCallCount(); } } catch (Exception ignore) { // Ignore error to avoid masking real exception. } throw EISException.resourceException(exception, this, session); } return result; } /** * Return the CCI connection to the EIS resource adapter. */ public Connection getCCIConnection() { return (Connection)getDatasourceConnection(); } /** * Return and cast the platform. */ public EISPlatform getEISPlatform() { return (EISPlatform)getDatasourcePlatform(); } /** * Return the RecordFactory. * The record factory is acquired from the ConnectionManager, * and used to create record to pass to interactions. */ public RecordFactory getRecordFactory() { return recordFactory; } /** * Set the RecordFactory. * The record factory is acquired from the ConnectionManager, * and used to create record to pass to interactions. */ public void setRecordFactory(RecordFactory recordFactory) { this.recordFactory = recordFactory; } /** * Rollback the local transaction on the datasource. */ @Override public void basicRollbackTransaction(AbstractSession session) throws DatabaseException { try { if (getEISPlatform().supportsLocalTransactions()) { getCCIConnection().getLocalTransaction().rollback(); } } catch (ResourceException exception) { throw EISException.resourceException(exception, this, session); } } /** * Return if the connection to the "data source" is connected. */ @Override protected boolean isDatasourceConnected() { return isConnected; } }