TempTableResultSet.java
/*
** Module : TempTableResultSet.java
** Abstract : A result set used to serialize temp table data and send it to a remote side.
**
** Copyright (c) 2013-2023, Golden Code Development Corporation.
**
** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
** 001 CA 20130610 Created initial version.
** 002 CA 20140110 Protected against NPEs, when used from Java side (for a OUTPUT parameter).
** 003 CA 20140124 Added NPE protection.
** 004 CA 20140214 Do not read _multiplex column; use the actual type instead of Hibernate type
** names when creating a new PropertyDefinition.
** 005 CA 20140626 Fixed usage from Java code: the default c'tor must not be used, always use
** the TempTableResultSet(boolean) c'tor with the argument set to false, as
** from Java code instances of this class can be used only in OUTPUT mode.
** 006 SVL 20140709 Legacy property names are passed in the result set.
** 007 OM 20140801 Initialize iterators after setting the rows from appserver call.
** 008 CA 20140812 Added warnings to APIs which should not be used outside of P2J internal code.
** 009 EVL 20160223 Javadoc fixes to make compatible with Oracle Java 8 for Solaris 10.
** 010 ECF 20160225 Changes required by new PropertyHelper implementation.
** 011 ECF 20190823 Temporary workaround for regression caused by new variables in temp-table
** DMOs. TODO: proper fix needed, as this workaround breaks dataset support.
** 012 CA 20190812 Added another c'tor, which allows remote side arguments.
** CA 20190826 Ensure the legacy table properties are returned in the correct legacy order.
** 013 CA 20191119 Added support for before-table.
** 014 CA 20200427 Flush the buffer before reading the records.
** 015 ECF 20200906 New ORM implementation.
** 016 OM 20201001 Improved DMO manipulation performance by caching slow Property annotation access.
** SVL 20201030 Reflect extension of PropertyDefinition.
** 017 SVL 20210614 Added copyRows parameter.
** SVL 20210701 Added clearRows.
** CA 20221006 Do not access tableHandle() in the FWD runtime, get the TempTable instance directly.
** Refs #6826
** OM 20221220 Simlpified TempTableBuilder.getOrderedPropertyNames() signature/implementation.
** IAS 20221029 Re-worked 'init' method.
** SVL 20230113 Improved performance by replacing some "for-each" loops with indexed "for" loops.
*/
/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 of the
** License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU Affero General Public License for more details.
**
** You may find a copy of the GNU Affero GPL version 3 at the following
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
**
** Additional terms under GNU Affero GPL version 3 section 7:
**
** Under Section 7 of the GNU Affero GPL version 3, the following additional
** terms apply to the works covered under the License. These additional terms
** are non-permissive additional terms allowed under Section 7 of the GNU
** Affero GPL version 3 and may not be removed by you.
**
** 0. Attribution Requirement.
**
** You must preserve all legal notices or author attributions in the covered
** work or Appropriate Legal Notices displayed by works containing the covered
** work. You may not remove from the covered work any author or developer
** credit already included within the covered work.
**
** 1. No License To Use Trademarks.
**
** This license does not grant any license or rights to use the trademarks
** Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
** of Golden Code Development Corporation. You are not authorized to use the
** name Golden Code, FWD, or the names of any author or contributor, for
** publicity purposes without written authorization.
**
** 2. No Misrepresentation of Affiliation.
**
** You may not represent yourself as Golden Code Development Corporation or FWD.
**
** You may not represent yourself for publicity purposes as associated with
** Golden Code Development Corporation, FWD, or any author or contributor to
** the covered work, without written authorization.
**
** 3. No Misrepresentation of Source or Origin.
**
** You may not represent the covered work as solely your work. All modified
** versions of the covered work must be marked in a reasonable way to make it
** clear that the modified work is not originating from Golden Code Development
** Corporation or FWD. All modified versions must contain the notices of
** attribution required in this license.
*/
package com.goldencode.p2j.persist;
import com.goldencode.p2j.persist.orm.*;
import java.sql.*;
import java.util.*;
/**
* Implements a method of serializing the metadata and the full data of a temp-table, specified
* by a {@link Temporary temp-table DMO}. The data is read fully in memory and sent to the remote
* side row by row; the DMO metadata is used to extract the properties and their settings (type
* and extent). Note that each send row will have the values in the same order as the DMO's
* properties.
* <p>
* The {@link #TempTableResultSet(Temporary, boolean, boolean, boolean)} must not be used outside
* of the P2J runtime, as it is dependent on the P2J's persistence frame to read the DMO's data.
* <p>
* From Java code, use the {@link #TempTableResultSet()} to instantiate a new, empty, result set;
* this will be used as a container to transport back the result and the metadat.
*/
public class TempTableResultSet
extends TableResultSet
{
/**
* The DMO associated with the temp table; this will be used to extract the DMO metadata and
* the table data.
*/
private Temporary dmo;
/** Iterator over the DMO's properties. */
private Iterator<PropertyDefinition> propIter;
/** Iterator over the temp-table's data. */
private Iterator<Object[]> rowIter;
/** Iterator over the temp-table rows' meta information. */
private Iterator<Object[]> rowMetaIter;
/**
* Default c'tor, explicitly added to allow instances of this class to be created on
* deserialization. Not for public use.
*/
public TempTableResultSet()
{
}
/**
* Create a new instance of this result set, when it acts as a container for an
* OUTPUT parameter received from the remote side.
*
* @param output
* Flag indicating this table is sent in OUTPUT mode. Must always be set to
* <code>true</code>.
*
* @throws IllegalStateException
* If the <code>output</code> parameter is not set to <code>true</code>.
*/
public TempTableResultSet(boolean output)
{
super(false, output, false);
if (!output)
{
throw new IllegalStateException("This c'tor must be used only in OUTPUT mode!");
}
}
/**
* Create a new instance of this result set, when it acts as a container for an
* OUTPUT parameter received from the remote side.
*
* @param output
* Flag indicating this table is sent in OUTPUT mode. Must always be set to
* <code>true</code>.
* @param append
* Flag indicating this table is sent in APPEND mode.
*
* @throws IllegalStateException
* If the <code>output</code> parameter is not set to <code>true</code>.
*/
public TempTableResultSet(boolean output, boolean append)
{
super(false, output, append);
if (!output)
{
throw new IllegalStateException("This c'tor must be used only in OUTPUT mode!");
}
}
/**
* Create a new instance of this result set and associate it with the metadata and data for
* the specified temp DMO.
*
* @param dmo
* DMO instance returned by a previous call to {@link TemporaryBuffer#define} on the
* source temporary buffer.
* @param input
* Flag indicating this table is sent in INPUT or INPUT-OUTPUT mode.
* @param output
* Flag indicating this table is sent in OUTPUT or INPUT-OUTPUT mode.
* @param append
* Flag indicating this table is sent in APPEND mode.
*
*/
public TempTableResultSet(Temporary dmo, boolean input, boolean output, boolean append)
{
this(dmo, input, output, append, true);
}
/**
* Create a new instance of this result set and associate it with the metadata and data for
* the specified temp DMO.
*
* @param dmo
* DMO instance returned by a previous call to {@link TemporaryBuffer#define} on the
* source temporary buffer.
* @param input
* Flag indicating this table is sent in INPUT or INPUT-OUTPUT mode.
* @param output
* Flag indicating this table is sent in OUTPUT or INPUT-OUTPUT mode.
* @param append
* Flag indicating this table is sent in APPEND mode.
* @param copyRows
* <code>true</code>> to transfer rows in INPUT direction. <code>false</code>> to transfer only
* table definitions.
*/
public TempTableResultSet(Temporary dmo, boolean input, boolean output, boolean append, boolean copyRows)
{
super(input, output, append);
this.dmo = dmo;
if (dmo != null)
{
init(copyRows);
}
}
/**
* Set the data sent by a remote side, after the remote appserver call.
*
* @param rows
* The data sent by the remote side.
*/
@Override
public void setRows(java.util.List<Object[]> rows)
{
super.setRows(rows);
rowIter = rows.iterator();
}
/**
* Set the meta row information sent by a remote side, after the remote appserver call.
*
* @param rowsMeta
* The meta information sent by the remote side.
*/
@Override
public void setRowsMeta(java.util.List<Object[]> rowsMeta)
{
super.setRowsMeta(rowsMeta);
rowMetaIter = rowsMeta.iterator();
}
/**
* Set the metadata sent by a remote side, after the remote appserver call.
*
* @param properties
* The metadata sent by the remote side.
*/
@Override
public void setProperties(java.util.List<PropertyDefinition> properties)
{
super.setProperties(properties);
propIter = properties.iterator();
}
/**
* Check if there are more properties for this DMO.
* <p>
* WARNING: this allows only one iteration over the properties and is for internal usage
* only. Use {@link #asResultSet()} and the associated {@link ResultSetMetaData} instead.
*
* @return See above.
*/
public boolean hasMoreProperties()
{
return (propIter == null) ? false : propIter.hasNext();
}
/**
* Returns the next property of this DMO.
* <p>
* WARNING: this allows only one iteration over the properties and is for internal usage
* only. Use {@link #asResultSet()} and the associated {@link ResultSetMetaData} instead.
*
* @return See above.
*/
public PropertyDefinition nextProperty()
{
return propIter.next();
}
/**
* Check if there are more rows for this table.
* <p>
* WARNING: this allows only one iteration over the properties and is for internal usage
* only. Use {@link #asResultSet()} instead.
*
* @return See above.
*/
public boolean hasMoreRows()
{
return (rowIter == null) ? false : rowIter.hasNext();
}
/**
* Returns the data for the next row.
* <p>
* WARNING: this allows only one iteration over the properties and is for internal usage
* only. Use {@link #asResultSet()} instead.
*
* @return See above.
*/
public Object[] nextRow()
{
return rowIter.next();
}
/**
* Used in conjunction with {@link #hasMoreRows()}, it provides a mechanism for subclasses to
* send the row meta information in an iterator-like form.
* <p>
* Override this method so that when {@link #hasMoreRows} returns <code>true</code>, this will
* return the next row in the table.
* <p>
* WARNING: this allows only one iteration over the properties and is for internal usage
* only. Use {@link #asResultSet()} and instead.
*
* @return Default it returns <code>null</code>.
*/
@Override
public Object[] nextRowMeta()
{
return rowMetaIter.next();
}
/**
* Delete row data.
*/
@Override
public void clearRows()
{
super.clearRows();
rowIter = null;
rowMetaIter = null;
}
/**
* Initialize this result set, by reading the entire temp-table in memory.
*
* @param copyRows
* <code>true</code>> to transfer rows in INPUT direction. <code>false</code>> to transfer only
* table definitions.
*
* TODO: improve this (i.e. use an open cursor or some other alternative to not read the data
* in memory)
*
*/
private void init(boolean copyRows)
{
TemporaryBuffer buffer = (TemporaryBuffer) ((BufferReference) dmo).buffer();
// this is executed on the top-level block finish, before the buffer scope finalizables.
// so we need to flush here, to ensure all records are in the database.
try
{
buffer.flush();
}
catch (ValidationException e1)
{
// TODO: can this happen?
}
Class<? extends DataModelObject> srcIface = buffer.getDMOInterface();
TempTable tt = buffer.getParentTable();
DmoMeta dmoInfo = DmoMetadataManager.getDmoInfo(srcIface);
String[] dmoProps = TempTableBuilder.getOrderedPropertyNames(tt);
Map<String, Integer> dmoPropIndex = new HashMap<>();
for (int i = 0; i < dmoProps.length; i++)
{
dmoPropIndex.put(dmoProps[i], i);
}
Iterator<Property> iter = dmoInfo.getFields(false); // skip properties that are not accessible via the DMO's API.
Map<Integer, PropertyDefinition> mprops = new TreeMap<>();
iter.forEachRemaining(prop -> mprops.put(dmoPropIndex.get(prop.name), new PropertyDefinition(prop)));
ArrayList<PropertyDefinition> lprops = new ArrayList<>(mprops.values());
if (copyRows)
{
java.util.List<Object[][]> rowData = TemporaryBuffer.readAllRows(dmo, lprops);
java.util.List<Object[]> rows = new ArrayList<>(rowData.size());
java.util.List<Object[]> rowsMeta = new ArrayList<>(rowData.size());
rowData.forEach(rd -> {
rows.add(rd[0]);
rowsMeta.add(rd[1]);
});
rowIter = rows.iterator();
rowMetaIter = rowsMeta.iterator();
}
propIter = lprops.iterator();
}
}