blob: e7cf254f3443a3cfe398d99ed07dab712b12e4bd [file] [log] [blame]
/*
* Copyright (c) 1998, 2021 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 v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// 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 jakarta.resource.*;
import jakarta.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 jakarta.resource.cci.Record createInputRecord(EISInteraction interaction, EISAccessor accessor) {
jakarta.resource.cci.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 jakarta.resource.cci.Record createOutputRecord(EISInteraction interaction, AbstractRecord translationRow, EISAccessor accessor) {
return null;
}
/**
* INTERNAL:
* Allow the platform to handle record to row conversion.
*/
public AbstractRecord buildRow(jakarta.resource.cci.Record record, EISInteraction interaction, EISAccessor accessor) {
jakarta.resource.cci.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<AbstractRecord> buildRows(jakarta.resource.cci.Record record, EISInteraction interaction, EISAccessor accessor) {
jakarta.resource.cci.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 jakarta.resource.cci.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(jakarta.resource.cci.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, jakarta.resource.cci.Record record, EISInteraction call, EISAccessor accessor) {
if (domMethod == null) {
Class<?>[] argumentTypes = (Class<?>[]) 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 jakarta.resource.cci.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.
*/
@Override
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);
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.
*/
@Override
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.
*/
@Override
public DatasourceCall buildNativeCall(String queryString) {
return new QueryStringInteraction(queryString);
}
}