/******************************************************************************* * 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 java.security.AccessController; import java.security.PrivilegedActionException; import java.util.*; import java.io.*; import java.lang.reflect.*; import javax.resource.*; import javax.resource.cci.*; import org.w3c.dom.Element; import org.eclipse.persistence.internal.helper.*; import org.eclipse.persistence.exceptions.*; import org.eclipse.persistence.queries.*; import org.eclipse.persistence.eis.interactions.*; import org.eclipse.persistence.internal.databaseaccess.DatasourceCall; import org.eclipse.persistence.internal.databaseaccess.DatasourcePlatform; import org.eclipse.persistence.internal.expressions.SQLStatement; import org.eclipse.persistence.internal.oxm.XMLConversionManager; import org.eclipse.persistence.internal.security.PrivilegedAccessHelper; import org.eclipse.persistence.internal.security.PrivilegedGetMethod; import org.eclipse.persistence.internal.security.PrivilegedMethodInvoker; import org.eclipse.persistence.internal.sessions.AbstractRecord; import org.eclipse.persistence.internal.sessions.AbstractSession; /** * <p>An <code>EISPlatform</code> defines any EIS adapter specific behavior. * This may include: * <ul> * <li>Instantiation of the adapter InteractionSpec * <li>Conversion from an adapter custom Record * <li>Perform platform specific record access * <li>Provide XML DOM record conversion * <li>Provide sequence support * </ul> * * <p><code>EISPlatform</code> also supports behavior for specifying the record * types supported and automatic data-conversion to strings. * * <p>Although use of the generic <code>EISPlatform</code> may be sufficient, * some adapters may require that a specific platform be defined. * * @author James * @since OracleAS TopLink 10<i>g</i> (10.0.3) */ public class EISPlatform extends DatasourcePlatform { protected boolean isIndexedRecordSupported; protected boolean isMappedRecordSupported; protected boolean isDOMRecordSupported; /** Can be used for adapters that only support String data/XML. */ protected boolean shouldConvertDataToStrings; /** Allows for usage of transaction to be disabled if not supported. */ protected boolean supportsLocalTransactions; /** Can be used if a commit is required after every interaction outside of a local transaction. */ protected boolean requiresAutoCommit; /** Can be used to convert from an adapter specific record. */ protected RecordConverter recordConverter; /** Used to reflectively provide XML record support as DOMRecord is not part of the JCA-CCI spec. */ protected Method domMethod; /** For XML usage, an XMLConversionManager instance is required */ protected XMLConversionManager xmlConversionManager; /** * Default constructor. */ public EISPlatform() { super(); setIsMappedRecordSupported(true); setIsIndexedRecordSupported(true); setIsDOMRecordSupported(false); setShouldConvertDataToStrings(false); setSupportsLocalTransactions(true); setRequiresAutoCommit(false); } /** * Return the record converter. */ public RecordConverter getRecordConverter() { return recordConverter; } /** * Set the record converter. * Can be used to convert from an adapter specific record. */ public void setRecordConverter(RecordConverter recordConverter) { this.recordConverter = recordConverter; } /** * Return if this platform requires auto commit of the local transaction * for interactions outside of an interaction. */ public boolean requiresAutoCommit() { return requiresAutoCommit; } /** * Set if this platform requires auto commit of the local transaction * for interactions outside of an interaction. */ public void setRequiresAutoCommit(boolean requiresAutoCommit) { this.requiresAutoCommit = requiresAutoCommit; } /** * Return if this platform supports local transactions. */ public boolean supportsLocalTransactions() { return supportsLocalTransactions; } /** * Set if this platform supports local transactions. */ public void setSupportsLocalTransactions(boolean supportsLocalTransactions) { this.supportsLocalTransactions = supportsLocalTransactions; } /** * Return if this platform supports JCA IndexedRecord. */ public boolean isIndexedRecordSupported() { return isIndexedRecordSupported; } /** * Set if this platform supports JCA IndexedRecord. */ public void setIsIndexedRecordSupported(boolean isIndexedRecordSupported) { this.isIndexedRecordSupported = isIndexedRecordSupported; } /** * Return if this platform supports JCA MappedRecord. */ public boolean isMappedRecordSupported() { return isMappedRecordSupported; } /** * Set if this platform supports JCA MappedRecord. */ public void setIsMappedRecordSupported(boolean isMappedRecordSupported) { this.isMappedRecordSupported = isMappedRecordSupported; } /** * Return if this platform supports XML/DOM Records. */ public boolean isDOMRecordSupported() { return isDOMRecordSupported; } /** * Set if this platform supports XML/DOM Records. */ public void setIsDOMRecordSupported(boolean isDOMRecordSupported) { this.isDOMRecordSupported = isDOMRecordSupported; } /** * Return if all data set into the adapter should be first converted to strings. */ public boolean shouldConvertDataToStrings() { return shouldConvertDataToStrings; } /** * Set if all data set into the adapter should be first converted to strings. */ public void setShouldConvertDataToStrings(boolean shouldConvertDataToStrings) { this.shouldConvertDataToStrings = shouldConvertDataToStrings; } /** * Allow the platform to build the interaction spec based on properties defined in the interaction. */ public InteractionSpec buildInteractionSpec(EISInteraction interaction) { return interaction.getInteractionSpec(); } /** * Allow the platform to create the appropriate type of record for the interaction. */ public Record createInputRecord(EISInteraction interaction, EISAccessor accessor) { Record input = interaction.createInputRecord(accessor); if (getRecordConverter() != null) { input = getRecordConverter().converterToAdapterRecord(input); } return input; } /** * Allow the platform to create the appropriate type of record for the interaction. * If an output record is not required then null is returned. */ public Record createOutputRecord(EISInteraction interaction, AbstractRecord translationRow, EISAccessor accessor) { return null; } /** * INTERNAL: * Allow the platform to handle record to row conversion. */ public AbstractRecord buildRow(Record record, EISInteraction interaction, EISAccessor accessor) { Record output = record; if (getRecordConverter() != null) { output = getRecordConverter().converterFromAdapterRecord(output); } return interaction.buildRow(output, accessor); } /** * Allow the platform to handle record to row conversion. */ public Vector buildRows(Record record, EISInteraction interaction, EISAccessor accessor) { Record output = record; if (getRecordConverter() != null) { output = getRecordConverter().converterFromAdapterRecord(output); } return interaction.buildRows(output, accessor); } /** * Allow the platform to handle the creation of the DOM record. * By default create a mapped record an assume it implements DOM as well. */ public Record createDOMRecord(String recordName, EISAccessor accessor) { try { return accessor.getRecordFactory().createMappedRecord(recordName); } catch (ResourceException exception) { throw EISException.resourceException(exception, accessor, null); } } /** * INTERNAL: * Allow the platform to handle the creation of the Record for the DOM record. * By default instantiate an EISDOMRecord which introspects the record for a getDOM method. */ public AbstractRecord createDatabaseRowFromDOMRecord(Record record, EISInteraction call, EISAccessor accessor) { return new EISDOMRecord(record); } /** * Retrieves the field value from the record. * This allows for the platform to perform any platform specific translation or conversion. */ public Object getValueFromRecord(String key, MappedRecord record, EISAccessor accessor) { return record.get(key); } /** * Stores the XML DOM value into the record. * This must be implemented by the platform if it support XML/DOM records. */ public void setDOMInRecord(Element dom, Record record, EISInteraction call, EISAccessor accessor) { if (domMethod == null) { Class[] argumentTypes = new Class[1]; argumentTypes[0] = Element.class; try { if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ domMethod = AccessController.doPrivileged(new PrivilegedGetMethod(record.getClass(), "setDom", argumentTypes, false)); }else{ domMethod = PrivilegedAccessHelper.getMethod(record.getClass(), "setDom", argumentTypes, false); } } catch (Exception notFound) { try { if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ domMethod = AccessController.doPrivileged(new PrivilegedGetMethod(record.getClass(), "setDOM", argumentTypes, false)); }else{ domMethod = PrivilegedAccessHelper.getMethod(record.getClass(), "setDOM", argumentTypes, false); } } catch (Exception cantFind) { throw new EISException(cantFind); } } } try { Object[] arguments = new Object[1]; arguments[0] = dom; if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ try{ AccessController.doPrivileged(new PrivilegedMethodInvoker(domMethod, record, arguments)); }catch (PrivilegedActionException ex){ throw (Exception)ex.getCause(); } }else{ PrivilegedAccessHelper.invokeMethod(domMethod, record, arguments); } } catch (Exception error) { throw new EISException(error); } } /** * Stores the field value into the record. * This allows for the platform to perform any platform specific translation or conversion. */ public void setValueInRecord(String key, Object value, MappedRecord record, EISAccessor accessor) { Object recordValue = value; if (shouldConvertDataToStrings() && !(value instanceof Record) && !(value instanceof Collection)) { recordValue = getConversionManager().convertObject(value, ClassConstants.STRING); } record.put(key, recordValue); } /** * Add the parameter. * Convert the parameter to a string and write it. * Convert rows to XML strings. */ public void appendParameter(Call call, Writer writer, Object parameter) { if (parameter instanceof Vector) { Vector records = (Vector)parameter; // May be a collection of record. for (int index = 0; index < records.size(); index++) { appendParameter(call, writer, (records).elementAt(index)); } } else if (parameter instanceof org.eclipse.persistence.oxm.record.DOMRecord) { String xml = ((org.eclipse.persistence.oxm.record.DOMRecord)parameter).transformToXML(); // For some reason the transform always prints the XML header, so trim it off. int start = xml.indexOf('>'); xml = xml.substring(start + 1, xml.length()); try { writer.write(xml); } catch (IOException exception) { throw ValidationException.fileError(exception); } } else { super.appendParameter(call, writer, parameter); } } /** * The platform holds its own instance of conversion manager to allow customization. */ public ConversionManager getConversionManager() { // For XML we need an XMLConversionManager instance if (isDOMRecordSupported()) { // Lazy init for serialization. if (xmlConversionManager == null) { // Clone the default to allow customers to easily override the conversion manager xmlConversionManager = (XMLConversionManager) XMLConversionManager.getDefaultXMLManager().clone(); xmlConversionManager.setLoader(super.getConversionManager().getLoader()); } return xmlConversionManager; } // For non-XML, return the ConversionManager instance from DatasourcePlatform return super.getConversionManager(); } /** * INTERNAL: * Override this method to throw an exception by default. * Platforms that support dynamic querying can override this to generate an EISInteraction. */ public DatasourceCall buildCallFromStatement(SQLStatement statement, DatabaseQuery query, AbstractSession session) { throw QueryException.noCallOrInteractionSpecified(); } /** * INTERNAL: * Return the correct call type for the native query string. * This allows EIS platforms to use different types of native calls. */ public DatasourceCall buildNativeCall(String queryString) { return new QueryStringInteraction(queryString); } }