blob: eb918172d0bed7c4c87893b031d087a6533a64a7 [file] [log] [blame]
/* -*-C++-*-
**********************************************************************
*
* File: LmRoutineJava.cpp
* Description:
*
* Created: 08/22/2003
* Language: C++
*
// @@@ START COPYRIGHT @@@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
// @@@ END COPYRIGHT @@@
**********************************************************************/
#include "lmjni.h"
#include "ComRtUtils.h"
#include "LmRoutineJava.h"
#include "LmJavaExceptionReporter.h"
#include "LmAssert.h"
#include "LmUtility.h"
#include "ComObjectName.h"
#include "ComSqlId.h"
//////////////////////////////////////////////////////////////////////
//
// Class LmRoutineJava
// If we get errors in creating this object, fill diagsArea and return
// error. Caller is responsbile to cleanup by calling destructor.
//
//////////////////////////////////////////////////////////////////////
LmRoutineJava::LmRoutineJava(
const char *sqlName,
const char *externalName,
const char *librarySqlName,
ComUInt32 numSqlParam,
LmParameter *returnValue,
ComUInt32 maxResultSets,
char *routineSig,
ComRoutineParamStyle paramStyle,
ComRoutineTransactionAttributes transactionAttrs,
ComRoutineSQLAccess sqlAccessMode,
ComRoutineExternalSecurity externalSecurity,
Int32 routineOwnerId,
const char *parentQid,
ComUInt32 inputRowLen,
ComUInt32 outputRowLen,
const char *currentUserName,
const char *sessionUserName,
LmParameter *parameters,
LmLanguageManagerJava *lm,
LmHandle routine,
LmContainer *container,
ComDiagsArea *da)
: LmRoutine(container, routine, sqlName, externalName, librarySqlName,
numSqlParam, maxResultSets,
COM_LANGUAGE_JAVA, paramStyle, transactionAttrs, sqlAccessMode,
externalSecurity,
routineOwnerId,
parentQid, inputRowLen, outputRowLen,
currentUserName, sessionUserName,
parameters, lm),
javaParams_(NULL),
retType_(LmJavaType::JT_NONE),
defaultCatSch_(FALSE),
connectionList_(collHeap())
{
JNIEnv *jni = (JNIEnv*)getLM()->jniEnv_;
setUdrForJavaMain((str_cmp_ne(externalName, "main") == 0) ? TRUE : FALSE);
// this is the internal SPJ used in CREATE PROCEDURE
// mark it for examining Java exceptions to determine SQL diagnostics later
if ((str_cmp_ne(container->getName(), "org.trafodion.sql.udr.LmUtility") == 0) &&
(str_cmp_ne(externalName, "validateMethod") == 0))
setIsInternalSPJ(TRUE);
else
setIsInternalSPJ(FALSE);
if (paramStyle == COM_STYLE_JAVA_OBJ)
return;
// Get method return type.
retType_ = LmJavaType(returnValue).getType();
//
// Now setup the parameters to the method.
//
// UDR-MAIN gets special treatment
//
// Allocate only one jvalue because main() takes only
// one parameter of type String[].
//
// Then create java.lang.String array of size numSqlParam_
// which equals to numSqlParam
//
if (isUdrForJavaMain())
{
numParamsInSig_ = 1; // Number of parameters in Java main method
javaParams_ = new (collHeap()) jvalue[1];
memset((char*)javaParams_, 0, sizeof(jvalue));
((jvalue *)javaParams_)->l = jni->NewObjectArray((jsize)numSqlParam_,
(jclass)lm->stringClass_,
NULL);
if (((jvalue *)javaParams_)->l == NULL)
{
*da << DgSqlCode(-LME_JVM_OUT_OF_MEMORY);
getLM()->exceptionReporter_->checkJVMException(da, 0);
}
return;
}
// For a Java main method we would have already returned and
// do not get this far. Logic in the rest of this method does
// not need to consider main methods as a special case.
// Get the total number of parameters present in the method
// signature.
LmJavaSignature lmSig(routineSig, collHeap());
Int32 result = lmSig.getParamCount();
if( result < (Int32)numSqlParam_ ) {
*da << DgSqlCode(-LME_INTERNAL_ERROR)
<< DgString0(": LmJavaSignature::getParamCount() returned an invalid value.");
return;
}
numParamsInSig_ = (ComUInt32)result;
//
// Setup Paramters for Non-main methods
//
// Allocate Java method parameter array for SQL
// and result set params
if (numParamsInSig_ > 0)
{
javaParams_ = new (collHeap()) jvalue[numParamsInSig_];
memset((char*)javaParams_, 0, numParamsInSig_ * sizeof(jvalue));
}
// Allocate a 1-element array for each OUT/INOUT mode parameter.
jvalue *jval = (jvalue*)javaParams_;
Int32 i = 0;
for (i = 0; i < (Int32)numSqlParam_; i++)
{
LmParameter &p = lmParams_[i];
if (p.direction() == COM_INPUT_COLUMN)
continue;
jobjectArray ja = NULL;
switch (LmJavaType(&p).getType())
{
case LmJavaType::JT_SHORT:
jval[i].l = (jobject)jni->NewShortArray(1);
break;
case LmJavaType::JT_INT:
jval[i].l = (jobject)jni->NewIntArray(1);
break;
case LmJavaType::JT_LONG:
jval[i].l = (jobject)jni->NewLongArray(1);
break;
case LmJavaType::JT_FLOAT:
jval[i].l = (jobject)jni->NewFloatArray(1);
break;
case LmJavaType::JT_DOUBLE:
jval[i].l = (jobject)jni->NewDoubleArray(1);
break;
case LmJavaType::JT_LANG_STRING:
ja = (jobjectArray) jni->NewObjectArray(1, (jclass)lm->stringClass_, NULL);
break;
case LmJavaType::JT_MATH_BIGDEC:
ja = (jobjectArray) jni->NewObjectArray(1, (jclass)lm->bigdecClass_, NULL);
break;
case LmJavaType::JT_SQL_DATE:
ja = (jobjectArray) jni->NewObjectArray(1, (jclass)lm->dateClass_, NULL);
break;
case LmJavaType::JT_SQL_TIME:
ja = (jobjectArray) jni->NewObjectArray(1, (jclass)lm->timeClass_, NULL);
break;
case LmJavaType::JT_SQL_TIMESTAMP:
ja = (jobjectArray) jni->NewObjectArray(1, (jclass)lm->stampClass_, NULL);
break;
case LmJavaType::JT_LANG_INTEGER:
ja = (jobjectArray) jni->NewObjectArray(1, (jclass)lm->intClass_, NULL);
break;
case LmJavaType::JT_LANG_LONG:
ja = (jobjectArray) jni->NewObjectArray(1, (jclass)lm->longClass_, NULL);
break;
case LmJavaType::JT_LANG_FLOAT:
ja = (jobjectArray) jni->NewObjectArray(1, (jclass)lm->floatClass_, NULL);
break;
case LmJavaType::JT_LANG_DOUBLE:
ja = (jobjectArray) jni->NewObjectArray(1, (jclass)lm->doubleClass_, NULL);
break;
default:
char str[LMJ_ERR_SIZE_256];
sprintf (str,
": Unknown parameter type at parameter position %d.",
i+1);
*da << DgSqlCode(-LME_INTERNAL_ERROR)
<< DgString0(str);
return;
}// switch()
if (ja != NULL)
jval[i].l = ja;
if (jval[i].l == NULL)
{
// OutOfMemory is the only error that could happen here.
*da << DgSqlCode(-LME_JVM_OUT_OF_MEMORY);
getLM()->exceptionReporter_->checkJVMException(da, 0);
return;
}
} // for()
// Allocate a 1-element array for each result set parameter.
for (i = (Int32)numSqlParam_; i < (Int32)numParamsInSig_; i++)
{
jobjectArray ja = NULL;
ja = jni->NewObjectArray(1, (jclass)lm->resultSetClass_, NULL);
if (ja != NULL)
jval[i].l = ja;
if (jval[i].l == NULL)
{
// OutOfMemory is the only error that could happen here.
*da << DgSqlCode(-LME_JVM_OUT_OF_MEMORY);
getLM()->exceptionReporter_->checkJVMException(da, 0);
return;
}
}
return;
}
LmRoutineJava::~LmRoutineJava()
{
JNIEnv *jni = (JNIEnv*)getLM()->jniEnv_;
jvalue *jval = (jvalue*)javaParams_;
// Free LmResultSet objects
// This will also close the Java result set objects
// and the Java connections that they are part of.
cleanupResultSets();
// Free LmConnection objects for default connections
// Closes any open default connections that do not have
// result sets associated with them
closeDefConnWithNoRS();
connectionList_.clear();
// Release array refs for params, indicated
// by non-null object.
for (Int32 i = 0; i < (Int32)numParamsInSig_; i++)
{
if (jval[i].l != NULL)
jni->DeleteLocalRef(jval[i].l);
}
// Free the Java parameter array
if (javaParams_)
NADELETEBASIC((jvalue *)javaParams_, collHeap());
}
// Generates a new authentication token for Definer Rights SPJ usage.
// The format of the token fields is as follows:
// o \5, \6 - identifies that it is a DR SPJ token - 2 bytes
// o Definer user ID - routine owner Id - 7 bytes - leading zero padded
// o UDR server Process Node id - 3 bytes - leading zero padded
// o UDR server Process Pid - 6 bytes - leading zero padded
// o Invoker password/token passed from executor - 68 bytes
const int DR_SPJ_TOKEN_PREFIX_LEN = 2;
const int DR_SPJ_TOKEN_USER_ID_LEN = 7;
const int DR_SPJ_TOKEN_NODE_LEN = ComSqlId::CPU_LEN; // 3
const int DR_SPJ_TOKEN_PID_LEN = ComSqlId::PIN_LEN; // 6
const int DR_SPJ_TOKEN_INVOKER_TOKEN_LEN = 68;
const int DR_SPJ_TOKEN_SEPARATOR_LEN = 1;
const int DR_SPJ_TOKEN_LEN =
DR_SPJ_TOKEN_PREFIX_LEN +
DR_SPJ_TOKEN_SEPARATOR_LEN +
DR_SPJ_TOKEN_USER_ID_LEN +
DR_SPJ_TOKEN_SEPARATOR_LEN +
DR_SPJ_TOKEN_NODE_LEN +
DR_SPJ_TOKEN_SEPARATOR_LEN +
DR_SPJ_TOKEN_PID_LEN +
DR_SPJ_TOKEN_SEPARATOR_LEN +
DR_SPJ_TOKEN_INVOKER_TOKEN_LEN;
LmResult LmRoutineJava::handleFinalCall(ComDiagsArea *diagsArea)
{
// this method is not yet used for Java
return LM_OK;
}
LmResult LmRoutineJava::generateDefAuthToken(char *defAuthToken, ComDiagsArea *da)
{
// Get the NODE, PID information for the UDR server process
const int MAX_PROGRAM_DIR_LEN = 1024;
char myProgramDir[MAX_PROGRAM_DIR_LEN+1];
short myProcessType;
Int32 myNode;
char myNodeName[MAX_SEGMENT_NAME_LEN+1];
Lng32 myNodeNum;
short myNodeNameLen = MAX_SEGMENT_NAME_LEN;
char myProcessName[PROCESSNAME_STRING_LEN];
Int64 myProcessStartTime;
pid_t myPid;
Lng32 retStatus = ComRtGetProgramInfo(myProgramDir, MAX_PROGRAM_DIR_LEN,
myProcessType,
myNode,
myPid,
myNodeNum,
myNodeName, myNodeNameLen,
myProcessStartTime,
myProcessName);
if (retStatus)
{
char errStr[LMJ_ERR_SIZE_256];
sprintf (errStr, ": Error returned from ComRtGetProgramInfo. Error is: %d.", retStatus);
*da << DgSqlCode(-LME_INTERNAL_ERROR)
<< DgString0(errStr);
return LM_ERR;
}
sprintf (defAuthToken,
"%s%s:%07d:%03u:%06u:%.68s",
"\005", "\006",
getRoutineOwnerId(),
myNode,
myPid,
getLM()->userPassword_
);
return LM_OK;
}
LmResult LmRoutineJava::invokeRoutine(void *inputRow,
void *outputRow,
ComDiagsArea *da)
{
// Delete the LmResultSet objects for re-invocations
// This will also close the Java result set objects
// and the Java connections that they are part of
cleanupResultSets(da);
// This will close any default connections that do not have
// result sets associated with them
closeDefConnWithNoRS(da);
// Cleanup the list maintained in LmUtlity.cpp file that will get
// populated with default connection objects that may get created
// during this routine's invocation.
lmUtilityInitConnList((JNIEnv*)getLM()->jniEnv_, (jmethodID)getLM()->connCloseId_);
// Set the default catalog & schema as system properties. These
// values are then retrieved by LmSQLMXDriver class for setting
// it on a getConnection() call
if (getDefaultCatSchFlag())
{
// Determine the catalog & schema values that will be in effect for
// this UDR. If the values are set globally(with catalog/schema
// props in CQD) they are used else the catalog/schema of
// the UDR are used.
const char *catalog =
(getLM()->sysCatalog_) ? getLM()->sysCatalog_ : udrCatalog_;
const char *schema =
(getLM()->sysSchema_) ? getLM()->sysSchema_ : udrSchema_;
if (getLM()->setSystemProperty("sqlmx.udr.catalog", catalog, da) == LM_ERR)
return LM_ERR;
if (getLM()->setSystemProperty("sqlmx.udr.schema", schema, da) == LM_ERR)
return LM_ERR;
}
const char *parentQid;
if ((parentQid = getParentQid()) == NULL)
parentQid = "NONE";
if (getLM()->setSystemProperty("sqlmx.udr.parentQid", parentQid, da) == LM_ERR)
return LM_ERR;
// Set SQL access mode in LmUtility. This will be checked while SPJ
// is trying to make a JDBC connection.
lmUtilitySetSqlAccessMode(getSqlAccessMode());
// Set Transaction Attribute in LmUtility. This will be checked while SPJ
// is trying to join a transaction.
lmUtilitySetTransactionAttrs(getTransactionAttrs());
LmResult result = LM_OK;
// Set the Definer Authentication Token as system property for
// Definer Rights SPJs. This value is then retrieved by LmSQLMXDriver class
// for setting the password property in getConnection() call.
if ( externalSecurity_ == COM_ROUTINE_EXTERNAL_SECURITY_DEFINER )
{
char *defAuthToken = new (collHeap()) char[DR_SPJ_TOKEN_LEN + 1];
result = generateDefAuthToken(defAuthToken, da);
if (result != LM_ERR)
result = getLM()->setSystemProperty("sqlmx.udr.defAuthToken", defAuthToken, da);
NADELETEBASIC(defAuthToken, collHeap());
if (result == LM_ERR)
{
char errStr[LMJ_ERR_SIZE_256];
sprintf (errStr,
": Error returned from setting System Property: %s.",
"sqlmx.udr.defAuthToken");
*da << DgSqlCode(-LME_INTERNAL_ERROR)
<< DgString0(errStr);
return LM_ERR;
}
}
lm_->setRoutineIsActive(TRUE);
switch (retType_)
{
case LmJavaType::JT_VOID:
result = voidRoutine(outputRow, NULL);
break;
default:
// Other return types are not supported. In the future if we
// support routines with non-void return types we will need to
// add case blocks to this switch statement.
LM_ASSERT(0);
break;
}
lm_->setRoutineIsActive(FALSE);
// Reset the sqlmx.udr.defAuthToken system property for
// Definer Rights SPJs.
if ( externalSecurity_ == COM_ROUTINE_EXTERNAL_SECURITY_DEFINER )
{
result = getLM()->clearSystemProperty("sqlmx.udr.defAuthToken", da);
if (result == LM_ERR)
{
char errStr[LMJ_ERR_SIZE_256];
sprintf (errStr,
": Error returned from Resetting System Property: %s.",
"sqlmx.udr.defAuthToken");
*da << DgSqlCode(-LME_INTERNAL_ERROR)
<< DgString0(errStr);
return LM_ERR;
}
}
// Get the list containing the default connection objects
// from LmUtility.cpp and create a LmConnection object for
// each item in that list.
NAList<jobject> &connList = lmUtilityGetConnList();
while(connList.entries())
{
jobject conn = connList[0];
LmConnection *lmConn =
new (collHeap()) LmConnection(getLM(), conn,
LmConnection::DEFAULT_CONN, getTransactionAttrs());
connectionList_.insert( lmConn );
connList.removeAt(0);
}
// reset SQL Access mode to COM_UNKNOWN_ROUTINE_SQL_ACCESS
lmUtilitySetSqlAccessMode(COM_UNKNOWN_ROUTINE_SQL_ACCESS);
// reset Transaction Attributes to COM_UNKNOWN_ROUTINE_TRANSACTION_ATTRIBUTE
lmUtilitySetTransactionAttrs(COM_UNKNOWN_ROUTINE_TRANSACTION_ATTRIBUTE);
return result;
}
// Deletes the LmResultSet object at a given index.
void LmRoutineJava::cleanupLmResultSet(ComUInt32 index,
ComDiagsArea *diagsArea)
{
LM_ASSERT(index < resultSetList_.entries());
((LmResultSetJava *)resultSetList_[index])->close( diagsArea );
resultSetList_.removeAt(index);
}
// Deletes the given LmResultSet object.
// Any error during the close will be reported in the
// diagsArea, if available. Asserts on an invalid pointer.
void LmRoutineJava::cleanupLmResultSet(LmResultSet *resultSet,
ComDiagsArea *diagsArea)
{
for (ComUInt32 index=0; index < resultSetList_.entries(); index++)
{
if (resultSetList_[index] == resultSet)
{
((LmResultSetJava *)resultSetList_[index])->close(diagsArea);
resultSetList_.removeAt(index);
return;
}
}
// passed in resultset param is not a valid LmResultSet pointer.
LM_ASSERT(0);
}
// Deletes all the LmResultSet objects from the resultSetList_
// and empties the list.
void LmRoutineJava::cleanupResultSets(ComDiagsArea *diagsArea)
{
// Free LmResultSetJava objects
while( resultSetList_.entries() )
cleanupLmResultSet( (ComUInt32) 0, diagsArea );
resultSetList_.clear();
}
// Closes any default connections in connectionList_,
// which do not have any result sets associated with
// them.
void LmRoutineJava::closeDefConnWithNoRS(ComDiagsArea *diagsArea)
{
ComUInt32 i = 0;
while( i < connectionList_.entries() )
{
LmConnection *lmConn = connectionList_[i];
if( lmConn->connType() == LmConnection::DEFAULT_CONN &&
lmConn->readyToClose() )
{
lmConn->close( diagsArea );
connectionList_.removeAt(i);
}
else
i++; // Increment only when LmConnection::close() is
// not called.
}
}
// This method checks if the passed in java.sql.ResultSet object
// reference (newRS) is already part of a LmResultSetJava
// object in the resultSetList_.
//
// The parameter newRS should not be NULL.
// Returns TRUE if a match is found FALSE otherwise
NABoolean LmRoutineJava::isDuplicateRS( LmHandle newRS )
{
JNIEnv *jni = (JNIEnv*)getLM()->jniEnv_;
LmResultSetJava *lmRS;
jobject newRSRef = (jobject)newRS;
ComUInt32 indx;
LM_ASSERT(newRS != NULL);
for (indx = 0; indx < resultSetList_.entries(); indx++)
{
lmRS = (LmResultSetJava *)resultSetList_[indx];
// IsSameObject() JNI call compares the object references
// of it's parameters
if (jni->IsSameObject( (jobject)lmRS->getResultSet(), newRSRef ))
return TRUE;
} // End for loop
return FALSE;
}
// This method creates a LmResultSetJava object for the
// passed in java.sql.ResultSet object (newRS) and inserts
// it into resultSetList_.
//
// Parameters:
// newRS - A java.sql.ResultSet object
// paramPos - The position (1-based) of newRS
// in the Java method signature.
// da - ComDiagsArea object to report any errors.
// This object should not be NULL.
LmResult LmRoutineJava::populateResultSetInfo(LmHandle newRS, Int32 paramPos,
ComDiagsArea *da)
{
LmResult result = LM_OK;
jobject newRSRef = (jobject)newRS;
LM_ASSERT(da != NULL);
// SPJ method can return same java.sql.ResultSet object in
// multiple OUT params but we ignore such duplicates.
// We also ignore a NULL java.sql.ResultSet object.
if( newRSRef == NULL || isDuplicateRS( newRSRef ))
return result;
LmResultSetJava::LmResultSetInfoStatus rsInfoStatus;
LmResultSetJava *newLmRS =
new (collHeap()) LmResultSetJava( getLM(),
(LmHandle)newRSRef,
paramPos,
getNameForDiags(),
rsInfoStatus,
connectionList_,
da );
// If the constructor returned a success status then insert
// the newLmRS object into resultSetList_ otherwise delete it.
if( rsInfoStatus == LmResultSetJava::RS_INFO_OK ) {
// Order the objects inserted in the resultSetList_ based on
// the order in which the result set cursors were opened.
ComUInt32 i;
LmResultSetJava *lmRS;
for( i = 0; i < resultSetList_.entries(); i++ )
{
lmRS = (LmResultSetJava *)resultSetList_[i];
if( newLmRS->rsCounter_ < lmRS->rsCounter_ )
break;
}
resultSetList_.insertAt( i, newLmRS );
}
else {
delete newLmRS;
newLmRS = NULL;
// Ignore closed result sets
if( rsInfoStatus == LmResultSetJava::RS_INFO_CLOSED )
result = LM_OK;
else
result = LM_ERR;
}
return result;
}
// Deletes the LmResultSetJava objects in resultSetList_
// which are over and above the routine's decalred max result set value.
void LmRoutineJava::deleteLmResultSetsOverMax(ComDiagsArea *diagsArea)
{
ComUInt32 toDelete = 0;
if( resultSetList_.entries() > maxResultSets_ )
toDelete = resultSetList_.entries() - maxResultSets_;
while( toDelete-- )
cleanupLmResultSet( maxResultSets_, diagsArea );
return;
}
// check validity of the object after calling the constructor
ComBoolean LmRoutineJava::isValid() const
{
if (retType_ > LmJavaType::JT_NONE &&
retType_ <= LmJavaType::JT_LAST)
return TRUE;
return FALSE;
}
//////////////////////////////////////////////////////////////////////
// voidRoutine: invoke a static Java method that returns void
//////////////////////////////////////////////////////////////////////
LmResult LmRoutineJava::voidRoutine(void *dataPtr, LmParameter *)
{
JNIEnv *jni = (JNIEnv*)getLM()->jniEnv_;
jni->CallStaticVoidMethodA((jclass)getContainerHandle(),
(jmethodID)routine_,
(jvalue*)javaParams_);
return LM_OK;
}