blob: c55cfd515f0164dbebaf85f06b89cf53894964c2 [file] [log] [blame]
/**********************************************************************
// @@@ 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 @@@
**********************************************************************/
/* -*-C++-*-
**************************************************************************
*
* File: QueryRewriteServer.cpp
* Description: MvQueryRewriteServer methods
* Created: 06/01/2009
* Language: C++
*
**************************************************************************
*/
#include <ComCextdecs.h>
#include "QueryRewriteServer.h"
#include "QRMessage.h"
#include "QRIpc.h"
#include "ComRtUtils.h"
#include "PortProcessCalls.h"
#include "cextdecs/cextdecs.h"
#include "seabed/ms.h"
using namespace QR;
static const short SEGMENT_NAME_LEN = 8;
CollHeap* MvQueryRewriteServer::heap_ = NULL;
NABoolean MvQueryRewriteServer::heapHasBeenSet_ = FALSE;
NAString MvQueryRewriteServer::fileNamePrefix_("w:\\qms\\debug\\mvqr");
IpcEnvironment* MvQueryRewriteServer::ipcEnv_ = NULL;
IpcServerClass* MvQueryRewriteServer::qmsServerClass_ = NULL;
IpcServer* MvQueryRewriteServer::qmsServer_ = NULL;
IpcServerClass* MvQueryRewriteServer::qmmServerClass_ = NULL;
IpcServer* MvQueryRewriteServer::qmmServer_ = NULL;
IpcServerClass* MvQueryRewriteServer::qmpServerClass_ = NULL;
IpcServer* MvQueryRewriteServer::qmpServer_ = NULL;
IpcEnvironment* MvQueryRewriteServer::getIpcEnv()
{
// Allocate static IpcEnvironment on first use. Can't use static object
// instead of pointer, because on seaquest, the IpcEnvironment ctor makes
// a system call before the seabed thread is available.
if (!ipcEnv_)
{
if (!heapHasBeenSet_)
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_INFO,
"Heap has not been specified to MvQueryRewriteServer, "
"IpcEnvironment being allocated on system heap.");
ipcEnv_ = new(heap_) IpcEnvironment(heap_, NULL, FALSE,
IPC_CLIENT_OR_UNSPECIFIED_SERVER,
TRUE);
}
return ipcEnv_;
}
NABoolean MvQueryRewriteServer::processExists(const short* processHandle)
{
char procName[200];
Int32 nid = 0;
Int32 pid = 0;
short result = 0;
NAProcessHandle phandle((SB_Phandle_Type *)processHandle);
Int32 guaRetcode = phandle.decompose();
if (!guaRetcode)
{
memset(procName, 0, sizeof(procName));
memcpy(procName, phandle.getPhandleString(), phandle.getPhandleStringLen());
result = msg_mon_get_process_info (procName, &nid, &pid);
}
else
result = guaRetcode;
return (result == 0);
}
char* MvQueryRewriteServer::getProcessName(IpcServerType serverType,
char* nodeName, /* not used for LINUX */
short cpu,
CollHeap* heap)
{
const char *overridingDefineName;
const char *actualPrefix;
char *period;
switch (serverType)
{
case IPC_SQLQMS_SERVER:
overridingDefineName = "_MX_QMS_PROCESS_PREFIX";
break;
case IPC_SQLQMP_SERVER:
overridingDefineName = "_MX_QMP_PROCESS_PREFIX";
break;
case IPC_SQLQMM_SERVER:
overridingDefineName = "_MX_QMM_PROCESS_PREFIX";
break;
default:
assertLogAndThrow1(CAT_SQL_COMP_QR_IPC, LL_ERROR,
FALSE, QRLogicException,
"Unknown server type in getProcessName(): %d",
serverType);
}
actualPrefix = getenv(overridingDefineName);
if (actualPrefix != NULL)
{
if (actualPrefix[0] != '$' || str_len(actualPrefix) > 4)
return NULL;
period = (char *) strchr(actualPrefix, '.');
if (period)
*period = '\0';
}
else
{
// No define, use standard prefix.
switch(serverType)
{
case IPC_SQLQMS_SERVER:
actualPrefix = QMS_PROCESS_PREFIX;
break;
case IPC_SQLQMP_SERVER:
actualPrefix = QMP_PROCESS_PREFIX;
break;
case IPC_SQLQMM_SERVER:
actualPrefix = QMM_PROCESS_PREFIX;
break;
default:
assertLogAndThrow1(CAT_SQL_COMP_QR_IPC, LL_ERROR,
FALSE, QRLogicException,
"Unknown server type in getProcessName(): %d",
serverType);
}
}
// Format required for Linux process name is $<name>
// defaults to $ZQSNNNN
char* procName = new(heap) char[100];
snprintf(procName, sizeof(procName), "%s%04d", actualPrefix, cpu);
return procName;
}
void MvQueryRewriteServer::getSegmentName(Int32 segmentNumber, char* segmentName)
{
short segmentNameLen;
short result = NODENUMBER_TO_NODENAME_(segmentNumber, segmentName,
SEGMENT_NAME_LEN, &segmentNameLen);
assertLogAndThrow1(CAT_SQL_COMP_QR_IPC, LL_ERROR,
result == 0, QRLogicException,
"Failed to get name for segment number %d", segmentNumber);
segmentName[segmentNameLen] = '\0';
}
IpcServer* MvQueryRewriteServer::createServerProcess(IpcServerClass* serverClass,
Int32 segmentNumber,
short cpu,
NABoolean usesTran)
{
IpcServerType serverType = serverClass->getServerType();
IpcServer* server = NULL;
ComDiagsArea* diagsArea = NULL;
IpcAllocateDiagsArea(diagsArea, heap_);
char segmentName[] = "NSK";
char* baseProcName = MvQueryRewriteServer::getProcessName(serverType, NULL, cpu, heap_);
server = serverClass->allocateServerProcess(&diagsArea, heap_, segmentName, cpu,
1, usesTran,
TRUE, 2, NULL, baseProcName);
if (!server)
QRLogger::logDiags(diagsArea, CAT_SQL_COMP_QR_IPC);
return server;
}
IpcServer* MvQueryRewriteServer::getQmsServer(DefaultToken publishDest,
NABoolean checkQms)
{
if (qmsServer_ && checkQms)
{
checkQmsServer();
}
if (!qmsServer_)
{
try
{
getIpcEnv(); // make sure ipcEnv_ has been set
const short SEGMENT_NAME_LEN = 50; // s/b 8 except for noncompliant names
delete qmsServerClass_; // If exists, may be wrong alloc type
// Can't specify IPC_USE_PROCESS for NT, or it will create a server
// process, but with a null control connection. Omitting the argument for
// NT here allows uniform code to handle both platforms below.
qmsServerClass_ = new IpcServerClass(ipcEnv_, IPC_SQLQMS_SERVER, IPC_USE_PROCESS);
// Look for QMS on the same cpu we are running on.
SB_Phandle_Type procHandle;
Int32 lc_cpu;
XPROCESSHANDLE_GETMINE_(&procHandle);
XPROCESSHANDLE_DECOMPOSE_ (&procHandle, &lc_cpu);
short myCpu = lc_cpu;
qmsServer_ = createServerProcess(qmsServerClass_, -1, myCpu, TRUE);
if (qmsServer_)
{
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_DEBUG,
"QMS process found on cpu #%d", qmsServer_->getServerId().getCpuNum());
initQms(qmsServer_, heap_);
}
else if (publishDest == DF_PRIVATE || publishDest == DF_BOTH)
{
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_INFO,
"Could not find local QMS, trying to start one...");
// Can't change allocation method of existing IpcServerClass, need a
// new one to spawn process.
delete qmsServerClass_;
qmsServerClass_ = new IpcServerClass(ipcEnv_, IPC_SQLQMS_SERVER,
IPC_SPAWN_OSS_PROCESS);
ComDiagsArea* diagsArea = NULL;
IpcAllocateDiagsArea(diagsArea, heap_);
char segmentName[SEGMENT_NAME_LEN + 1];
getSegmentName(-1, segmentName);
qmsServer_ = qmsServerClass_->allocateServerProcess(&diagsArea, heap_,
segmentName, -1);
if (qmsServer_)
{
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_INFO,
"QMS process started on cpu #%d", qmsServer_->getServerId().getCpuNum());
initQms(qmsServer_, heap_);
}
else
{
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_ERROR,
"Failed to allocate server process for QMS");
QRLogger::logDiags(diagsArea, CAT_SQL_COMP_QR_IPC);
}
}
else
{
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_ERROR,
"Could not find local QMS.");
}
}
catch(...)
{
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_ERROR,
"Exception when allocating server process for QMS");
return NULL;
}
}
return qmsServer_;
} // End of getQmsServer
void MvQueryRewriteServer::checkQmsServer()
{
char procName[200];
Int32 nid = 0;
Int32 pid = 0;
short result = 0;
NAProcessHandle phandle((SB_Phandle_Type *)&(qmsServer_->getServerId().getPhandle().phandle_));
Int32 guaRetcode = phandle.decompose();
if (!guaRetcode)
{
memset(procName, 0, sizeof(procName));
memcpy(procName, phandle.getPhandleString(), phandle.getPhandleStringLen());
result = msg_mon_get_process_info (procName, &nid, &pid);
}
else
result = guaRetcode;
if (result)
{
qmsServer_ = NULL;
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_INFO, "A QMS process has died.");
}
}
IpcServer* MvQueryRewriteServer::getQmmServer()
{
if (qmmServer_)
return qmmServer_;
getIpcEnv(); // make sure ipcEnv_ has been set
IpcServerClass* qmmServerClass = new(heap_) IpcServerClass(ipcEnv_,
IPC_SQLQMM_SERVER,
IPC_USE_PROCESS);
short cpu;
char segmentName[] = "NSK";
SB_Phandle_Type procHandle;
Int32 lc_cpu;
XPROCESSHANDLE_GETMINE_(&procHandle);
XPROCESSHANDLE_DECOMPOSE_ (&procHandle, &lc_cpu);
cpu = lc_cpu;
qmmServer_ = createServerProcess(qmmServerClass, 1, cpu, FALSE);
if (qmmServer_)
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_DEBUG, "QMM process found");
else
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_ERROR,
"Failed to locate server process for QMM on cpu %d ", cpu);
return qmmServer_;
} // End of getQmmServer
void MvQueryRewriteServer::resetQmmServer()
{
qmmServer_ = NULL;
}
IpcServer* MvQueryRewriteServer::getQmpServer()
{
if (!qmpServer_)
{
short cpu = IPC_CPU_DONT_CARE;
getIpcEnv(); // make sure ipcEnv_ has been set
//Phandle wrapper in porting layer
NAProcessHandle phandle((SB_Phandle_Type *)&(ipcEnv_->getMyOwnProcessId()
.getPhandle().phandle_));
short error = phandle.decompose();
if ( !error)
{
cpu = phandle.getCpu();
}
else
{
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_ERROR,
"PROCESSHANDLE_DECOMPOSE_ returned error %d", error);
cpu = IPC_CPU_DONT_CARE; // just in case (shouldn't have changed)
}
qmpServerClass_ = new(heap_) IpcServerClass(ipcEnv_, IPC_SQLQMP_SERVER);
qmpServer_ = qmpServerClass_->allocateServerProcess(NULL, NULL, NULL, cpu);
if (qmpServer_)
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_INFO,
"QMP process started on cpu #%d", qmpServer_->getServerId().getCpuNum());
else
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_ERROR,
"Failed to allocate server process for QMP");
}
return qmpServer_;
} // End of getQmpServer
// Send a MATCH message to the given qms process, and receive a result
// descriptor in response.
QRXmlMessageObj* MvQueryRewriteServer::sendMatchMessage(IpcServer* qms,
XMLString* qryDescText,
CollHeap * heap)
{
NAString optName("optimizer");
QRMessageStream msgStream(ipcEnv_, optName, heap, QR::MATCH_REQUEST);
QRXmlMessageObj* msgPtr = new QRXmlMessageObj(qryDescText, QR::MATCH_REQUEST);
// Add QMS process as a recipient of the message.
assertLogAndThrow(CAT_SQL_COMP_QR_IPC, LL_ERROR,
qms, QRLogicException,
"Null qms passed to sendMatchMessage");
IpcConnection* conn = qms->getControlConnection();
msgStream.addRecipient(conn);
msgStream.clearAllObjects();
// Insert message object into the message stream
msgStream << *msgPtr;
// Send the message stream to the server
msgStream.send();
msgPtr->decrRefCount();
// Read the reply from the server
msgStream.setType(QR::MATCH_RESPONSE);
msgStream.receive();
QRXmlMessageObj* xmlResponse = NULL;
if (msgStream.moreObjects())
{
Lng32 t = msgStream.getNextObjType();
switch (t)
{
case QR::MATCH_RESPONSE:
// Get the message object containing the result descriptor. This
// is returned as the function result, and the caller must
// decrement the reference count when through with it.
xmlResponse = new QRXmlMessageObj(NULL, QR::MATCH_RESPONSE);
msgStream >> *xmlResponse;
break;
case QR::STATUS_RESPONSE:
{
QRStatusMessageObj* statusResponse = new QRStatusMessageObj();
msgStream >> *statusResponse;
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_DEBUG,
"STATUS RESPONSE received: %d\n", (Int32)statusResponse->getStatusCode());
statusResponse->decrRefCount();
}
break;
default:
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_ERROR, "Unexpected response type: %d.", t);
break;
}
if (msgStream.moreObjects())
{
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_WARN,
"Match request received one or more extraneous response "
"objects, which were discarded");
msgStream.clearAllObjects();
}
}
else
{
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_WARN,
"No response object in message stream from QMS");
checkQmsServer();
}
return xmlResponse;
} // sendMatchMessage
QRRequestResult MvQueryRewriteServer::initQms(IpcServer* qmsServer,
CollHeap* heap)
{
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_DEBUG, "INITIALIZE request sent to QMS...");
QRRequestResult response = sendInitializeMessage(qmsServer, heap);
if (response == Success)
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_DEBUG, "...Initialization succeeded");
else
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_ERROR, "INITIALIZATION FAILED, result = %d",
response);
return response;
}
QRRequestResult MvQueryRewriteServer::sendInitializeMessage(IpcServer* qms,
CollHeap* heap)
{
NAString optName("optimizer");
QRMessageStream msgStream(ipcEnv_, optName, heap, QR::INITIALIZE_REQUEST);
QRSimpleMessageObj* msgPtr = new QRSimpleMessageObj(QR::INITIALIZE_REQUEST);
// Add QMS process as a recipient of the message.
assertLogAndThrow(CAT_SQL_COMP_QR_IPC, LL_ERROR,
qms, QRLogicException,
"Null qms passed to sendInitializeMessage()");
IpcConnection* conn = qms->getControlConnection();
msgStream.addRecipient(conn);
msgStream.clearAllObjects();
// Insert message object into the message stream
msgStream << *msgPtr;
// Send the message stream to the server
msgStream.send();
msgPtr->decrRefCount();
// Read the reply from the server
msgStream.setType(QR::STATUS_RESPONSE);
msgStream.receive();
QRRequestResult status = ProtocolError; // unless response read successfully
if (msgStream.moreObjects())
{
Lng32 t = msgStream.getNextObjType();
if (t == QR::STATUS_RESPONSE)
{
QRStatusMessageObj* statusResponse = new QRStatusMessageObj();
msgStream >> *statusResponse;
// Must extract the status code before decrementing ref count.
status = statusResponse->getStatusCode();
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_DEBUG,
"STATUS RESPONSE received: %d\n", (Int32)status);
statusResponse->decrRefCount();
}
else
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_ERROR, "Unexpected response type: %d.", t);
if (msgStream.moreObjects())
{
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_WARN,
"Initialize request received one or more extraneous response "
"objects, which were discarded");
msgStream.clearAllObjects();
}
}
else
{
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_WARN,
"No response object in message stream from QMS");
if (qmsServer_)
checkQmsServer();
}
return status;
} // sendInitializeMessage
// This function was copied from cli/Context.cpp.
// *****************************************************************************
// *
// * Julian timestamp is converted to standard format:
// * YYYY-MM-DD HH:MM:SS.MMM.MMM
// *
// * @Param buffer | char * | OUT |
// * NUll-terminated char array in which result is returned.
// *
// * @Param GMT_Time | short * | IN |
// * is the julian timestamp to be converted.
// *
// * @End
// *****************************************************************************
void MvQueryRewriteServer::formatTimestamp(
char *buffer, // Output
Int64 GMT_Time) // Input
{
if (GMT_Time == 0)
{
buffer[0] = '\0';
return;
}
short Date_and_Time[8];
short & Year = *((short *)&(Date_and_Time[0]));
short & Month = *((short *)&(Date_and_Time[1]));
short & Day = *((short *)&(Date_and_Time[2]));
short & Hour = *((short *)&(Date_and_Time[3]));
short & Minute = *((short *)&(Date_and_Time[4]));
short & Second = *((short *)&(Date_and_Time[5]));
short & Millisecond = *((short *)&(Date_and_Time[6]));
short & Microsecond = *((short *)&(Date_and_Time[7]));
short errorNumber;
bool isGMT = false;
Int64 julianTime;
//#if defined (NA_YOS)
// If supplied value is not a legal julian timestmap, punt
if (GMT_Time > 274958971199999999LL || // The year 4000
GMT_Time < 146728398400000000LL) // The year 1
{
*buffer = 0;
return;
}
//#endif
//Convert to local time
julianTime = CONVERTTIMESTAMP(GMT_Time,0,-1,&errorNumber);
// If we can't convert, just show GMT
if (errorNumber)
isGMT = true;
// Decompose timestamp
INTERPRETTIMESTAMP(julianTime,Date_and_Time);
// 0123456789012345678901234567
//"YYYY-MM-DD HH:MM:SS.mmm.mmm"
NUMOUT(buffer,Year,10,4);
buffer[4] = '-';
NUMOUT(&buffer[5],Month,10,2);
buffer[7] = '-';
NUMOUT(&buffer[8],Day,10,2);
buffer[10] = ' ';
NUMOUT(&buffer[11],Hour,10,2);
buffer[13] = ':';
NUMOUT(&buffer[14],Minute,10,2);
buffer[16] = ':';
NUMOUT(&buffer[17],Second,10,2);
buffer[19] = '.';
NUMOUT(&buffer[20],Millisecond,10,3);
buffer[23] = '.';
NUMOUT(&buffer[24],Microsecond,10,3);
if (isGMT)
{
buffer[27] = ' ';
buffer[28] = 'G';
buffer[29] = 'M';
buffer[30] = 'T';
buffer[31] = 0;
}
else
buffer[27] = 0;
} // End of formatTimestamp
void MvQueryRewriteServer::getFormattedTimestamp(char* buffer)
{
formatTimestamp(buffer, NA_JulianTimestamp());
}
// ***************************************************************
// ***************************************************************
QR::QRRequestResult
MvQueryRewriteServer::sendPublishMessage(const NAString* descriptorText,
const NAString& serverName,
IpcServer*& server,
CollHeap * heap)
{
// String constant used in logging entries.
const static char PUBLISH[] = "PUBLISH";
if (!descriptorText)
{
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_ERROR,
"null descriptorText passed to "
"MvQueryRewriteServer::sendPublishMessage()");
return InternalError;
}
if (!server)
{
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_ERROR,
"null server passed to MvQueryRewriteServer::sendPublishMessage()");
return InternalError;
}
// Only messages we deal with here are publish.
const char* requestName = PUBLISH;
IpcMessageObjType msgType = QR::PUBLISH_REQUEST;
QRRequestResult result;
QRMessageStream msgStream(server->getServerClass()->getEnv(),
serverName,
heap,
msgType);
QRXmlMessageObj* msgPtr = new QRXmlMessageObj(descriptorText, msgType);
// Add server process as a recipient of the message.
msgStream.addRecipient(server->getControlConnection());
msgStream.clearAllObjects();
// Insert message object into the message stream, and send it.
msgStream << *msgPtr;
msgStream.send();
msgPtr->decrRefCount();
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_INFO,
"Publish message sent, awaiting reply...");
// Read the reply from the server
msgStream.receive();
if (!msgStream.moreObjects())
{
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_ERROR,
"No response object in message stream from %s", serverName.toCharStar());
if (!processExists((short *)&(server->getServerId().getPhandle().phandle_)))
{
// If called from catman to publish directly to local qms, server
// refers to that qms process. If it is not there, nothing we can do
// here. It will be initialized with all published MVs when it comes
// back up.
if (server->getServerClass()->getServerType() == IPC_SQLQMS_SERVER)
{
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_WARN,
"Can't publish to qms: process does not exist");
return Unable;
}
// Check periodically until QMM is restarted. If we terminate, qmm may
// have already restarted and found us, so will not create a new qmp.
// Also, since we were a client of the former qmm, the new one will not
// receive a system message notifying it of our termination.
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_INFO,
"QMM is gone, trying to connect to new incarnation...");
server = NULL;
resetQmmServer(); // so a new one will be looked for
while (!server)
{
server = getQmmServer();
if (!server)
{
DELAY(1000);
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_INFO, "Trying again...");
}
}
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_INFO,
"Connection to new QMM established, resending message.");
return sendPublishMessage(descriptorText, serverName, server, heap);
}
return ProtocolError;
}
else if (msgStream.getNextObjType() != QR::STATUS_RESPONSE)
{
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_ERROR,
"Wrong response object returned from %s request", requestName);
return ProtocolError;
}
else
{
QRStatusMessageObj statusObj;
msgStream >> statusObj;
result = statusObj.getStatusCode();
if (result == QR::Success)
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_INFO,
"%s request succeeded", requestName);
else
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_ERROR,
"%s request failed with status %d", requestName, result);
if (msgStream.moreObjects())
{
QRLogger::log(CAT_SQL_COMP_QR_IPC, LL_WARN,
"%s request received one or more extraneous response "
"objects, which were discarded", requestName);
msgStream.clearAllObjects();
}
return result;
}
} // sendPublishMessage
void extractDefineAndThenPutEnvIfFound(char *defineName)
{
} // static void extractDefineAndThenPutEnvIfFound()