/******************************************************************************* * 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.interactions; import java.io.*; import java.util.*; import javax.resource.*; import javax.resource.cci.*; import org.eclipse.persistence.internal.helper.*; import org.eclipse.persistence.internal.sessions.AbstractRecord; import org.eclipse.persistence.internal.sessions.AbstractSession; import org.eclipse.persistence.internal.databaseaccess.Accessor; import org.eclipse.persistence.internal.databaseaccess.DatasourceCall; import org.eclipse.persistence.eis.*; /** * Defines the specification for a call to a JCA interaction. * Builds the input and output records from the arguments. * * @author James * @since OracleAS TopLink 10<i>g</i> (10.0.3) */ public abstract class EISInteraction extends DatasourceCall { /** Adapter specific interaction spec. */ protected InteractionSpec interactionSpec; /** Name of the function the interaction describes. */ protected String functionName; /** Name to pass to the input record creation. */ protected String inputRecordName; /** Adapter specific properties may be added. */ protected Map properties; /** Holds database row of input values. */ protected AbstractRecord inputRow; /** Defines the arguments to the interaction, these can be the values or argument names/fields the values come from. */ protected Vector arguments; /** * Defines the output argument names as defined in the output record for the interaction. * This is shared as indexed interaction may still have mapped results. */ protected Vector outputArgumentNames; /** Defines the field values the output arguments of the interaction map to. These are order dependent with the names. */ protected Vector outputArguments; /** Path to the desired output record if nested. */ protected String outputResultPath; /** * PUBLIC: * Default constructor. */ public EISInteraction() { super(); this.functionName = ""; this.inputRecordName = ""; this.outputResultPath = ""; } /** * PUBLIC: * Define the output argument to the interaction and the field/argument name to be substitute for it. * This is only required if an output row is not used. * The parameterAndArgumentFieldName is the name of the output record argument expected, * and is the field or argument name to be used to be used for it. * These names are assumed to be the same, if not this method can be called with two arguments. */ public void addOutputArgument(String parameterAndArgumentFieldName) { addOutputArgument(parameterAndArgumentFieldName, parameterAndArgumentFieldName); } /** * PUBLIC: * Define the output argument to the interaction and the field/argument name to be substitute for it. * This is only required if an output row is not used. * The parameterName is the name of the output record argument expected. * The argumentFieldName is the field or argument name to be used to be used for it. * If these names are the same (as they normally are) this method can be called with a single argument. */ public void addOutputArgument(String parameterName, String argumentFieldName) { getOutputArgumentNames().addElement(parameterName); getOutputArguments().addElement(new DatabaseField(argumentFieldName)); } /** * The argument fields or values to the interaction that map into the input record. */ public Vector getArguments() { // This is lazy initialized to conserv space on calls that have no parameters. if (arguments == null) { arguments = new Vector(); } return arguments; } /** * INTERNAL: * The argument fields or values to the interaction that map into the input record. */ public void setArguments(Vector arguments) { this.arguments = arguments; } /** * Return if argumented. */ public boolean hasArguments() { return (arguments != null) && (!arguments.isEmpty()); } public boolean isEISInteraction() { return true; } /** * PUBLIC: * The output result path defines the root key for the MappedRecord that * the desired interaction result is nested into. * This is required for read interactions that need a nested record to build from the mapped object. */ public String getOutputResultPath() { return outputResultPath; } /** * PUBLIC: * The output result path defines the root key for the MappedRecord that * the desired interaction result is nested into. * This is required for read interactions that need a nested record to build from the mapped object. */ public void setOutputResultPath(String outputResultPath) { this.outputResultPath = outputResultPath; } /** * The argument names for the output record. */ public Vector getOutputArgumentNames() { // This is lazy initialized to conserv space on calls that have no parameters. if (outputArgumentNames == null) { outputArgumentNames = new Vector(); } return outputArgumentNames; } /** * The argument fields to the interaction that map into the output record. */ public Vector getOutputArguments() { // This is lazy initialized to conserv space on calls that have no parameters. if (outputArguments == null) { outputArguments = new Vector(); } return outputArguments; } /** * The output arguments. */ public void setOutputArguments(Vector outputArguments) { this.outputArguments = outputArguments; } /** * Set the output argument names. */ public void setOutputArgumentNames(Vector outputArgumentNames) { this.outputArgumentNames = outputArgumentNames; } /** * Return if argumented. */ public boolean hasOutputArguments() { return (outputArguments != null) && (!outputArguments.isEmpty()); } /** * Set the default record name from the descriptor. */ public void prepare(AbstractSession session) { if (getInputRecordName().length() == 0) { if ((getQuery() != null) && (getQuery().getDescriptor() instanceof EISDescriptor)) { EISDescriptor descriptor = (EISDescriptor)getQuery().getDescriptor(); setInputRecordName(descriptor.getDataTypeName()); } else { setInputRecordName("input"); } } super.prepare(session); } /** * Create the appropriate record element for the data value. * If the value is a collection, create a collection of elements, * if the value is a map, create a nested map, * otherwise just return the value (primitive data). */ public Object createRecordElement(String elementName, Object value, EISAccessor accessor) { try { Object element = value; // Handle nested collections. if (element instanceof List) { // Convert each element in the list. List values = (List)element; List elements = new Vector(values.size()); for (int index = 0; index < values.size(); index++) { elements.add(createRecordElement(elementName, values.get(index), accessor)); } element = elements; // Handle nested rows. } else if (value instanceof AbstractRecord) { AbstractRecord valuesRow = (AbstractRecord)value; // The record name for the row must be determined, // currently the SDK uses the table name of the row's field for this, // ideally this would be a property on the row. String recordName = elementName; if (valuesRow.size() > 0) { recordName = valuesRow.getFields().get(0).getTableName(); } MappedRecord record = accessor.getRecordFactory().createMappedRecord(recordName); for (Iterator keysIterator = valuesRow.getFields().iterator(); keysIterator.hasNext();) { DatabaseField field = (DatabaseField)keysIterator.next(); Object elementValue = createRecordElement(field.getName(), valuesRow.get(field), accessor); accessor.getEISPlatform().setValueInRecord(field.getName(), elementValue, record, accessor); } element = record; } return element; } catch (ResourceException exception) { throw EISException.resourceException(exception, accessor, null); } } /** * PUBLIC: * Return the JCA InteractionSpec that defines this EIS interaction. * The InteractionSpec is JCA adapter specific and typically * defines the function name. */ public InteractionSpec getInteractionSpec() { return interactionSpec; } /** * PUBLIC: * Set the JCA InteractionSpec that defines this EIS interaction. * The InteractionSpec is JCA adapter specific and typically * defines the function name. */ public void setInteractionSpec(InteractionSpec interactionSpec) { this.interactionSpec = interactionSpec; } /** * Return the string for logging purposes. */ public String getLogString(Accessor accessor) { StringWriter writer = new StringWriter(); writer.write("Executing "); writer.write(toString()); writer.write(Helper.cr()); writer.write("\tspec => "); writer.write(String.valueOf(getInteractionSpec())); writer.write(Helper.cr()); writer.write("\tproperties => "); writer.write(String.valueOf(getProperties())); writer.write(Helper.cr()); writer.write("\tinput => ["); if (!getParameters().isEmpty()) { for (Iterator iterator = getParameters().iterator(); iterator.hasNext();) { Object parameter = iterator.next(); writer.write(String.valueOf(parameter)); if (iterator.hasNext()) { writer.write(", "); } else { writer.write("]"); } } } else { writer.write(String.valueOf(getInputRow())); writer.write("]"); } return writer.toString(); } /** * Arguments to the interaction can be passed two ways. * The entire argument row can be converted to the input record, * or the arguments from the row can be translated into the interaction parameters. */ public void translate(AbstractRecord translationRow, AbstractRecord modifyRow, AbstractSession session) { if (modifyRow != null) { // Avoid clearing a prepared row. setInputRow(modifyRow); } if (hasArguments()) { List parametersValues = new ArrayList(getArguments().size()); for (int index = 0; index < getArguments().size(); index++) { Object argument = getArguments().elementAt(index); // The argument is either a value or a databasefield that needs to be translated. if ((argument != null) && (argument instanceof DatabaseField)) { DatabaseField field = (DatabaseField)argument; Object value = translationRow.get(field); //BUG#12345:modifyRow is null for reads if ((value == null) && (modifyRow != null)) { value = modifyRow.get(field); } parametersValues.add(value); } else { parametersValues.add(argument); } } setParameters(parametersValues); } } /** * Create the appropriate input record for this interaction. * Populate the data into the record from this interaction's arguments. */ public abstract Record createInputRecord(EISAccessor accessor); /** * INTERNAL: * Build a database row from the record returned from the interaction. */ public abstract AbstractRecord buildRow(Record record, EISAccessor accessor); /** * Build a collection of database rows from the Record returned from the interaction. * This handles IndexedRecords used as sets of result records, * and a single MappedRecord with a list of result records. */ public Vector buildRows(Record record, EISAccessor accessor) { Vector rows = null; if (record instanceof IndexedRecord) { IndexedRecord indexedRecord = (IndexedRecord)record; rows = new Vector(indexedRecord.size()); for (int index = 0; index < indexedRecord.size(); index++) { Object element = indexedRecord.get(index); if (element instanceof Record) { rows.addElement(buildRow((Record)element, accessor)); } else { // It is a single row record. rows.add(buildRow(record, accessor)); return rows; } } } else if (record instanceof MappedRecord) { MappedRecord mappedRecord = (MappedRecord)record; // Handle the case of a single output argument of the entire row contained within the return record. if (getOutputResultPath().length() > 0) { Object element = mappedRecord.get(getOutputResultPath()); // Handles nested rows inside an IndexedRecord, MappedRecord or List. if (element instanceof IndexedRecord) { return buildRows((IndexedRecord)element, accessor); } else if (element instanceof MappedRecord) { mappedRecord = (MappedRecord)element; } else if (element instanceof List) { List elements = (List)element; rows = new Vector(elements.size()); for (int index = 0; index < elements.size(); index++) { Object elementValue = elements.get(index); if (elementValue instanceof Record) { rows.addElement(buildRow((Record)elementValue, accessor)); } else { rows.add(elementValue); } } return rows; } } rows = new Vector(1); AbstractRecord row = new EISMappedRecord(mappedRecord, accessor); rows.add(row); } else { rows = new Vector(1); } return rows; } /** * PUBLIC: * Return the function name of the interaction. * A function name is a common properties for a CCI InteractionSpec. * This cannot be generically set on the spec as it is not in the interface * however the EIS platform may make use of this in build the interaction spec. */ public String getFunctionName() { return functionName; } /** * PUBLIC: * Set the function name of the interaction. * A function name is a common properties for a CCI InteractionSpec. * This cannot be generically set on the spec as it is not in the interface * however the EIS platform may make use of this in build the interaction spec. */ public void setFunctionName(String functionName) { this.functionName = functionName; } /** * PUBLIC: * Return the input record name. * This is used as the name passed to the adapter when creating the input record. */ public String getInputRecordName() { return inputRecordName; } /** * PUBLIC: * Set the input record name. * This is used as the name passed to the adapter when creating the input record. */ public void setInputRecordName(String recordName) { this.inputRecordName = recordName; } /** * Returns the adapter specific properties. */ public Map getProperties() { if (properties == null) { properties = new HashMap(5); } return properties; } /** * PUBLIC: * Returns the adapter property associated the given name. * If the interaction is associated with a query that has a descriptor, * The descriptor's properties are automatically inherited. */ public Object getProperty(String name) { Object value = getProperties().get(name); // Check descriptor property if available. if ((value == null) && (getQuery() != null) && (getQuery().getDescriptor() != null)) { value = getQuery().getDescriptor().getProperty(name); } return value; } /** * Set the adapter specific properties. */ public void setProperties(Map properties) { this.properties = properties; } /** * PUBLIC: * Set the adapter specific property. */ public void setProperty(String name, Object value) { getProperties().put(name, value); } /** * INTERNAL: * Return the input database row. */ public AbstractRecord getInputRow() { return inputRow; } /** * PUBLIC: * Set the input database row. */ public void setInputRow(AbstractRecord inputRow) { this.inputRow = inputRow; } public String toString() { return Helper.getShortClassName(getClass()) + "(" + getFunctionName() + ")"; } }