LegacyOpenClientCaller.java
/*
** Module : LegacyOpenClientCaller.java
** Abstract : Defines APIs to manage the arguments and perform pseudo-dynamic calls, for the case when
** OpenClient uses the 'invokeWithArgs' API.
**
** Copyright (c) 2023-2024, Golden Code Development Corporation.
**
** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
** 001 CA 20230712 Created initial version.
** 002 CA 20240328 Added APIs to associate a pseudo-dynamic argument as a parameter to a static dataset/table.
** Automatically clean up created resources during argument processing, which are still valid.
** 003 CA 20240512 Do not register invalid resources created from the varargs via 'getTableHandle' or
** 'getDataSetHandle'.
*/
/*
** 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.util;
import java.util.*;
import com.goldencode.p2j.oo.lang.*;
import com.goldencode.p2j.persist.*;
import com.goldencode.p2j.security.*;
import com.goldencode.p2j.util.ProcedureManager.ProcedureHelper;
/**
* Defines APIs to manage the arguments sent by the {@link LegacyJavaAppserverClient#invokeWithArgs} call.
* <p>
* The target program acts as a controller, where it identifies the program to dispatch to call from the
* arguments or other means. Only the OUTPUT or INPUT-OUTPUT argument can be sent back to the caller, via
* the {@link #setArgument} and other setter APIs.
* <p>
* Arguments need to be retrieved via the getter APIs, and the target controller will need to prepare them,
* as needed, to be eventually passed to the {@link #invokeExternalProgram}, {@link #invokeMethod} or
* {@link #invokeStaticMethod}, to invoke the real target.
* <p>
* The getter, setters or otherwise APIs can be called only from the context of the target external program
* acting as a controller. Using these APIs from other code will throw a {@link ErrorConditionException}.
*/
public class LegacyOpenClientCaller
{
/** Context-local state. */
private static final ContextLocal<WorkArea> local = new ContextLocal<WorkArea>()
{
protected WorkArea initialValue()
{
return new WorkArea();
};
};
/**
* Associate the given dataset with the dataset-handle parameter at the given index.
* <p>
* This will act as if the given dataset reference has been defined as a parameter for the external program.
*
* @param idx
* The 1-based index for the argument.
* @param dataset
* The handle to a static dataset or a handle where to store the dynamic dataset, otherwise.
*/
public static void asDataSetParameter(int idx, handle dataset)
{
WorkArea wa = local.get();
securityCheck(wa, true);
validateIndex(wa, idx);
idx = idx - 1;
if (wa.argTypes[idx] != LegacyJavaAppserverApi.TYPE_DATASET_HANDLE)
{
ErrorManager.recordOrThrowError(-1,
"Argument at index " + (idx + 1) + " must be a DATASET-HANDLE to " +
"associate it as parameter.");
return;
}
boolean isInput = wa.modes.charAt(idx) == 'I';
boolean isOutput = wa.modes.charAt(idx) == 'O';
boolean isInputOutput = wa.modes.charAt(idx) == 'U';
DataSetParameter dsp = (DataSetParameter) wa.arguments[idx];
if (dataset.getResource() instanceof DataSet)
{
DataSet ds = (DataSet) dataset.getResource();
if (ds._dynamic())
{
ErrorManager.recordOrThrowError(-1,
"Argument at index " + (idx + 1 ) + " cannot be associated as " +
"parameter - the given dataset is dynamic.");
return;
}
ds = ds.getProxyDataSet();
DataSet.associate(dsp, ds, isInput || isInputOutput, isOutput || isInputOutput);
wa.boundArguments[idx] = true;
}
else
{
DataSet.createDynamicDataSet(dsp, dataset, isInput || isInputOutput, isOutput || isInputOutput);
wa.boundArguments[idx] = true;
}
}
/**
* Associate the given temp-table with the table-handle parameter at the given index.
* <p>
* This will act as if the given DMO reference has been defined as a parameter for the external program.
*
* @param idx
* The 1-based index for the argument.
* @param table
* The handle to a static temp-table or a handle where to store the dynamic temp-table, otherwise.
*/
public static void asTableParameter(int idx, handle table)
{
WorkArea wa = local.get();
securityCheck(wa, true);
validateIndex(wa, idx);
idx = idx - 1;
if (wa.argTypes[idx] != LegacyJavaAppserverApi.TYPE_TABLE_HANDLE)
{
ErrorManager.recordOrThrowError(-1,
"Argument at index " + (idx + 1) + " must be a TABLE-HANDLE to " +
"associate it as parameter.");
return;
}
boolean isInput = wa.modes.charAt(idx) == 'I';
boolean isOutput = wa.modes.charAt(idx) == 'O';
boolean isInputOutput = wa.modes.charAt(idx) == 'U';
TableParameter tp = (TableParameter) wa.arguments[idx];
if (table.getResource() instanceof TempTable)
{
TempTable tt = (TempTable) table.getResource();
if (tt._dynamic())
{
ErrorManager.recordOrThrowError(-1,
"Argument at index " + (idx + 1 ) + " cannot be associated as " +
"parameter - the given temp-table is dynamic.");
return;
}
TemporaryBuffer.associate(tp,
(Temporary) tt.defaultBufferHandleNative(),
isInput || isInputOutput,
isOutput || isInputOutput);
wa.boundArguments[idx] = true;
}
else
{
TemporaryBuffer.createDynamicTable(tp, table, isInput || isInputOutput, isOutput || isInputOutput);
wa.boundArguments[idx] = true;
}
}
/**
* Get the number of arguments sent by the remote side.
*
* @return See above.
*/
public static integer getNumArguments()
{
WorkArea wa = local.get();
securityCheck(wa);
return new integer(wa.argTypes.length);
}
/**
* Check if the argument at the specified index was sent as INPUT.
*
* @param idx
* The 1-based index for the argument.
*
* @return <code>true</code> if the argument's mode is INPUT.
*/
public static boolean isInput(int idx)
{
WorkArea wa = local.get();
securityCheck(wa);
validateIndex(wa, idx);
char mode = wa.modes.charAt(idx - 1);
return mode == 'I';
}
/**
* Check if the argument at the specified index was sent as OUTPUT.
*
* @param idx
* The 1-based index for the argument.
*
* @return <code>true</code> if the argument's mode is OUTPUT.
*/
public static boolean isOutput(int idx)
{
WorkArea wa = local.get();
securityCheck(wa);
validateIndex(wa, idx);
char mode = wa.modes.charAt(idx - 1);
return mode == 'O';
}
/**
* Check if the argument at the specified index was sent as INPUT-OUTPUT.
*
* @param idx
* The 1-based index for the argument.
*
* @return <code>true</code> if the argument's mode is INPUT-OUTPUT.
*/
public static boolean isInputOutput(int idx)
{
WorkArea wa = local.get();
securityCheck(wa);
validateIndex(wa, idx);
char mode = wa.modes.charAt(idx - 1);
return mode == 'U';
}
/**
* Get the argument's type at the specified index.
*
* @param idx
* The 1-based index for the argument.
*
* @return The string representation of the argument's 4GL type.
*/
public static String getParameterType(int idx)
{
WorkArea wa = local.get();
securityCheck(wa);
validateIndex(wa, idx);
String argTypeErr = null;
switch (wa.argTypes[idx - 1])
{
case LegacyJavaAppserverApi.TYPE_INTEGER:
return BaseDataType.Type.INTEGER.getTypeName();
case LegacyJavaAppserverApi.TYPE_INT64:
return BaseDataType.Type.INT64.getTypeName();
case LegacyJavaAppserverApi.TYPE_DECIMAL:
return BaseDataType.Type.DECIMAL.getTypeName();
case LegacyJavaAppserverApi.TYPE_MEMPTR:
return BaseDataType.Type.MEMPTR.getTypeName();
case LegacyJavaAppserverApi.TYPE_CHARACTER:
return BaseDataType.Type.CHARACTER.getTypeName();
case LegacyJavaAppserverApi.TYPE_LONGCHAR:
return BaseDataType.Type.LONGCHAR.getTypeName();
case LegacyJavaAppserverApi.TYPE_LOGICAL:
return BaseDataType.Type.LOGICAL.getTypeName();
case LegacyJavaAppserverApi.TYPE_DATE:
return BaseDataType.Type.DATE.getTypeName();
case LegacyJavaAppserverApi.TYPE_DATETIME:
return BaseDataType.Type.DATETIME.getTypeName();
case LegacyJavaAppserverApi.TYPE_DATETIMETZ:
return BaseDataType.Type.DATETIME_TZ.getTypeName();
case LegacyJavaAppserverApi.TYPE_RECID:
return BaseDataType.Type.RECID.getTypeName();
case LegacyJavaAppserverApi.TYPE_RAW:
return BaseDataType.Type.RAW.getTypeName();
case LegacyJavaAppserverApi.TYPE_ROWID:
return BaseDataType.Type.ROWID.getTypeName();
case LegacyJavaAppserverApi.TYPE_DATASET_HANDLE:
return BaseDataType.Type.DATASET_HANDLE.getTypeName();
case LegacyJavaAppserverApi.TYPE_TABLE_HANDLE:
return BaseDataType.Type.TABLE_HANDLE.getTypeName();
// unsupported types
case LegacyJavaAppserverApi.TYPE_BLOB:
argTypeErr = "BLOB";
break;
case LegacyJavaAppserverApi.TYPE_CLOB:
argTypeErr = "CLOB";
break;
case LegacyJavaAppserverApi.TYPE_HANDLE:
argTypeErr = "HANDLE";
break;
case LegacyJavaAppserverApi.TYPE_UNKNOWN:
argTypeErr = "UNKNOWN";
break;
case LegacyJavaAppserverApi.TYPE_DATASET:
argTypeErr = "DATASET";
break;
case LegacyJavaAppserverApi.TYPE_TABLE:
argTypeErr = "TABLE";
break;
default:
ErrorManager.recordOrThrowError(-1, "Argument type " + wa.argTypes[idx - 1] + " is unknown.");
return null;
}
ErrorManager.recordOrThrowError(-1, "Argument type " + argTypeErr + " is not supported.");
return null;
}
/**
* Invoke the target external program.
* <p>
* The argument types must be specified, so FWD runtime can build the proper argument for i.e.
* TABLE-HANDLE or DATASET-HANDLE.
*
* @param name
* The target external program.
* @param ptypes
* The argument data-types.
* @param args
* The arguments.
*/
public static void invokeExternalProgram(String name, character[] ptypes, Object[] args)
{
invokeExternalProgram(new character(name), ptypes, args);
}
/**
* Invoke the target external program.
* <p>
* The argument types must be specified, so FWD runtime can build the proper argument for i.e.
* TABLE-HANDLE or DATASET-HANDLE.
*
* @param name
* The target external program.
* @param ptypes
* The argument data-types.
* @param args
* The arguments.
*/
public static void invokeExternalProgram(character name, character[] ptypes, Object[] args)
{
WorkArea wa = local.get();
securityCheck(wa);
Object[] oargs = resolveArgs(null, ptypes, args, false);
ControlFlowOps.invokeExternalProcedure(name, false, null, false, null, null, oargs);
}
/**
* Invoke the target static method in the given legacy class.
* <p>
* The argument types must be specified, so FWD runtime can build the proper argument for i.e.
* TABLE-HANDLE or DATASET-HANDLE.
*
* @param clsName
* The target legacy class name.
* @param modes
* The argument modes.
* @param ptypes
* The argument data-types.
* @param args
* The arguments.
*/
public static Object invokeStaticMethod(String clsName,
String name,
String modes,
character[] ptypes,
Object[] args)
{
return invokeStaticMethod(new character(clsName), new character(name), modes, ptypes, args);
}
/**
* Invoke the target static method in the given legacy class.
* <p>
* The argument types must be specified, so FWD runtime can build the proper argument for i.e.
* TABLE-HANDLE or DATASET-HANDLE.
*
* @param clsName
* The target legacy class name.
* @param modes
* The argument modes.
* @param ptypes
* The argument data-types.
* @param args
* The arguments.
*/
public static Object invokeStaticMethod(character clsName,
String name,
String modes,
character[] ptypes,
Object[] args)
{
return invokeStaticMethod(clsName, new character(name), modes, ptypes, args);
}
/**
* Invoke the target static method in the given legacy class.
* <p>
* The argument types must be specified, so FWD runtime can build the proper argument for i.e.
* TABLE-HANDLE or DATASET-HANDLE.
*
* @param clsName
* The target legacy class name.
* @param modes
* The argument modes.
* @param ptypes
* The argument data-types.
* @param args
* The arguments.
*/
public static Object invokeStaticMethod(String clsName,
character name,
String modes,
character[] ptypes,
Object[] args)
{
return invokeStaticMethod(new character(clsName), name, modes, ptypes, args);
}
/**
* Invoke the target static method in the given legacy class.
* <p>
* The argument types must be specified, so FWD runtime can build the proper argument for i.e.
* TABLE-HANDLE or DATASET-HANDLE.
*
* @param clsName
* The target legacy class name.
* @param modes
* The argument modes.
* @param ptypes
* The argument data-types.
* @param args
* The arguments.
*/
public static Object invokeStaticMethod(character clsName,
character name,
String modes,
character[] ptypes,
Object[] args)
{
WorkArea wa = local.get();
securityCheck(wa);
Object[] oargs = resolveArgs(modes, ptypes, args, true);
return ObjectOps.invoke(clsName, name, null, oargs);
}
/**
* Invoke the target method in the given legacy class instance.
* <p>
* The argument types must be specified, so FWD runtime can build the proper argument for i.e.
* TABLE-HANDLE or DATASET-HANDLE.
*
* @param ref
* The target instance.
* @param name
* The target legacy method name.
* @param modes
* The argument modes.
* @param ptypes
* The argument data-types.
* @param args
* The arguments.
*/
public static Object invokeMethod(object<? extends _BaseObject_> ref,
String name,
String modes,
character[] ptypes,
Object[] args)
{
return invokeMethod(ref, new character(name), modes, ptypes, args);
}
/**
* Invoke the target method in the given legacy class instance.
* <p>
* The argument types must be specified, so FWD runtime can build the proper argument for i.e.
* TABLE-HANDLE or DATASET-HANDLE.
*
* @param ref
* The target instance.
* @param name
* The target legacy method name.
* @param modes
* The argument modes.
* @param ptypes
* The argument data-types.
* @param args
* The arguments.
*/
public static Object invokeMethod(object<? extends _BaseObject_> ref,
character name,
String modes,
character[] ptypes,
Object[] args)
{
WorkArea wa = local.get();
securityCheck(wa);
Object[] oargs = resolveArgs(modes, ptypes, args, true);
return ObjectOps.invoke(ref, name, null, oargs);
}
/**
* Get the argument at the specified position, as a <code>TABLE-HANDLE</code>.
*
* @param idx
* The 1-based argument index.
*
* @return The argument's value.
*/
public static handle getTableHandle(int idx)
{
WorkArea wa = local.get();
securityCheck(wa);
return (handle) toLegacy(wa, idx);
}
/**
* Set the argument at the specified position, as a <code>TABLE-HANDLE</code>.
*
* @param idx
* The 1-based argument index.
* @param val
* The argument's value.
*/
public static void setTableHandle(int idx, handle val)
{
WorkArea wa = local.get();
securityCheck(wa);
validateIndex(wa, idx, true);
idx = idx - 1;
wa.arguments[idx] = toJava(wa, idx, LegacyJavaAppserverApi.TYPE_TABLE_HANDLE, val);
}
/**
* Get the argument at the specified position, as a <code>DATASET-HANDLE</code>.
*
* @param idx
* The 1-based argument index.
*
* @return The argument's value.
*/
public static handle getDataSetHandle(int idx)
{
WorkArea wa = local.get();
securityCheck(wa);
return (handle) toLegacy(wa, idx);
}
/**
* Set the argument at the specified position, as a <code>DATASET-HANDLE</code>.
*
* @param idx
* The 1-based argument index.
* @param val
* The argument's value.
*/
public static void setDataSetHandle(int idx, handle val)
{
WorkArea wa = local.get();
securityCheck(wa);
validateIndex(wa, idx, true);
idx = idx - 1;
wa.arguments[idx] = toJava(wa, idx, LegacyJavaAppserverApi.TYPE_DATASET_HANDLE, val);
}
/**
* Get the argument at the specified position, as a {@link memptr}.
*
* @param idx
* The 1-based argument index.
*
* @return The argument's value.
*/
public static memptr getMemptr(int idx)
{
WorkArea wa = local.get();
securityCheck(wa);
return (memptr) toLegacy(wa, idx);
}
/**
* Set the argument at the specified position, as a {@link memptr}.
*
* @param idx
* The 1-based argument index.
* @param val
* The argument's value.
*/
public static void setMemptr(int idx, memptr val)
{
WorkArea wa = local.get();
securityCheck(wa);
validateIndex(wa, idx, true);
idx = idx - 1;
wa.arguments[idx] = toJava(wa, idx, LegacyJavaAppserverApi.TYPE_MEMPTR, val);
}
/**
* Get the argument at the specified position, as a {@link integer}.
*
* @param idx
* The 1-based argument index.
*
* @return The argument's value.
*/
public static integer getInteger(int idx)
{
WorkArea wa = local.get();
securityCheck(wa);
return (integer) toLegacy(wa, idx);
}
/**
* Set the argument at the specified position, as a {@link integer}.
*
* @param idx
* The 1-based argument index.
* @param val
* The argument's value.
*/
public static void setInteger(int idx, integer val)
{
WorkArea wa = local.get();
securityCheck(wa);
validateIndex(wa, idx, true);
idx = idx - 1;
wa.arguments[idx] = toJava(wa, idx, LegacyJavaAppserverApi.TYPE_INTEGER, val);
}
/**
* Get the argument at the specified position, as a {@link int64}.
*
* @param idx
* The 1-based argument index.
*
* @return The argument's value.
*/
public static int64 getInt64(int idx)
{
WorkArea wa = local.get();
securityCheck(wa);
return (int64) toLegacy(wa, idx);
}
/**
* Set the argument at the specified position, as a {@link int64}.
*
* @param idx
* The 1-based argument index.
* @param val
* The argument's value.
*/
public static void setInt64(int idx, int64 val)
{
WorkArea wa = local.get();
securityCheck(wa);
validateIndex(wa, idx, true);
idx = idx - 1;
wa.arguments[idx] = toJava(wa, idx, LegacyJavaAppserverApi.TYPE_INT64, val);
}
/**
* Get the argument at the specified position, as a {@link decimal}.
*
* @param idx
* The 1-based argument index.
*
* @return The argument's value.
*/
public static decimal getDecimal(int idx)
{
WorkArea wa = local.get();
securityCheck(wa);
return (decimal) toLegacy(wa, idx);
}
/**
* Set the argument at the specified position, as a {@link decimal}.
*
* @param idx
* The 1-based argument index.
* @param val
* The argument's value.
*/
public static void setDecimal(int idx, decimal val)
{
WorkArea wa = local.get();
securityCheck(wa);
validateIndex(wa, idx, true);
idx = idx - 1;
wa.arguments[idx] = toJava(wa, idx, LegacyJavaAppserverApi.TYPE_DECIMAL, val);
}
/**
* Get the argument at the specified position, as a {@link character}.
*
* @param idx
* The 1-based argument index.
*
* @return The argument's value.
*/
public static character getCharacter(int idx)
{
WorkArea wa = local.get();
securityCheck(wa);
return (character) toLegacy(wa, idx);
}
/**
* Set the argument at the specified position, as a {@link character}.
*
* @param idx
* The 1-based argument index.
* @param val
* The argument's value.
*/
public static void setCharacter(int idx, character val)
{
WorkArea wa = local.get();
securityCheck(wa);
validateIndex(wa, idx, true);
idx = idx - 1;
wa.arguments[idx] = toJava(wa, idx, LegacyJavaAppserverApi.TYPE_CHARACTER, val);
}
/**
* Get the argument at the specified position, as a {@link longchar}.
*
* @param idx
* The 1-based argument index.
*
* @return The argument's value.
*/
public static longchar getLongchar(int idx)
{
WorkArea wa = local.get();
securityCheck(wa);
return (longchar) toLegacy(wa, idx);
}
/**
* Set the argument at the specified position, as a {@link longchar}.
*
* @param idx
* The 1-based argument index.
* @param val
* The argument's value.
*/
public static void setLongchar(int idx, longchar val)
{
WorkArea wa = local.get();
securityCheck(wa);
validateIndex(wa, idx, true);
idx = idx - 1;
wa.arguments[idx] = toJava(wa, idx, LegacyJavaAppserverApi.TYPE_LONGCHAR, val);
}
/**
* Get the argument at the specified position, as a {@link logical}.
*
* @param idx
* The 1-based argument index.
*
* @return The argument's value.
*/
public static logical getLogical(int idx)
{
WorkArea wa = local.get();
securityCheck(wa);
return (logical) toLegacy(wa, idx);
}
/**
* Set the argument at the specified position, as a {@link logical}.
*
* @param idx
* The 1-based argument index.
* @param val
* The argument's value.
*/
public static void setLogical(int idx, logical val)
{
WorkArea wa = local.get();
securityCheck(wa);
validateIndex(wa, idx, true);
idx = idx - 1;
wa.arguments[idx] = toJava(wa, idx, LegacyJavaAppserverApi.TYPE_LOGICAL, val);
}
/**
* Get the argument at the specified position, as a {@link date}.
*
* @param idx
* The 1-based argument index.
*
* @return The argument's value.
*/
public static date getDate(int idx)
{
WorkArea wa = local.get();
securityCheck(wa);
return (date) toLegacy(wa, idx);
}
/**
* Set the argument at the specified position, as a {@link date}.
*
* @param idx
* The 1-based argument index.
* @param val
* The argument's value.
*/
public static void setDate(int idx, date val)
{
WorkArea wa = local.get();
securityCheck(wa);
validateIndex(wa, idx, true);
idx = idx - 1;
wa.arguments[idx] = toJava(wa, idx, LegacyJavaAppserverApi.TYPE_DATE, val);
}
/**
* Get the argument at the specified position, as a {@link datetime}.
*
* @param idx
* The 1-based argument index.
*
* @return The argument's value.
*/
public static datetime getDatetime(int idx)
{
WorkArea wa = local.get();
securityCheck(wa);
return (datetime) toLegacy(wa, idx);
}
/**
* Set the argument at the specified position, as a {@link datetime}.
*
* @param idx
* The 1-based argument index.
* @param val
* The argument's value.
*/
public static void setDatetime(int idx, datetime val)
{
WorkArea wa = local.get();
securityCheck(wa);
validateIndex(wa, idx, true);
idx = idx - 1;
wa.arguments[idx] = toJava(wa, idx, LegacyJavaAppserverApi.TYPE_DATETIME, val);
}
/**
* Get the argument at the specified position, as a {@link datetimetz}.
*
* @param idx
* The 1-based argument index.
*
* @return The argument's value.
*/
public static datetimetz getDatetimetz(int idx)
{
WorkArea wa = local.get();
securityCheck(wa);
return (datetimetz) toLegacy(wa, idx);
}
/**
* Set the argument at the specified position, as a {@link datetimetz}.
*
* @param idx
* The 1-based argument index.
* @param val
* The argument's value.
*/
public static void setDatetimetz(int idx, datetimetz val)
{
WorkArea wa = local.get();
securityCheck(wa);
validateIndex(wa, idx, true);
idx = idx - 1;
wa.arguments[idx] = toJava(wa, idx, LegacyJavaAppserverApi.TYPE_DATETIMETZ, val);
}
/**
* Get the argument at the specified position, as a {@link recid}.
*
* @param idx
* The 1-based argument index.
*
* @return The argument's value.
*/
public static recid getRecid(int idx)
{
WorkArea wa = local.get();
securityCheck(wa);
return (recid) toLegacy(wa, idx);
}
/**
* Set the argument at the specified position, as a {@link recid}.
*
* @param idx
* The 1-based argument index.
* @param val
* The argument's value.
*/
public static void setRecid(int idx, recid val)
{
WorkArea wa = local.get();
securityCheck(wa);
validateIndex(wa, idx, true);
idx = idx - 1;
wa.arguments[idx] = toJava(wa, idx, LegacyJavaAppserverApi.TYPE_RECID, val);
}
/**
* Get the argument at the specified position, as a {@link raw}.
*
* @param idx
* The 1-based argument index.
*
* @return The argument's value.
*/
public static raw getRaw(int idx)
{
WorkArea wa = local.get();
securityCheck(wa);
return (raw) toLegacy(wa, idx);
}
/**
* Set the argument at the specified position, as a {@link raw}.
*
* @param idx
* The 1-based argument index.
* @param val
* The argument's value.
*/
public static void setRaw(int idx, raw val)
{
WorkArea wa = local.get();
securityCheck(wa);
validateIndex(wa, idx, true);
idx = idx - 1;
wa.arguments[idx] = toJava(wa, idx, LegacyJavaAppserverApi.TYPE_RAW, val);
}
/**
* Get the argument at the specified position, as a {@link rowid}.
*
* @param idx
* The 1-based argument index.
*
* @return The argument's value.
*/
public static rowid getRowid(int idx)
{
WorkArea wa = local.get();
securityCheck(wa);
return (rowid) toLegacy(wa, idx);
}
/**
* Set the argument at the specified position, as a {@link rowid}.
*
* @param idx
* The 1-based argument index.
* @param val
* The argument's value.
*/
public static void setRowid(int idx, rowid val)
{
WorkArea wa = local.get();
securityCheck(wa);
validateIndex(wa, idx, true);
idx = idx - 1;
wa.arguments[idx] = toJava(wa, idx, LegacyJavaAppserverApi.TYPE_ROWID, val);
}
/**
* Set the argument at the specified position.
*
* @param idx
* The 1-based argument index.
* @param val
* The argument's value.
*/
public static void setArgument(int idx, BaseDataType val)
{
WorkArea wa = local.get();
securityCheck(wa);
validateIndex(wa, idx, true);
idx = idx - 1;
wa.arguments[idx] = toJava(wa, idx, -1, val);
}
/**
* Store all the OUTPUT or INPUT-OUTPUT arguments received in the given array to be transferred back to the
* caller.
*
* @param args
* The arguments.
*/
public static void setArguments(Object... args)
{
WorkArea wa = local.get();
securityCheck(wa);
if (args.length != wa.arguments.length)
{
ErrorManager.recordOrThrowError(-1, "Argument length must be " + wa.arguments.length);
return;
}
for (int i = 0; i < args.length; i++)
{
if (wa.boundArguments[i])
{
ErrorManager.recordOrShowError(-1,
"Argument at index " + (i + 1) +
" is already associated as parameter - skipping.",
false);
// bound arguments will be skipped.
continue;
}
Object arg = args[i];
if (arg instanceof jobject)
{
jobject j = (jobject) arg;
arg = j.isUnknown() ? null : ((jobject) arg).ref();
}
wa.arguments[i] = toJava(wa, i, -1, arg);
}
}
/**
* For the {@link LegacyJavaAppserverClient#invokeWithArgs} call, initialize the argument state, before the
* call was performed.
*
* @param argTypes
* The argument types.
* @param modes
* The argument modes.
* @param args
* The arguments.
*/
static void initializeArguments(int[] argTypes, String modes, Object[] args)
{
WorkArea wa = local.get();
wa.withArgsCall = true;
wa.argTypes = argTypes;
wa.arguments = args;
wa.originalArguments = new Object[args.length];
System.arraycopy(args, 0, wa.originalArguments, 0, args.length);
wa.boundArguments = new boolean[args.length];
wa.modes = modes;
// validation for unsupported types
for (int i = 0; i < wa.arguments.length; i++)
{
String argTypeErr = null;
int type = wa.argTypes[i];
switch (type)
{
// complex types
case LegacyJavaAppserverApi.TYPE_TABLE_HANDLE:
case LegacyJavaAppserverApi.TYPE_DATASET_HANDLE:
case LegacyJavaAppserverApi.TYPE_MEMPTR:
// simple types
case LegacyJavaAppserverApi.TYPE_INTEGER:
case LegacyJavaAppserverApi.TYPE_INT64:
case LegacyJavaAppserverApi.TYPE_DECIMAL:
case LegacyJavaAppserverApi.TYPE_CHARACTER:
case LegacyJavaAppserverApi.TYPE_LONGCHAR:
case LegacyJavaAppserverApi.TYPE_LOGICAL:
case LegacyJavaAppserverApi.TYPE_DATE:
case LegacyJavaAppserverApi.TYPE_DATETIME:
case LegacyJavaAppserverApi.TYPE_DATETIMETZ:
case LegacyJavaAppserverApi.TYPE_RECID:
case LegacyJavaAppserverApi.TYPE_RAW:
case LegacyJavaAppserverApi.TYPE_ROWID:
break;
// unsupported types
case LegacyJavaAppserverApi.TYPE_BLOB:
argTypeErr = "BLOB";
break;
case LegacyJavaAppserverApi.TYPE_CLOB:
argTypeErr = "CLOB";
break;
case LegacyJavaAppserverApi.TYPE_HANDLE:
argTypeErr = "HANDLE";
break;
case LegacyJavaAppserverApi.TYPE_UNKNOWN:
argTypeErr = "UNKNOWN";
break;
case LegacyJavaAppserverApi.TYPE_DATASET:
argTypeErr = "DATASET";
break;
case LegacyJavaAppserverApi.TYPE_TABLE:
argTypeErr = "TABLE";
break;
default:
argTypeErr = "[undefined type " + type + "]";
break;
}
if (argTypeErr != null)
{
ErrorManager.recordOrThrowError(-1, "Argument type " + argTypeErr + " is not supported.");
return;
}
}
}
/**
* For the {@link LegacyJavaAppserverClient#invokeWithArgs} call, clear the argument state, once the call
* has finished.
*/
static void clearArguments()
{
WorkArea wa = local.get();
// all arguments which are not bound need to be cleaned up
for (int i = 0; i < wa.arguments.length; i++)
{
if (wa.boundArguments[i])
{
// bound arguments are cleaned automatically
continue;
}
int type = wa.argTypes[i];
switch (type)
{
// complex types
case LegacyJavaAppserverApi.TYPE_TABLE_HANDLE:
if (wa.originalArguments[i] != wa.arguments[i])
{
// clean original first
TableParameter table = (TableParameter) wa.originalArguments[i];
handle htable = table.getTableHandle();
if (htable != null && htable._isValid())
{
HandleOps.delete(htable);
}
}
// clean argument second
TableParameter table = (TableParameter) wa.arguments[i];
handle htable = table.getTableHandle();
if (htable != null && htable._isValid())
{
HandleOps.delete(htable);
}
break;
case LegacyJavaAppserverApi.TYPE_DATASET_HANDLE:
if (wa.originalArguments[i] != wa.arguments[i])
{
// clean original first
DataSetParameter dataset = (DataSetParameter) wa.originalArguments[i];
handle hdataset = dataset.get();
if (hdataset != null && hdataset._isValid())
{
HandleOps.delete(hdataset);
}
}
// clean argument second
DataSetParameter dataset = (DataSetParameter) wa.arguments[i];
handle hdataset = dataset.get();
if (hdataset != null && hdataset._isValid())
{
HandleOps.delete(hdataset);
}
break;
case LegacyJavaAppserverApi.TYPE_MEMPTR:
if (wa.originalArguments[i] != wa.arguments[i])
{
// clean original first
((memptr) wa.originalArguments[i]).setLength(0);
}
// clean argument second
((memptr) wa.arguments[i]).setLength(0);
break;
}
}
wa.withArgsCall = false;
wa.argTypes = null;
wa.arguments = null;
wa.originalArguments = null;
wa.boundArguments = null;
wa.modes = null;
for (int i = 0; i < wa.resources.size(); i++)
{
WrappedResource res = wa.resources.get(i);
if (res.valid())
{
ErrorManager.nestedSilent(() -> ((Deletable) res).delete());
}
}
wa.resources.clear();
}
/**
* For a setter, convert the given argument to its representation so it can be sent back to the remote
* side.
*
* @param wa
* The context-local instance.
* @param idx
* The 0-based argument index.
* @param argType
* The argument type, one of the {@link LegacyJavaAppserverApi#integer()} APIs.
* @param arg
* The argument instance.
*
* @return The argument, in the form where it can be stored in {@link WorkArea#arguments}, to be sent
* back to the remote side.
*/
private static Object toJava(WorkArea wa, int idx, int argType, Object arg)
{
// TODO: extent
if (wa.modes.charAt(idx) == 'I')
{
ErrorManager.recordOrShowError(-1,
"INPUT argument " + (idx + 1) +
" from the remote call can not be changed.",
false);
return wa.arguments[idx];
}
if (arg == null)
{
return null;
}
int type = wa.argTypes[idx];
if (argType != -1 && argType != type)
{
ErrorManager.recordOrThrowError(-1,
"Attempting to set argument as type " + argType +
" but the remote parameter is of type " + type + ".");
return arg;
}
switch (type)
{
// complex types
case LegacyJavaAppserverApi.TYPE_TABLE_HANDLE:
TableParameter tp = (TableParameter) wa.arguments[idx];
tp.getResultSet().setResultSet(null);
OutputTableHandleCopier tcopier = new OutputTableHandleCopier(tp, (handle) arg);
tcopier.finished();
return tp;
case LegacyJavaAppserverApi.TYPE_DATASET_HANDLE:
DataSetParameter dsp = (DataSetParameter) wa.arguments[idx];
OutputDataSetHandleCopier dscopier = new OutputDataSetHandleCopier(dsp, (handle) arg);
dscopier.finished();
return dsp;
case LegacyJavaAppserverApi.TYPE_MEMPTR:
return arg;
// simple types
case LegacyJavaAppserverApi.TYPE_INTEGER:
case LegacyJavaAppserverApi.TYPE_INT64:
case LegacyJavaAppserverApi.TYPE_DECIMAL:
case LegacyJavaAppserverApi.TYPE_CHARACTER:
case LegacyJavaAppserverApi.TYPE_LONGCHAR:
case LegacyJavaAppserverApi.TYPE_LOGICAL:
case LegacyJavaAppserverApi.TYPE_DATE:
case LegacyJavaAppserverApi.TYPE_DATETIME:
case LegacyJavaAppserverApi.TYPE_DATETIMETZ:
case LegacyJavaAppserverApi.TYPE_RECID:
case LegacyJavaAppserverApi.TYPE_RAW:
case LegacyJavaAppserverApi.TYPE_ROWID:
return arg;
}
ErrorManager.recordOrThrowError(-1, "Argument type " + type + " is not supported.");
return null;
}
/**
* Get the argument at the given 1-based index in a {@link BaseDataType} form, used by the legacy converted
* code.
* @param wa
* The context-local instance.
* @param idx
* The 1-based argument index.
*
* @return See above.
*/
private static BaseDataType toLegacy(WorkArea wa, int idx)
{
validateIndex(wa, idx);
idx = idx - 1;
// TODO: extent
int type = wa.argTypes[idx];
Object arg = wa.arguments[idx];
switch (type)
{
// complex types
case LegacyJavaAppserverApi.TYPE_TABLE_HANDLE:
handle tth = new handle();
TemporaryBuffer.createDynamicTable((TableParameter) arg, tth, true, false);
if (tth._isValid())
{
wa.resources.add(tth.getResource());
}
return tth;
case LegacyJavaAppserverApi.TYPE_DATASET_HANDLE:
handle dsh = new handle();
DataSet.createDynamicDataSet((DataSetParameter) arg, dsh, true, false);
if (dsh._isValid())
{
wa.resources.add(dsh.getResource());
}
return dsh;
case LegacyJavaAppserverApi.TYPE_MEMPTR:
return new memptr((memptr) arg);
// simple types
case LegacyJavaAppserverApi.TYPE_INTEGER:
return new integer((integer) arg);
case LegacyJavaAppserverApi.TYPE_INT64:
return new int64((int64) arg);
case LegacyJavaAppserverApi.TYPE_DECIMAL:
return new decimal((decimal) arg);
case LegacyJavaAppserverApi.TYPE_CHARACTER:
return new character((character) arg);
case LegacyJavaAppserverApi.TYPE_LONGCHAR:
return new longchar((longchar) arg);
case LegacyJavaAppserverApi.TYPE_LOGICAL:
return new logical((logical) arg);
case LegacyJavaAppserverApi.TYPE_DATE:
return new date((date) arg);
case LegacyJavaAppserverApi.TYPE_DATETIME:
return new datetime((datetime) arg);
case LegacyJavaAppserverApi.TYPE_DATETIMETZ:
return new datetimetz((datetimetz) arg);
case LegacyJavaAppserverApi.TYPE_RECID:
return new recid((recid) arg);
case LegacyJavaAppserverApi.TYPE_RAW:
return new raw((raw) arg);
case LegacyJavaAppserverApi.TYPE_ROWID:
return new rowid((rowid) arg);
}
ErrorManager.recordOrThrowError(-1, "Argument type " + type + " is not supported.");
return null;
}
/**
* Resolve the arguments passed to {@link #invokeExternalProgram}, {@link #invokeStaticMethod} or
* {@link #invokeMethod}, so that they can be passed to the target invocation.
*
* @param modes
* The argument modes.
* @param ptypes
* The string-representation of the argument types.
* @param args
* The arguments.
* @param isOO
* Flag indicating if this is a OO call.
*
* @return See above.
*/
private static Object[] resolveArgs(String modes, character[] ptypes, Object[] args, boolean isOO)
{
if (modes != null && modes.length() != args.length)
{
ErrorManager.recordOrThrowError(-1, "Argument mode length and argument length does not match!");
return args;
}
if (isOO && modes == null)
{
ErrorManager.recordOrThrowError(-1, "For a OO call, the argument modes must be specified!");
return args;
}
if (modes != null)
{
modes = modes.toUpperCase();
for (int i = 0; i < modes.length(); i++)
{
char c = modes.charAt(i);
if (c != 'I' && c != 'U' && c != 'O')
{
ErrorManager.recordOrThrowError(-1, "Invalid argument mode at position " + (i + 1) + ": " + c);
return args;
}
}
}
Object[] oargs = new Object[args.length];
for (int i = 0; i < args.length; i++)
{
String ptype = ptypes[i].getValue();
Object arg = args[i];
if (arg instanceof jobject)
{
arg = ((jobject) arg).ref();
}
char pmode = (modes == null ? (char) -1 : modes.charAt(i));
switch (ptype.toUpperCase())
{
// complex types
case "TABLE-HANDLE":
if (isOO)
{
switch (pmode)
{
case 'I':
arg = new InputTableHandle((handle) arg);
break;
case 'O':
arg = new OutputTableHandle((handle) arg);
break;
case 'U':
arg = new InputOutputTableHandle((handle) arg);
break;
}
}
else
{
arg = new TableParameter((handle) arg);
}
break;
case "DATASET-HANDLE":
if (isOO)
{
switch (pmode)
{
case 'I':
arg = new InputDataSetHandle((handle) arg);
break;
case 'O':
arg = new OutputDataSetHandle((handle) arg);
break;
case 'U':
arg = new InputOutputDataSetHandle((handle) arg);
break;
}
}
else
{
arg = new DataSetParameter((handle) arg);
}
break;
case "MEMPTR":
// do nothing
break;
// simple types
case "INTEGER":
case "INT64":
case "DECIMAL":
case "CHARACTER":
case "LONGCHAR":
case "LOGICAL":
case "DATE":
case "DATETIME":
case "DATETIMETZ":
case "DATETIME-TZ":
case "RECID":
case "RAW":
case "ROWID":
// do nothing
break;
// unsupported types
case "BLOB":
case "CLOB":
case "HANDLE":
case "UNKNOWN":
case "DATASET":
case "TABLE":
ErrorManager.recordOrThrowError(-1, "Parameter type " + ptype + " is not supported!");
break;
default:
ErrorManager.recordOrThrowError(-1, "Parameter type " + ptype + " is unknown!");
break;
}
oargs[i] = arg;
}
return oargs;
}
/**
* Validate the given index to be in the {@link WorkArea#arguments} range.
*
* @param wa
* The context-local instance.
* @param idx
* The 1-based index.
*/
private static void validateIndex(WorkArea wa, int idx)
{
validateIndex(wa, idx, false);
}
/**
* Validate the given index to be in the {@link WorkArea#arguments} range.
*
* @param wa
* The context-local instance.
* @param idx
* The 1-based index.
* @param validateBound
* Flag indicating if the {@link WorkArea#boundArguments} needs to be false.
*/
private static void validateIndex(WorkArea wa, int idx, boolean validateBound)
{
if (idx < 1 || idx > wa.arguments.length)
{
ErrorManager.recordOrThrowError(-1, "LegacyOpenClientCaller: index " + idx +
" is out of range [1.." + wa.arguments.length + "].");
return;
}
if (validateBound && wa.boundArguments[idx - 1])
{
ErrorManager.recordOrThrowError(-1, "Parameter " + idx + " is already associated as parameter.");
return;
}
}
/**
* Check if the call is being performed in the context of the OpenClient call. This means that
* {@link ProcedureHelper#_thisProcedure()} must be the same as {@link ProcedureHelper#_rootProcedure()}.
*
* @param wa
* The context-local instance.
* @param root
* Flag indicating we must be in the root external block.
*/
private static void securityCheck(WorkArea wa)
{
securityCheck(wa, false);
}
/**
* Check if the call is being performed in the context of the OpenClient call. This means that
* {@link ProcedureHelper#_thisProcedure()} must be the same as {@link ProcedureHelper#_rootProcedure()}.
*
* @param wa
* The context-local instance.
* @param root
* Flag indicating we must be in the root external block.
*/
private static void securityCheck(WorkArea wa, boolean root)
{
// all API calls must be done from the context of the root caller, and only if the root caller is
// an 'invokeWithArgs'
if (!wa.withArgsCall)
{
ErrorManager.recordOrThrowError(-1, "LegacyOpenClientCaller APIs can be accessed only if the remote " +
"call was performed via 'invokeWithArgs'!");
return;
}
Object thisProc = wa.pm._thisProcedure();
Object rootProc = wa.pm._rootProcedure();
if (thisProc != rootProc)
{
ErrorManager.recordOrThrowError(-1, "LegacyOpenClientCaller APIs can be accessed only from the " +
"context of the invoked program: " + rootProc.getClass());
return;
}
if (root && TransactionManager.getNestingLevel() > 3)
{
ErrorManager.recordOrThrowError(-1,
"'asDataSetParameter' or 'asTableParameter' can be used only from " +
"the root external program for the proxy call.");
return;
}
}
/**
* Stores global data relating to the state of the current context.
*/
private static class WorkArea
{
/** Helper to use the {@link ProcedureManager} without any context local lookups. */
private ProcedureHelper pm = ProcedureManager.getProcedureHelper();
/** Flag indicating that the root call is via the {@link LegacyJavaAppserverClient#invokeWithArgs}. */
private boolean withArgsCall;
/** The received argument types. */
private int[] argTypes = null;
/** The received argument modes. */
private String modes = null;
/** The received argument. */
private Object[] arguments = null;
/** The original references for the arguments. */
private Object[] originalArguments = null;
/** Flags indicating which [INPUT-]OUTPUT arameters have been bound to a 4GL converted variable. */
private boolean[] boundArguments = null;
/** List of resources created by {@link LegacyOpenClientCaller#toLegacy}. */
private List<WrappedResource> resources = new ArrayList<>();
}
}