From 8f27e65bddd7d4b8515ce620fb485fdd78fcdf89 Mon Sep 17 00:00:00 2001 From: Constantin Jucovschi Date: Fri, 24 Apr 2009 07:20:22 -0400 Subject: Initial commit --- servercomm/Makefile.am | 50 + servercomm/callbackmgr.cc | 172 ++ servercomm/callbackmgr.hh | 136 ++ servercomm/httpserver.cc | 1371 ++++++++++++ servercomm/httpserver.hh | 173 ++ servercomm/httpserver.icc | 31 + servercomm/manager.cc | 1885 +++++++++++++++++ servercomm/servercomm.cc | 1399 +++++++++++++ servercomm/servercomm.hh | 1120 ++++++++++ servercomm/servercomm.icc | 31 + servercomm/servercomm2.cc | 4051 ++++++++++++++++++++++++++++++++++++ servercomm/test/Makefile | 117 ++ servercomm/test/template_inst.hh | 138 ++ servercomm/test/test_dbcontent.cc | 161 ++ servercomm/test/test_oid.cc | 229 ++ servercomm/test/test_servercomm.cc | 128 ++ 16 files changed, 11192 insertions(+) create mode 100644 servercomm/Makefile.am create mode 100644 servercomm/callbackmgr.cc create mode 100644 servercomm/callbackmgr.hh create mode 100644 servercomm/httpserver.cc create mode 100644 servercomm/httpserver.hh create mode 100644 servercomm/httpserver.icc create mode 100644 servercomm/manager.cc create mode 100644 servercomm/servercomm.cc create mode 100644 servercomm/servercomm.hh create mode 100644 servercomm/servercomm.icc create mode 100644 servercomm/servercomm2.cc create mode 100644 servercomm/test/Makefile create mode 100644 servercomm/test/template_inst.hh create mode 100644 servercomm/test/test_dbcontent.cc create mode 100644 servercomm/test/test_oid.cc create mode 100644 servercomm/test/test_servercomm.cc (limited to 'servercomm') diff --git a/servercomm/Makefile.am b/servercomm/Makefile.am new file mode 100644 index 0000000..07e962f --- /dev/null +++ b/servercomm/Makefile.am @@ -0,0 +1,50 @@ +# -*-Makefile-*- (for Emacs) +# +# This file is part of rasdaman community. +# +# Rasdaman community is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Rasdaman community 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with rasdaman community. If not, see . +# +# Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +# rasdaman GmbH. +# +# For more information please see +# or contact Peter Baumann via . +# +# MAKEFILE FOR: +# module servercomm +# +# COMMENTS: +# +################################################################## + +AM_CXXFLAGS=@BASEDBCXXFLAGS@ +AM_LDFLAGS=@BASEDBLDFLAGS@ + +noinst_LIBRARIES=libservercomm.a +libservercomm_a_SOURCES=../clientcomm/rpcif_xdr.c ../clientcomm/rpcif_svc.cc servercomm.cc \ + servercomm2.cc manager.cc callbackmgr.cc httpserver.cc \ + ../mymalloc/mymalloc_svc.cc ../mymalloc/mymalloc.h \ + ../clientcomm/rpcif.h callbackmgr.hh httpserver.hh httpserver.icc \ + servercomm.hh servercomm.icc +BUILT_SOURCES=../clientcomm/rpcif_xdr.c ../clientcomm/rpcif.h ../clientcomm/rpcif_svc.cc + +.PHONY: ../clientcomm/rpcif_xdr.c ../clientcomm/rpcif.h ../clientcomm/rpcif_svc.cc +../clientcomm/rpcif_xdr.c: + cd ../clientcomm ; $(MAKE) rpcif_xdr.c + +../clientcomm/rpcif.h: + cd ../clientcomm ; $(MAKE) rpcif.h + +../clientcomm/rpcif_svc.cc: + cd ../clientcomm ; $(MAKE) rpcif_svc.cc diff --git a/servercomm/callbackmgr.cc b/servercomm/callbackmgr.cc new file mode 100644 index 0000000..ff5cb08 --- /dev/null +++ b/servercomm/callbackmgr.cc @@ -0,0 +1,172 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see . +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see +* or contact Peter Baumann via . +/ +/** + * SOURCE: callbackmgr.hh + * + * MODULE: servercomm + * CLASS: CallBackManager + * + * COMMENTS: + * No Comments +*/ + + +#include +#include +#include + +#include "raslib/rmdebug.hh" +#include "debug.hh" + +#include "servercomm/callbackmgr.hh" + + +CallBackManager::CallBackManager(unsigned int size) +{ + RMDBGONCE(1, RMDebug::module_servercomm, "CallBackManager", "CallBackManager(" << size << ")" ) + + callbacks = new callback_desc_t[size]; + maxCallbacks = size; + numPending = 0; + overflowDetected = 0; +} + + +CallBackManager::~CallBackManager(void) +{ + RMDBGONCE(1, RMDebug::module_servercomm, "CallBackManager", "~CallBackManager()" ) + + delete [] callbacks; +} + + +void CallBackManager::setMaximumSize(unsigned int size) +{ + RMDBGONCE(1, RMDebug::module_servercomm, "CallBackManager", "setMaximumSize(" << size << ")" ) + + callback_desc_t *newcb = new callback_desc_t[size]; + + if (callbacks != NULL) + { + if (numPending != 0) + { + unsigned int copysize = (numPending > size) ? size : numPending; + memcpy(newcb, callbacks, copysize * sizeof(callback_desc_t)); + if (copysize < numPending) + overflowDetected = 1; + } + delete [] callbacks; + } + callbacks = newcb; + maxCallbacks = size; +} + + +int CallBackManager::findCallback(callback_f function, void *context) const +{ + unsigned int i; + + // No system calls from this function! + for (i=0; i= maxCallbacks) + { + overflowDetected = 1; + return -1; + } + + callbacks[numPending].function = function; + callbacks[numPending].context = context; + + numPending++; + + return 0; +} + + +int CallBackManager::registerUniqueCallback(callback_f function, void *context) +{ + if (findCallback(function, context) == -1) + return registerCallback(function, context); + else + return -1; +} + + +int CallBackManager::removeCallback(callback_f function, void *context) +{ + int i; + + // restrictive environment here too, no system calls! + i = findCallback(function, context); + if (i != -1) + { + if (i < (int)numPending-1) + memmove(callbacks+i, callbacks+(i+1), (numPending-i-1)*sizeof(callback_desc_t)); + numPending--; + return 0; + } + return -1; +} + + +unsigned int CallBackManager::getNumCallbacks(void) const +{ + return numPending; +} + + +int CallBackManager::executePending(void) +{ + unsigned int i; + + RMDBGENTER(2, RMDebug::module_servercomm, "CallBackManager", "executePending()" ) + + for (i=0; i. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see +* or contact Peter Baumann via . +/ +/** + * SOURCE: callbackmgr.hh + * + * MODULE: servercomm + * CLASS: CallBackManager + * + * COMMENTS: + * No Comments +*/ + + +#ifndef _CALLBACK_MANAGER_ +#define _CALLBACK_MANAGER_ + + +//@ManMemo: Module: {\bf servercomm} + +/*@Doc: + The class CallBackManager stores function- and context pointers + that are executed in the order they were registered in when + calling executePending(). This system replaces the alarm handler + approach formerly used which suffered from a restrictive + environment where mallocs and printfs were not legal. Since + neither call must happen when a callback function is registered + the maximum number of callbacks must be fixed in the constructor. +*/ + +class CallBackManager +{ + public: + /// constructor + CallBackManager(unsigned int size=1024); + /** + Constructor; size is the maximum number of callback slots to + reserve. + */ + + /// destructor + ~CallBackManager(void); + + /// Resizes the number of callback slots at run-time + void setMaximumSize(unsigned int size); + + /// callback function type + typedef void (*callback_f)(void*); + /** + The type of a callback function is void f(void *context). The + value of context is the one specified when registering. It's + up to the function to cast the value. + */ + + /// register new callback + int registerCallback(callback_f function, void *context); + /** + Register a new callback function. Returns 0 for OK, -1 if + the callback table had an overflow. Since it must be possible + to call this function from an alarm handler there may not be + any mallocs or prints here. + */ + + /// register new callback, ensuring uniqueness + int registerUniqueCallback(callback_f function, void *context); + /** + Same as registerCallback, but makes sure this callback isn't + pending already in which case it does nothing. + */ + + /// remove callback + int removeCallback(callback_f function, void *context); + /** + Deregister a callback function. Returns 0 for OK, -1 for + not found. + */ + + /// Get the number of callback functions registered. + unsigned int getNumCallbacks(void) const; + + /// execute pending callbacks + int executePending(void); + /** + Execute all pending callback functions and clear the list + afterwards. Returns the number of callback functions executed. + */ + + + private: + + /// find a matching function/context pair + int findCallback(callback_f function, void *context) const; + /** + Searches the pending callbacks for the one specified by the + function parameters and returns its index if found, -1 otherwise. + */ + + //@{ Internal storage structure for callbacks. + typedef struct callback_desc_s { + callback_f function; + void *context; + } callback_desc_t; + //@} + + callback_desc_t *callbacks; + + /// Maximum size of callback list + unsigned int maxCallbacks; + + /// Currently occupied callback slots + unsigned int numPending; + + /// Flag that's set to 1 if the callback table overflowed. + int overflowDetected; +}; + +#endif diff --git a/servercomm/httpserver.cc b/servercomm/httpserver.cc new file mode 100644 index 0000000..514f6b8 --- /dev/null +++ b/servercomm/httpserver.cc @@ -0,0 +1,1371 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see . +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see +* or contact Peter Baumann via . +/ +/** + * SOURCE: httpserver.cc + * + * MODULE: httpserver + * CLASS: HttpServer + * + * COMMENTS: + * No Comments +*/ + +#include "mymalloc/mymalloc.h" + +static const char rcsid[] = "@(#)servercomm, HttpServer: $Id: httpserver.cc,v 1.54 2005/09/03 21:05:14 rasdev Exp $"; + +#include +#include +#include // for time() +#include + +#include // for signal() +#include // for alarm(), gethostname() +#include + +#include "raslib/rmdebug.hh" +#include "raslib/rminit.hh" +#include "raslib/error.hh" +#include "raslib/minterval.hh" +#include "raslib/endian.hh" +#include "raslib/basetype.hh" + +#include "servercomm/httpserver.hh" +#include "qlparser/qtnode.hh" +#include "qlparser/qtdata.hh" +#include "catalogmgr/typefactory.hh" + +#include "mddmgr/mddcoll.hh" +#include "tilemgr/tile.hh" + +#include "relcatalogif/mddtype.hh" +#include "relcatalogif/mdddomaintype.hh" +#include "relcatalogif/settype.hh" + +#ifdef PURIFY +#include +#endif + +#ifdef RMANBENCHMARK + // to control ZLibCompression::decompTimer and ZLibCompression::compTimer + #include "zlibcompression.hh" +#endif + +#include "httpserver/httpserver.h" + +#include "qlparser/qtmdd.hh" +#include "qlparser/qtatomicdata.hh" +#include "qlparser/qtcomplexdata.hh" +#include "qlparser/qtintervaldata.hh" +#include "qlparser/qtmintervaldata.hh" +#include "qlparser/qtpointdata.hh" +#include "qlparser/qtstringdata.hh" + +#define UNEXPECTED_INTERNAL_ERROR 10000 + +// ok message for log output; should go into a central file +#define MSG_OK "ok" + +// include and extern declarations for the query parsing +#include "qlparser/querytree.hh" +extern int yyparse(); +extern void yyreset(); +extern QueryTree* parseQueryTree; +extern ParseInfo* parseError; +extern char* beginParseString; +extern char* iterParseString; +extern unsigned long maxTransferBufferSize; +extern char* dbSchema; +extern MDDColl* mddConstants; + +// this is a temporary thing +extern int globalOptimizationLevel; + +#ifdef RMANBENCHMARK +#if BASEDB == 1 +static o2_ClientStat newO2Stats; +static o2_ClientStat oldO2Stats; +#endif +#endif + +// defined in servercomm2.cc +extern char globalHTTPSetTypeStructure[]; + +// This currently represents the one and only client active at one time. +static HttpServer::ClientTblElt globalClientContext(ServerComm::HTTPCLIENT, 1); + +// At the beginning, no servercomm object exists. +HttpServer* HttpServer::actual_httpserver = 0; + +// This is a local inline function to enable encoding of longs into chars +static inline void encodeLong(char* res, const r_Long* val) +{ + *res = *(char*)val; + *(res+1) = *((char*)val+1); + *(res+2) = *((char*)val+2); + *(res+3) = *((char*)val+3); +} + +static inline void encodeULong(char* res, const r_ULong* val) +{ + *res = *(char*)val; + *(res+1) = *((char*)val+1); + *(res+2) = *((char*)val+2); + *(res+3) = *((char*)val+3); +} + +// This function takes a binary data block and returns a vector of mddTransferEncodings +void +getMDDs(int binDataSize, char *binData, int Endianess, vector &resultVector) +{ + char *currentPos; + char *stringBuffer; + char *binDataBuffer = NULL; + r_Long intValue; + HttpServer::MDDEncoding *currentMDD; + r_Endian::r_Endianness myEndianess = r_Endian::get_endianness(); + + //RMInit::logOut << "Starting getMDDs , binDataSize is " << binDataSize << endl; + + stringBuffer = (char*)mymalloc(1024); + currentPos = binData; + // Examine the whole array... + while(currentPos < (binData+binDataSize)) + { + // Create new MDDEncoding object + currentMDD = new HttpServer::MDDEncoding(); + // get objectType + memcpy(&intValue, currentPos, sizeof(r_Long)); + // intValue = *(int *)intValue; + if(myEndianess != Endianess) + { + intValue = r_Endian::swap(intValue); + } + currentMDD->setObjectType(intValue); + currentPos+=4; + //RMInit::logOut << "object type is " << intValue << endl; + + // get objectTypeName + strcpy(stringBuffer,currentPos); + if(strcmp(stringBuffer,"null") != 0) + currentMDD->setObjectTypeName(strdup(stringBuffer)); + currentPos += strlen(stringBuffer) + 1; + //RMInit::logOut << "objectTypeName is " << stringBuffer << endl; + + // get typeStructure + strcpy(stringBuffer,currentPos); + if(strcmp(stringBuffer,"null") != 0) + currentMDD->setTypeStructure(strdup(stringBuffer)); + currentPos += strlen(stringBuffer) + 1; + //RMInit::logOut << "typeStructure is " << stringBuffer << endl; + + // get typeLength + memcpy(&intValue, currentPos, sizeof(r_Long)); + //intValue = *(int *)currentPos; + if(myEndianess != Endianess) + { + intValue = r_Endian::swap(intValue); + } + currentMDD->setTypeLength(intValue); + currentPos+=sizeof(r_Long); + //RMInit::logOut << "type length is " << intValue << endl; + + // get domain + strcpy(stringBuffer,currentPos); + if(strcmp(stringBuffer,"null") != 0) + currentMDD->setDomain(strdup(stringBuffer)); + currentPos += strlen(stringBuffer) + 1; + //RMInit::logOut << "domain is " << stringBuffer << endl; + + // get tileSize - later this will be the storage_layout + strcpy(stringBuffer,currentPos); + if(strcmp(stringBuffer,"null") != 0) + currentMDD->setTileSize(strdup(stringBuffer)); + currentPos += strlen(stringBuffer) + 1; + //RMInit::logOut << "tileSize is " << stringBuffer << endl; + // get oid + strcpy(stringBuffer,currentPos); + if(strcmp(stringBuffer,"null") != 0) + currentMDD->setOID(strdup(stringBuffer)); + currentPos += strlen(stringBuffer) + 1; + //RMInit::logOut << "OID is " << stringBuffer << endl; + // get dataSize + /* RMInit::logOut << "Byte1: " << (short)*currentPos << endl; + RMInit::logOut << "Byte2: " << (short)*(currentPos+1) << endl; + RMInit::logOut << "Byte3: " << (short)*(currentPos+2) << endl; + RMInit::logOut << "Byte4: " << (short)*(currentPos+3) << endl;*/ + memcpy(&intValue, currentPos, sizeof(r_Long)); + //intValue = *(int *)currentPos; + if(myEndianess != Endianess) + { + intValue = r_Endian::swap(intValue); + } + currentMDD->setDataSize(intValue); + currentPos+=sizeof(r_Long); + //RMInit::logOut << "Data size is " << intValue << endl; + + // get binData + binDataBuffer = (char*)mymalloc(intValue+1); + memcpy(binDataBuffer,currentPos,intValue); + currentMDD->setBinData(binDataBuffer); + currentPos += intValue; + + // Put object into result vector + resultVector.insert(resultVector.begin(),currentMDD); + } + // free Buffer + free(stringBuffer); + + //RMInit::logOut << "vector has " << resultVector.size() << " entries." << endl; + +} + + +/********************************************************************** + * Class MDDEncoding + * + * IMPORTANT: This class does not copy its parameters for performance reasons! + * So the memory allocated for the parameters must not be freed in + * the calling functions or methods, this is done by the destructor + * of this class! + * + *********************************************************************/ +HttpServer::MDDEncoding::MDDEncoding() +{ + objectType = 0; + objectTypeName = NULL; + typeStructure = NULL; + typeLength = 0; + domain = NULL; + tileSize = NULL; + oidString = NULL; + dataSize = 0; + binData = NULL; + stringRepresentation = (char*)mymalloc(4096); +}; + +HttpServer::MDDEncoding::~MDDEncoding() +{ + if (objectTypeName != NULL) + free(objectTypeName); + if (typeStructure != NULL) + free(typeStructure); + if (domain != NULL) + free(domain); + if (tileSize != NULL) + free(tileSize); + if (oidString != NULL) + free(oidString); + // binData is freed elsewhere! + free(stringRepresentation); +} + +void HttpServer::MDDEncoding::setObjectType(int type) +{ + objectType = type; +} + +void HttpServer::MDDEncoding::setObjectTypeName(char *name) +{ + if(objectTypeName) + free(objectTypeName); + objectTypeName = name; +} + +void HttpServer::MDDEncoding::setTypeStructure(char *type) +{ + if(typeStructure) + free(typeStructure); + typeStructure = type; +} + +void HttpServer::MDDEncoding::setTypeLength(int len) +{ + typeLength = len; +} + +void HttpServer::MDDEncoding::setDomain(char *dom) +{ + if(domain) + free(domain); + domain = dom; +} + +void HttpServer::MDDEncoding::setTileSize(char *size) +{ + if(tileSize) + free(tileSize); + tileSize = size; +} + +void HttpServer::MDDEncoding::setOID(char *o) +{ + if(oidString) + free(oidString); + oidString = o; +} + +void HttpServer::MDDEncoding::setDataSize(int size) +{ + dataSize = size; +} + +void HttpServer::MDDEncoding::setBinData(char *data) +{ + if(binData) + free(binData); + binData = data; +} + +const char* HttpServer::MDDEncoding::toString() +{ + char *intBuffer = (char*)mymalloc(128); + + strcpy(stringRepresentation,"\n\n MDD information: \n ObjectTypeName: "); + strcat(stringRepresentation,objectTypeName); + strcat(stringRepresentation,"\n ObjectType: "); + sprintf(intBuffer,"%d",objectType); + strcat(stringRepresentation,intBuffer); + strcat(stringRepresentation,"\n OID: "); + strcat(stringRepresentation,oidString); + strcat(stringRepresentation,"\n Domain: "); + strcat(stringRepresentation,domain); + strcat(stringRepresentation,"\n TypeStructure: "); + strcat(stringRepresentation,typeStructure); + strcat(stringRepresentation,"\n tileSize: "); + strcat(stringRepresentation,tileSize); + strcat(stringRepresentation,"\n DataSize: "); + sprintf(intBuffer,"%d",dataSize); + strcat(stringRepresentation,intBuffer); + + free(intBuffer); + return stringRepresentation; +} + + +/************************************************************************* + * Method name...: HttpServer() (constructor) + ************************************************************************/ +HttpServer::HttpServer() +{ + if( actual_httpserver ) + { + std::cerr << "Internal Error: Tried to instantiate more than one HttpServer object." << endl; + exit(1); + } + + // So that it should be never freed + globalClientContext.currentUsers++; + actual_httpserver = this; + flagInformRasMgr = false; + isHttpServer = true; +} + +HttpServer::HttpServer( unsigned long timeOut, unsigned long managementInterval , unsigned long listenPort, char* rasmgrHost, unsigned int rasmgrPort,char* serverName) +:ServerComm( timeOut, managementInterval, listenPort, rasmgrHost, rasmgrPort,serverName) +{ + if( actual_httpserver ) + { + std::cerr << "Internal Error: Tried to instantiate more than one HTTPServer object." << endl; + exit(1); + } + + actual_httpserver = this; + + flagInformRasMgr = false; + + // So that it should be never freed + globalClientContext.currentUsers++; + + isHttpServer = true; +} + +/************************************************************************* + * Method name...: ~HttpServer() (destructor) + ************************************************************************/ +HttpServer::~HttpServer() +{ + // delete communication object + if( admin ) delete admin; + + actual_httpserver = 0; +} + + + +/************************************************************************* + * Method name...: startHttpServer() + ************************************************************************/ +void termSignalHandler(int sig); + + +void +HttpServer::startRpcServer() + throw( r_Error ) +{ + // create administraion object (O2 session is initialized) + admin = AdminIf::instance(); + if( !admin ) + throw r_Error( r_Error::r_Error_BaseDBMSFailed ); + + // simulating command line arguments + const char* dummy[] = { "rasserver", "-c", "httpserver.conf" }; + + signal (SIGTERM, termSignalHandler); + cout << "RasDaMan server "<(dummy)); + +} + +void termSignalHandler(int sig) + { + static int in_progress=0; + + if (in_progress) return; + + in_progress = 1; + + if(HttpServer::actual_httpserver) + { + HttpServer::actual_httpserver->stopRpcServer(); + } + + } + +void clearLastClient(); // is down at end of file + + +void +HttpServer::stopRpcServer() +{ + RMInit::logOut << " Shutdown request received." << endl; + // RMInit::logOut << "Unregistering interface..."; + // has to be adapted for HTTP server + // svc_unregister( RPCIF, RPCIFVERS ); + // RMInit::logOut << "ok" << endl; + + RMInit::logOut << "Clearing clients..." << endl; + + clearLastClient(); + RMInit::logOut << "informing rasmgr..." << endl; + informRasMGR(SERVER_DOWN); + cout << "rasdaman server " << serverName << " is down." << endl; + + exit(0); + // svc_exit(); +} + +HttpServer::ClientTblElt* +HttpServer::getClientContext( unsigned long clientId ) +{ + // this is a simplification and only works for one client + globalClientContext.currentUsers++; + return &globalClientContext; +} + +/************************************************************************* + * Method name...: printServerStatus( ) + ************************************************************************/ +void +HttpServer::printServerStatus( ostream& s ) +{ + unsigned long currentTime = time(NULL); + + s << endl; + s << "HTTP Server state information at " << endl; // << ctime((time_t*)¤tTime) << endl; + s << " Transaction active.............: " << ( transactionActive ? "yes" : "no" ) << endl; + s << " Max. transfer buffer size......: " << maxTransferBufferSize << " bytes" << endl; + s << endl; + + // Purify? + /* + s << "memory map----------------------------------------------------" << endl; + + // memorymap(1); + + struct mallinfo meminfo = mallinfo(); + + s << "space in arena : " << meminfo.arena << endl; + s << "number of small blocks : " << meminfo.smblks << endl; + s << "number of ordinary blocks : " << meminfo.ordblks << endl; + s << "space in free ordinary blocks : " << meminfo.fordblks << endl; + s << "space in used ordinary blocks : " << meminfo.uordblks << endl; + + s << "additional space from last call: " << meminfo.uordblks - memUsed << endl; + + memUsed = meminfo.uordblks; + + s << "end memory map------------------------------------------------" << endl << endl; + */ +} + +//For future, to make this shit nicer + +#ifdef LITTLE_ENDIAN + const int systemEndianess = 1; +#else + const int systemEndianess = 0; +#endif + +int encodeAckn(char*&result, int ackCode = 99) // 99 is the 'OK', but we have other ack to + { + result = (char*)mymalloc(1); + *result = ackCode; + return 1; + } + +void cleanExecuteQueryRes(ExecuteQueryRes& res) { + if(res.typeStructure){ + free(res.typeStructure); + res.typeStructure=NULL; + } + if(res.token){ + free(res.token); + res.token=NULL; + } + if(res.typeName){ + free(res.typeName); + res.typeName=NULL; + } +} + +int encodeError(char*&result, const r_ULong errorNo, const r_ULong lineNo,const r_ULong columnNo, const char *text) + { + int totalLength = 14 + strlen(text) + 1; + result = (char*)mymalloc(totalLength); + char *currentPos = result; + // fill it with data + *currentPos = 0; // result is error + currentPos++; + *currentPos = systemEndianess; + currentPos++; + encodeULong(currentPos, &errorNo); + currentPos += sizeof(r_ULong); + encodeULong(currentPos, &lineNo); + currentPos += sizeof(r_ULong); + encodeULong(currentPos, &columnNo); + currentPos += sizeof(r_ULong); + strcpy(currentPos, text); + + return totalLength; + } + +//a dirty hack: +static long lastCallingClientId=-1; +// this is needed to get a died client lost. Who designed this should do it better + + +long +HttpServer::processRequest( unsigned long callingClientId, char* baseName, int rascommand, + char* query, int binDataSize, char* binData, int Endianess, char*& result, char *capability ) +{ + +lastCallingClientId=callingClientId; + + //RMInit::logOut << "Start Method Processrequest ... " << endl; +try + { // put to catch all errors which come up here, so HTTP-Server doesn't crush because of them + + long returnValue=0; + unsigned short execResult=0; + unsigned short dummy=0; + ExecuteQueryRes resultError; + // needed for result type MDD collection + r_Minterval resultDom; + char* typeName; + char* typeStructure; + char* currentPos; + r_OId oid; + bool valid; + unsigned short currentFormat; + // vector with mdds in transfer encoding + vector transferredMDDs; + Tile* resultTile; // temporary tile with the whole MDD + int i; + unsigned short objType; + r_OId *roid; + int totalLength; + long l; + ClientTblElt* context; + // server endianess + r_Endian::r_Endianness serverEndian; + if(capability) + { unsigned long resultCode; + resultCode=accessControl.crunchCapability(capability); + + if(resultCode) + { + flagInformRasMgr = true; // Used in doIt_http... + + return encodeError(result, resultCode,0,0, ""); + } + } + + + switch (rascommand) + { + case 1: + // commOpenDB + // call openDB (userName initialized with "") + openDB(callingClientId, baseName, ""); + return encodeAckn(result); + break; + + case 2: + // commCloseDB + // call closeDB + flagInformRasMgr = true; // Used in doIt_http... + closeDB(callingClientId); + return encodeAckn(result); + break; + + case 3: + // commBTreadOnly + // call beginTA + beginTA(callingClientId, 1); + return encodeAckn(result); + break; + + case 4: + // commBTreadWrite + // call beginTA + beginTA(callingClientId, 0); + return encodeAckn(result); + break; + + case 5: + // commCT + // call commitTA + commitTA(callingClientId); + return encodeAckn(result); + break; + + case 6: + // commAT + // call abortTA + abortTA(callingClientId); + return encodeAckn(result); + break; + + case 7: + // commIsOpenTA + return encodeAckn(result, (transactionActive==callingClientId) ? 99 : 98); + break; + + case 8: + // commQueryExec + // call executeQuery (result contains error information) + resultError.token=NULL; + resultError.typeName=NULL; + resultError.typeStructure=NULL; + execResult = executeQuery(callingClientId, query, resultError); + // now we distinguish between different result types + if(execResult == 0) { + // MDD results + int totalLength = 6; // total length of result in bytes + r_Long numMDD = 0; // number of MDD objects in the result + ClientTblElt* context; + vector resultTiles; // contains all TransTiles representing the resulting MDDs + resultTiles.reserve(20); + vector resultTypes; + resultTypes.reserve(20); + vector resultDomains; + resultDomains.reserve(20); + vector resultOIDs; + resultOIDs.reserve(20); + // Here should be something like the following. Unfortunately, + // context->transferColl seems to be 0 here. I don't know why. + // Thats why a global variable was introduced in + // servercomm2.cc. + // CollectionType* collectionType = + // (CollectionType*) context->transferColl->getMDDCollType(); + // char* collTypeStructure = collectionType->getTypeStructure(); + + while(getNextMDD(callingClientId, resultDom, typeName, typeStructure, oid, + currentFormat) == 0) { + numMDD++; + // create TransTile for whole data from the tiles stored in context->transTiles + context = getClientContext( callingClientId ); + // that should be enough, just transfer the whole thing ;-) + resultTile = new Tile( context->transTiles ); + + // server endianess + r_Endian::r_Endianness serverEndian = r_Endian::get_endianness(); + + // This currently represents the one and only client active at one time. + // stupid!!! if((context->clientId == 1) && (strcmp(context->clientIdText, ServerComm::HTTPCLIENT) == 0) && (serverEndian != r_Endian::r_Endian_Big)) + if(serverEndian != r_Endian::r_Endian_Big) + { + // RMInit::logOut << "Changing endianness..." << flush; + // calling client is a http-client(java -> always BigEndian) and server has LittleEndian + char *tpstruct; + r_Base_Type *useType; + tpstruct = resultTile->getType()->getTypeStructure(); + useType = (r_Base_Type*)(r_Type::get_any_type(tpstruct)); + + char* tempT = (char*)mymalloc(sizeof(char) * resultTile->getSize()); + + // change the endianness of the entire tile for identical domains for src and dest + r_Endian::swap_array(useType, resultDom, resultDom, resultTile->getContents(), tempT); + resultTile->setContents(tempT); + + delete useType; + free(tpstruct); + + // RMInit::logOut << MSG_OK << endl; + } + + resultTiles.push_back(resultTile); + resultTypes.push_back(typeStructure); + resultDomains.push_back(resultDom.get_string_representation()); + resultOIDs.push_back(oid); + //oid.print_status(RMInit::logOut); + + // this is normally done in getNextTile(). But this is not called, so we do it here. + (*(context->transferDataIter))++; + if( *(context->transferDataIter) != context->transferData->end() ) { + // clean context->transtile if necessary + if( context->transTiles ){ + delete context->transTiles; + context->transTiles = 0; + } + // clean context->tileIter if necessary + if( context->tileIter ){ + delete context->tileIter; + context->tileIter = 0; + } + } + + // clean typeName if necessary + if( typeName ){ + free(typeName); + typeName = 0; + } + } + + // clean typeName if necessary + if( typeName ){ + free(typeName); + typeName = 0; + } + + // prepare for transfer + // calculate total length of the result array + for(i = 0; i < numMDD; i++) { + totalLength += strlen(resultTypes[i]) + 1; + totalLength += strlen(resultDomains[i]) + 1; + // OID might be NULL + if(resultOIDs[i].get_string_representation() != NULL) + totalLength += strlen(resultOIDs[i].get_string_representation()) + 1; + else + totalLength++; + totalLength += resultTiles[i]->getSize(); + totalLength += 4; + } + // for the type of the collection + totalLength += strlen(globalHTTPSetTypeStructure) + 1; + + // allocate the result + result = (char*)mymalloc(totalLength); + currentPos = result; + // fill it with data + *currentPos = 1; // result is MDD collection + currentPos++; + *currentPos = systemEndianess; + currentPos++; + // type of the collection + strcpy(currentPos, globalHTTPSetTypeStructure); + currentPos += strlen(globalHTTPSetTypeStructure) + 1; + encodeLong(currentPos, &numMDD); + currentPos += sizeof(r_Long); + // encode MDDs + for(i = 0; i < numMDD; i++) { + r_Long dummy = resultTiles[i]->getSize(); + strcpy(currentPos, resultTypes[i]); + currentPos += strlen(resultTypes[i]) + 1; + strcpy(currentPos, resultDomains[i]); + currentPos += strlen(resultDomains[i]) + 1; + // OID might be NULL + if(resultOIDs[i].get_string_representation() != NULL) + { + strcpy(currentPos, resultOIDs[i].get_string_representation()); + currentPos += strlen(resultOIDs[i].get_string_representation()) + 1; + } + else + { + *currentPos = '\0'; + currentPos++; + } + encodeLong(currentPos, &dummy); + currentPos += sizeof(r_Long); + memcpy(currentPos, resultTiles[i]->getContents(), dummy); + currentPos += dummy; + } + + // delete all the temporary storage + for(i = 0; i < numMDD; i++) { + delete resultTiles[i]; + free(resultTypes[i]); + free(resultDomains[i]); + } + + returnValue = totalLength; + + } else if(execResult == 1) { + // the result is a collection of scalars. + int totalLength = 6; // total length of result in bytes + r_Long numElem = 0; // number of MDD objects in the result + ClientTblElt* context; + vector resultElems; // contains all TransTiles representing the resulting MDDs + resultElems.reserve(20); + vector resultLengths; + resultLengths.reserve(20); + + // we have to get the type of the collection + totalLength += strlen(resultError.typeStructure) + 1; + + // then we have to get all elements in the collection + unsigned short dummyRes; + char* buffer; + unsigned int bufferSize; + // This will probably not work for empty collections. Only if getNextElement + // returns 2 in this case. I really don't get it. + unsigned short moreElems = 0; + + while( moreElems == 0 ) { + moreElems = getNextElement(callingClientId, buffer, bufferSize); + + //RMInit::logOut << "More elems is " << moreElems << endl; + numElem++; + resultElems.push_back(buffer); + resultLengths.push_back(bufferSize); + // length of data + totalLength += bufferSize; + // this will be length of type + totalLength += 1; + // size of each element + totalLength += sizeof(r_Long); + } + + // allocate the result + result = (char*)mymalloc(totalLength); + currentPos = result; + // fill it with data + *currentPos = 2; // result is collection of other types + currentPos++; + *currentPos = systemEndianess; + currentPos++; + // type of the collection + strcpy(currentPos, resultError.typeStructure); + currentPos += strlen(resultError.typeStructure) + 1; + // number of elements + encodeLong(currentPos, &numElem); + currentPos += sizeof(r_Long); + + // and finally copy them together + for(i = 0; i < numElem; i++) { + // This should be the type of the element + *currentPos = '\0'; + currentPos++; + // length in bytes of the element + r_ULong convDummy = resultLengths[i]; + encodeULong(currentPos, &convDummy); + currentPos += sizeof(r_ULong); + // actual data + memcpy(currentPos, resultElems[i], resultLengths[i]); + currentPos += resultLengths[i]; + } + + // delete all the temporary storage + for(i = 0; i < numElem; i++) { + free(resultElems[i]); + } + + returnValue = totalLength; + + } else if(execResult == 2) { + int totalLength = 7; // total length of result in bytes + // the result collection is empty. It is returned as an empty MDD collection. + // allocate the result + result = (char*)mymalloc(totalLength); + currentPos = result; + // fill it with data + *currentPos = 1; // result is MDD collection + currentPos++; + *currentPos = systemEndianess; + currentPos++; + // here the type of the collection should be added, currently empty string! + *currentPos = '\0'; + currentPos++; + // now set the number of results to zero + r_Long dummy=0; + encodeLong(currentPos, &dummy); + + returnValue = totalLength; + + } else if(execResult == 4 || execResult == 5) { + // parse error or execution error + returnValue = encodeError(result, resultError.errorNo,resultError.lineNo, resultError.columnNo, resultError.token); + + } else { + // unknow error + returnValue = -10; + } + + cleanExecuteQueryRes(resultError); + + // insert error handling here + return returnValue; + break; + + case 9: + // commUpdateExec + // empty transfer structures (only to be sure, this case probably cannot occur :-) + returnValue = 0; + valid = false; + while(!transferredMDDs.empty()) + { + RMInit::logOut << "Freeing old transfer structures..." << std::flush; + free(transferredMDDs.back()->binData); + delete(transferredMDDs.back()); + transferredMDDs.pop_back(); + RMInit::logOut << MSG_OK << endl; + } + + // do we have an insert statement? + if(binDataSize > 0) + { + // yes, it is an insert statement => analyze and prepare the MDDs + // create an empty MDD-Set in the client context + initExecuteUpdate(callingClientId); + + // Analyze the binData array (the uploaded mdds) + getMDDs(binDataSize,binData,Endianess,transferredMDDs); + + // Store all MDDs + //RMInit::logOut << "vector has " << transferredMDDs.size() << " entries." << endl; + returnValue = 0; + while(!transferredMDDs.empty() && (returnValue == 0)) + { + //RMInit::logOut << "Element:" << transferredMDDs.back()->toString() << endl; + // insert MDD into MDD-Set of client context + r_Minterval tempStorage(transferredMDDs.back()->domain); + + roid = new r_OId(transferredMDDs.back()->oidString); + // OID valid + valid = roid->is_valid(); + if(valid) + { + // parse the query string to get the collection name + char* collection; + char* startPtr; + char* endPtr; + startPtr = endPtr = query; + collection = new char[ endPtr - startPtr + 1 ]; + // one for insert, one for into and one for the collection name + for(int i = 0; i<3; i++) + { + // delete spaces, tabs ... + while((*endPtr == ' ' || *endPtr == '\t' || *endPtr == '\r' || *endPtr == '\n') && *endPtr != '\0') endPtr++; + startPtr = endPtr; + // parse next word + while(*endPtr != ' ' && *endPtr != '\t' && *endPtr != '\r' && *endPtr != '\n' && *endPtr != '\0') endPtr++; + if(endPtr - startPtr >= 1) + { + collection = new char[endPtr - startPtr + 1]; + strncpy(collection, startPtr, endPtr - startPtr); + collection[endPtr - startPtr] = '\0'; + } + } + //RMInit::logOut << "collection: " << collection << endl; + + //RMInit::logOut << "OID is valid: " << endl; + //roid->print_status(RMInit::logOut); + execResult = startInsertPersMDD( callingClientId, collection, tempStorage, + transferredMDDs.back()->typeLength, + transferredMDDs.back()->objectTypeName, *roid); + } + // OID not valid + else + { + //RMInit::logOut << "OID is NOT valid" << endl; + + execResult = startInsertTransMDD( callingClientId, tempStorage, + transferredMDDs.back()->typeLength, + transferredMDDs.back()->objectTypeName ); + } + + //clean roid, we don't needed anymore + if(roid) { + delete roid; + roid = NULL; + } + + if(execResult != 0) + { + // no or wrong mdd type - return error message + unsigned long errNo; + if(strlen(transferredMDDs.back()->objectTypeName) < 1) + errNo=966; + else + { + switch(execResult) + { + case 2: + errNo = 965; + break; + case 3: + errNo = 959; + break; + case 4: + errNo = 952; + break; + case 5: + errNo = 957; + break; + default: + errNo = 350; + } + RMInit::logOut << "Error: while inserting MDD" << endl; + } + + returnValue = encodeError(result, errNo, 0, 0, transferredMDDs.back()->objectTypeName); + + //clean up + while(!transferredMDDs.empty()) + { + RMInit::logOut << "Freeing old transfer structures..." << std::flush; + free(transferredMDDs.back()->binData); + delete(transferredMDDs.back()); + transferredMDDs.pop_back(); + RMInit::logOut << MSG_OK << endl; + } + + + + return returnValue; + } + else + { + // create RPCMarray data structure - formats are currently hardcoded (r_Array) + RPCMarray *rpcMarray = (RPCMarray*)mymalloc( sizeof(RPCMarray) ); + rpcMarray->domain = strdup(transferredMDDs.back()->domain); + rpcMarray->cellTypeLength = transferredMDDs.back()->typeLength; + rpcMarray->currentFormat = r_Array; + rpcMarray->storageFormat = r_Array; + rpcMarray->data.confarray_len = transferredMDDs.back()->dataSize; + rpcMarray->data.confarray_val = transferredMDDs.back()->binData; + + /* + RMInit::logOut << "Creating RPCMarray with domain " << rpcMarray->domain << ", size " << + rpcMarray->data.confarray_len << ", typeLength " << rpcMarray->cellTypeLength << " ..." + << flush; + */ + + // split tile if a tileSize (an MInterval) has been specified + r_Minterval* splitInterval = NULL; + if(transferredMDDs.back()->tileSize != NULL) + { + splitInterval = new r_Minterval(transferredMDDs.back()->tileSize); + RMInit::logOut << "Splitinterval is " << splitInterval << endl; + } + // now insert the tile(s) + if(valid) + { + insertTileSplitted( callingClientId, 1, rpcMarray, splitInterval ); + } + else + { + insertTileSplitted( callingClientId, 0, rpcMarray, splitInterval ); + } + + // free the stuff + free(rpcMarray->domain); + free(rpcMarray); + delete(transferredMDDs.back()); + transferredMDDs.pop_back(); + delete(splitInterval); + } + } + + // end insertion into client structure + if(valid) + endInsertMDD(callingClientId, 1); + else + endInsertMDD( callingClientId, 0 ); + } + + if(returnValue == 0) + { + //RMInit::logOut << "Executing query: " << query << flush; + // until now now error has occurred => execute the query + ExecuteUpdateRes returnStructure; + returnStructure.token=NULL; + if(!valid) + execResult = executeUpdate( callingClientId, (const char*)query , returnStructure ); + // query was executed successfully + if (execResult == 0) { + // allocate the result + result = (char*)mymalloc(1); + currentPos = result; + *currentPos = 99; // result is an acknowledgement + returnValue = 1; + } + // parsing or execution error + else if( execResult == 2 || execResult == 3 ) { + returnValue = encodeError(result, returnStructure.errorNo,returnStructure.lineNo, returnStructure.columnNo, returnStructure.token); + } + else { + // unknow error + returnValue = -10; + } + + if(returnStructure.token) { + free(returnStructure.token); + returnStructure.token=NULL; + } + } + return returnValue; + break; + case 10: + // getnewoid + accessControl.wantToWrite(); + objType = 1; + roid = new r_OId(); + ServerComm::getNewOId(lastCallingClientId, objType, *roid); + //roid->print_status(RMInit::logOut); + + // prepare returnValue + totalLength = 9; // total length of result in bytes + totalLength += strlen(roid->get_system_name()) + 1; + totalLength += strlen(roid->get_base_name()) + 1; + // allocate the result + result = (char*)mymalloc(totalLength); + currentPos = result; + // fill it with data + *currentPos = 4; // result is a OID + currentPos++; + + // system + strcpy(currentPos, roid->get_system_name()); + currentPos += strlen(roid->get_system_name()) + 1; + + // base + strcpy(currentPos, roid->get_base_name()); + currentPos += strlen(roid->get_base_name()) + 1; + + + context = getClientContext( callingClientId ); + // server endianess + serverEndian = r_Endian::get_endianness(); + // This currently represents the one and only client active at one time. + if((context->clientId == 1) && + (strcmp(context->clientIdText, ServerComm::HTTPCLIENT) == 0) && + (serverEndian != r_Endian::r_Endian_Big)) + { + // calling client is a http-client(java -> always BigEndian) and server has LittleEndian + double tmp = roid->get_local_oid(); + *(double*)currentPos = r_Endian::swap(tmp); + } + else + *(double*)currentPos = roid->get_local_oid(); + //currentPos += 8; + returnValue = totalLength; + + //clean up we don't need roid anymore + if(roid) { + delete roid; + roid=NULL; + } + + return returnValue; + break; + default: + return -10; + break; + } + } + catch(r_Eno_permission &e) // this shouldn't be here, but ... + { + return encodeError(result, e.get_errorno(),0,0, ""); + } + catch(...) + { + // really? flagInformRasMgr = true; // client should ask for a new server + + return encodeError(result, UNEXPECTED_INTERNAL_ERROR,0,0, ""); + } + +} + +//********************************************************************** + +#include "raslib/error.hh" +#include "raslib/rminit.hh" + +#include "httpserver/defs.h" +#include "httpserver/protos.h" +#include "httpserver/types.h" +#include "httpserver/server.h" + + +extern struct ServerBase Server; + +extern struct Logging *LogBase; + +void Select(int socket); // wrap around to put timeout in it + +int HttpServer::doIt_httpserver( int argc, char *argv[] ) +{ + pid_t ChildPId; /* -> Server.ChildInfo */ + + RMInit::logOut << "Initialising parameters for HTTP server... " << endl; + RMInit::logOut.flush(); + Initialize( argc, argv, &Server ); + + RMInit::logOut << "Initialising server socket for HTTP server... " << endl; + RMInit::logOut.flush(); + listen( Server.SockFD, 5 ); + RMInit::logOut << "Waiting for client calls... " << endl; + RMInit::logOut.flush(); + + informRasMGR(SERVER_AVAILABLE); + + for(;;) + { + Select( Server.SockFD); + Accept( Server.SockFD, &Server.Client ); + strcpy( Server.Client.Host.IPAddrString, inet_ntoa( Server.Client.Socket.sin_addr ) ); + + if( Server.Client.Host.IPAddrString == NULL ) + strcpy( Server.Client.Host.IPAddrString, "0.0.0.0" ); + + Server.Client.Host.IPAddress = inet_addr( Server.Client.Host.IPAddrString ); + Server.Client.Comm.ConnStatus = CONN_UNDEFINED; + InitHTTPMsg( &Server.Client.Response ); + InitReqInfo( &Server.Client.Request ); + LogMsg( LG_SERVER, INFO, "INFO: ====== Connection from %s accepted...", + Server.Client.Host.IPAddrString ); + + HandleRequest( &Server.Client ); + LogMsg( LG_SERVER, INFO, "INFO: ====== EOT. Disconnecting." ); + + close( Server.Client.SockFD ); + + if(flagInformRasMgr == true) + { informRasMGR(SERVER_AVAILABLE); + flagInformRasMgr = false; + } + +#ifdef PURIFY + purify_printf( "Request finished." ); + purify_new_leaks(); +#endif + + } + // otherwise Exit(OK) should have been called + return -1; +} + +extern int noTimeOut; +void Select(int socket) + { + static time_t lastEntry =0; // last time we entered here + + fd_set read_fd_set, http_fd_set; + + FD_ZERO(&http_fd_set); + FD_SET(socket,&http_fd_set); + + const int checkTimeOutInterval=30; + + struct timeval timeout; + timeout.tv_sec=checkTimeOutInterval; + timeout.tv_usec=0 ; + + lastEntry = time(NULL); + while(1) + { read_fd_set=http_fd_set; + //cout<<"HTTP Server is waiting..."< getClientContext( lastCallingClientId ); + clnt->transaction.abort(); + clnt->database.close(); + sc->informRasMGR(SERVER_AVAILABLE); + // cout<<"Http server, client timeout, available again..."< getClientContext( lastCallingClientId ); + clnt->transaction.abort(); + clnt->database.close(); + RMInit::logOut << MSG_OK << endl; + } + } + diff --git a/servercomm/httpserver.hh b/servercomm/httpserver.hh new file mode 100644 index 0000000..47d64ba --- /dev/null +++ b/servercomm/httpserver.hh @@ -0,0 +1,173 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see . +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see +* or contact Peter Baumann via . +/ +/** + * INCLUDE: httpserver.hh + * + * MODULE: servercomm + * CLASS: HttpServer + * + * COMMENTS: + * No Comments +*/ + +#ifndef _HTTPSERVER_ +#define _HTTPSERVER_ + +#include "servercomm/servercomm.hh" + +//@ManMemo: Module: {\bf servercomm} + +/*@Doc: + The class HttpServer describes the one and only server communication object + that can exist in a RasDaMan server. It manages listening for clients and + maps incoming calls to the respective procedures (which reside in the + file manager.cc). + + This class implements the functions useful for HTTP communication + and is based on a copy of servercomm.hh (Version 1.48). +*/ + + +class HttpServer : public ServerComm +{ +public: + + /// the class represents an MDD in HTTP transfer encoding + class MDDEncoding + { + public: + + int objectType; + char *objectTypeName; + char *typeStructure; + int typeLength; + char *domain; + char *tileSize; + char *oidString; + int dataSize; + char *binData; + char *stringRepresentation; + + /// default constructor + MDDEncoding(); + + /// destructor + ~MDDEncoding(); + + // set objectType + void setObjectType(int type); + + // set objectTypeName + void setObjectTypeName(char *name); + + // set typeStructure + void setTypeStructure(char* type); + + // set typeLength + void setTypeLength(int len); + + // set domain + void setDomain(char *dom); + + // set oid + void setOID(char *o); + + // set tile size + void setTileSize(char *size); + + // set dataSize + void setDataSize(int size); + + // set binData + void setBinData(char *data); + + // print Values + const char* toString(); + }; + + + /// stores a pointer to the actual servercomm object, only one can exist at a time + static HttpServer* actual_httpserver; + + // the class uses the class ClientTblElt from ServerComm because it is used + // in some other files of the server, e.g., qlparser/qtmddaccess.cc or + // qlparser/qtcommand.cc or qlparser/qtinsert.cc all include servercomm.hh + + /// default constructor + HttpServer(); + + // the acual constructor + HttpServer( unsigned long timeOut, unsigned long managementInterval , unsigned long listenPort, char* rasmgrHost, unsigned int rasmgrPort,char* serverName); + + /// destructor + virtual ~HttpServer(); + + /// forces the server to listen for client calls + virtual void startRpcServer() throw( r_Error ); + + /// stops the server + virtual void stopRpcServer(); + + /// print server status to {\tt s} + virtual void printServerStatus( ostream& s=cout ); + + /// Executes a retrieval query and prepare the result for HTTP transer. + virtual long processRequest( unsigned long callingClientId, char* baseName, + int rascommand, char* query, int binDataSize, char *binData, + int Endianess, char* &result, char *capability ); + /** + Executes a query and prepares the complete result for transfer via + HTTP. The length of the result is returned. The first parameter is + the unique client id for which the query should be executed. The + second parameter The third parameter is the query itself represented + as a string. {\tt result} will contain a pointer to the result as + needed for HTTP transfer. This pointer has to be freed by the caller + using free. + + Return values on Error: + \begin{tabular}{lll} + -1 && parse errror\\ + -2 && execution error\\ + -3 && unknown error\\ + \end{tabular} + + Question: How to transfer the result? + */ + + /// returns a pointer to the context of the calling client, 0 it there is no context + virtual ClientTblElt* getClientContext( unsigned long ClientId ); + /** + Returns a pointer to the context of the calling client. Currently always + the same global context is returned. + */ + private: + int doIt_httpserver( int argc, char *argv[] ); + + + bool flagInformRasMgr; // used to trigger informRasMGR(SERVERAVAILABLE) + +}; + +#include "httpserver.icc" + +#endif diff --git a/servercomm/httpserver.icc b/servercomm/httpserver.icc new file mode 100644 index 0000000..d1b00bd --- /dev/null +++ b/servercomm/httpserver.icc @@ -0,0 +1,31 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see . +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see +* or contact Peter Baumann via . +/ +/** + * INLINE SOURCE: httpserver.icc + * + * MODULE: servercomm + * CLASS: ServerComm + * + * COMMENTS: + * No Comments +*/ diff --git a/servercomm/manager.cc b/servercomm/manager.cc new file mode 100644 index 0000000..8177919 --- /dev/null +++ b/servercomm/manager.cc @@ -0,0 +1,1885 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see . +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see +* or contact Peter Baumann via . +/ +/** + * SOURCE: manager.cc + * + * MODULE: servercomm + * CLASS: - + * + * COMMENTS: + * No Comments +*/ + +#include "mymalloc/mymalloc.h" +static const char rcsid[] = "@(#)manager, ServerComm: $Id: manager.cc,v 1.92 2005/09/03 21:05:14 rasdev Exp $"; + +#include +using namespace std; + +#include // for strcat() +#include // for time() +#include + +// server option for switching off garbageCollection (Formerly there were problems +// because of illegal functions called in signal handler, see man 5 attributes; +// these are no longer relevant since the introduction of the CallBackManager) +extern int noTimeOut; + + +// Put it in front of any typedef bool ... because o2 is using bool as a variable. +// #include "o2template_CC.hxx" + +#include // for signal() +#include // for alarm() + +#ifndef SOLARIS + #define PORTMAP // define to use function declarations for old interfaces + #include +#else // HPUX + #include +#endif + +#ifndef _RPCIF_ + #define _RPCIF_ + #include "clientcomm/rpcif.h" +#endif + +#include "raslib/rmdebug.hh" +#include "raslib/minterval.hh" +#include "raslib/oid.hh" +#include "raslib/endian.hh" + +#include "servercomm/servercomm.hh" + + +extern "C" { +void garbageCollection( int ); +void garbageCollectionDummy ( int ); +} + + +// +// Section for global variables pointing to RPC return data. +// +static GetExtendedErrorInfo rpcExtendedErrorInfo = { 0, 0}; +static ServerVersionRes rpcServerVersionRes = { 0, 0}; +static u_short rpcDummy = 0; +static OpenDBRes rpcOpenDBRes = { 0, 0}; + +static ServerStatRes rpcServerStatRes = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, { 0, 0 } }; +static ExecuteQueryRes rpcExecuteQueryRes = { 0, 0, 0, 0, 0, 0, 0 }; +static GetMDDRes rpcGetMDDRes = { 0, 0, 0, 0, 0 }; +static GetElementRes rpcGetElementRes = { 0, { 0, 0 } }; +static GetTileRes rpcGetTileRes = { 0, 0 }; +static ExecuteUpdateRes rpcExecuteUpdateRes = { 0, 0, 0, 0, 0 }; +static GetCollRes rpcGetCollRes = { 0, 0, 0, 0, 0 }; +static GetCollOIdsRes rpcGetCollOidsRes = { 0, 0, 0, 0, 0, { 0, 0 } }; +static OIdRes rpcOidRes = { 0, 0 }; +static GetTypeStructureRes rpcGetTypeStructureRes = { 0, 0 }; +static ObjectTypeRes procResult = { 0, 0 }; + +extern char *secureResultBufferForRPC; + +void freeDynamicRPCData() +{ + // rpcserverstat + if( rpcServerStatRes.clientTable.clientTable_len ) + { + for( int i=0; idomain ) free( rpcGetTileRes.marray->domain ); + // if( rpcGetTileRes.marray->data ) free( rpcGetTileRes.marray->data ); + free( rpcGetTileRes.marray ); + rpcGetTileRes.marray = 0; + } + + // rpcexecuteupdate + if( rpcExecuteUpdateRes.token ) free( rpcExecuteUpdateRes.token ); + rpcExecuteUpdateRes.token = 0; + rpcExecuteUpdateRes.status = 0; + rpcExecuteUpdateRes.errorNo = 0; + rpcExecuteUpdateRes.lineNo = 0; + rpcExecuteUpdateRes.columnNo = 0; + + // rpcgetcollbyname, rpcgetcollbyoid + if( rpcGetCollRes.typeName ) free( rpcGetCollRes.typeName ); + if( rpcGetCollRes.typeStructure ) free( rpcGetCollRes.typeStructure ); + if( rpcGetCollRes.oid ) free( rpcGetCollRes.oid ); + if( rpcGetCollRes.collName ) free( rpcGetCollRes.collName ); + rpcGetCollRes.oid = 0; + rpcGetCollRes.typeStructure = 0; + rpcGetCollRes.typeName = 0; + rpcGetCollRes.collName = 0; + + // rpcgetcolloidsbyname, rpcgetcolloidsbyoid + if( rpcGetCollOidsRes.typeName ) free( rpcGetCollOidsRes.typeName ); + if( rpcGetCollOidsRes.typeStructure ) free( rpcGetCollOidsRes.typeStructure ); + if( rpcGetCollOidsRes.oid ) free( rpcGetCollOidsRes.oid ); + if( rpcGetCollOidsRes.collName ) free( rpcGetCollOidsRes.collName ); + rpcGetCollOidsRes.oid = 0; + rpcGetCollOidsRes.typeStructure = 0; + rpcGetCollOidsRes.typeName = 0; + rpcGetCollOidsRes.collName = 0; + + if( rpcGetCollOidsRes.oidTable.oidTable_len ) + { + for( int i=0; icallback_mgr.executePending(); + signal( SIGALRM, garbageCollection ); +} + + +/************************************************************* + * + * + * SOURCE: manager.cc + * + * MODULE: servercomm + * + * + * COMMENTS: + * + ************************************************************/ + +#ifdef LINUX +#define RPCFUNCTIONDEF(name, param) name##_svc( param, struct svc_req* ) +#define RPCFUNCTIONDEFP(name, param) *name##_svc( param, struct svc_req* ) +#else +#define RPCFUNCTIONDEF(name, param) name##_svc( param, struct svc_req* ) +#define RPCFUNCTIONDEFP(name, param) *name##_svc( param, struct svc_req* ) +#endif + +#ifdef LINUX +extern "C" +#endif + +//@ManMemo: Module: {\bf servercomm} + +/** + The global remote procedure \Ref{rpcgetserverversion_1} is called by a client to determine + version numbers of the server. +*/ + +ServerVersionRes* +RPCFUNCTIONDEF( rpcgetserverversion_1, int* ) +{ + freeDynamicRPCData(); + + rpcServerVersionRes.serverVersionNo = RMANVERSION / 1000.0; + rpcServerVersionRes.rpcInterfaceVersionNo = RPCVERSION / 1000.0; + + return &rpcServerVersionRes; +} + + + +#ifdef LINUX +extern "C" +#endif + +//@ManMemo: Module: {\bf servercomm} + +/** + The global remote procedure \Ref{rpcshutdown_1} is called by a client to shut + down the server. +*/ + +unsigned short* +RPCFUNCTIONDEF( rpcshutdown_1, int* ) +{ + rpcDummy = 0; + freeDynamicRPCData(); + // we don't allow the client to shut down the server. This is done thru rasmgr + // may be is would be better to redesign this server interface and drop several + // functions + return &rpcDummy; +} + + + +#ifdef LINUX +extern "C" +#endif + +//@ManMemo: Module: {\bf servercomm} + +/** + The global remote procedure \Ref{rpcServerStat_1} is called by a clientcomm + object on the client system to display server statistics. +*/ + +ServerStatRes* +RPCFUNCTIONDEF( rpcserverstat_1, int* ) +{ + secureResultBufferForRPC = (char*)&rpcServerStatRes; + + freeDynamicRPCData(); + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // log server status +#ifdef RMANDEBUG + sc->printServerStatus( RMInit::dbgOut ); +#endif + + // get server status + sc->getServerStatus( rpcServerStatRes ); + + return &rpcServerStatRes; +} + + + +#ifdef LINUX +extern "C" +#endif + +//@ManMemo: Module: {\bf servercomm} + +/** + The global remote procedure \Ref{rpckilltableentry_1} is called by a clientcomm + object on the client system to kill a specific client table entry on the server. +*/ + +unsigned short* +RPCFUNCTIONDEF( rpckilltableentry_1, unsigned long* killId ) +{ + secureResultBufferForRPC = (char*)&rpcDummy; + rpcDummy = 0; + unsigned long kId = *killId; + + freeDynamicRPCData(); + + accessControl.wantToWrite(); + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + RMInit::logOut << " Kill specific table entry request received for client ID " << kId << "." << endl; + + if( sc && !sc->clientTbl.empty() ) + { + + list::iterator iter; + iter = sc->clientTbl.begin(); + + while ( *iter != NULL ) + { + if( (*iter)->clientId == kId ) + { + RMInit::logOut << "ID " << (*iter)->clientId << " found, deleting..." << endl; + + sc->deleteClientTblEntry( (*iter)->clientId ); + + // if this dead client has locked the transaction semaphor, unlock it + if( sc->transactionActive == kId ) + sc->transactionActive = 0; + + // client IDs are unique, so this was the only one... + break; + } + else + iter++; + } + // The following is a necessary dummy command for ONC RPC for not + // misinterpreting the above break command as a return + iter = sc->clientTbl.begin(); + } +return &rpcDummy; +} + + + +#ifdef LINUX +extern "C" +#endif + +//@ManMemo: Module: {\bf servercomm} + +/** +*/ + +unsigned short* +RPCFUNCTIONDEF( rpcalive_1, unsigned long* callingClientId ) +{ + secureResultBufferForRPC = (char*)&rpcDummy; + rpcDummy = 0; + unsigned long cci = *callingClientId; + + freeDynamicRPCData(); + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // Call the corresponding method. + rpcDummy = sc->aliveSignal( cci ); + + // Return the result + return &rpcDummy; +} + + + +#ifdef LINUX +extern "C" +#endif + +//@ManMemo: Module: {\bf servercomm} + +/*@Doc: + The global remote procedure {\tt rpcopendb_1()} is called by a clientcomm + object on the client system to open a database on a RasDaMan server. +*/ + +OpenDBRes* +RPCFUNCTIONDEF( rpcopendb_1, OpenDBParams* params ) +{ + secureResultBufferForRPC = (char*)&rpcOpenDBRes; + + unsigned long* clientId = new unsigned long; + + freeDynamicRPCData(); + + char client[256]; + client[0] = '\0'; + strcat( client, "unknown" ); + + const char* dbName = params->dbName; + const char* userName = params->userName; + + RMInit::logOut << "Client called ... "; + + // + // Create a new entry in the client table (should be moved to ServerComm): + // + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // Create a new client table element and initialize it. + ServerComm::ClientTblElt* contextStore; + contextStore = new ServerComm::ClientTblElt( client, ++(sc->clientCount) ); + + // give the client id back to to client via the pointer variable + *clientId = sc->clientCount; + // Put the context information in the static control list + sc->clientTbl.push_back( contextStore ); + + // RMInit::logOut << "assigned id " << *clientId << endl; + + // check acces permission + + rpcOpenDBRes.status=accessControl.crunchCapability(params->capability); + // + // Open the database: (only if acces control is OK) + // + if(rpcOpenDBRes.status==0) + rpcOpenDBRes.status = sc->openDB( *clientId, dbName, userName ); + + rpcOpenDBRes.clientID = *clientId; + delete clientId; + + // If database is not successfully opened, the client table entry is deleted again. + if( rpcOpenDBRes.status != 0 ) + sc->deleteClientTblEntry( rpcOpenDBRes.clientID ); + + return &rpcOpenDBRes; +} + + + +#ifdef LINUX +extern "C" +#endif + +//@ManMemo: Module: {\bf servercomm} + +/*@Doc: + The global remote procedure {\tt rpcclosedb_1()} is called by a clientcomm + object on the client system to close a database on a RasDaMan server. +*/ + +unsigned short* +RPCFUNCTIONDEF( rpcclosedb_1, unsigned long* callingClientId ) +{ + secureResultBufferForRPC = (char*)&rpcDummy; + + unsigned long cci; + cci = *callingClientId; + + freeDynamicRPCData(); + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // Call the corresponding method. + rpcDummy = sc->closeDB( cci ); + + if( rpcDummy == 0 ) + sc->deleteClientTblEntry( cci ); + + return &rpcDummy; +} + + + +#ifdef LINUX +extern "C" +#endif + +//@ManMemo: Module: {\bf servercomm} + +/*@Doc: + The global remote procedure {\tt rpccreatedb_1()} is called by a clientcomm + object on the client system to create a database on a RasDaMan server. +*/ +unsigned short* +RPCFUNCTIONDEF( rpccreatedb_1, char** name ) +{ + secureResultBufferForRPC = (char*)&rpcDummy; + + char* dbname = *name; + + freeDynamicRPCData(); + + accessControl.wantToWrite(); + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // Call the corresponding method. (createDB doesn't actually return something + // other than 0, so this can be used in later extensions) + rpcDummy = sc->createDB( dbname ); + + // Return the result + return &rpcDummy; +} + + +#ifdef LINUX +extern "C" +#endif + +//@ManMemo: Module: {\bf servercomm} + +/*@Doc: + The global remote procedure {\tt rpcdestroydb_1()} is called by a clientcomm + object on the client system to destroy a database on a RasDaMan server. +*/ +unsigned short* +RPCFUNCTIONDEF( rpcdestroydb_1, char** name ) +{ + secureResultBufferForRPC = (char*)&rpcDummy; + + char* dbname = *name; + + freeDynamicRPCData(); + + accessControl.wantToWrite(); + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // Call the corresponding method. (createDB doesn't actually return something + // other than 0, so this can be used in later extensions) + rpcDummy = sc->destroyDB( dbname ); + + // Return the result + return &rpcDummy; +} + + + +#ifdef LINUX +extern "C" +#endif + +//@ManMemo: Module: {\bf servercomm} + +/*@Doc: + The global remote procedure {\tt rpcBeginTA()} is called by a clientcomm + object on the client system to begin a new transaction in a session with + a RasDaMan server. +*/ + +unsigned short* +RPCFUNCTIONDEF( rpcbeginta_1, BeginTAParams* params ) +{ + secureResultBufferForRPC = (char*)&rpcDummy; + + freeDynamicRPCData(); + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // check acces permission + rpcDummy = accessControl.crunchCapability(params->capability); + + // Call the corresponding method. (only if acces control is OK) + if(rpcDummy==0) + rpcDummy = sc->beginTA( params->clientID, params->readOnly ); + + // Return the result + return &rpcDummy; +} + + + +#ifdef LINUX +extern "C" +#endif + +//@ManMemo: Module: {\bf servercomm} + +/*@Doc: + The global remote procedure {\tt rpcCommitTA()} is called by a clientcomm + object on the client system to commit the current transaction in a session with + a RasDaMan server. +*/ + +unsigned short* +RPCFUNCTIONDEF( rpccommitta_1, unsigned long* callingClientId ) +{ + secureResultBufferForRPC = (char*)&rpcDummy; + + unsigned long cci; + cci = *callingClientId; + + freeDynamicRPCData(); + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // Call the corresponding method. + rpcDummy = sc->commitTA( cci ); + + // Return the result + return &rpcDummy; +} + + + +#ifdef LINUX +extern "C" +#endif + +//@ManMemo: Module: {\bf servercomm} + +/*@Doc: + The global remote procedure {\tt rpcAbortTA()} is called by a clientcomm + object on the client system to abort the current transaction in a session with + a RasDaMan server. +*/ + +unsigned short* +RPCFUNCTIONDEF( rpcabortta_1, unsigned long* callingClientId ) +{ + secureResultBufferForRPC = (char*)&rpcDummy; + + unsigned long cci; + cci = *callingClientId; + + freeDynamicRPCData(); + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // Call the corresponding method. + rpcDummy = sc->abortTA( cci ); + + // Return the result + return &rpcDummy; +} + + + +#ifdef LINUX +extern "C" +#endif + +//@ManMemo: Module: {\bf servercomm} + +/*@Doc: + The global remote procedure {\tt rpcexecutequery_1()} is called by a clientcomm + object on the client system to initiate a retrieval query execution on a RasDaMan + server. +*/ + +ExecuteQueryRes* +RPCFUNCTIONDEF( rpcexecutequery_1, ExecuteQueryParams* params ) +{ + secureResultBufferForRPC = (char*)&rpcExecuteQueryRes; + + unsigned short returnValue; + unsigned long callingClientId = params->clientID; + const char* query = params->query; + + freeDynamicRPCData(); + + // prevent RPC from NULL pointers in case of exceptions + char *prev1 = strdup(""); + char *prev2 = strdup(""); + char *prev3 = strdup(""); + rpcExecuteQueryRes.token = prev1; + rpcExecuteQueryRes.typeName = prev2; + rpcExecuteQueryRes.typeStructure = prev3; + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // Call the corresponding method. + returnValue = sc->executeQuery( callingClientId, (const char*) query, rpcExecuteQueryRes ); + + // if no exception was thrown we are here, first check if the pointers have changed + if( rpcExecuteQueryRes.token != prev1 ) free(prev1); + if( rpcExecuteQueryRes.typeName != prev2 ) free(prev2); + if( rpcExecuteQueryRes.typeStructure != prev3 ) free(prev3); + + // than check if the pointers are NULL + if( !rpcExecuteQueryRes.token ) rpcExecuteQueryRes.token = strdup(""); + if( !rpcExecuteQueryRes.typeName ) rpcExecuteQueryRes.typeName = strdup(""); + if( !rpcExecuteQueryRes.typeStructure ) rpcExecuteQueryRes.typeStructure = strdup(""); + + // set missing parts of return structure + rpcExecuteQueryRes.status = returnValue; + + + return &rpcExecuteQueryRes; +} + + +#ifdef LINUX +extern "C" +#endif + +//@ManMemo: Module: {\bf servercomm} + +/*@Doc: + The global remote procedure {\tt rpcgetnextmdd_1()} is called by a clientcomm + object on the client system to transmit the next element of a MDD collection + from a RasDaMan server. +*/ + +GetMDDRes* +RPCFUNCTIONDEF( rpcgetnextmdd_1, unsigned long* callingClientId ) +{ + RMDBGENTER(2, RMDebug::module_servercomm, "Manager", "rpcgetnextmdd_1" ) + + secureResultBufferForRPC = (char*)&rpcGetMDDRes; + + r_Minterval mddDomain; + r_OId oid; + + freeDynamicRPCData(); + + // prevent RPC from NULL pointers + char *prev1 = strdup(""); + char *prev2 = strdup(""); + char *prev3 = strdup(""); + char *prev4 = strdup(""); + + rpcGetMDDRes.typeName = prev1; + rpcGetMDDRes.typeStructure = prev2; + rpcGetMDDRes.domain = prev3; + rpcGetMDDRes.oid = prev4; + + accessControl.wantToRead(); + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // Call the corresponding method and return the result + rpcGetMDDRes.status = sc->getNextMDD( *callingClientId, mddDomain, + rpcGetMDDRes.typeName, + rpcGetMDDRes.typeStructure, oid, + rpcGetMDDRes.currentFormat ); + rpcGetMDDRes.domain = mddDomain.get_string_representation(); + rpcGetMDDRes.oid = oid.get_string_representation() ? strdup( oid.get_string_representation() ) : strdup(""); + + // if no exceptions... + if( rpcGetMDDRes.typeName != prev1 ) free(prev1); + if( rpcGetMDDRes.typeStructure != prev2 ) free(prev2); + if( rpcGetMDDRes.domain != prev3 ) free(prev3); + if( rpcGetMDDRes.oid != prev4 ) free(prev4); + + // prevent RPC from NULL pointers + if( !rpcGetMDDRes.typeName ) rpcGetMDDRes.typeName = strdup(""); + if( !rpcGetMDDRes.typeStructure ) rpcGetMDDRes.typeStructure = strdup(""); + // the other 2 are not null + + RMDBGEXIT(2, RMDebug::module_servercomm, "Manager", "rpcgetnextmdd_1" ) + return &rpcGetMDDRes; +} + + + +#ifdef LINUX +extern "C" +#endif + +//@ManMemo: Module: {\bf servercomm} + +/*@Doc: + The global remote procedure {\tt rpcgetnextelement_1()} is called by a clientcomm + object on the client system to transmit the next element of a non-MDD collection + from a RasDaMan server. +*/ + +GetElementRes* +RPCFUNCTIONDEF( rpcgetnextelement_1, unsigned long* callingClientId ) +{ + RMDBGENTER(2, RMDebug::module_servercomm, "Manager", "rpcgetnextelement_1" ) + + secureResultBufferForRPC = (char*)&rpcGetElementRes; + + freeDynamicRPCData(); + + accessControl.wantToRead(); + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // Call the corresponding method and return the result + rpcGetElementRes.status = sc->getNextElement( *callingClientId, rpcGetElementRes.data.confarray_val, + rpcGetElementRes.data.confarray_len ); + + RMDBGEXIT(2, RMDebug::module_servercomm, "Manager", "rpcgetnextelement_1" ) + return &rpcGetElementRes; +} + + + +#ifdef LINUX +extern "C" +#endif + +//@ManMemo: Module: {\bf servercomm} + +/*@Doc: + The global remote procedure \Ref{rpcgetmddbyoid_1} is called by a clientcomm + object on the client system to receive an MDD by its oid. +*/ + +GetMDDRes* +RPCFUNCTIONDEF( rpcgetmddbyoid_1, OIdSpecParams* params ) +{ + RMDBGENTER(2, RMDebug::module_servercomm, "Manager", "rpcgetmddbyoid_1" ) + + secureResultBufferForRPC = (char*)&rpcGetMDDRes; + + r_Minterval mddDomain; + r_OId oid( params->oid ); + + freeDynamicRPCData(); + // prevent RPC from NULL pointers if exception occur + char *prev1 = strdup(""); + char *prev2 = strdup(""); + char *prev3 = strdup(""); + char *prev4 = strdup(""); + + rpcGetMDDRes.typeName = prev1; + rpcGetMDDRes.typeStructure = prev2; + rpcGetMDDRes.domain = prev3; + rpcGetMDDRes.oid = prev4; + + accessControl.wantToRead(); + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // Call the corresponding method and return the result + rpcGetMDDRes.status = sc->getMDDByOId( params->clientID, oid, mddDomain, + rpcGetMDDRes.typeName, + rpcGetMDDRes.typeStructure, + rpcGetMDDRes.currentFormat ); + rpcGetMDDRes.domain = mddDomain.get_string_representation(); + rpcGetMDDRes.oid = oid.get_string_representation() ? strdup( oid.get_string_representation() ) : strdup(""); + + // prevent RPC from NULL pointers + if( rpcGetMDDRes.typeName != prev1) free(prev1); + if( rpcGetMDDRes.typeStructure != prev2) free(prev2); + if( rpcGetMDDRes.domain != prev3 ) free(prev3); + if( rpcGetMDDRes.oid != prev4 ) free(prev4); + + if( !rpcGetMDDRes.typeName ) rpcGetMDDRes.typeName = strdup(""); + if( !rpcGetMDDRes.typeStructure ) rpcGetMDDRes.typeStructure = strdup(""); + // the other 2 are not null + + RMDBGEXIT(2, RMDebug::module_servercomm, "Manager", "rpcgetmddbyoid_1" ) + return &rpcGetMDDRes; +} + + + +#ifdef LINUX +extern "C" +#endif + +//@ManMemo: Module: {\bf servercomm} + +/*@Doc: + The global remote procedure {\tt rpcgetnexttile_1()} is called by a clientcomm + object on the client system to transmit the next element of a MDD collection + from a RasDaMan server. +*/ + +GetTileRes* +RPCFUNCTIONDEF( rpcgetnexttile_1, unsigned long* callingClientId ) +{ + RMDBGENTER(2, RMDebug::module_servercomm, "Manager", "rpcgetnexttile_1" ) + + secureResultBufferForRPC = (char*)&rpcGetTileRes; + + freeDynamicRPCData(); + + // fake data for security + RPCMarray *secureRpcMarray = (RPCMarray*)mymalloc(sizeof(RPCMarray)); + secureRpcMarray->domain = strdup(""); + secureRpcMarray->cellTypeLength = 1; + secureRpcMarray->currentFormat = 1; + secureRpcMarray->storageFormat = 1; + secureRpcMarray->data.confarray_len = 1; + secureRpcMarray->data.confarray_val = strdup(""); + + rpcGetTileRes.marray = secureRpcMarray; + + RPCMarray *tempRpcMarray; + + accessControl.wantToRead(); + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // Call the corresponding method and return the result + rpcGetTileRes.status = sc->getNextTile( *callingClientId, &tempRpcMarray); //&rpcGetTileRes.marray ); + // if this throws, secure... is nice initialized + + rpcGetTileRes.marray = tempRpcMarray; + + free(secureRpcMarray->data.confarray_val); + free(secureRpcMarray->domain); + free(secureRpcMarray); + + RMDBGEXIT(2, RMDebug::module_servercomm, "Manager", "rpcgetnexttile_1" ) + return &rpcGetTileRes; +} + + + +#ifdef LINUX +extern "C" +#endif +//@ManMemo: Module: {\bf servercomm} + +/** +*/ +unsigned short* +RPCFUNCTIONDEF( rpcendtransfer_1, unsigned long* callingClientId ) +{ + unsigned long cci = *callingClientId; + + secureResultBufferForRPC = (char*)&rpcDummy; + + freeDynamicRPCData(); + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // Call the corresponding method. + rpcDummy = sc->endTransfer( cci ); + + // Return the result + return &rpcDummy; +} + + + +#ifdef LINUX +extern "C" +#endif +//@ManMemo: Module: {\bf servercomm} + +/** +*/ +unsigned short* +RPCFUNCTIONDEF( rpcinitexecuteupdate_1, unsigned long* callingClientId ) +{ + secureResultBufferForRPC = (char*)&rpcDummy; + + unsigned long cci; + cci = *callingClientId; + + freeDynamicRPCData(); + + accessControl.wantToWrite(); + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // Call the corresponding method. + rpcDummy = sc->initExecuteUpdate( cci ); + + // Return the result + return &rpcDummy; +} + + + +#ifdef LINUX +extern "C" +#endif + +//@ManMemo: Module: {\bf servercomm} + +/*@Doc: + The global remote procedure {\tt rpcexecuteupdate_1()} is called by a clientcomm + object on the client system to initiate an update query execution on a RasDaMan + server. +*/ + +ExecuteUpdateRes* +RPCFUNCTIONDEF( rpcexecuteupdate_1, ExecuteQueryParams* params ) +{ + secureResultBufferForRPC = (char*)&rpcExecuteUpdateRes; + + unsigned short returnValue; + unsigned long callingClientId = params->clientID; + const char* query = params->query; + + freeDynamicRPCData(); + + // prevent RPC from NULL pointers + char *prev1 = strdup(""); + rpcExecuteUpdateRes.token = prev1; + + accessControl.wantToWrite(); + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // Call the corresponding method. + returnValue = sc->executeUpdate( callingClientId, (const char*) query, rpcExecuteUpdateRes ); + + // set missing parts of return structure + rpcExecuteUpdateRes.status = returnValue; + + // prevent RPC from NULL pointers + if( rpcExecuteUpdateRes.token != prev1 ) free(prev1); + + if( !rpcExecuteUpdateRes.token ) rpcExecuteUpdateRes.token = strdup(""); + + return &rpcExecuteUpdateRes; +} + + + +#ifdef LINUX +extern "C" +#endif +//@ManMemo: Module: {\bf servercomm} + +/** +*/ +unsigned short* +RPCFUNCTIONDEF( rpcstartinserttransmdd_1, InsertTransMDDParams* params ) +{ + secureResultBufferForRPC = (char*)&rpcDummy; + + unsigned long callingClientId = params->clientID; + // const char* collName = params->collName; // not used + const char* typeName = params->typeName; + unsigned long typeLength = params->typeLength; + r_Minterval mddDomain( params->domain ); + + freeDynamicRPCData(); + + accessControl.wantToWrite(); + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // Call the corresponding method. + rpcDummy = sc->startInsertTransMDD( callingClientId, mddDomain, typeLength, typeName ); + + // Return the result + return &rpcDummy; +} + + + +#ifdef LINUX +extern "C" +#endif +//@ManMemo: Module: {\bf servercomm} + +/** +*/ +unsigned short* +RPCFUNCTIONDEF( rpcstartinsertpersmdd_1, InsertPersMDDParams* params ) +{ + secureResultBufferForRPC = (char*)&rpcDummy; + + unsigned long callingClientId = params->clientID; + const char* collName = params->collName; + const char* typeName = params->typeName; + unsigned long typeLength = params->typeLength; + r_Minterval mddDomain( params->domain ); + r_OId oid( params->oid ); + + freeDynamicRPCData(); + + accessControl.wantToWrite(); + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // Call the corresponding method. + rpcDummy = sc->startInsertPersMDD( callingClientId, (const char*) collName, mddDomain, typeLength, typeName, oid ); + + // Return the result + return &rpcDummy; +} + + + +#ifdef LINUX +extern "C" +#endif +//@ManMemo: Module: {\bf servercomm} + +/** +*/ +unsigned short* +RPCFUNCTIONDEF( rpcinserttile_1, InsertTileParams* params ) +{ + secureResultBufferForRPC = (char*)&rpcDummy; + + freeDynamicRPCData(); + + accessControl.wantToWrite(); + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // Call the corresponding method. + rpcDummy = sc->insertTile( params->clientID, params->isPersistent, params->marray ); + + // Return the result + return &rpcDummy; +} + + + +#ifdef LINUX +extern "C" +#endif + +//@ManMemo: Module: {\bf servercomm} + +/** +*/ +unsigned short* +RPCFUNCTIONDEF( rpcendinsertmdd_1, EndInsertMDDParams* params ) +{ + secureResultBufferForRPC = (char*)&rpcDummy; + + freeDynamicRPCData(); + + accessControl.wantToWrite(); + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // Call the corresponding method. + rpcDummy = sc->endInsertMDD( params->clientID, params->isPersistent ); + + // Return the result + return &rpcDummy; +} + + + +#ifdef LINUX +extern "C" +#endif + +//@ManMemo: Module: {\bf servercomm} + +/*@Doc: + The global remote procedure {\tt rpcInsertMDD()} is called by a clientcomm + object on the client system to insert a MDD object in an existing MDD + collection on a RasDaMan server. +*/ + +unsigned short* +RPCFUNCTIONDEF( rpcinsertmdd_1, InsertMDDParams* params ) +{ + secureResultBufferForRPC = (char*)&rpcDummy; + + unsigned long callingClientId = params->clientID; + const char* collName = params->collName; + const char* typeName = params->typeName; + RPCMarray* rpcMarray = params->marray; + + r_OId oid( params->oid ); + + freeDynamicRPCData(); + + accessControl.wantToWrite(); + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // Call the corresponding method. + rpcDummy = sc->insertMDD( callingClientId, collName, rpcMarray, typeName, oid ); + + return &rpcDummy; +} + + + +#ifdef LINUX +extern "C" +#endif + +//@ManMemo: Module: {\bf servercomm} + +/*@Doc: + The global remote procedure {\tt rpcgetcollbyname_1()} is called by a clientcomm + object on the client system to initiate the lookup of a MDD collection by its name + on a RasDaMan server. +*/ + +GetCollRes* +RPCFUNCTIONDEF( rpcgetcollbyname_1, NameSpecParams* params ) +{ + RMDBGENTER(2, RMDebug::module_servercomm, "Manager", "rpcgetcollbyname_1" ) + + secureResultBufferForRPC = (char*)&rpcGetCollRes; + + r_OId oid; + + freeDynamicRPCData(); + + char *prev1 = strdup(""); + char *prev2 = strdup(""); + char *prev3 = strdup(""); + char *prev4 = strdup(""); + rpcGetCollRes.typeName = prev1; + rpcGetCollRes.typeStructure = prev2; + rpcGetCollRes.oid = prev3; + rpcGetCollRes.collName = prev4; + + accessControl.wantToRead(); + + unsigned long callingClientId = params->clientID; + const char* collName = params->name; + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // Call the corresponding method. + rpcGetCollRes.status = sc->getCollByName( callingClientId, collName, + rpcGetCollRes.typeName, + rpcGetCollRes.typeStructure, oid ); + + rpcGetCollRes.oid = oid.get_string_representation() ? strdup( oid.get_string_representation() ) : strdup(""); + rpcGetCollRes.collName = params->name ? strdup( params->name ) : strdup(""); + + // prevent RPC from NULL pointers + if( rpcGetCollRes.typeName != prev1) free(prev1); + if( rpcGetCollRes.typeStructure != prev2) free(prev2); + if( rpcGetCollRes.oid != prev3) free(prev3); + if( rpcGetCollRes.collName != prev4) free(prev4); + + if( !rpcGetCollRes.typeName ) rpcGetCollRes.typeName = strdup(""); + if( !rpcGetCollRes.typeStructure ) rpcGetCollRes.typeStructure = strdup(""); + + // Return the result + RMDBGEXIT(2, RMDebug::module_servercomm, "Manager", "rpcgetcollbyname_1" ) + return &rpcGetCollRes; +} + + + +#ifdef LINUX +extern "C" +#endif + +//@ManMemo: Module: {\bf servercomm} + +/*@Doc: + The global remote procedure {\tt rpcgetcollbyoid_1()} is called by a clientcomm + object on the client system to initiate the lookup of a MDD collection by its oid + on a RasDaMan server. +*/ + +GetCollRes* +RPCFUNCTIONDEF( rpcgetcollbyoid_1, OIdSpecParams* params ) +{ + RMDBGENTER(2, RMDebug::module_servercomm, "Manager", "rpcgetcollbyoid_1" ) + + secureResultBufferForRPC = (char*)&rpcGetCollRes; + + r_OId oid( params->oid ); + + freeDynamicRPCData(); + // prevent RPC from NULL pointers + char *prev1 = strdup(""); + char *prev2 = strdup(""); + char *prev3 = strdup(""); + char *prev4 = strdup(""); + rpcGetCollRes.typeName = prev1; + rpcGetCollRes.typeStructure = prev2; + rpcGetCollRes.collName = prev3; + rpcGetCollRes.oid = prev4; + + accessControl.wantToRead(); + + unsigned long callingClientId = params->clientID; + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // Call the corresponding method. + rpcGetCollRes.status = sc->getCollByOId( callingClientId, oid, rpcGetCollRes.typeName, + rpcGetCollRes.typeStructure, rpcGetCollRes.collName ); + + rpcGetCollRes.oid = oid.get_string_representation() ? strdup( oid.get_string_representation() ) : strdup(""); + + // prevent RPC from NULL pointers + if( rpcGetCollRes.typeName != prev1) free(prev1); + if( rpcGetCollRes.typeStructure != prev2) free(prev2); + if( rpcGetCollRes.collName != prev3) free(prev3); + if( rpcGetCollRes.oid != prev4) free(prev4); + + if( !rpcGetCollRes.typeName ) rpcGetCollRes.typeName = strdup(""); + if( !rpcGetCollRes.typeStructure ) rpcGetCollRes.typeStructure = strdup(""); + if( !rpcGetCollRes.collName ) rpcGetCollRes.collName = strdup(""); + // oid is not null, + + // Return the result + RMDBGEXIT(2, RMDebug::module_servercomm, "Manager", "rpcgetcollbyoid_1" ) + return &rpcGetCollRes; +} + + + +#ifdef LINUX +extern "C" +#endif + +//@ManMemo: Module: {\bf servercomm} + +/*@Doc: + The global remote procedure {\tt rpcgetcolloidsbyname_1()} is called by a clientcomm + object on the client system to retrieve the collection of oids by the collection's name. +*/ + +GetCollOIdsRes* +RPCFUNCTIONDEF( rpcgetcolloidsbyname_1, NameSpecParams* params ) +{ + RMDBGENTER(2, RMDebug::module_servercomm, "Manager", "rpcgetcolloidsbyname_1" ) + + secureResultBufferForRPC = (char*)&rpcGetCollOidsRes; + + r_OId oid; + + freeDynamicRPCData(); + + char *prev1 = strdup(""); + char *prev2 = strdup(""); + char *prev3 = strdup(""); + char *prev4 = strdup(""); + rpcGetCollOidsRes.typeName = prev1; + rpcGetCollOidsRes.typeStructure = prev2; + rpcGetCollOidsRes.collName = prev3; + rpcGetCollRes.oid = prev4; + + accessControl.wantToRead(); + + unsigned long callingClientId = params->clientID; + const char* collName = params->name; + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // Call the corresponding method. + rpcGetCollOidsRes.status = sc->getCollOIdsByName( callingClientId, collName, rpcGetCollOidsRes.typeName, + rpcGetCollOidsRes.typeStructure, oid, + rpcGetCollOidsRes.oidTable.oidTable_val, rpcGetCollOidsRes.oidTable.oidTable_len ); + + rpcGetCollOidsRes.oid = oid.get_string_representation() ? strdup( oid.get_string_representation() ) : strdup(""); + rpcGetCollOidsRes.collName = params->name ? strdup( params->name ) : strdup(""); + + // prevent RPC from NULL pointers + if( rpcGetCollOidsRes.typeName != prev1 ) free(prev1); + if( rpcGetCollOidsRes.typeStructure != prev2 ) free(prev2); + if( rpcGetCollOidsRes.collName != prev3 ) free(prev3); + if( rpcGetCollRes.oid != prev4) free(prev4); + + if( !rpcGetCollOidsRes.typeName ) rpcGetCollOidsRes.typeName = strdup(""); + if( !rpcGetCollOidsRes.typeStructure ) rpcGetCollOidsRes.typeStructure = strdup(""); + if( !rpcGetCollOidsRes.collName ) rpcGetCollOidsRes.collName = strdup(""); + // oid is not null + + // Return the result + RMDBGEXIT(2, RMDebug::module_servercomm, "Manager", "rpcgetcolloidsbyname_1" ) + return &rpcGetCollOidsRes; +} + + + + +#ifdef LINUX +extern "C" +#endif + +//@ManMemo: Module: {\bf servercomm} + +/*@Doc: + The global remote procedure {\tt rpcgetcolloidsbyoid_1()} is called by a clientcomm + object on the client system to retrieve the collection of oids by the collection's oid. +*/ + +GetCollOIdsRes* +RPCFUNCTIONDEF( rpcgetcolloidsbyoid_1, OIdSpecParams* params ) +{ + RMDBGENTER(2, RMDebug::module_servercomm, "Manager", "rpcgetcolloidsbyname_1" ) + + secureResultBufferForRPC = (char*)&rpcGetCollOidsRes; + + r_OId oid( params->oid ); + + freeDynamicRPCData(); + + char *prev1 = strdup(""); + char *prev2 = strdup(""); + char *prev3 = strdup(""); + char *prev4 = strdup(""); + rpcGetCollOidsRes.typeName = prev1; + rpcGetCollOidsRes.typeStructure = prev2; + rpcGetCollOidsRes.collName = prev3; + rpcGetCollRes.oid = prev4; + + accessControl.wantToRead(); + + unsigned long callingClientId = params->clientID; + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // Call the corresponding method. + rpcGetCollOidsRes.status = sc->getCollOIdsByOId( callingClientId, oid, + rpcGetCollOidsRes.typeName, + rpcGetCollOidsRes.typeStructure, + rpcGetCollOidsRes.oidTable.oidTable_val, + rpcGetCollOidsRes.oidTable.oidTable_len, + rpcGetCollOidsRes.collName ); + + rpcGetCollOidsRes.oid = oid.get_string_representation() ? strdup( oid.get_string_representation() ) : strdup(""); + + // prevent RPC from NULL pointers + if( rpcGetCollOidsRes.typeName != prev1 ) free(prev1); + if( rpcGetCollOidsRes.typeStructure != prev2 ) free(prev2); + if( rpcGetCollOidsRes.collName != prev3 ) free(prev3); + if( rpcGetCollRes.oid != prev4) free(prev4); + + if( !rpcGetCollOidsRes.typeName ) rpcGetCollOidsRes.typeName = strdup(""); + if( !rpcGetCollOidsRes.typeStructure ) rpcGetCollOidsRes.typeStructure = strdup(""); + if( !rpcGetCollOidsRes.collName ) rpcGetCollOidsRes.collName = strdup(""); + + // Return the result + RMDBGEXIT(2, RMDebug::module_servercomm, "Manager", "rpcgetcolloidsbyname_1" ) + return &rpcGetCollOidsRes; +} + + + +#ifdef LINUX +extern "C" +#endif + +//@ManMemo: Module: {\bf servercomm} + +/*@Doc: + The global remote procedure {\tt rpcinsertcoll_1()} is called by a clientcomm + object on the client system to create a MDD collection for further use on + a RasDaMan server. +*/ + +unsigned short* +RPCFUNCTIONDEF( rpcinsertcoll_1, InsertCollParams* params ) +{ + secureResultBufferForRPC = (char*)&rpcDummy; + + unsigned long callingClientId = params->clientID; + const char* collName = params->collName; + const char* typeName = params->typeName; + + freeDynamicRPCData(); + + accessControl.wantToWrite(); + + r_OId oid( params->oid ); + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // Call the corresponding method. + rpcDummy = sc->insertColl( callingClientId, collName, typeName, oid ); + + // Return the result + return &rpcDummy; +} + + + +#ifdef LINUX +extern "C" +#endif + +//@ManMemo: Module: {\bf servercomm} + +/*@Doc: + The global remote procedure {\tt rpcdeletecollbyname_1()} is called by a clientcomm + object on the client system to delete a existing MDD collection. +*/ + +unsigned short* +RPCFUNCTIONDEF( rpcdeletecollbyname_1, NameSpecParams* params ) +{ + secureResultBufferForRPC = (char*)&rpcDummy; + + unsigned long callingClientId = params->clientID; + const char* collName = params->name; + + freeDynamicRPCData(); + + accessControl.wantToWrite(); + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // Call the corresponding method. + rpcDummy = sc->deleteCollByName( callingClientId, collName ); + + // Return the result + return &rpcDummy; +} + + + +#ifdef LINUX +extern "C" +#endif + +//@ManMemo: Module: {\bf servercomm} + +/*@Doc: + The global remote procedure {\tt rpcdeleteobjbyoid_1()} is called by a clientcomm + object on the client system to delete an object specified by oid. +*/ + +unsigned short* +RPCFUNCTIONDEF( rpcdeleteobjbyoid_1, OIdSpecParams* params ) +{ + secureResultBufferForRPC = (char*)&rpcDummy; + + unsigned long callingClientId = params->clientID; + r_OId oid( params->oid ); + + freeDynamicRPCData(); + + accessControl.wantToWrite(); + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // Call the corresponding method. + rpcDummy = sc->deleteObjByOId( callingClientId, oid ); + + // Return the result + return &rpcDummy; +} + + + +#ifdef LINUX +extern "C" +#endif + +//@ManMemo: Module: {\bf servercomm} + +/*@Doc: + The global remote procedure {\tt rpcremoveobjfromcoll_1()} is called by a clientcomm + object on the client system to delete an object from a collection. +*/ + +unsigned short* +RPCFUNCTIONDEF( rpcremoveobjfromcoll_1, RemoveObjFromCollParams* params ) +{ + secureResultBufferForRPC = (char*)&rpcDummy; + + unsigned long callingClientId = params->clientID; + const char* collName = params->collName; + r_OId oid( params->oid ); + + freeDynamicRPCData(); + + accessControl.wantToWrite(); + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // Call the corresponding method. + rpcDummy = sc->removeObjFromColl( callingClientId, collName, oid ); + + // Return the result + return &rpcDummy; +} + + + +#ifdef LINUX +extern "C" +#endif + +//@ManMemo: Module: {\bf servercomm} + +/*@Doc: +*/ + +ObjectTypeRes* +RPCFUNCTIONDEF( rpcgetobjecttype_1, OIdSpecParams* params ) +{ + secureResultBufferForRPC = (char*)&procResult; + + freeDynamicRPCData(); + + accessControl.wantToRead(); + + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // Call the corresponding method. + r_OId oid( params->oid ); + + procResult.status = sc->getObjectType( params->clientID, oid, procResult.objType ); + + // Return the result + return &procResult; +} + + + +#ifdef LINUX +extern "C" +#endif + +//@ManMemo: Module: {\bf servercomm} + +/*@Doc: +*/ + +OIdRes* +RPCFUNCTIONDEF( rpcgetnewoid_1, NewOIdParams* params ) +{ + secureResultBufferForRPC = (char*)&rpcOidRes; + + freeDynamicRPCData(); + + char *prev1 = strdup(""); + rpcOidRes.oid = prev1; + + accessControl.wantToWrite(); + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // Call the corresponding method. + r_OId oid; + + rpcOidRes.status = sc->getNewOId( params->clientID, params->objType, oid ); + rpcOidRes.oid = oid.get_string_representation() ? strdup( oid.get_string_representation() ) : strdup(""); + free(prev1); + + // Return the result + return &rpcOidRes; +} + + +// Callback for callback manager, performs actual garbage collection. +// The context points to the ServerComm object (see registering call in +// garbageCollection() ) +static void callback_garbage_collection(void *context) +{ + ServerComm *sc = (ServerComm*)context; + + if( sc && !sc->clientTbl.empty() ) + { + RMInit::dbgOut << "Garbage Collection ... " << flush; + +#ifdef RMANDEBUG + sc->printServerStatus( RMInit::dbgOut ); +#endif + + if (!noTimeOut) + { + list::iterator iter; + iter = sc->clientTbl.begin(); + unsigned long now = time( NULL ); + + RMDBGONCE(2, RMDebug::module_servercomm, "Manager", "checking " << sc->clientTbl.size() << " clients..." ); + while ( iter != sc->clientTbl.end() ) + { + if( now - (*iter)->lastActionTime >= sc->clientTimeout ) + { + RMInit::logOut << "Message: Found timed-out client with id " << (*iter)->clientId + << " (" << (*iter)->clientIdText << ", " + << now - (*iter)->lastActionTime << "s)..." << flush; + + if( sc->deleteClientTblEntry( (*iter)->clientId ) == 0 ) + { + RMInit::logOut << "deleted." << endl; + + sc->informRasMGR(SERVER_AVAILABLE); + // reset the iterator (otherwise, it would skip one object + // because one was deleted) + iter = sc->clientTbl.begin(); + } + else + { + RMInit::logOut << "deletion postponed." << endl; + iter++; + } + } + else + iter++; + } + } + RMInit::dbgOut << "garbage collection done." << endl; + } +} + + +#ifdef LINUX +extern "C" +#endif +void +garbageCollection( int ) +{ + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + /* + * Just register the callbacks because we're not allowed to do any + * mallocs or prints in the alarm handler environment. The pending + * callbacks will be executed whenever there's RPC activity. + */ + sc->callback_mgr.registerUniqueCallback(callback_garbage_collection, (void*)sc); + + // Re-initialize the signal handler to point to this function + signal( SIGALRM, garbageCollection); + + // Reset the alarm + alarm( (unsigned int)sc->garbageCollectionInterval ); +} + + +#ifdef LINUX +extern "C" +#endif +void +garbageCollectionDummy( int ) +{ + /* Dummy garbage collection function for avoiding reentrance of the callback manager. + Does nothing but reinstall the signal. */ + signal( SIGALRM, garbageCollection); + alarm( (unsigned int)(ServerComm::actual_servercomm->garbageCollectionInterval) ); +} + + +#ifdef LINUX +extern "C" +#endif +GetTypeStructureRes* +RPCFUNCTIONDEF( rpcgettypestructure_1, GetTypeStructureParams* params ) +{ + secureResultBufferForRPC = (char*)&rpcGetTypeStructureRes; + + freeDynamicRPCData(); + + char *prev1 = strdup(""); + rpcGetTypeStructureRes.typeStructure = prev1; + + accessControl.wantToRead(); + + // Get a pointer to the actual servercomm object. + ServerComm* sc = ServerComm::actual_servercomm; + + // Call the corresponding method. + rpcGetTypeStructureRes.status = sc->getTypeStructure( params->clientID, params->typeName, + params->typeType, rpcGetTypeStructureRes.typeStructure ); + + // prevent RPC from NULL pointers + if( rpcGetTypeStructureRes.typeStructure != prev1 ) free(prev1); + + if( !rpcGetTypeStructureRes.typeStructure ) rpcGetTypeStructureRes.typeStructure = strdup(""); + + // Return the result + return &rpcGetTypeStructureRes; +} + + + +#ifdef LINUX +extern "C" +#endif +int* +RPCFUNCTIONDEF( rpcgetserverendian_1, int * ) +{ + freeDynamicRPCData(); + + static int result; + +#ifdef LITTLE_ENDIAN + result = 1; +#else + result = 0; +#endif + + return &result; +} + + +#ifdef LINUX +extern "C" +#endif +unsigned short* +RPCFUNCTIONDEF( rpcsetservertransfer_1, SetServerTransferParams* params ) +{ + freeDynamicRPCData(); + secureResultBufferForRPC = (char*)&rpcDummy; + + ServerComm* sc = ServerComm::actual_servercomm; + + rpcDummy = (unsigned short)(sc->setTransferMode( params->clientID, params->format, params->formatParams )); + + return &rpcDummy; +} + + +#ifdef LINUX +extern "C" +#endif +GetExtendedErrorInfo * +RPCFUNCTIONDEF( rpcgeterrorinfo_1, void * params ) +{ + freeDynamicRPCData(); + + ServerComm* sc = ServerComm::actual_servercomm; + + rpcExtendedErrorInfo.errorText = (char*) sc->getExtendedErrorInfo(); + + return &rpcExtendedErrorInfo; +} + +#ifdef LINUX +extern "C" +#endif +unsigned short* +RPCFUNCTIONDEF( rpcsetserverstorage_1, SetServerTransferParams* params ) +{ + secureResultBufferForRPC = (char*)&rpcDummy; + + freeDynamicRPCData(); + + ServerComm* sc = ServerComm::actual_servercomm; + + rpcDummy = (unsigned short)(sc->setStorageMode(params->clientID, params->format, params->formatParams )); + + return &rpcDummy; +} + diff --git a/servercomm/servercomm.cc b/servercomm/servercomm.cc new file mode 100644 index 0000000..5982453 --- /dev/null +++ b/servercomm/servercomm.cc @@ -0,0 +1,1399 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see . +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see +* or contact Peter Baumann via . +/ +/** + * SOURCE: servercomm.cc + * + * MODULE: servercomm + * CLASS: ServerComm + * + * COMMENTS: + * None +*/ + +#include "mymalloc/mymalloc.h" + +// --- these defs should go into a central constant definition section, +// as they define externally observable behavior -- PB 2003-nov-15 + +// waiting period until client is considered dead [secs] +#define CLIENT_TIMEOUT 3600 + +// timeout for select() call at server startup [secs] +#define TIMEOUT_SELECT 30 + +// period after which the next garbage collection is scheduled [secs] +#define GARBCOLL_INTERVAL 600 + +// console output describing successful/unsuccessful actions +#define MSG_OK "ok" +#define MSG_FAILED "failed" + +// rasserver exit codes (selection of value sometimes unclear :-( +#define EXITCODE_ZERO 0 +#define EXITCODE_ONE 1 +#define EXITCODE_RASMGR_FAILED 10 // Why 10 ? +// --- + +static const char rcsid[] = "@(#)servercomm, ServerComm: $Id: servercomm.cc,v 1.146 2006/01/03 00:23:53 rasdev Exp $"; + +#include + +#include +#include +#include // for time() +#include + +#include // for signal() +#include // for alarm(), gethostname() +#include + +#ifdef SOLARIS + #define PORTMAP // define to use function declarations for old interfaces + #include + + int _rpcpmstart = 0; + + // function prototype with C linkage + extern "C" int gethostname(char *name, int namelen); +#else // HPUX + #include +#endif + +#include + +#include "raslib/rmdebug.hh" +#include "raslib/rminit.hh" +#include "raslib/error.hh" +#include "raslib/minterval.hh" +#include "raslib/parseparams.hh" +#include "compression/tilecompression.hh" + +#include "servercomm/servercomm.hh" +#include "qlparser/qtnode.hh" +#include "qlparser/qtdata.hh" +#include "catalogmgr/typefactory.hh" + +#include "tilemgr/tile.hh" + +#include "relcatalogif/mddtype.hh" +#include "relcatalogif/mdddomaintype.hh" +#include "relcatalogif/settype.hh" +#include "mddmgr/mddcoll.hh" +#include "mddmgr/mddcolliter.hh" +#include "mddmgr/mddobj.hh" + +#include "debug.hh" + +#ifdef PURIFY +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +// init globals for server initialization +// RMINITGLOBALS('S') + +// Once again a function prototype. The first one is for the RPC dispatcher +// function located in the server stub file rpcif_svc.c and the second one +// is for the garbage collection function pointed to by the signal handler. +extern "C" +{ + // void rpcif_1( struct svc_req*, register SVCXPRT* ); + void garbageCollection( int ); +} +extern char* rpcif_1( struct svc_req*, register SVCXPRT* ); +extern bool bMemFailed; + +// this is a temporary thing +extern int globalOptimizationLevel; +extern unsigned long maxTransferBufferSize; + +// At the beginning, no servercomm object exists. +ServerComm* ServerComm::actual_servercomm = 0; + +list ServerComm::clientTbl; +unsigned long ServerComm::clientCount = 0; + +#include "rnprotocol/srvrasmgrcomm.hh" + +extern SrvRasmgrComm rasmgrComm; + +/************************************************************************* + * Method name...: ServerComm() (constructor) + ************************************************************************/ +ServerComm::ServerComm() + : clientTimeout( rasmgrComm.getTimeout() ), + garbageCollectionInterval( GARBCOLL_INTERVAL ), + transactionActive( 0 ), + memUsed( 0 ), + admin( 0 ), + errorText( 0 ) +{ + if( actual_servercomm ) + { + RMInit::logOut << "Internal Error: Tried to instantiate more than one ServerComm object." << endl; + exit( EXITCODE_ONE ); + } + + // uniqueClientContext = NULL; + isHttpServer = false; + + actual_servercomm = this; +} + +ServerComm::ServerComm( unsigned long timeOut, unsigned long managementInterval , unsigned long listenPort, char* rasmgrHost, unsigned int rasmgrPort,char* serverName) + : clientTimeout( timeOut ), + garbageCollectionInterval( managementInterval ), + transactionActive( 0 ), + memUsed( 0 ), + admin( 0 ), + errorText( 0 ) +{ + if( actual_servercomm ) + { + RMInit::logOut << "Internal Error: Tried to instantiate more than one ServerComm object." << endl; + exit( EXITCODE_ONE ); + } + + actual_servercomm = this; + this->listenPort = listenPort; + this->rasmgrHost = rasmgrHost; + this->rasmgrPort = rasmgrPort; + this->serverName = serverName; + + isHttpServer = false; + //uniqueClientContext = NULL; +} + + + +/************************************************************************* + * Method name...: ~ServerComm() (destructor) + ************************************************************************/ +ServerComm::~ServerComm() +{ + // delete communication object + if( admin ) delete admin; + + actual_servercomm = 0; +} + +/************************************************************************* + * Method name...: startRpcServer() + ************************************************************************/ +void rpcSignalHandler(int sig); +void our_svc_run(); + +void +ServerComm::startRpcServer() + throw( r_Error ) +{ + // create administraion object (O2 session is initialized) + admin = AdminIf::instance(); + if( !admin ) + throw r_Error( r_Error::r_Error_BaseDBMSFailed ); + + register SVCXPRT *transp; + + // install a signal handler to catch alarm signals for garbage collection + signal( SIGALRM, garbageCollection ); + + RMInit::logOut << "Testing if no other rasdaman RPC server with my listenPort (0x"<< hex << listenPort << dec <<") is already running on this CPU..." << flush; + + + char* myName = new char[256]; + + if( gethostname( myName, 256 ) ) + RMInit::logOut << endl << "Unable to determine my own hostname. Skipping this test." << endl; + else + if( clnt_create( myName, listenPort, RPCIFVERS, "tcp" ) ) + { + RMInit::logOut << MSG_FAILED << endl; + exit( EXITCODE_ZERO ); + } + else + RMInit::logOut << MSG_OK << endl; + + delete[] myName; + + (void) pmap_unset(listenPort, RPCIFVERS); + + RMInit::logOut << "Creating UDP services..." << flush; + transp = svcudp_create(RPC_ANYSOCK); + if (transp == NULL) + { + RMInit::logOut << MSG_FAILED << endl; + throw r_Error( r_Error::r_Error_General ); + } + + RMInit::logOut << "registering UDP interface..." << flush; + if (!svc_register(transp, listenPort, RPCIFVERS, rpcif_1_caller, IPPROTO_UDP)) + { + RMInit::logOut << MSG_FAILED << endl; + throw r_Error( r_Error::r_Error_General ); + } + RMInit::logOut << MSG_OK << endl; + + RMInit::logOut << "Creating TCP services..." << flush; + transp = svctcp_create(RPC_ANYSOCK, 0, 0); + if (transp == NULL) + { + RMInit::logOut << MSG_FAILED << endl; + throw r_Error( r_Error::r_Error_General ); + } + + RMInit::logOut << "registering TCP interface..." << flush; + if (!svc_register(transp, listenPort, RPCIFVERS, rpcif_1_caller, IPPROTO_TCP)) + { + RMInit::logOut << MSG_FAILED << endl; + throw r_Error( r_Error::r_Error_General ); + } + RMInit::logOut << MSG_OK << endl; + + RMInit::logOut << "Setting alarm clock for next garbage collection to " << garbageCollectionInterval + << " secs..."; RMInit::logOut.flush(); + alarm( (unsigned int)garbageCollectionInterval ); + RMInit::logOut << MSG_OK << endl; + + RMInit::logOut << "Global query optimization level is " << globalOptimizationLevel << "." << endl; + + signal (SIGTERM, rpcSignalHandler); + informRasMGR(SERVER_AVAILABLE); + RMInit::logOut << "rasdaman server "<0) + { + svc_getreqset(&read_fd_set); + TALK( "RPC Request executed." ); + } + + if(rasp<=0) + { + TALK( "our_svc_run(): Error: Timeout." ); // or a signal + // execute all pending callbacks. Redirect alarm signal first to make sure no + // reentrance is possible! + + // these should abort any pending transaction + signal( SIGALRM, garbageCollectionDummy ); + ServerComm::actual_servercomm->callback_mgr.executePending(); + signal( SIGALRM, garbageCollection ); + timeout.tv_sec= TIMEOUT_SELECT; + timeout.tv_usec=0; + + if(accessControl.isClient()==false) + { + // regularly tell the rasmgr that we are available. There is a scenario for DoS-attack (or bug, or firewall-problem) + // when a client allocates itself a server and never calls, so the server is not usable any more. + // but we have to find a smarter way of doing this, we need rasmgr-rasserver communication! + ServerComm::actual_servercomm->informRasMGR(SERVER_REGULARSIG); + } + if(bMemFailed) + { + // no reason to continue + RMInit::logOut << "Internal error: rasserver: memory exhausted." << endl << flush; + exit( EXITCODE_ONE ); + } + } + } + + LEAVE( "our_svc_run" ); +} + + +void rpcSignalHandler(int sig) +{ + static int in_progress=0; // our sema to prevent signal-in-signal + + if (in_progress) + return; + + in_progress = 1; + + // busy wait +#define SIGNAL_WAIT_CYCLES 1000000 + for(long j=0;jstopRpcServer(); + } +} // rpcSignalHandler() + +void +ServerComm::stopRpcServer() +{ + RMInit::logOut << "Shutdown request received." << endl; + + // Determine when next garbage collection would have occured + unsigned long nextGarbColl = time( NULL ); + struct itimerval rttimer; + getitimer( ITIMER_REAL, &rttimer ); + nextGarbColl += rttimer.it_value.tv_sec; + RMInit::logOut << "Next garbage collection would have been in " << rttimer.it_value.tv_sec << " sec, at " + << ctime((time_t*)&nextGarbColl); + + RMInit::logOut << "Unregistering interface..."; + svc_unregister( listenPort, RPCIFVERS ); + + RMInit::logOut << "shutting down services..."; + abortEveryThingNow(); + + RMInit::logOut << "informing rasmgr..."; + informRasMGR(SERVER_DOWN); + RMInit::logOut << MSG_OK << endl; + + RMInit::logOut << "rasdaman server " << serverName <<" is down." << endl; + + exit(0); + // svc_exit(); +} + +// quick hack function used when stopping server to abort transaction and close db +void +ServerComm::abortEveryThingNow() +{ + ENTER( "ServerComm::abortEveryThingNow()" ); + + list::iterator iter; + ServerComm *sc=ServerComm::actual_servercomm; + iter = sc->clientTbl.begin(); + + while ( iter != sc->clientTbl.end() ) + { + ServerComm::ClientTblElt *clnt = *iter; + + clnt->transaction.abort(); + clnt->database.close(); + + iter++; + } + + LEAVE( "ServerComm::abortEveryThingNow()" ); +} + +/************************************************************************* + * rpcif_1_caller(struct svc_req *rqstp, SVCXPRT *transp) + * indirect caller for rpcif_1, which is automaticaly generated and can't + * be hand-edited. It provides services for parallel-processing + *************************************************************************/ +void rpcif_1_caller(struct svc_req *rqstp, SVCXPRT *transp) +{ + ENTER( "rpcif_1_caller(_,_)" ); + + bool flagTransactionReady=false; + + bool isGetExtendedError=false; + + switch (rqstp->rq_proc) + { + //case RPCCOMMITTA: no, closeDB only, because abortTA and commitTA + //case RPCABORTTA: do automatically a closeDB and both reporting to RasMGR "available" brings problems + + case RPCCLOSEDB: flagTransactionReady=true; break; + + case RPCGETERRORINFO: isGetExtendedError = true; break; + } + + if(isGetExtendedError == false) ServerComm::actual_servercomm->clearExtendedErrorInfo(); + + char* errTxt = rpcif_1(rqstp,transp); + + if(isGetExtendedError && bMemFailed) { + // the client got the message + // no reason to continue + + signal( SIGALRM, garbageCollectionDummy ); + ServerComm::actual_servercomm->callback_mgr.executePending(); + RMInit::logOut << "Internal error: rasserver panic: memory exhausted, terminating forcefully." << endl << flush; + exit(1); + } + + if (errTxt) + { + // this is not necessary, since the client gets an error code '42' + //RMInit::logOut << "rasserver: general exception from server caught by rpcif_1_caller!" << endl << errTxt << endl; + //RMInit::logOut.flush(); + //cerr << "rasserver: general exception from server caught by rpcif_1_caller!" << endl << errTxt << endl; + ServerComm::actual_servercomm->setExtendedErrorInfo(errTxt); + free(errTxt); + errTxt = 0; + + //Answer "Remote system error " to the client + //svcerr_systemerr (transp); don't think that's necessary any more, for the same reason + } + + ServerComm::actual_servercomm->clientEndRequest(); + + if(flagTransactionReady) + ServerComm::actual_servercomm->informRasMGR(SERVER_AVAILABLE); + + LEAVE( "rpcif_1_caller()" ); +} + + +/************************************************************************* + * Method name...: informRasMGR( int what ) with a helper function + ************************************************************************/ +#include + +// writeWholeMessage: +// send message via (open, connected) socket to rasmgr +// called by informRasMGR. +// return values: +// -1 error +// >0 success + +int writeWholeMessage(int socket,char *destBuffer,int buffSize) +{ + ENTER( "writeWholeMessage( socket=" << socket << ", destBuffer=" << (destBuffer?destBuffer:"(null)") << ", buffSize=" << buffSize << " )" ); + + // we write the whole message, including the ending '\0', which is already in + // the buffSize provided by the caller + int totalLength=0; + int writeNow; + while(1) + { + writeNow = write(socket,destBuffer+totalLength,buffSize-totalLength); + if(writeNow == -1) + { + TALK( "writeWholeMessage: bad socket write returned " << writeNow << ", errno=" << errno ); + if(errno == EINTR) + continue; // read was interrupted by signal (on bad SO's) + LEAVE( "writeWholeMessage(): error, errno=" << errno ); + return -1; // another error + } + totalLength+=writeNow; + + if( totalLength==buffSize ) + break; // THE END + } + + LEAVE( "writeWholeMessage() -> " << totalLength ); + return totalLength; +} + + +long infCount=0; // for debug only + +// ServerComm::informRasMGR: +// send message code to rasmgr (e.g., "still alive"), incl socket management +// return code: +// none, but function does exit() on error 8-() +void ServerComm::informRasMGR( int what ) +{ + ENTER( "ServerComm::informRasMGR, what=" << what ); + TALK( "Informing dispatcher " << rasmgrHost << " port=" << rasmgrPort << " that server is available" ); + //what: 0 - going down + // 1 - available + // 2 - regular signal + + if (what == SERVER_AVAILABLE) + accessControl.resetForNewClient(); + + struct protoent* getprotoptr = getprotobyname("tcp"); + struct hostent *hostinfo = gethostbyname(rasmgrHost); + if (hostinfo==NULL) + { + cerr<< serverName<<": informRasMGR: cannot locate RasMGR host "<h_addr; + + int sock; // socket for rasmgr communication + bool ok=false; // did we get a socket? + + // FIXME: This is _extremely_ ugly. Should be something like + // 10 retries, with exponentially growing waits, such as 0, 1ms, 2ms, 4ms, ... + // have prepared defs, but code is the original one. -- PB 2003-nov-15 + // long maxRetry = NUM_RETRIES_SERVER_ALIVE; + // #define NUM_RETRIES_SERVER_ALIVE 10 + + long retry =0; + long maxRetry=10000000; // ten millions! + long talkInterval=100000; + // creating socket + for(retry=0;retryp_proto); + TALK( "Socket=" << sock << " protocol(tcp)=" << getprotoptr->p_proto ); + + if(sock<0) + { + // if ( (retry%talkInterval) == 0) + { + cerr << "Error in server '" << serverName << "': cannot open socket to rasmgr, (" << strerror(errno) << ')' << " still retrying..." << endl; + TALK( "ServerComm::informRasMGR: cannot open socket to RasMGR: " << strerror(errno) << "; retry " << retry << " of " << maxRetry ); + RMInit::logOut << "Error: cannot open socket to rasmgr, (" << strerror(errno) << ')'<" << result ); + if (result < 0) + { + // if( (retry%talkInterval) == 0) + { + cerr << "Error in server '" << serverName << "': Connection to rasmgr failed (still retrying): "<::iterator iter; + + iter = clientTbl.begin(); + while( iter != clientTbl.end() && (*iter)->clientId != clientId ) + { + TALK( " inspecting entry with clientID " << (*iter)->clientId ); + iter++; + } + + if( iter != clientTbl.end() && clientId && (*iter)->clientId == clientId ) + { + returnValue = *iter; + + // Valid entry was found, so increase the number of current users and + // reset the client's lastActionTime to now. + + (*iter)->currentUsers++; + (*iter)->lastActionTime = time( NULL ); + TALK( "valid entry found, current users now: " << (*iter)->currentUsers ); + } + } + + // this output will be done lateron, in the caller: + // if( returnValue == 0 ) + // RMInit::logOut << "Error: client not registered." << endl; + + // this trick did not work, broke the HTTP server + // if(isHttpServer==false ) uniqueClientContext = returnValue; + +#ifdef RMANDEBUG + ServerComm::printServerStatus( RMInit::logOut ); // pretty verbose +#endif + + LEAVE( "ServerComm::getClientContext()" ); + return returnValue; +} + +void +ServerComm::clientEndRequest() +{ + ENTER( "ServerComm::clientEndRequest()" ); + +#ifdef RMANDEBUG + printServerStatus( RMInit::dbgOut ); // pretty verbose +#endif + + // this trick did not work, broke the HTTp server + + // if(isHttpServer==false && uniqueClientContext != NULL) + // uniqueClientContext->endRequest(); + + LEAVE( "ServerComm::clientEndRequest()" ); +} + +/************************************************************************* + * Method name...: printServerStatus( ) + ************************************************************************/ +void +ServerComm::printServerStatus( ostream& s ) +{ + unsigned long currentTime = time(NULL); + + s << "Server state information at " << endl; // << ctime((time_t*)¤tTime) << endl; + s << " Inactivity time out of clients.: " << clientTimeout << " sec" << endl; + s << " Server management interval.....: " << garbageCollectionInterval << " sec" << endl; + s << " Transaction active.............: " << ( transactionActive ? "yes" : "no" ) << endl; + s << " Max. transfer buffer size......: " << maxTransferBufferSize << " bytes" << endl; + s << " Next available cliend id.......: " << clientCount+1 << endl; + s << " No. of client table entries....: " << clientTbl.size() << endl << endl; + + if( !clientTbl.empty() ) + { + list::iterator iter; + + // display contents of client table + s << "client table dump---------------------------------------------" << endl; + for( iter = clientTbl.begin(); iter != clientTbl.end(); iter++ ) + { + if (*iter==NULL) + { + s << "Error: null context found." << endl; + RMInit::logOut << "Error: null context found." << endl; + continue; + } + + s << "Client ID : " << (*iter)->clientId << endl + << "Current Users : " << (*iter)->currentUsers << endl + << "Client location: " << (*iter)->clientIdText << endl + << "User name : " << (*iter)->userName << endl + << "Database in use: " << (*iter)->baseName << endl + << "Creation time : " << endl // ctime((time_t*)&(*iter)->creationTime) + << "Last action at : " << endl // ctime((time_t*)&(*iter)->lastActionTime) + << "MDD collection : " << (*iter)->transferColl << endl + << "MDD iterator : " << (*iter)->transferCollIter << endl + << "Current PersMDD: " << (*iter)->assembleMDD << endl + << "Current MDD : " << (*iter)->transferMDD << endl + << "Tile vector : " << (*iter)->transTiles << endl + << "Tile iterator : " << (*iter)->tileIter << endl + << "Block byte cntr: " << (*iter)->bytesToTransfer << endl << endl; + } + s << "end client table dump-----------------------------------------" << endl; + + } + + /* + s << "memory map----------------------------------------------------" << endl; + + // memorymap(1); + + struct mallinfo meminfo = mallinfo(); + + s << "space in arena : " << meminfo.arena << endl; + s << "number of small blocks : " << meminfo.smblks << endl; + s << "number of ordinary blocks : " << meminfo.ordblks << endl; + s << "space in free ordinary blocks : " << meminfo.fordblks << endl; + s << "space in used ordinary blocks : " << meminfo.uordblks << endl; + + s << "additional space from last call: " << meminfo.uordblks - memUsed << endl; + + memUsed = meminfo.uordblks; + + s << "end memory map------------------------------------------------" << endl << endl; +*/ +} + + + +/************************************************************************* + * Method name...: getServerStatus( ) + ************************************************************************/ +void +ServerComm::getServerStatus( ServerStatRes& returnStruct ) +{ + returnStruct.inactivityTimeout = clientTimeout; + returnStruct.managementInterval = garbageCollectionInterval; + returnStruct.transactionActive = transactionActive; + returnStruct.maxTransferBufferSize = maxTransferBufferSize; + returnStruct.nextClientId = clientCount+1; + returnStruct.clientNumber = clientTbl.size(); + + if( !clientTbl.empty() ) + { + list::iterator iter; + int i; + + returnStruct.clientTable.clientTable_len = clientTbl.size(); + returnStruct.clientTable.clientTable_val = (RPCClientEntry*) mymalloc( sizeof(RPCClientEntry) * clientTbl.size() ); + + for( iter = clientTbl.begin(), i=0; iter != clientTbl.end(); iter++, i++ ) + { + returnStruct.clientTable.clientTable_val[i].clientId = (*iter)->clientId; + returnStruct.clientTable.clientTable_val[i].clientIdText = strdup( (*iter)->clientIdText ); + returnStruct.clientTable.clientTable_val[i].userName = strdup( (*iter)->userName ); + returnStruct.clientTable.clientTable_val[i].baseName = strdup( (*iter)->baseName ); + returnStruct.clientTable.clientTable_val[i].creationTime = (*iter)->creationTime; + returnStruct.clientTable.clientTable_val[i].lastActionTime = (*iter)->lastActionTime; + returnStruct.clientTable.clientTable_val[i].transferColl = (unsigned long)((*iter)->transferColl); + returnStruct.clientTable.clientTable_val[i].transferIter = (unsigned long)((*iter)->transferCollIter); + returnStruct.clientTable.clientTable_val[i].assembleMDD = (unsigned long)((*iter)->assembleMDD); + returnStruct.clientTable.clientTable_val[i].transferMDD = (unsigned long)((*iter)->transferMDD); + returnStruct.clientTable.clientTable_val[i].transTiles = (unsigned long)((*iter)->transTiles); + returnStruct.clientTable.clientTable_val[i].tileIter = (unsigned long)((*iter)->tileIter); + returnStruct.clientTable.clientTable_val[i].bytesToTransfer = (*iter)->bytesToTransfer; + } + } + + struct mallinfo meminfo = mallinfo(); + + returnStruct.memArena = meminfo.arena; + returnStruct.memSmblks = meminfo.smblks; + returnStruct.memOrdblks = meminfo.ordblks; + returnStruct.memFordblks = meminfo.fordblks; + returnStruct.memUordblks = meminfo.uordblks; +} + + +/************************************************************************* + * Method name...: addClientTblEntry( ClientTblElt *context ) + ************************************************************************/ +void +ServerComm::addClientTblEntry( ClientTblElt *context ) throw ( r_Error ) +{ + ENTER( "addClientTblEntry()" ); + + if (context==NULL) + { + RMInit::logOut << "Error: ServerComm::addClientTblEntry(): client context is NULL." << endl; + throw r_Error( r_Error::r_Error_RefNull ); + } + + clientTbl.push_back( context ); + +#ifdef RMANDEBUG + ServerComm::printServerStatus( RMInit::logOut ); // quite verbose +#endif + + LEAVE( "addClientTblEntry()" ); +} + + +/************************************************************************* + * Method name...: deleteClientTblEntry( unsigned long clientId ) + ************************************************************************/ +unsigned short +ServerComm::deleteClientTblEntry( unsigned long clientId ) +{ + ENTER( "deleteClientTblEntry( " << clientId << " )" ); + + unsigned short returnValue = 0; + + ClientTblElt* context = getClientContext( clientId ); + + if( !context ) + { + TALK( "Warning: in ServerComm::deleteClientTblEntry(): null context, " << "client " << clientId << " not found." ); + return 1; // desired client id was not found in the client table + } + + if( context->currentUsers > 1 ) + { + // In this case, the client table entry was under use before our getClientContext() call. + context->release(); + TALK( "Client context of user "<transaction.abort(); + transactionActive = 0; + } + + // close the database if it isn't already closed + // (e.g. after connection breakdowns) + if( strcmp( context->baseName, "none" ) != 0 ) + { + RMInit::logOut << "closing database..." << RMInit::logOut.flush(); + + context->database.close(); + + // reset database name + delete[] context->baseName; + context->baseName = new char[5]; + strcpy( context->baseName, "none" ); + } + +#ifdef RMANDEBUG + ServerComm::printServerStatus( RMInit::logOut ); // can be pretty verbose +#endif + + // remove the entry from the client table + list::iterator iter; + for( iter=clientTbl.begin(); iter != clientTbl.end() && (*iter)->clientId != clientId; iter++ ) + ; + if ( iter != clientTbl.end() ) + clientTbl.erase( iter ); + + // delete the client table entry data itself + // (delete is controlled by the destructor of the ClientTblElt object) + delete context; + + TALK( "client table now has " << clientTbl.size() << " entries." ); + + LEAVE( "deleteClientTblEntry()" ); + return returnValue; +} + +/************************************************************************* + * Method name...: ServerComm::getExtendedErrorInfo() + ************************************************************************/ + +const char * +ServerComm::getExtendedErrorInfo() +{ + if(errorText == NULL) + return "(no error info)"; + + return errorText; +} + +/************************************************************************* + * Method name...: ServerComm::getExtendedErrorInfo() + ************************************************************************/ + +void ServerComm::setExtendedErrorInfo(const char *newErrorText) +{ + clearExtendedErrorInfo(); + + errorText = new char [strlen(newErrorText)+1]; + strcpy(errorText,newErrorText); +} + + +/************************************************************************* + * Method name...: ServerComm::getExtendedErrorInfo() + ************************************************************************/ + +void ServerComm::clearExtendedErrorInfo() +{ + if(errorText) delete[] errorText; + + errorText = NULL; +} + +/************************************************************************* + * Method name...: ClientTblElt( const char* clientText, + * unsigned long client ) (constructor) + ************************************************************************/ +ServerComm::ClientTblElt::ClientTblElt( const char* clientText, unsigned long client ) + : clientId( client ), + currentUsers(0), + clientIdText(0), + userName(0), + baseName(0), + creationTime(0), + lastActionTime(0), + transferFormat(r_Array), + transferFormatParams(0), + exactFormat(1), + storageFormat(r_Array), + storageFormatParams(0), + encodedData(0), + encodedSize(0), + totalRawSize(0), + totalTransferedSize(0), + transferColl(0), + transferCollIter(0), + transferData(0), + transferDataIter(0), + assembleMDD(0), + transferMDD(0), + transTiles(0), + tileIter(0), + deletableTiles(0), + bytesToTransfer(0), + persMDDCollections(0), + taTimer(0), + transferTimer(0), + evaluationTimer(0), + clientParams(0) +{ + ENTER( "ServerComm::ClientTblElt::ClientTblElt( clientText=" << (clientText?clientText:"(null)") << ", client=0x" << hex << client << dec << " )" ); + + creationTime = time( NULL ); + + clientIdText = new char[strlen(clientText)+1]; + strcpy( clientIdText, clientText ); + + baseName = new char[5]; + strcpy( baseName, "none" ); + + userName = new char[8]; + strcpy( userName, "unknown" ); + + clientParams = new r_Parse_Params(); + clientParams->add("exactformat", &exactFormat, r_Parse_Params::param_type_int); + + LEAVE( "ServerComm::ClientTblElt::ClientTblElt()" ); +} + + +ServerComm::ClientTblElt::~ClientTblElt() +{ + releaseTransferStructures(); + + // delete the clientIdText member + delete[] clientIdText; + // delete the baseName member + delete[] baseName; + // delete user name + delete[] userName; + // delete transfer format parameters + if (transferFormatParams != NULL) + delete [] transferFormatParams; + + if (storageFormatParams != NULL) + delete [] storageFormatParams; + + if(clientParams) + delete clientParams; +} + + +void +ServerComm::ClientTblElt::release() +{ + if( currentUsers == 0 ) + RMInit::logOut << "Warning: releasing a non-active client." << endl; + + currentUsers--; + lastActionTime = time( NULL ); +} + +void +ServerComm::ClientTblElt::endRequest() +{ + if(currentUsers != 0) + RMInit::logOut << "Warning: Client ended request without releasing context. Forcing release now." << endl; + + currentUsers=0; + lastActionTime = time( NULL ); +} + +void +ServerComm::ClientTblElt::releaseTransferStructures() +{ + ENTER( "ServerComm::ClientTblElt::releaseTransferStructures()" ); + RMDBGENTER(2, RMDebug::module_servercomm, "ServerComm::ClientTblElt", "releaseTransferStructures()") + // delete the transfer iterator + + if( transferCollIter ) + { + RMDBGMIDDLE(2, RMDebug::module_servercomm, "ServerComm::ClientTblElt", "release transferCollIter" ) + delete transferCollIter; + transferCollIter = 0; + } + + // delete transfer data + if( transferData ) + { + RMDBGMIDDLE(2, RMDebug::module_servercomm, "ServerComm::ClientTblElt", "release transferData" ) + + QtNode::QtDataList::iterator dataIter; + + // delete list elements + for( dataIter=transferData->begin(); dataIter!=transferData->end(); dataIter++ ) + if( *dataIter ) + { + // Note: The following consistency check does not hold for the case when data objects occur + // more than once in the result set (e.g., constants). + + // Consistency Check: should be the last reference. + // if( (*dataIter)->getRefNo() > 1 ) + // { + // RMInit::logOut << endl << "Internal error in releaseTransferStructures: references left, object " << RMInit::logOut.flush(); + // (*dataIter)->printStatus( RMInit::logOut ); + // RMInit::logOut << endl; + // } + + // Just tupel elements which are not further referenced are deleted. + if (*dataIter) + { + (*dataIter)->deleteRef(); + (*dataIter) = 0; + } + } + delete transferData; + transferData = 0; + } + + // delete the transfer collection + // the transferData will check objects because of the bugfix. therefore the objects may deleted only after the check. + if( transferColl ) + { + RMDBGMIDDLE(2, RMDebug::module_servercomm, "ServerComm::ClientTblElt", "release transferColl" ) + transferColl->releaseAll(); + delete transferColl; + transferColl = 0; + } + + // delete transfer data iterator + if( transferDataIter ) + { + RMDBGMIDDLE(2, RMDebug::module_servercomm, "ServerComm::ClientTblElt", "release transferDataIter" ) + delete transferDataIter; + transferDataIter = 0; + } + + // delete the temporary PersMDDObj + if( assembleMDD ) + { + RMDBGMIDDLE(2, RMDebug::module_servercomm, "ServerComm::ClientTblElt", "release assembleMDD" ) + delete assembleMDD; + assembleMDD = 0; + } + + // delete the transfer MDDobj + if( transferMDD ) + { + RMDBGMIDDLE(2, RMDebug::module_servercomm, "ServerComm::ClientTblElt", "release transferMDD" ) + delete transferMDD; + transferMDD = 0; + } + + // vector< Tile* >* transTiles; + if( transTiles ) + { + RMDBGMIDDLE(2, RMDebug::module_servercomm, "ServerComm::ClientTblElt", "release transTiles" ) + // Tiles are deleted by the MDDObject owing them. + // release( transTiles->begin(), transTiles->end() ); + delete transTiles; + transTiles = 0; + } + + // vector< Tile* >::iterator* tileIter; + if( tileIter ) + { + RMDBGMIDDLE(2, RMDebug::module_servercomm, "ServerComm::ClientTblElt", "release tileIter" ) + delete tileIter; + tileIter = 0; + } + + // delete deletable tiles + if( deletableTiles ) + { + RMDBGMIDDLE(2, RMDebug::module_servercomm, "ServerComm::ClientTblElt", "release deletableTiles" ) + + vector::iterator iter; + + for( iter=deletableTiles->begin(); iter!=deletableTiles->end(); iter++ ) + if( *iter ) + delete *iter; + + delete deletableTiles; + deletableTiles = 0; + } + + // delete persistent MDD collections + if( persMDDCollections ) + { + RMDBGMIDDLE(2, RMDebug::module_servercomm, "ServerComm::ClientTblElt", "release persMDDCollections" ) + + vector::iterator collIter; + + for( collIter=persMDDCollections->begin(); collIter!=persMDDCollections->end(); collIter++ ) + if( *collIter ) + { + RMDBGMIDDLE(2, RMDebug::module_servercomm, "ServerComm::ClientTblElt", "before PersMDDColl::releaseAll()" ) + (*collIter)->releaseAll(); + RMDBGMIDDLE(2, RMDebug::module_servercomm, "ServerComm::ClientTblElt", "after PersMDDColl::releaseAll()" ) + delete *collIter; + } + + delete persMDDCollections; + persMDDCollections = 0; + } + + // transfer compression + if (encodedData != NULL) + { + free(encodedData); + encodedData = NULL; encodedSize = 0; + } + +#ifdef RMANBENCHMARK + // Attention: taTimer is deleted in either commitTA() or abortTA(). + + if( evaluationTimer ) delete evaluationTimer; + evaluationTimer = 0; + + if( transferTimer ) delete transferTimer; + transferTimer = 0; +#endif + RMDBGEXIT(2, RMDebug::module_servercomm, "ServerComm::ClientTblElt", "releaseTransferStructures()") + LEAVE( "ServerComm::ClientTblElt::releaseTransferStructures()" ); +} + + +/****************************************************************************************** +*** This class shouldn't be here, later it will be put in its own file +******************************************************************************************/ + +// learned from license.cc +#ifdef LINUX +extern "C" { + extern char *strptime __P ((__const char *__s, __const char *__fmt, struct tm *__tp)); +} +#endif + +AccessControl accessControl; + + +AccessControl::AccessControl() +{ + initDeltaT=0; + resetForNewClient(); +} + +AccessControl::~AccessControl() +{ +} + +void AccessControl::setServerName(const char *serverName) +{ + strcpy(this->serverName,serverName); +} + +void AccessControl::initSyncro(const char *syncroString) +{ + struct tm brokentime; + strptime(syncroString,"%d:%m:%Y:%H:%M:%S",&brokentime); + initDeltaT= difftime (time(NULL) ,mktime(&brokentime) ); + // cout<<"DeltaT="< " << CAPABILITY_REFUSED ); + return CAPABILITY_REFUSED; + } + + *digest=0; + digest++;digest++; + digest[32]=0; + TALK( "Digest="< " << CAPABILITY_REFUSED ); + return CAPABILITY_REFUSED; + } + + char *rights=strstr(capaQ,"$E")+2; + char *timeout=strstr(capaQ,"$T")+2; + char *cServerName=strstr(capaQ,"$N")+2; + // end of cServername is $D, $->0 by digest + + struct tm brokentime; + strptime(timeout,"%d:%m:%Y:%H:%M:%S",&brokentime); + double DeltaT= difftime (mktime(&brokentime),time(NULL) ); + + //for the moment, DEC makes trouble + // if(DeltaT < initDeltaT) return CAPABILITY_REFUSED; //!!! Capability too old + // cout<<"DeltaT="< server name doesn't match, got: " << cServerName << ", need: " << serverName << " -> " << CAPABILITY_REFUSED ); + return CAPABILITY_REFUSED; //!!! Call is not for me + } + + okToRead = false; // looks like a 'true' never gets reset: -- PB 2006-jan-02 + okToWrite = false; // -dito- + for(int i=0;*rights!='$' && *rights && i<2; rights++,i++) + { + //We only have 2 rights defined now + if(*rights=='R') + okToRead = true; + if(*rights=='W') + okToWrite= true; + } + + weHaveClient=true; + + TALK( "capability crunched: digest=" << digest << ", rights=" << rights << ", timeout=" << timeout << "(remaining time: " << DeltaT << "), cServerName=" << cServerName << ", okToRead=" << okToRead << ", okToWrite=" << okToWrite << "" ); + + LEAVE( "AccessControl::crunchCapability() -> 0 (ok)" ); + return 0; // OK for now +} + +int AccessControl::messageDigest(const char *input,char *output,const char *mdName) +{ + EVP_MD_CTX mdctx; + const EVP_MD *md; + unsigned int md_len, i; + unsigned char md_value[100]; + + OpenSSL_add_all_digests(); + + md = EVP_get_digestbyname(mdName); + + if(!md) + return 0; + + EVP_DigestInit(&mdctx, md); + EVP_DigestUpdate(&mdctx,input, strlen(input)); + EVP_DigestFinal(&mdctx, md_value, &md_len); + + for(i = 0; i < md_len; i++) + sprintf(output+i+i,"%02x", md_value[i]); + + return strlen(output); +} + +void AccessControl::wantToRead() +{ + if(okToRead==false) + { + TALK( "AccessControl::wantToRead(): error: no permission for read operation." ); + throw r_Eno_permission(); //r_Error(NO_PERMISSION_FOR_OPERATION); + } +} + +void AccessControl::wantToWrite() +{ + if(okToWrite==false) + { + TALK( "AccessControl::wantToWrite(): error: no permission for write operation." ); + throw r_Eno_permission(); //r_Error(NO_PERMISSION_FOR_OPERATION); + } + +} + + diff --git a/servercomm/servercomm.hh b/servercomm/servercomm.hh new file mode 100644 index 0000000..2d34e69 --- /dev/null +++ b/servercomm/servercomm.hh @@ -0,0 +1,1120 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see . +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see +* or contact Peter Baumann via . +/ +/** + * INCLUDE: servercomm.hh + * + * MODULE: servercomm + * CLASS: ServerComm + * + * COMMENTS: + * None +*/ + +#ifndef _SERVERCOMM_ +#define _SERVERCOMM_ + +// Put it in front of any typedef bool ... because o2 is using bool as a variable. +// #include "o2template_CC.hxx" + +#include + +#include +#include "raslib/error.hh" +#include "raslib/oid.hh" +#include "raslib/minterval.hh" + +#include "reladminif/adminif.hh" +#include "reladminif/databaseif.hh" +#include "reladminif/transactionif.hh" +#include "relcatalogif/basetype.hh" + +#include "servercomm/callbackmgr.hh" + +#ifndef _RPCIF_ + #define _RPCIF_ + #include "clientcomm/rpcif.h" +#endif + +#ifdef DECALPHA +#include +#endif + +// forward declarations +class QtData; +class MDDObj; +class MDDCollIter; +class MDDColl; +class RMTimer; +class r_Parse_Params; + +//@ManMemo: Module: {\bf servercomm} + +/*@Doc: + The class servercomm describes the one and only server communication object + that can exist in a RasDaMan RPC server. It manages listening for client and + maps incoming calls to the respective remote procedures (which reside in the + file manager.cc). These remote procedures are global functions + which mainly concern with RPC call processing and finally call the methods + of this servercomm class to forward client requests. +*/ + +class ServerComm +{ + public: + + /// the class defines an entry of the client table + class ClientTblElt + { + public: + /// default constructor + ClientTblElt( const char* clientIdText, unsigned long clientId ); + /** + Default constructor that takes the information to be placed in the + clientIdText field of the client table entry and the unique ID to + be placed in the clientId field. + */ + + /// destructor + ~ClientTblElt(); + + /// release client context + void release(); + /** + Releasing the client context means to decrease the currentUsers counter + and to update lastActionTime. + */ + + void endRequest(); + + /// releases transfer collection/iterator + void releaseTransferStructures(); + /** + The method releases transfer collection and iterator. As the collection is a + persistent one, care has to be taken that creation and deletion is done + within the same transaction. + */ + + /// unique client identification assigned by the server + unsigned long clientId; + + /// counter indicating the number of current users + unsigned int currentUsers; + + /// binding information about the client (IP address and TCP port number) + char* clientIdText; + + /// Name of the client user name (if available) + char* userName; + + /// Name of the actual database (if one is open) + char* baseName; + + /// time when the database was opened (for curiosity purposes) + unsigned long creationTime; + + /// time of the client's last action (for garbage collection purposes) + unsigned long lastActionTime; + + /// convert raw array data to this data format before transfer + r_Data_Format transferFormat; + char* transferFormatParams; + /// send data to client in the exact transfer format + int exactFormat; + /// store array data in this data format in the database + r_Data_Format storageFormat; + char* storageFormatParams; + + /// the tile data converted into the transfer format, if required + void* encodedData; + unsigned long encodedSize; + /// for establishing the compression ratio + unsigned long totalRawSize; + unsigned long totalTransferedSize; + + /// pointer to an MDD collection + MDDColl* transferColl; + /** + For collection of MDD constants with an update query. + */ + + /// pointer to an iterator for collection transferColl + MDDCollIter* transferCollIter; + + /// pointer to the query result which is currently in transfer + std::vector* transferData; + /** + For the result of the last query (NULL if the result is completely delivered to the client). + */ + + /// point to an iterator for transfer data + std::vector::iterator* transferDataIter; + + /// pointer to a persistent MDD object for tile based transfers + MDDObj* assembleMDD; + + /// pointer to an MDD object for tile base transfer + MDDObj* transferMDD; + + /// std::vector storing tiles of actual MDD for transfer + std::vector< Tile* >* transTiles; + + /// iterator for the std::vector above + std::vector< Tile* >::iterator* tileIter; + + /// std::vector storing pointers to transient tiles + std::vector< Tile* >* deletableTiles; + /** + The tiles referenced by these pointers are border tiles dynamically created in getNextMDD(). + They do not belong to any MDD object, and, therefore, they have to be deleted explicitly. + */ + + /// bytes to transfer in actual tile (valid only if tile is larger than {\tt MAXTRANSBYTES}) + unsigned long bytesToTransfer; + + /// std::vector of persistent MDD collections in use + std::vector< MDDColl* >* persMDDCollections; + + /// object representing the actual database + DatabaseIf database; + + /// object representing the actual transaction (only one at a time possible) + TransactionIf transaction; + + /// pointer to a timer for recording transaction time + RMTimer* taTimer; + + /// pointer to a timer for recording transfer time + RMTimer* transferTimer; + + /// pointer to a timer for recording evaluation time + RMTimer* evaluationTimer; + + /// parameter object + r_Parse_Params *clientParams; + + private: + /// empty private definition prevents of using the copy constructor + ClientTblElt( const ClientTblElt& ){}; + }; + + /// default constructor + ServerComm(); + + /// constructor getting the client time out and the time interval for management routines, together with listen port, rasmgr host and port and the server name + ServerComm( unsigned long timeOut, unsigned long managementInterval , unsigned long listenPort, char* rasmgrHost, unsigned int rasmgrPort, char* serverName); + + /// destructor + virtual ~ServerComm(); + + /// forces the server to listen for client calls + virtual void startRpcServer() throw( r_Error ); + + /// stops the server + virtual void stopRpcServer(); + + // informs RasMGR: + void informRasMGR(int); + #define SERVER_DOWN 0 + #define SERVER_AVAILABLE 1 + // 2 is server crushed, but it's generated by rasmgr! + // regularly signal the rasmgr that we are available + #define SERVER_REGULARSIG 3 + + /// adds an entry to the client table (used in RasServerEntry) + void addClientTblEntry( ClientTblElt *context ) throw ( r_Error ); + /** + Adds the context entry passed to the client table. + Throws an exception if context==NULL. + */ + + /// deletes an entry of the client table (must be public because it is used in the global garbage collection function) + unsigned short deleteClientTblEntry( unsigned long ClientId ); + /** + Deletes the entry of the client table corresponding to the given client id. + If no corresponding id is found, false is returned. + */ + + // quick hack function used when stopping server to abort transaction and close db + void abortEveryThingNow(); + + /// print server status with client table content to {\tt s} + virtual void printServerStatus( ostream& s=cout ); + + /// get server status + virtual void getServerStatus( ServerStatRes &returnStruct ); + + /// the client table which holds information about the calling clients + static std::list clientTbl; + + /// last used client ID (this is increased by one to get the clientId for the next client) + static unsigned long clientCount; + + /// inactivity timeout in seconds after which pending client data is deleted + const unsigned long clientTimeout; + + /// do a garbage collection every {\tt garbageCollectionInterval} seconds (ONC RPC only) + const unsigned long garbageCollectionInterval; + + /// flag for active o2 transaction (stores the clientID of the owner of the active transaction, or 0 if none open) + unsigned long transactionActive; + + /// memory used by malloc in ordinary blocks (set in dumpClientTable) + long memUsed; + + /// stores a pointer to the actual servercomm object, only one can exist at a time + static ServerComm* actual_servercomm; + + /** + the callback manager object for garbage collection, license check, ... + Always instantiated with default size, resized on startup if required. + */ + CallBackManager callback_mgr; + + //@Man: Communication methods + //@{ + /// + + /// process the client's alive signal + virtual unsigned short aliveSignal( unsigned long client ); + /** + The method take the alive signal of a client and updates the last action time. + + Return values: + \begin{tabular}{lll} + 0 && operation was successfull\\ + 1 && client context not found\\ + \end{tabular} + */ + + /// + /// open database + virtual unsigned short openDB( unsigned long callingClientId, const char* dbName, const char* userName ); + /** + The method opens the database with {\tt dbName}. The return value means the following: + + \begin{tabular}{lll} + 0 && database successfully opened\\ + 1 && client context not found\\ + 2 && database does not exist\\ + 3 && database is already open\\ + \end{tabular} + */ + + /// close current database + virtual unsigned short closeDB( unsigned long callingClientId ); + /** + The return value has the following meaning: + \begin{tabular}{lll} + 0 && operation was successful\\ + 1 && client context not found\\ + \end{tabular} + */ + + /// create a database + virtual unsigned short createDB( char* name ); + + /// destroy a database + virtual unsigned short destroyDB( char* name ); + + /// + /// open transaction + virtual unsigned short beginTA( unsigned long callingClientId, unsigned short readOnly=0 ); + /** + The return value has the following meaning: + \begin{tabular}{lll} + 0 && operation was successful\\ + 1 && client context not found\\ + 2 && other transaction already active\\ + \end{tabular} + */ + + + /// commit current transaction + virtual unsigned short commitTA( unsigned long callingClientId ); + /** + The return value has the following meaning: + \begin{tabular}{lll} + 0 && operation was successful\\ + 1 && client context not found\\ + \end{tabular} + */ + + + /// abort current transaction + virtual unsigned short abortTA( unsigned long callingClientId ); + /** + The return value has the following meaning: + \begin{tabular}{lll} + 0 && operation was successful\\ + 1 && client context not found\\ + \end{tabular} + */ + + /// is transaction open currently? + /** + The return value has the following meaning: + \begin{tabular}{lll} + true && a transaction is open\\ + false && no transaction is open\\ + \end{tabular} + */ + + virtual bool isTAOpen( unsigned long callingClientId ); + + /// + /// executes a retrieval query and prepares the result for transfer with \Ref{getNextMDD}. + virtual unsigned short executeQuery( unsigned long callingClientId, const char* query, ExecuteQueryRes &returnStructure ); + /** + Executes a query and puts the result in the actual transfer collection. + The first parameter is the unique client id + for which the query should be executed. The second parameter is the + query itself represented as a string. + + Return values + \begin{tabular}{lll} + 0 && operation was successful - result collection holds MDD elements\\ + 1 && operation was successful - result collection holds non-MDD elements\\ + 2 && operation was successful - result collection has no elements\\ + 3 && client context not found\\ + 4 && parse errror\\ + 5 && execution error\\ + \end{tabular} + + Communication protocol (return value = 0) + \begin{tabular}{lll} + \Ref{executeQuery} && \\ + -> && \Ref{getNextMDD} \\ + && -> && \Ref{getNextTile} \\ + && && : \\ + && :\\ + \Ref{endTransfer} \\ + \end{tabular} + + Communication protocol (return value = 1) + \begin{tabular}{lll} + \Ref{executeQuery} && \\ + -> && \Ref{getNextElement} \\ + && :\\ + \Ref{endTransfer} \\ + \end{tabular} + */ + + /// + /// get the domain of the next MDD of the actual transfer collection + virtual unsigned short getNextMDD( unsigned long callingClientId, + r_Minterval &mddDomain, + char* &typeName, + char* &typeStructure, + r_OId &oid, + unsigned short ¤tFormat ); + /** + The Method gets the domain of the next MDD of the actual transfer collection. + The first parameter is the unique client id. The second parameter returns the + domain of the MDD to be transfered. {\tt typeName} returns the name of the + MDD type and its structure. + Transfer of MDD data is tile-based using the method \Ref{getNextTile}. + + Return values: + \begin{tabular}{lll} + 0 && operation was successful, at least one MDD is left in the transfer collection\\ + 1 && nothing left in the transfer collection\\ + 2 && client context not found, no tiles in the MDD object, no actual transfer collection \\ + \end{tabular} + */ + + /// get the next scalar element in the actual transfer collection. + virtual unsigned short getNextElement( unsigned long callingClientId, + char* &buffer, + unsigned int &bufferSize ); + /** + The Method gets the next non-MDD element in the actual transfer collection. + The first parameter is the unique client id. The second parameter returns a + pointer to the memory occupied by the next element and the third one delivers + the size of the buffer. + + Return values: + \begin{tabular}{lll} + 0 && operation was successful, at least one element is left in the transfer collection\\ + 1 && operation succesful, nothing left in the transfer collection\\ + 2 && client context not found, no tiles in the MDD object, no actual transfer collection \\ + \end{tabular} + */ + + /// get an MDD by OId + virtual unsigned short getMDDByOId( unsigned long callingClientId, + r_OId &oid, + r_Minterval &mddDomain, + char* &typeName, + char* &typeStructure, + unsigned short ¤tFormat ); + /** + The Method gets an MDD by OId {\tt oid}. If the MDD is found, it is initialized as transfer + object and can be picked up by \Ref{getNextTile} calls (tile-based transfer). + + Additionally, the method returns domain, type name, and type structure of the found MDD + object by reference parameters. + + Return values: + \begin{tabular}{lll} + 0 && operation was successful\\ + 1 && client context not found\\ + 2 && object with this oid not found\\ + 3 && object has no tiles + \end{tabular} + + Communication protocol + \begin{tabular}{lll} + \Ref{getMDDByOId} \\ + -> && \Ref{getNextTile} \\ + && : \\ + \Ref{endTransfer} \\ + \end{tabular} + */ + + /// get next tile of the actual MDD of the actual transfer collection + virtual unsigned short getNextTile( unsigned long callingClientId, + RPCMarray** rpcMarray ); + /** + The Method gets the next tile of the actual MDD of the actual transfer collection. + The first parameter is the unique client id. The second parameter is the + RPC representation of the Marray representing the tile. If a tile is too large to be + transferred in one piece, the data is split. To get the rest of the data, consecutively + use this method. + + Return values: + \begin{tabular}{lll} + 0 && operation was successful, no further MDDs are left\\ + 1 && operation was successful, at least one MDD is left in the transfer collection\\ + 2 && operation was successful, at least one tile is left in the actual MDD\\ + 3 && operation was successful, at least one block is left in the actual tile\\ + 4 && client context not found, no tiles in the MDD object, no actual transfer collection \\ + && or nothing left in the collection\\ + \end{tabular} + + Examples of valid return value chains: + \begin{itemize} + \item To be transferred: 1 MDD consisting of 1 tile (which is too large)\\ + \begin{verbatim} + 3 ->...-> 3 -> 0 + \end{verbatim} + \item To be transferred: 1 MDD consisting of 2 tiles (the first is too large)\\ + \begin{verbatim} + 3 ->...-> 3 -> 2 -> 0 + |--------------| | + 1st tile 2nd tile + \end{verbatim} + \item To be transferred: 2 MDDs, each consisting of 1 tile (none too large)\\ + \begin{verbatim} + 1 -> 0 + \end{verbatim} + \item To be transferred: 3 MDDs, the first (A) consisting of 1 tile (not too large),\\ + the second (B) consisting of 2 tiles (B1, B2, of which the first is too large), + the third (C) consisting of 2 tiles (C1, C2, of which the second is too large), + \begin{verbatim} + 1 -> 3 ->...-> 3 -> 2 -> 1 -> 2 -> 3 ->...-> 3 -> 0 + | |--------------| | | |--------------| + | B1 B2 C1 C2 + | |-------------------| |-------------------| + A B C + \end{verbatim} + \end{itemize} + */ + + /// process the client's alive signal + virtual unsigned short endTransfer( unsigned long client ); + /** + The method terminates a transfer session and releases all transfer structures. + + Return values: + \begin{tabular}{lll} + 0 && operation was successfull\\ + 1 && client context not found\\ + \end{tabular} + */ + + /// + /// prepares transfer of MDD constants and execution of update query + virtual unsigned short initExecuteUpdate( unsigned long callingClientId ); + /** + Return values: + \begin{tabular}{lll} + 0 && operation was successful\\ + 1 && client context not found\\ + \end{tabular} + + Communication protocol + \begin{tabular}{lll} + \Ref{initExecuteUpdate} && \\ + -> && \Ref{startInsertTransMDD} \\ + && -> && \Ref{insertTile} \\ + && && :\\ + && \Ref{endInsertMDD}\\ + && :\\ + \Ref{executeUpdate} && \\ + \end{tabular} + + Note: Method \Ref{executeUpdate} can be invoked without the \Ref{initExecuteUpdate} + prolog in case of no constant MDD objects. + */ + + /// executes an update query + virtual unsigned short executeUpdate( unsigned long callingClientId, const char* query, ExecuteUpdateRes &returnStructure ); + /** + Executes an update query. + The first parameter is the unique client id + for which the query should be executed. The second parameter is the + query itself represented as a string. + + Return values: + \begin{tabular}{lll} + 0 && operation was successful\\ + 1 && client context not found\\ + 2 && parse errror\\ + 3 && execution error\\ + \end{tabular} + */ + + /// + /// prepares an MDD (transient) for transfer of tiles + virtual unsigned short startInsertTransMDD( unsigned long callingClientId, + r_Minterval &domain, + unsigned long typeLength, + const char* typeName ); + /** + Creates an object for tile based transfer with method \Ref{insertTile}. + + The first parameter is the unique client id for which the MDD should be created. + The second parameter is the + name of the collection to insert the MDD object. The third parameter holds the + spatial domain of the following MDD object and {\tt typeLength} specifies the size of + the base type in bytes. The last one gives the type structure as string representation. + + Return values: + \begin{tabular}{lll} + 0 && operation was successful\\ + 1 && client context not found\\ + 2 && MDD type name not found\\ + 3 && MDD and its type are incompatible\\ + \end{tabular} + */ + + /// create a new persistent MDD object for tile based transfers + virtual unsigned short startInsertPersMDD( unsigned long callingClientId, + const char* collName, + r_Minterval& domain, + unsigned long typeLength, + const char* typeName, + r_OId& oid ); + /** + Creates an object for tile based transfer with method \Ref{insertTile} to be + inserted into the specified MDD collection. + + Parameters + \begin{tabular}{lll} + {\tt callingClientId} && unique client id of the calling client\\ + {\tt collName} && name of the collection to insert the MDD object\\ + {\tt domain} && spatial domain\\ + {\tt typeLength} && size of base type in bytes\\ + {\tt typeName} && type structure as string representation\\ + {\tt oid} && object identifier\\ + \end{tabular} + + Return values + \begin{tabular}{lll} + 0 && operation was successful\\ + 1 && client context not found\\ + 2 && MDD type name not found\\ + 3 && types of MDD and collection are incompatible\\ + 4 && MDD and its type are incompatible\\ + 5 && collection does not exist\\ + 6 && creation of persistent object failed\\ + \end{tabular} + + Communication protocol + \begin{tabular}{lll} + \Ref{startInsertPersMDD} && \\ + -> && \Ref{insertTile} \\ + && :\\ + \Ref{endInsertMDD} && \\ + \end{tabular} + */ + + /// insert a tile into a persistent MDD object + virtual unsigned short insertTile( unsigned long callingClientId, + int isPersistent, + RPCMarray* rpcMarray ); + /** + Inserts a tile into the current MDD object. + + Parameters + \begin{tabular}{lll} + {\tt callingClientId} && unique client id of the calling client\\ + {\tt isPersistent} && determines wheather it is a persistent or a transient tile\\ + {\tt rpcMarray} && RPC representation of the tile\\ + \end{tabular} + + Return values: + \begin{tabular}{lll} + 0 && operation was successful\\ + 1 && client context not found\\ + 2 && base type name of inserting tile is not supported\\ + \end{tabular} + */ + + + + // inserts a tile into a persistent MDD object splitting it up according to + // parameter tileSize + virtual unsigned short insertTileSplitted( unsigned long callingClientId, + int isPersistent, + RPCMarray* rpcMarray, + r_Minterval* tileSize ); + /** + Splits and inserts a tile into the current MDD object. + + Parameters + \begin{tabular}{lll} + {\tt callingClientId} && unique client id of the calling client\\ + {\tt isPersistent} && determines wheather it is a persistent or a transient tile\\ + {\tt rpcMarray} && RPC representation of the tile\\ + {\tt tileSize} && r_Minterval specifying the tile-size\\ + \end{tabular} + + Return values: + \begin{tabular}{lll} + 0 && operation was successful\\ + 1 && client context not found\\ + 2 && base type name of inserting tile is not supported\\ + \end{tabular} + */ + + /// finnishes the MDD creation and inserts the MDD into the collection + virtual unsigned short endInsertMDD( unsigned long callingClientId, + int isPersistent ); + /** + Parameters + \begin{tabular}{lll} + {\tt callingClientId} && unique client id of the calling client\\ + {\tt isPersistent} && determines wheather it is a persistent or a transient MDD\\ + \end{tabular} + */ + + /// + /// insert object into collection + virtual unsigned short insertMDD( unsigned long callingClientId, + const char* collName, + RPCMarray *rpcMarray, + const char* typeName, r_OId& oid ); + /** + Inserts an object into an MDD collection. It is transfered in one piece. + + Parameters + \begin{tabular}{lll} + {\tt callingClientId} && unique client id of the calling client\\ + {\tt collName} && name of the collection to insert the MDD object\\ + {\tt rpcMarray} && RPC representation of the MDD object\\ + {\tt typeName} && type structure as string representation\\ + {\tt oid} && object identifier\\ + \end{tabular} + + Return values: + \begin{tabular}{lll} + 0 && operation was successful\\ + 1 && client context not found\\ + 2 && MDD type name not found\\ + 3 && types of MDD and collection are incompatible\\ + 4 && MDD and its type are incompatible\\ + 5 && collection does not exist\\ + 6 && creation of persistent object failed\\ + \end{tabular} + */ + + /// + /// prepare an MDD collection for transfer with getNextMDD() + virtual unsigned short getCollByName( unsigned long callingClientId, + const char* collName, + char* &typeName, + char* &typeStructure, + r_OId &oid ); + /** + ATTENTION: This function is not used at the moment. It hast + to be adapted to transferData. + + Prepares an MDD collection for transfer with getNextMDD(). + + Parameters + \begin{tabular}{lll} + {\tt callingClientId} && unique client id of the calling client\\ + {\tt collName} && name of the collection to be got\\ + {\tt typeName} && returns name of the collection type\\ + {\tt typeStructure} && returns structure of the collection type\\ + {\tt oid} && returns oid of the collection\\ + \end{tabular} + + The first parameter is the unique client id. The second parameter is the + name of the collection to get. {\tt typeName} returns the name of the + collection type and {\tt typeStructure} its type structure. + + Return values: + \begin{tabular}{lll} + 0 && operation was successful - collection has some elements\\ + 1 && operation was successful - collection has no elements\\ + 2 && collection is not known\\ + 3 && client context not found\\ + \end{tabular} + */ + + /// prepare an MDD collection for transfer with getNextMDD() + virtual unsigned short getCollByOId( unsigned long callingClientId, + r_OId &oid, + char* &typeName, + char* &typeStructure, + char* &collName ); + /** + ATTENTION: This function is not used at the moment. It hast + to be adapted to transferData. + + Prepares an MDD collection for transfer with \Ref{getNextMDD}. + + Parameters + \begin{tabular}{lll} + {\tt callingClientId} && unique client id of the calling client\\ + {\tt oid} && oid of the collection to be got\\ + {\tt typeName} && returns name of the collection type\\ + {\tt typeStructure} && returns structure of the collection type\\ + {\tt collName} && returns name of collection\\ + \end{tabular} + + Return values: + \begin{tabular}{lll} + 0 && operation was successful - collection has some elements\\ + 1 && operation was successful - collection has no elements\\ + 2 && collection is not known\\ + 3 && client context not found\\ + \end{tabular} + */ + + /// gets oids of the collection specified by name + virtual unsigned short getCollOIdsByName( unsigned long callingClientId, + const char* collName, + char* &typeName, + char* &typeStructure, + r_OId &oid, + RPCOIdEntry* &oidTable, + unsigned int &oidTableSize ); + /** + Gets the collection of oids of the collection with {\tt collName}. + + Parameters + \begin{tabular}{lll} + {\tt callingClientId} && unique client id of the calling client\\ + {\tt collName} && name of the collection to be got\\ + {\tt typeName} && returns name of the collection type\\ + {\tt typeStructure} && returns structure of the collection type\\ + {\tt oid} && returns object identifier\\ + {\tt oidTable} && returns an array of pointers to oids\\ + {\tt oidTableSize} && returns the no of elements in the table\\ + \end{tabular} + + Return values: + \begin{tabular}{lll} + 0 && operation was successful - collection has some elements\\ + 1 && operation was successful - collection has no elements\\ + 2 && collection is not known\\ + 3 && client context not found\\ + \end{tabular} + */ + + /// gets oids of the collection specified by name + virtual unsigned short getCollOIdsByOId( unsigned long callingClientId, + r_OId &oid, + char* &typeName, + char* &typeStructure, + RPCOIdEntry* &oidTable, + unsigned int &oidTableSize, + char* &collName ); + /** + Gets the collection of oids of the collection with {\tt collName}. + + Parameters + \begin{tabular}{lll} + {\tt callingClientId} && unique client id of the calling client\\ + {\tt oid} && oid of the collection to be got\\ + {\tt typeName} && returns name of the collection type\\ + {\tt typeStructure} && returns structure of the collection type\\ + {\tt oidTable} && returns an array of pointers to oids\\ + {\tt oidTableSize} && returns the no of elements in the table\\ + {\tt collName} && returns name of collection\\ + \end{tabular} + + Return values: + \begin{tabular}{lll} + 0 && operation was successful - collection has some elements\\ + 1 && operation was successful - collection has no elements\\ + 2 && collection is not known\\ + 3 && client context not found\\ + \end{tabular} + */ + + /// + /// create new MDD collection + virtual unsigned short insertColl( unsigned long callingClientId, + const char* collName, + const char* typeName, + r_OId& oid ); + /** + Creates a new MDD collection. + + Parameters + \begin{tabular}{lll} + {\tt callingClientId} && unique client id of the calling client\\ + {\tt collName} && name of the collection to be created\\ + {\tt typeName} && name of the collection type\\ + {\tt oid} && object identifier\\ + \end{tabular} + + Return values: + \begin{tabular}{lll} + 0 && operation was successful\\ + 1 && client context not found\\ + 2 && collection type name not found\\ + 3 && collection name exists already in the db + \end{tabular} + */ + + /// + /// delete MDD collection + virtual unsigned short deleteCollByName( unsigned long callingClientId, + const char* collName ); + /** + Deletes an MDD collection. The first parameter is the unique client id + for which the collection should be deleted. The second parameter is the + name for the collection to be deleted. + + Return values: + \begin{tabular}{lll} + 0 && operation was successful\\ + 1 && client context not found\\ + 2 && collection with name does not exist\\ + \end{tabular} + */ + + /// delete object by oid + virtual unsigned short deleteObjByOId( unsigned long callingClientId, r_OId& oid ); + /** + Deletes the object with {\tt oid}. + The first parameter is the unique client id for which the object should be + deleted. + + Return values: + \begin{tabular}{lll} + 0 && operation was successful\\ + 1 && client context not found\\ + 2 && object with oid does not exist\\ + \end{tabular} + */ + + /// + /// remove object specified by oid from collection specified by name + virtual unsigned short removeObjFromColl( unsigned long callingClientId, + const char* collName, r_OId& oid ); + /** + The method removes the object with {\\t oid} from collection with {\tt collName}. + The first parameter is the unique client id for which the object should be removed. + + Return values: + \begin{tabular}{lll} + 0 && operation was successful\\ + 1 && client context not found\\ + 2 && specified collection does not exist\\ + 3 && specified object does not exist in the collection\\ + \end{tabular} + */ + + /// + /// get new object identifier + virtual unsigned short getNewOId( unsigned long callingClientId, + unsigned short objType, r_OId& oid ); + /** + Creates a new oid and gives it back by the refernce parameter {\tt oid}. + {\tt objType} determines the type of object for which that oid is allocated. The folowing + values are supported: 1 = MDD, 2 = Collection. + + Return values: + \begin{tabular}{lll} + 0 && operation was successful\\ + 1 && client context not found\\ + 2 && error while creating oid\\ + \end{tabular} + */ + + /// get type of object by oid + virtual unsigned short getObjectType( unsigned long callingClientId, + r_OId& oid, unsigned short &objType ); + /** + Determines the type of the object indicated by {\tt oid}. The type is returned by the + reference parameter {\tt objType}. The folowing types are supported: 1 = MDD, 2 = Collection. + + Return values: + \begin{tabular}{lll} + 0 && operation was successful\\ + 1 && client context not found\\ + 2 && oid not found\\ + \end{tabular} + */ + + /// get type structure of a type name + virtual unsigned short getTypeStructure( unsigned long callingClientId, + const char* typeName, + unsigned short typeType, + char* &typeStructure); + /** + Determines the type structure of the type specified by {\tt typeName}. The type + either can be a set type (typeType=1), an mdd type (typeType=2), or a base type + (typeType = 3). + + Return values: + \begin{tabular}{lll} + 0 && operation was successful\\ + 1 && client context not found\\ + 2 && type name not found\\ + \end{tabular} + */ + + /// set the data format used for transferring data to the client + virtual unsigned short setTransferMode( unsigned long callingClientId, + unsigned short format, const char* formatParams ); + /** + Sets the data format used by the server to transfer data to the client to + format which is of type r_Data_Format. + + Return values: + \begin{tabular}{lll} + 0 && operation was successful\\ + 1 && client context not found\\ + 2 && unknown or unsupported data format\\ + \end{tabular} + */ + + /// set the data format for storing data into the database + virtual unsigned short setStorageMode( unsigned long callingClientId, + unsigned short format, const char *formatParams ); + /** + return values exactly like setTransferMode() + */ + /// + //@} + + /// returns a pointer to the context of the calling client, 0 it there is no context + virtual ClientTblElt* getClientContext( unsigned long ClientId ); + /** + Returns a pointer to the context of the calling client. This is done by + searching the client table maintained by the server for the given client id. + If there is no context corresponding to the client id, 0 is returned. + + Attention: After a client context was successfully received it has to be + released using its member function release(); + */ + + // get, set and clear extended error info. + const char *getExtendedErrorInfo(); + + void setExtendedErrorInfo(const char*); + + void clearExtendedErrorInfo(); + + void clientEndRequest(); + + // constant for clientID + static const char* HTTPCLIENT; + + private: + /// copy constructor is private and therefore can not be used + ServerComm( const ServerComm& );//and then why this? : clientTimeout(3600), garbageCollectionInterval(600){;}; + + protected: + /// make sure a tile has the correct data format, converting if necessary + static int ensureTileFormat( r_Data_Format &hasFmt, r_Data_Format needFmt, + const r_Minterval &dom, const BaseType *type, + char *&data, unsigned long &size, int repack, + int owner, const char *params = NULL); + ///returns the following: + static const int ENSURE_TILE_FORMAT_OK; + static const int ENSURE_TILE_FORMAT_BAD; + + /// pointer to the actual administration interface object + AdminIf* admin; + + char *errorText; + + unsigned long listenPort; + char* rasmgrHost; + unsigned int rasmgrPort; + char* serverName; + + bool isHttpServer; + //this trick didn't work and we are out of time + // ClientTblElt *uniqueClientContext; +}; + + +/// indirect caller for rpcif_1 +void rpcif_1_caller(struct svc_req *rqstp,SVCXPRT *transp); + + +/****************************************************************************************** +*** This class shouldn't be here, later it will be put in its own file +******************************************************************************************/ + + +class AccessControl + { + public: + AccessControl(); + ~AccessControl(); + void initSyncro(const char *); + void setServerName(const char *serverName); + + void resetForNewClient(); + int crunchCapability(const char*); + /* 0 - ok + 804 - capability refused + */ + + void wantToRead(); // both throw + void wantToWrite(); + bool isClient(); + private: + int messageDigest(const char *input,char *output,const char *mdName); + double initDeltaT; + char serverName[100]; + + bool okToRead; + bool okToWrite; + bool weHaveClient; + }; +extern AccessControl accessControl; + +#include "servercomm.icc" + +#endif diff --git a/servercomm/servercomm.icc b/servercomm/servercomm.icc new file mode 100644 index 0000000..ebc1d6c --- /dev/null +++ b/servercomm/servercomm.icc @@ -0,0 +1,31 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see . +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see +* or contact Peter Baumann via . +/ +/** + * INLINE SOURCE: servercomm.icc + * + * MODULE: servercomm + * CLASS: ServerComm + * + * COMMENTS: + * None +*/ diff --git a/servercomm/servercomm2.cc b/servercomm/servercomm2.cc new file mode 100644 index 0000000..7fcbef2 --- /dev/null +++ b/servercomm/servercomm2.cc @@ -0,0 +1,4051 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see . +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see +* or contact Peter Baumann via . +/ +/** + * SOURCE: servercomm2.cc + * + * MODULE: servercomm + * CLASS: ServerComm + * + * PURPOSE: + * + * COMMENTS: + * - FIXME: catch exceptions in all operations + * - return values & their meaning see servercomm.hh + * - FIXME: "client not registered" delivers sometimes 1, sometimes 3 + * +*/ + +#include "mymalloc/mymalloc.h" + +static const char rcsid[] = "@(#)servercomm2, ServerComm: $Id: servercomm2.cc,v 1.121 2005/09/07 23:23:31 rasdev Exp $"; + +// after some time please take this and everything related to it out (26.06.2001) +#define ANDREAS_2306 + +#include +#include +#include +#include // for log(), exp(), floor() +#include // time +#include + +#ifdef PURIFY +#include +#endif + +#ifdef RMANBENCHMARK + // to control GenericCompression::decompTimer and GenericCompression::compTimer + #include "gencompression.hh" + #include "zlibcompression.hh" +#endif + +#ifdef SOLARIS + #define PORTMAP // define to use function declarations for old interfaces + #include + + extern int _rpcpmstart; + + // function prototype with C linkage + extern "C" int gethostname(char *name, int namelen); +#else // HPUX + #include +#endif + +#include + +#include "raslib/rmdebug.hh" +#include "debug.hh" +#include "raslib/rminit.hh" +#include "raslib/error.hh" +#include "raslib/minterval.hh" +#include "raslib/mddtypes.hh" +#include "raslib/basetype.hh" +#include "raslib/endian.hh" +#include "raslib/odmgtypes.hh" +#include "raslib/parseparams.hh" +// for transfer compression +#include "compression/tilecompression.hh" + +#include "servercomm/servercomm.hh" +#include "catalogmgr/typefactory.hh" + +#include "mddmgr/mddcoll.hh" +#include "mddmgr/mddobj.hh" +#include "mddmgr/mddcolliter.hh" +#include "tilemgr/tile.hh" + +#include "relcatalogif/mddtype.hh" +#include "relcatalogif/mdddomaintype.hh" +#include "relcatalogif/settype.hh" +#include "reladminif/eoid.hh" + +#include "qlparser/qtmdd.hh" +#include "qlparser/qtatomicdata.hh" +#include "qlparser/qtcomplexdata.hh" +#include "qlparser/qtintervaldata.hh" +#include "qlparser/qtmintervaldata.hh" +#include "qlparser/qtpointdata.hh" +#include "qlparser/qtstringdata.hh" + +// console output describing successful/unsuccessful actions +#define MSG_OK "ok" +#define MSG_FAILED "failed" + +// include and extern declarations for the query parsing +#include "qlparser/querytree.hh" +extern int yyparse(void *); +extern void yyreset(); +#ifdef NOPRE +char* ppInBuf = 0; +char* ppOutBuf = 0; +void ppreset() +{ + RMInit::logOut << "Error: Preprocessor not compiled in." << std::endl; + RMInit::dbgOut << "Error: Preprocessor not compiled in." << std::endl; + throw r_Error(ILLEGALSTATEREACHED); +} + +int ppparse() +{ + RMInit::logOut << "Error: Preprocessor not compiled in." << std::endl; + RMInit::dbgOut << "Error: Preprocessor not compiled in." << std::endl; + throw r_Error(ILLEGALSTATEREACHED); +} +#else +extern char* ppInBuf; +extern char* ppOutBuf; +extern void ppreset(); +extern int ppparse(); +#endif +extern bool udfEnabled; + +extern QueryTree* parseQueryTree; +extern ParseInfo* parseError; +extern char* beginParseString; +extern char* iterParseString; +extern unsigned long maxTransferBufferSize; +extern char* dbSchema; + +MDDColl* mddConstants=0; + +ServerComm::ClientTblElt* currentClientTblElt=0; + +// Once again a function prototype. The first one is for the RPC dispatcher +// function located in the server stub file rpcif_svc.c and the second one +// is for the garbage collection function pointed to by the signal handler. +extern "C" +{ + // static void rpcif_1( struct svc_req*, register SVCXPRT* ); + char* rpcif_1( struct svc_req*, register SVCXPRT* ); + void garbageCollection( int ); +} + +// this is a temporary thing +extern int globalOptimizationLevel; + +// This is needed in httpserver.cc +char globalHTTPSetTypeStructure[4096]; + +// constant for clientID +const char* ServerComm::HTTPCLIENT = "HTTPClient"; + +///ensureTileFormat returns the following: +const int ServerComm::ENSURE_TILE_FORMAT_OK=0; +const int ServerComm::ENSURE_TILE_FORMAT_BAD=-1; + +/************************************************************************* + * Method name...: openDB( unsigned long callingClientId, + * const char* dbName , + * const char* userName ) + ************************************************************************/ +unsigned short +ServerComm::openDB( unsigned long callingClientId, + const char* dbName, + const char* userName ) +{ + RMDBGENTER( 4, RMDebug::module_servercomm, "ServerComm", "openDB" ) + + unsigned short returnValue=0; + + RMInit::logOut << "Request: openDB '" << dbName << "'..." << std::flush; + + ClientTblElt* context = getClientContext( callingClientId ); + + if( context != 0 ) + { + // release transfer collection/iterator + context->releaseTransferStructures(); + + // open the database + try + { + context->database.open( dbName ); + } + catch(r_Error& err) + { + if(err.get_kind() == r_Error::r_Error_DatabaseUnknown) + { + RMInit::logOut << "Error: database does not exist." << endl; + returnValue = 2; + } + else if(err.get_kind() == r_Error::r_Error_DatabaseOpen) + { + // ignore re-open to be fault tolerant -- PB 2004-dec-16 + // RMInit::logOut << "Error: database is already open." << endl; + returnValue = 3; + } + else + { + RMInit::logOut << "Error: exception " << err.get_errorno() << ": " << err.what() << endl; + //should be something else. but better than no message about the problem at all + returnValue = 2; + } + } + + if( returnValue == 0 ) + { + // database was successfully opened, so assign db and user name + delete[] context->baseName; + context->baseName = new char[strlen( dbName )+1]; + strcpy( context->baseName, dbName ); + + delete[] context->userName; + context->userName = new char[strlen( userName )+1]; + strcpy( context->userName, userName ); + + RMInit::logOut << MSG_OK << std::endl; + } + + context->release(); + + // ignore "already open" error to be more fault tolerant -- PB 2004-dec-16 + if( returnValue == 3 ) + { + RMInit::logOut << "Warning: database already open for user '" << userName << "', ignoring command." << endl; + returnValue = 0; + } + } + else + { + RMInit::logOut << "Error: client not registered." << std::endl; + returnValue = 1; + } + + RMDBGEXIT( 4, RMDebug::module_servercomm, "ServerComm", "openDB" ) + return returnValue; +} + + + +/************************************************************************* + * Method name...: closeDB( unsigned long callingClientId ) + ************************************************************************/ +unsigned short +ServerComm::closeDB( unsigned long callingClientId ) +{ + RMDBGENTER( 4, RMDebug::module_servercomm, "ServerComm", "closeDB" ) + unsigned short returnValue; + + RMInit::logOut << "Request: closeDB..." << std::flush; + + ClientTblElt* context = getClientContext( callingClientId ); + + if( context != 0 ) + { + // release transfer collection/iterator + context->releaseTransferStructures(); + + // If the current transaction belongs to this client, abort it. + if( transactionActive == callingClientId ) + { + RMInit::logOut << "Warning: transaction is open; aborting this transaction..." << std::flush; + + context->transaction.abort(); + transactionActive = 0; + } + + // close the database + context->database.close(); + + // reset database name + delete[] context->baseName; + context->baseName = new char[5]; + strcpy( context->baseName, "none" ); + + returnValue = 0; + + context->release(); + +#ifdef PURIFY + purify_new_leaks(); +#endif + + RMInit::logOut << MSG_OK << endl; + } + else + { + RMInit::logOut << "Error: client not registered." << std::endl; + returnValue = 1; + } + + RMDBGEXIT( 4, RMDebug::module_servercomm, "ServerComm", "closeDB" ) + return returnValue; +} + + + +/************************************************************************* + * Method name...: createDB( char* name ) + ************************************************************************/ +unsigned short +ServerComm::createDB( char* name ) +{ + unsigned short returnValue; + + // FIXME: what about client id? -- PB 2005-aug-27 + + RMInit::logOut << "Request: createDB '" << name << "'..." << std::flush; + + DatabaseIf* tempDbIf = new DatabaseIf; + + // create the database + try + { + tempDbIf->createDB( name, dbSchema ); + RMInit::logOut << MSG_OK << endl; + } + catch(r_Error& myErr) + { + RMInit::logOut << "Error: exception " << myErr.get_errorno() << ": " << myErr.what() << std::endl; + } + catch(std::bad_alloc) + { + RMInit::logOut << "Error: cannot allocate memory." << std::endl; + throw; + } + catch(...) + { + RMInit::logOut << std::endl << "Error: Unspecified exception." << std::endl; + } + + delete tempDbIf; + + // FIXME: set proper return value on failure (update .hh!) -- PB 2005-aug-27 + returnValue = 0; + + return returnValue; +} + + + +/************************************************************************* + * Method name...: destroyDB( char* name ) + ************************************************************************/ +unsigned short +ServerComm::destroyDB( char* name ) +{ + // Note: why no check for client id here? -- PB 2005-aug-25 + + unsigned short returnValue = 0; + + RMInit::logOut << "Request: destroyDB '" << name << "'..." << std::flush; + + DatabaseIf* tempDbIf = new DatabaseIf; + + // begin a temporary transaction because persistent data (as databases) + // can only be manipulated within active transactions. + // tempTaIf->begin(tempDbIf); + + // destroy the database + tempDbIf->destroyDB( name ); + + // commit the temporary transaction + // tempTaIf->commit(); + + delete tempDbIf; + + RMInit::logOut << MSG_OK << std::endl; + + return returnValue; +} + + + +/************************************************************************* + * Method name...: beginTA( unsigned long callingClientId ) + ************************************************************************/ +unsigned short +ServerComm::beginTA( unsigned long callingClientId, + unsigned short readOnly ) +{ + RMDBGENTER( 4, RMDebug::module_servercomm, "ServerComm", "beginTA" ) + unsigned short returnValue; + + RMInit::logOut << "Request: beginTA (" << ( readOnly ? "read" : "write" ) << ")..." << std::flush; + + ClientTblElt* context = getClientContext( callingClientId ); + + if( context == 0 ) + { + RMInit::logOut << "Error: client not registered." << std::endl; + returnValue = 1; + } + else if ( transactionActive ) + { + RMInit::logOut << "Error: transaction already active." << std::endl; + returnValue = 2; + context->release(); + } + else + { + // release transfer collection/iterator + context->releaseTransferStructures(); + +#ifdef RMANBENCHMARK + if( RManBenchmark > 0 ) + context->taTimer = new RMTimer("ServerComm", "transaction time "); +#endif + try + { + // start the transaction + context->transaction.begin( &(context->database), readOnly ); + RMInit::logOut << MSG_OK << endl; + } + catch(r_Error& err) + { + RMInit::logOut << "Error: exception " << err.get_errorno() << ": " << err.what() << std::endl; + context->release(); + throw; + } + + // lock the semaphor + transactionActive = callingClientId; + returnValue = 0; + + context->release(); + } + + RMDBGEXIT( 4, RMDebug::module_servercomm, "ServerComm", "beginTA" ) + return returnValue; +} + + + +/************************************************************************* + * Method name...: commitTA( unsigned long callingClientId ) + ************************************************************************/ +unsigned short +ServerComm::commitTA( unsigned long callingClientId ) +{ + RMDBGENTER( 4, RMDebug::module_servercomm, "ServerComm", "commitTA" ) + unsigned short returnValue; + + ClientTblElt* context = getClientContext( callingClientId ); + + RMInit::logOut << "Request: commitTA..." << std::flush; + + if( context != 0 ) + { +#ifdef RMANBENCHMARK + RMTimer* commitTimer = 0; + + if( RManBenchmark > 0 ) + commitTimer = new RMTimer("ServerComm", "commit time "); +#endif + + // release transfer collection/iterator within the transaction they are created + context->releaseTransferStructures(); + + // commit the transaction + context->transaction.commit(); + + // unlock the semaphor + transactionActive = 0; + + returnValue = 0; + +#ifdef RMANBENCHMARK + if( commitTimer ) delete commitTimer; +#endif + + context->release(); + + RMInit::logOut << MSG_OK << endl; + } + else + { + RMInit::logOut << "Error: client not registered." << std::endl; + returnValue = 1; + } + +#ifdef RMANBENCHMARK + if( context->taTimer ) + delete context->taTimer; + context->taTimer = 0; +#endif + + RMDBGEXIT( 4, RMDebug::module_servercomm, "ServerComm", "commitTA" ) + return returnValue; +} + + + +/************************************************************************* + * Method name...: abortTA( unsigned long callingClientId ) + ************************************************************************/ +unsigned short +ServerComm::abortTA( unsigned long callingClientId ) +{ + RMDBGENTER( 4, RMDebug::module_servercomm, "ServerComm", "abortTA" ) + unsigned short returnValue; + + RMInit::logOut << "Request: abortTA..." << std::flush; + + ClientTblElt* context = getClientContext( callingClientId ); + + if( context != 0 ) + { + // release transfer collection/iterator within the transaction they are created + context->releaseTransferStructures(); + + // abort the transaction + context->transaction.abort(); + + // unlock the semaphor + transactionActive = 0; + + returnValue = 0; + + context->release(); + + RMInit::logOut << MSG_OK << endl; + } + else + { + RMInit::logOut << "Error: client not registered." << std::endl; + returnValue = 1; + } + +#ifdef RMANBENCHMARK + if( context->taTimer ) delete context->taTimer; + context->taTimer = 0; +#endif + + RMDBGEXIT( 4, RMDebug::module_servercomm, "ServerComm", "abortTA" ) + return returnValue; +} + + +/************************************************************************* + * Method name...: isTAOpen() + * as right now only one transaction can be active per server, + * we only have to check the sema. + * returns: + * true iff a transaction is open + ************************************************************************/ +bool +ServerComm::isTAOpen( unsigned long callingClientId ) +{ + RMDBGENTER( 4, RMDebug::module_servercomm, "ServerComm", "isTAOpen" ) + + RMInit::logOut << "Request: isTAOpen..." << std::flush; + + bool returnValue = transactionActive; + + RMInit::logOut << MSG_OK << (transactionActive?", is active.":", is not active.") << endl; + + RMDBGEXIT( 4, RMDebug::module_servercomm, "ServerComm", "isTAOpen" ) + return returnValue; +} + + + +unsigned short +ServerComm::insertColl( unsigned long callingClientId, + const char* collName, + const char* typeName, + r_OId& oid ) +{ + RMDBGENTER( 4, RMDebug::module_servercomm, "ServerComm", "insertColl" ) + unsigned short returnValue = 0; + + RMInit::logOut << "Request: insertColl '" << collName << "' with type '" << typeName << "'..." << std::flush; + + ClientTblElt* context = getClientContext( callingClientId ); + + if( context != 0 ) + { + // delete old transfer structures + context->releaseTransferStructures(); + + // + // create the collenction + // + + // get collection type + CollectionType* collType = (CollectionType*)TypeFactory::mapSetType( typeName ); + + if( collType ) + { + try + { + MDDColl* coll = MDDColl::createMDDCollection(collName, OId(oid.get_local_oid()), collType); + delete coll; + RMInit::logOut << MSG_OK << endl; + } + catch( r_Error& obj ) + { + if (obj.get_kind() == r_Error::r_Error_NameNotUnique) + { + RMInit::logOut << "Error: collection exists already." << std::endl; + returnValue = 3; + } + else + { + RMInit::logOut << "Error: cannot create collection: " << obj.get_errorno() << " " << obj.what() << std::endl; + //this should be another code... + returnValue = 3; + } + } + + } + else + { + RMInit::logOut << "Error: unknown collection type: '" << typeName << "'." << std::endl; + returnValue = 2; + } + + // + // done + // + + context->release(); + } + else + { + RMInit::logOut << "Error: client not registered." << std::endl; + returnValue = 1; + } + + RMDBGEXIT( 4, RMDebug::module_servercomm, "ServerComm", "insertColl" ) + return returnValue; +} + + + +/************************************************************************* + * Method name...: deleteCollByName( unsigned long callingClientId, + * const char* collName ) + ************************************************************************/ +unsigned short +ServerComm::deleteCollByName( unsigned long callingClientId, + const char* collName ) +{ + RMDBGENTER( 4, RMDebug::module_servercomm, "ServerComm", "deleteCollByName(" << callingClientId << ", " << collName << ")") + unsigned short returnValue; + + RMInit::logOut << "Request: deleteCollByName '" << collName << "'..." << std::flush; + + ClientTblElt* context = getClientContext( callingClientId ); + + if( context != 0 ) + { + // delete old transfer structures + context->releaseTransferStructures(); + + // + // delete collenction + // + + // delete root object with collection name + if (MDDColl::dropMDDCollection(collName)) + { + RMDBGMIDDLE( 4, RMDebug::module_servercomm, "ServerComm", "collection dropped") + RMInit::logOut << MSG_OK << std::endl; + returnValue = 0; + } + else + { + RMDBGMIDDLE( 4, RMDebug::module_servercomm, "ServerComm", "did not drop collection") + RMInit::logOut << "Error: collection does not exist." << std::endl; + returnValue = 2; + } + + // + // done + // + + context->release(); + } + else + { + RMInit::logOut << "Error: client not registered." << std::endl; + returnValue = 1; + } + + RMDBGEXIT( 4, RMDebug::module_servercomm, "ServerComm", "deleteCollByName(" << callingClientId << ", " << collName << ") " << returnValue) + return returnValue; +} + + + +unsigned short +ServerComm::deleteObjByOId( unsigned long callingClientId, + r_OId& oid ) +{ + RMDBGENTER( 4, RMDebug::module_servercomm, "ServerComm", "deleteObjByOId(" << callingClientId << ", " << oid << ")"); + unsigned short returnValue; + + RMInit::logOut << "Request: deleteObjByOId " << oid << "..." << std::flush; + + ClientTblElt* context = getClientContext( callingClientId ); + + if( context != 0 ) + { + // delete old transfer structures + context->releaseTransferStructures(); + + // determine type of object + OId oidIf( oid.get_local_oid() ); + + RMDBGMIDDLE( 4, RMDebug::module_servercomm, "ServerComm", "OId of object " << oidIf) + OId::OIdType objType = oidIf.getType(); + + switch( objType ) + { + case OId::MDDOID: + // FIXME: why not deleted?? -- PB 2005-aug-27 + RMInit::logOut << "found MDD object; NOT deleted yet..." << MSG_OK << std::endl; + RMDBGMIDDLE( 4, RMDebug::module_servercomm, "ServerComm", "not deleting mdd object") + returnValue = 0; + break; + case OId::MDDCOLLOID: + RMInit::logOut << "deleting collection..." << std::flush; + // delete root object with collection name + if (MDDColl::dropMDDCollection(oidIf)) + { + RMInit::logOut << MSG_OK << std::endl; + RMDBGMIDDLE( 4, RMDebug::module_servercomm, "ServerComm", "deleted mdd coll") + returnValue = 0; + } + else + { + RMInit::logOut << "Error: Collection does not exist." << std::endl; + RMDBGMIDDLE( 4, RMDebug::module_servercomm, "ServerComm", "did not delete mdd coll") + returnValue = 2; + } + break; + default: + RMInit::logOut << "Error: object has unknown type: " << objType << std::endl; + returnValue = 2; + } + + // + // done + // + + context->release(); + } + else + { + RMInit::logOut << "Error: client not registered." << std::endl; + returnValue = 1; + } + + RMDBGEXIT( 4, RMDebug::module_servercomm, "ServerComm", "deleteObjByOId(" << callingClientId << ", " << oid << ")" << returnValue) + return returnValue; +} + + + +unsigned short +ServerComm::removeObjFromColl( unsigned long callingClientId, + const char* collName, + r_OId& oid ) +{ + RMDBGENTER( 4, RMDebug::module_servercomm, "ServerComm", "removeObjFromColl(" << callingClientId << ", " << collName << ", " << oid << ")") + unsigned short returnValue; + + RMInit::logOut << "Request: removeObjFromColl '" << collName << "', oid " << oid << "..." << std::flush; + + ClientTblElt* context = getClientContext( callingClientId ); + + if( context != 0 ) + { + // delete old transfer structures + context->releaseTransferStructures(); + + OId oidIf( oid.get_local_oid() ); + + RMDBGMIDDLE( 4, RMDebug::module_servercomm, "ServerComm", "mdd object oid " << oidIf) + + // open collection + MDDColl* coll = 0; + + try + { + coll = MDDColl::getMDDCollection(collName); + RMDBGMIDDLE( 4, RMDebug::module_servercomm, "ServerComm", "retrieved mdd coll") + } + catch(r_Error& obj) + { + // collection name invalid + if (obj.get_kind() == r_Error::r_Error_ObjectUnknown) + { + RMInit::logOut << "Error: collection not found." << std::endl; + returnValue = 2; + } + else + { + RMInit::logOut << "Error " << obj.get_errorno() << ": " << obj.what() << std::endl; + // there should be another return code + returnValue = 2; + } + coll = NULL; + } + catch(std::bad_alloc) + { + RMInit::logOut << "Error: cannot allocate memory." << std::endl; + throw; + } + catch(...) + { + // collection name invalid + RMInit::logOut << "Error: unspecified exception." << std::endl; + returnValue = 2; + } + + if( coll ) + { + if (coll->isPersistent()) + { + RMDBGMIDDLE( 4, RMDebug::module_servercomm, "ServerComm", "retrieved persistent mdd coll") + + OId collId; + coll->getOId(collId); + RMDBGMIDDLE( 4, RMDebug::module_servercomm, "ServerComm", "mdd coll oid " << collId) + MDDColl::removeMDDObject(collId, oidIf); + + // no error management yet -> returnValue = 3 + + RMInit::logOut << MSG_OK << std::endl; + returnValue = 0; + + delete coll; + } + } + + // + // done + // + + context->release(); + } + else + { + RMInit::logOut << "Error: client not registered." << std::endl; + returnValue = 1; + } + + RMDBGEXIT( 4, RMDebug::module_servercomm, "ServerComm", "removeObjFromColl(" << callingClientId << ", " << collName << ", " << oid << ") " << returnValue) + return returnValue; +} + + +unsigned short +ServerComm::insertMDD( unsigned long callingClientId, + const char* collName, + RPCMarray* rpcMarray, + const char* typeName, + r_OId& oid ) +{ + RMDBGENTER( 4, RMDebug::module_servercomm, "ServerComm", "insertMDD" ) + unsigned short returnValue = 0; + + RMInit::logOut << "Request: insertMDD type='" << typeName << "' into collection '" << collName << "'..." << std::flush; + + ClientTblElt* context = getClientContext( callingClientId ); + r_Data_Format myDataFmt = r_Array; + r_Data_Format myCurrentFmt = r_Array; + + if( context != 0 ) + { + // delete old transfer structures + context->releaseTransferStructures(); + + // + // insert the object into the collection + // + + // Determine the type of the MDD to be inserted. + const MDDType* mddType = TypeFactory::mapMDDType( typeName ); + if( mddType ) + { + if( mddType->getSubtype() != MDDType::MDDONLYTYPE ) + { + // + // open the collection + // + + MDDColl* collection=0; + MDDColl* almost = 0; + + try + { + almost = MDDColl::getMDDCollection( collName ); + if (almost->isPersistent()) + collection = (MDDColl*)almost; + else + { + RMInit::logOut << "Error: inserting into system collection is illegal." << std::endl; + context->release(); //!!! + throw r_Error(SYSTEM_COLLECTION_NOT_WRITABLE); + } + } + catch(std::bad_alloc) + { + RMInit::logOut << "Error: cannot allocate memory." << std::endl; + context->release(); //!!! + throw; + } + catch (r_Error& err) + { + RMInit::logOut << "Error " << err.get_errorno() << ": " << err.what() << std::endl; + returnValue = 5; + context->release(); //!!! + throw; + } + catch(...) + { + returnValue = 5; + RMInit::logOut << "Error: unspecific exception during collection read." << std::endl; + context->release(); + return returnValue; + } + + // + // check MDD and collection type for compatibility + // + + r_Minterval domain( rpcMarray->domain ); + + RMDBGIF(4, RMDebug::module_servercomm, "ServerComm", \ + char* collTypeStructure = collection->getCollectionType()->getTypeStructure(); \ + char* mddTypeStructure = mddType->getTypeStructure(); \ + RMInit::dbgOut << std::endl << "Collection type structure.: " << collTypeStructure << std::endl \ + << "MDD type structure........: " << mddTypeStructure << std::endl \ + << "MDD domain................: " << domain << std::endl; \ + free( collTypeStructure ); \ + free( mddTypeStructure ); ) + + if( !mddType->compatibleWithDomain( &domain ) ) + { + // free resources + collection->releaseAll(); + delete collection; + + // return error + returnValue = 4; + RMInit::logOut << "Error: MDD type is not compatible wrt. its domain: " << domain << std::endl; + + context->release(); + + return returnValue; + } + + if( !collection->getCollectionType()->compatibleWith( mddType ) ) + { + // free resources + collection->releaseAll(); + delete collection; + + // return error + returnValue = 3; + RMInit::logOut << "Error: MDD and collection types are incompatible." << std::endl; + + context->release(); + + return returnValue; + } + + // + // create persistent MDD object + // + + MDDBaseType* mddBaseType = (MDDBaseType*)mddType; + char* dataPtr = rpcMarray->data.confarray_val; + unsigned long dataSize = rpcMarray->data.confarray_len; + // reset data area from rpc structure so that it is not deleted + // deletion is done by TransTile resp. Tile + rpcMarray->data.confarray_len = 0; + rpcMarray->data.confarray_val = 0; + int getMDDData=0; + const BaseType* baseType = mddBaseType->getBaseType(); + unsigned long byteCount = domain.cell_count() * rpcMarray->cellTypeLength; + //r_Data_Format storageFormat = (r_Data_Format)(rpcMarray->storageFormat); + + MDDObj* mddObj=0; + StorageLayout ms; + ms.setTileSize(StorageLayout::DefaultTileSize); + ms.setIndexType(StorageLayout::DefaultIndexType); + ms.setTilingScheme(StorageLayout::DefaultTilingScheme); + if (domain.dimension() == StorageLayout::DefaultTileConfiguration.dimension()) + ms.setTileConfiguration(StorageLayout::DefaultTileConfiguration); + + try + { + mddObj = new MDDObj(mddBaseType, domain, OId(oid.get_local_oid()), ms); + } + catch(std::bad_alloc) + { + RMInit::logOut << "Error: cannot allocate memory." << std::endl; + context->release(); //!!! + throw; + } + catch (r_Error& obj) + { + RMInit::logOut << "Error " << obj.get_errorno() << ": " << obj.what() << std::endl; + context->release(); //!!! + throw; + } + catch(...) + { + returnValue = 6; + RMInit::logOut << "Error: unspecific exception during creation of persistent object." << std::endl; + + context->release(); + + return returnValue; + } + + myDataFmt = (r_Data_Format)(rpcMarray->storageFormat); + myCurrentFmt = (r_Data_Format)(rpcMarray->currentFormat); + RMInit::dbgOut << "oid " << oid + << ", domain " << domain + << ", cell length " << rpcMarray->cellTypeLength + << ", data size " << dataSize + << ", rpc storage " << myDataFmt + << ", rpc transfer " << myCurrentFmt << " " << std::flush; + + // store in the specified storage format; the current tile format afterwards will be the + // requested format if all went well, but use the (new) current format to be sure. + // Don't repack here, however, because it might be retiled before storage. + if(ensureTileFormat(myCurrentFmt, myDataFmt, domain, + baseType, dataPtr, dataSize, 0, 1, context->storageFormatParams) != ENSURE_TILE_FORMAT_OK) + { + //FIXME returnValue + returnValue = 6; + RMInit::logOut << "Error: illegal tile format for creating object." << std::endl; + + context->release(); + + return returnValue; + } + + // if compressed, getMDDData is != 0 + if (myCurrentFmt != r_Array) + getMDDData = dataSize; + + // This should check the compressed size rather than the raw data size + if( RMInit::tiling && dataSize > StorageLayout::DefaultTileSize ) + { + r_Range edgeLength = (r_Range)floor(exp((1/(r_Double)domain.dimension())* + log((r_Double)StorageLayout::DefaultTileSize/rpcMarray->cellTypeLength))); + + if (edgeLength <1) + edgeLength=1; + + r_Minterval tileDom( domain.dimension() ); + for( int i=0; isetParameters(context->storageFormatParams); + vector< Tile *>* tileSet = entireTile->splitTile( tileDom ); + if (entireTile->isPersistent()) + entireTile->setPersistent(0); + delete entireTile; + + RMInit::logOut << "creating " << tileSet->size() << " tile(s)..." << std::flush; + + for( vector::iterator iter = tileSet->begin(); iter != tileSet->end(); iter++ ) + mddObj->insertTile( *iter ); + + // delete the vector again + delete tileSet; + } + else + { + Tile* tile = 0; + + if (r_Tile_Compression::check_data_format(myDataFmt) == r_Tile_Compression::COMPRESSION) + { + tile = new Tile( domain, baseType, dataPtr, getMDDData, myDataFmt ); + RMInit::dbgOut << "insertTile created new TransTile (" << myDataFmt << "), "; + } + else + { + RMInit::logOut << "Error: Unsupported data format '" << myCurrentFmt << "', will ingore it..." << std::flush; + tile = new Tile( domain, baseType, dataPtr, getMDDData ); + } + + tile->setParameters(context->storageFormatParams); + RMInit::dbgOut << "one tile..." << std::flush; + mddObj->insertTile( tile ); + } + + collection->insert( mddObj ); + + // free transient memory + collection->releaseAll(); + + delete collection; + + // + // done + // + + RMInit::logOut << MSG_OK << std::endl; + } + else + { + RMInit::logOut << "Error: MDD type name '" << typeName << "' has no base type." << std::endl; + returnValue = 2; + } + } + else + { + RMInit::logOut << "Error: MDD type name '" << typeName << "' not found." << std::endl; + returnValue = 2; + } + + context->release(); + } + else + { + RMInit::logOut << "Error: client not registered." << std::endl; + returnValue = 1; + } + + RMDBGEXIT( 4, RMDebug::module_servercomm, "ServerComm", "insertMDD " << returnValue) + return returnValue; +} + + +unsigned short +ServerComm::insertTileSplitted( unsigned long callingClientId, + int isPersistent, + RPCMarray* rpcMarray, + r_Minterval* tileSize) +{ + RMDBGENTER( 4, RMDebug::module_servercomm, "ServerComm", "insertTileSplitted" ) + unsigned short returnValue = 0; + + RMInit::logOut << "Request: insertTile..." << std::flush; + + ClientTblElt* context = getClientContext( callingClientId ); + + if( context != 0 ) + { + BaseType* baseType = NULL; + + if( isPersistent ) + baseType = (BaseType*)context->assembleMDD->getCellType(); + else + baseType = (BaseType*)context->transferMDD->getCellType(); + + // The type of the tile has to be the one of the MDD. + // type check missing + + if( baseType != NULL ) + { + r_Minterval domain( rpcMarray->domain ); + char* dataPtr = rpcMarray->data.confarray_val; + unsigned long dataSize = rpcMarray->data.confarray_len; + // reset data area from rpc structure so that it is not deleted + // deletion is done by TransTile resp. Tile + rpcMarray->data.confarray_len = 0; + rpcMarray->data.confarray_val = 0; + int getMDDData = 0; + r_Data_Format myDataFmt = (r_Data_Format)(rpcMarray->storageFormat); + r_Data_Format myCurrentFmt = (r_Data_Format)(rpcMarray->currentFormat); + RMDBGMIDDLE( 2, RMDebug::module_server, "ServerComm", "insertTileSplitted - rpc storage format : " << myDataFmt) + RMDBGMIDDLE( 2, RMDebug::module_server, "ServerComm", "insertTileSplitted - rpc transfer format : " << myCurrentFmt) + // store in specified storage format; use (new) current format afterwards + // Don't repack here because of possible retiling. + if(ensureTileFormat(myCurrentFmt, myDataFmt, domain, + baseType, dataPtr, dataSize, 0, 1, context->storageFormatParams) != ENSURE_TILE_FORMAT_OK) + { + //FIXME returnValue + returnValue = 1; + + context->release(); + RMDBGEXIT( 4, RMDebug::module_servercomm, "ServerComm", "insertTileSplitted - ensureTileFormat Failed" ) + + return returnValue; + } + + if (myCurrentFmt != r_Array) + getMDDData = dataSize; + + Tile* tile = 0; + + if (r_Tile_Compression::check_data_format(myDataFmt) == r_Tile_Compression::COMPRESSION) + { + RMInit::dbgOut << "insertTile created new TransTile (" << myDataFmt << "), "; + } + else + { + RMInit::logOut << "Warning: Unsupported data format '" << myCurrentFmt << "', will be ignored..." << std::flush; + myDataFmt = r_Array; + } + tile = new Tile( domain, baseType, dataPtr, getMDDData, myDataFmt ); + + // for java clients only: check endianness and split tile if necessary + if(strcmp(context->clientIdText, ServerComm::HTTPCLIENT) == 0) + { + // check endianess + r_Endian::r_Endianness serverEndian = r_Endian::get_endianness(); + if(serverEndian != r_Endian::r_Endian_Big) + { + RMInit::dbgOut << "changing endianness..." << std::flush; + + // we have to swap the endianess + char *tpstruct; + r_Base_Type *useType; + tpstruct = baseType->getTypeStructure(); + useType = (r_Base_Type*)(r_Type::get_any_type(tpstruct)); + + char* tempT = (char*)mymalloc(sizeof(char) * tile->getSize()); + + // change the endianness of the entire tile for identical domains for src and dest + r_Endian::swap_array(useType, domain, domain, tile->getContents(), tempT); + tile->setContents(tempT); + + delete useType; + free(tpstruct); + } + + // Split the tile! + vector< Tile *>* tileSet = tile->splitTile( *tileSize ); + RMInit::dbgOut << "Now inserting splitted tile..."; + for( vector::iterator iter = tileSet->begin(); iter != tileSet->end(); iter++ ) + { + (*iter)->setParameters(context->storageFormatParams); + if( isPersistent ) + context->assembleMDD->insertTile( *iter ); + else + context->transferMDD->insertTile( *iter ); + } + // delete the vector again + delete tile; + delete tileSet; + } + // for c++ clients: insert tile + else + { + //insert one single tile + // later, we should take into consideration the default server tile-size! + RMInit::logOut << "Now inserting single tile..."; + tile->setParameters(context->storageFormatParams); + if( isPersistent ) + context->assembleMDD->insertTile( tile ); + else + context->transferMDD->insertTile( tile ); + //do not access tile again, because it was already deleted in insertTile + } + // + // done + // + + RMInit::logOut << MSG_OK << std::endl; + } + else + RMInit::logOut << "Error: tile and MDD base type do not match." << std::endl; + + context->release(); + } + else + { + RMInit::logOut << "Error: client not registered." << std::endl; + returnValue = 1; + } + + RMDBGEXIT( 4, RMDebug::module_servercomm, "ServerComm", "insertTileSplitted" ) + return returnValue; +} + + +unsigned short +ServerComm::insertTile( unsigned long callingClientId, + int isPersistent, + RPCMarray* rpcMarray ) +{ + // no log here, is done in RNP comm. + + unsigned short returnValue = insertTileSplitted(callingClientId, isPersistent, rpcMarray, NULL); + + return returnValue; +} + + +unsigned short +ServerComm::startInsertPersMDD( unsigned long callingClientId, + const char* collName, + r_Minterval &domain, + unsigned long typeLength, + const char* typeName, + r_OId& oid ) +{ + RMDBGENTER( 4, RMDebug::module_servercomm, "ServerComm", "startInsertPersMDD" ) + unsigned short returnValue = 0; + + RMInit::logOut << "Request: startInsertPersMDD type '" << typeName + << "', collection '" << collName << "', domain " << domain << ", cell length " << typeLength + << ", " << domain.cell_count()*typeLength << " bytes..." << std::flush; + + ClientTblElt* context = getClientContext( callingClientId ); + + if( context != 0 ) + { + // delete old transfer structures + context->releaseTransferStructures(); + + // Determine the type of the MDD to be inserted. + const MDDType* mddType = TypeFactory::mapMDDType( typeName ); + + if( mddType ) + { + if( mddType->getSubtype() != MDDType::MDDONLYTYPE ) + { + MDDBaseType* mddBaseType = (MDDBaseType*)mddType; + + try + { + // store PersMDDColl for insert operation at the end of the transfer + context->transferColl = MDDColl::getMDDCollection( collName ); + if (!context->transferColl->isPersistent()) + { + RMInit::logOut << "Error: inserting into system collection is illegal." << std::endl; + context->release(); //!!! + throw r_Error(SYSTEM_COLLECTION_NOT_WRITABLE); + } + } + catch (r_Error& obj) + { + RMInit::logOut << "Error " << obj.get_errorno() << ": " << obj.what() << std::endl; + context->release(); //!!! + throw; + } + catch(std::bad_alloc) + { + RMInit::logOut << "Error: cannot allocate memory." << std::endl; + context->release(); //!!! + throw; + } + catch(...) + { + returnValue = 5; + RMInit::logOut << "Error: unspecific exception while opening collection." << std::endl; + + context->release(); + + return returnValue; + } + + // + // check MDD and collection type for compatibility + // + + RMDBGIF(4, RMDebug::module_servercomm, "ServerComm", \ + char* collTypeStructure = context->transferColl->getCollectionType()->getTypeStructure(); \ + char* mddTypeStructure = mddType->getTypeStructure(); \ + RMInit::dbgOut << std::endl << "Collection type structure.: " << collTypeStructure << std::endl \ + << "MDD type structure........: " << mddTypeStructure << std::endl \ + << "MDD domain................: " << domain << std::endl; \ + free( collTypeStructure ); \ + free( mddTypeStructure ); ) + + if( !mddType->compatibleWithDomain( &domain ) ) + { + // free resources + context->transferColl->releaseAll(); + delete context->transferColl; + context->transferColl = 0; + + // return error + returnValue = 4; + RMInit::logOut << "Error: MDD type not compatible wrt. its domain: " << domain << MSG_FAILED << std::endl; + + context->release(); + + return returnValue; + } + + if( !context->transferColl->getCollectionType()->compatibleWith( mddType ) ) + { + // free resources + context->transferColl->releaseAll(); + delete context->transferColl; + context->transferColl = 0; + + // return error + returnValue = 3; + RMInit::logOut << "Error: incompatible MDD and collection types." << std::endl; + + context->release(); + + return returnValue; + } + + // + // Create persistent MDD for further tile insertions + // + + StorageLayout ms; + ms.setTileSize(StorageLayout::DefaultTileSize); + ms.setIndexType(StorageLayout::DefaultIndexType); + ms.setTilingScheme(StorageLayout::DefaultTilingScheme); + if (domain.dimension() == StorageLayout::DefaultTileConfiguration.dimension()) + ms.setTileConfiguration(StorageLayout::DefaultTileConfiguration); + try + { + context->assembleMDD = new MDDObj( mddBaseType, domain, OId( oid.get_local_oid() ), ms ); + } + catch (r_Error& err) + { + RMInit::logOut << "Error: while creating persistent tile: " << err.get_errorno() << ": " << err.what() << std::endl; + context->release(); //!!! + throw; + } + catch(std::bad_alloc) + { + RMInit::logOut << "Error: cannot allocate memory." << std::endl; + context->release(); //!!! + throw; + } + catch(...) + { + returnValue = 6; + RMInit::logOut << "Error: unspecific exception during creation of persistent object." << std::endl; + + context->release(); + + return returnValue; + } + + RMInit::logOut << MSG_OK << std::endl; + } + else + { + RMInit::logOut << "Error: MDD type '" << typeName << "' has no base type..." << std::endl; + returnValue = 2; + } + } + else + { + RMInit::logOut << "Error: MDD type name '" << typeName << "' not found." << std::endl; + returnValue = 2; + } + + context->release(); + } + else + { + RMInit::logOut << "Error: client not registered." << std::endl; + returnValue = 1; + } + + RMDBGEXIT( 4, RMDebug::module_servercomm, "ServerComm", "startInsertPersMDD" ) + return returnValue; +} + + +/************************************************************************* + * Method name...: executeQuery( unsigned long callingClientId, + * const char* query + * ExecuteQueryRes &returnStructure ) + ************************************************************************/ +unsigned short +ServerComm::executeQuery( unsigned long callingClientId, + const char* query, + ExecuteQueryRes &returnStructure ) +{ + RMDBGENTER( 4, RMDebug::module_servercomm, "ServerComm", "executeQuery" ) + unsigned short returnValue=0; + + // set all to zero as default. They are not really applicable here. + returnStructure.errorNo = 0; + returnStructure.lineNo = 0; + returnStructure.columnNo = 0; + + RMInit::logOut << "Request: executeQquery '" << query << "'..." << std::flush; + + ClientTblElt* context = getClientContext( callingClientId ); + + if( context != 0 ) + { +#ifdef RMANBENCHMARK + RMTimer* queryOptimizationTimer = 0; + + Tile::relTimer.start(); + Tile::relTimer.pause(); + Tile::opTimer.start(); + Tile::opTimer.pause(); + ZLibCompression::decompTimer.start(); + ZLibCompression::decompTimer.pause(); + ZLibCompression::compTimer.start(); + ZLibCompression::compTimer.pause(); + + if( RManBenchmark > 0 ) + RMInit::bmOut << "Query: " << query << std::endl; +#endif + +#ifdef PURIFY + purify_printf( "%s\n", query ); +#endif + + // delete old transfer collection/iterator + context->releaseTransferStructures(); + + // + // execute the query + // + +#ifdef RMANBENCHMARK + if( RManBenchmark > 0 ) + queryOptimizationTimer = new RMTimer("ServerComm", "optimization time"); +#endif + + + + QueryTree* qtree = new QueryTree(); // create a query tree object... + parseQueryTree = qtree; // ...and assign it to the global parse query tree pointer; + + currentClientTblElt = context; // assign current client table element (temporary) + + int ppRet = 0; + int parserRet = 0; + + udfEnabled = 0; // Forced for RNP, but only temporary... + if(udfEnabled) + { + // + // preprocess + // + RMInit::logOut << "preprocessing..." << std::flush; + ppInBuf = (char *)query; + ppreset(); + ppRet = ppparse(); + + RMInit::dbgOut << "new query: '" << ppOutBuf << "'..." << std::flush; + + // initialize the input string parameters + beginParseString = ppOutBuf; + iterParseString = ppOutBuf; + } + else + { + beginParseString = (char*)query; + iterParseString = (char*)query; + } + + yyreset(); + + RMInit::logOut << "parsing..." << std::flush; + + parserRet=yyparse(0); + if((ppRet == 0) && (parserRet == 0)) + { + try + { + RMDBGIF(1, RMDebug::module_servercomm, "ServerComm::executeQuery", \ + qtree->printTree( 2, RMInit::logOut); + ); + + RMInit::logOut << "checking semantics..." << std::flush; + qtree->checkSemantics(); + + unsigned int localOptimizationLevel = globalOptimizationLevel < qtree->getOptimizationLevel() ? + globalOptimizationLevel : qtree->getOptimizationLevel(); + + RMInit::logOut << "optimizing (level " << localOptimizationLevel << ")..." << std::flush; + qtree->optimize( localOptimizationLevel ); + + RMDBGIF(1, RMDebug::module_servercomm, "ServerComm::executeQuery", \ + qtree->printTree( 2, RMInit::logOut ); + ); + +#ifdef RMANBENCHMARK + if( queryOptimizationTimer ) + { + delete queryOptimizationTimer; + queryOptimizationTimer = 0; + } + + if( RManBenchmark > 0 ) + context->evaluationTimer = new RMTimer("ServerComm", "evaluation time "); +#endif + qtree->printTree( 2, std::cout ); + //qtree->checkSemantics(); + //qtree->printTree( 2, std::cout ); + RMInit::logOut << "evaluating..." << std::flush; + context->transferData = qtree->evaluateRetrieval(); + } + catch( ParseInfo& info ) + { + // this is the old error handling which has been here for quite some time + // dealing with errors when release data + context->releaseTransferStructures(); + + returnValue = 5; // execution error + + // set the error values of the return structure + returnStructure.errorNo = info.getErrorNo(); + returnStructure.lineNo = info.getLineNo(); + returnStructure.columnNo = info.getColumnNo(); + returnStructure.token = strdup( info.getToken().c_str() ); + + RMInit::logOut << "Error: cannot parse query (1)." << std::endl; + info.printStatus( RMInit::logOut ); + } + catch( r_Ebase_dbms& myErr ) + { + RMInit::logOut << "Error: base DBMS exception: " << myErr.what() << std::endl; + + // release data + context->releaseTransferStructures(); + context->release(); //!!! + + //delete parser data + parseQueryTree = 0; + currentClientTblElt = 0; + delete qtree; + + returnValue = 42; // general serialisable exception + + throw; + } + catch( r_Error& myErr ) + { + RMInit::logOut << "Error: " << myErr.get_errorno() << " " << myErr.what() << std::endl; + + // release data + context->releaseTransferStructures(); + context->release(); //!!! + + //delete parser data + parseQueryTree = 0; + currentClientTblElt = 0; + delete qtree; + + returnValue = 5; + + throw; + } + catch(std::bad_alloc) + { + RMInit::logOut << "Error: cannot allocate memory." << std::endl; + + // release data + context->releaseTransferStructures(); + context->release(); //!!! + + //delete parser data + parseQueryTree = 0; + currentClientTblElt = 0; + delete qtree; + + throw; + } + catch(...) + { + RMInit::logOut << "Error: unspecific exception." << std::endl; + + context->releaseTransferStructures(); + context->release(); //!!! + + //delete parser data + parseQueryTree = 0; + currentClientTblElt = 0; + delete qtree; + + returnValue = 5; + + throw; + } + + if( returnValue == 0 ) + { + if( context->transferData != 0 ) + { + // create the transfer iterator + context->transferDataIter = new vector::iterator; + *(context->transferDataIter) = context->transferData->begin(); + + // + // set typeName and typeStructure + // + + // The type of first result object is used to determine the type of the result + // collection. + if( *(context->transferDataIter) != context->transferData->end() ) + { + QtData* firstElement = (**(context->transferDataIter)); + + if( firstElement->getDataType() == QT_MDD ) + { + QtMDD* mddObj = (QtMDD*)firstElement; + const BaseType* baseType = mddObj->getMDDObject()->getCellType(); + r_Minterval domain = mddObj->getLoadDomain(); + + MDDType* mddType = new MDDDomainType( "tmp", (BaseType*)baseType, domain ); + SetType* setType = new SetType( "tmp", mddType ); + + returnStructure.typeName = strdup( setType->getTypeName() ); + returnStructure.typeStructure = setType->getTypeStructure(); // no copy + + TypeFactory::addTempType( setType ); + TypeFactory::addTempType( mddType ); + } + else + { + returnValue = 1; // evaluation ok, non-MDD elements + + returnStructure.typeName = strdup(""); + + // hack set type + char* elementType = firstElement->getTypeStructure(); + returnStructure.typeStructure = (char*)mymalloc( strlen(elementType) + 6 ); + sprintf( returnStructure.typeStructure, "set<%s>", elementType ); + free( elementType ); + } + + strcpy(globalHTTPSetTypeStructure, returnStructure.typeStructure); + + RMInit::logOut << MSG_OK << ", result type '" << returnStructure.typeStructure << "', " << context->transferData->size() << " element(s)." << std::endl; + } + else + { + RMInit::logOut << MSG_OK << ", result is empty." << std::endl; + returnValue = 2; // evaluation ok, no elements + + returnStructure.typeName = strdup(""); + returnStructure.typeStructure = strdup(""); + } + } + else + { + RMInit::logOut << MSG_OK << ", result is empty." << std::endl; + returnValue = 2; // evaluation ok, no elements + } + } + } + else + { + if(ppRet) + { + RMInit::logOut << MSG_OK << ",result is empty." << std::endl; + returnValue = 2; // evaluation ok, no elements + } + else // parse error + { + if( parseError ) + { + returnStructure.errorNo = parseError->getErrorNo(); + returnStructure.lineNo = parseError->getLineNo(); + returnStructure.columnNo = parseError->getColumnNo(); + returnStructure.token = strdup( parseError->getToken().c_str() ); + + delete parseError; + parseError = 0; + } + else + { + returnStructure.errorNo = 309; + RMInit::logOut << "Internal Error: Unknown parse error."; + } + + yyreset(); // reset the input buffer of the scanner + + RMInit::logOut << "Error: cannot parse query (2)." << std::endl; + returnValue = 4; + } + } + + parseQueryTree = 0; + currentClientTblElt = 0; + delete qtree; + + // + // done + // + +#ifdef RMANBENCHMARK + if( queryOptimizationTimer ) + delete queryOptimizationTimer; + + // Evaluation timer can not be stopped because some time spent in the transfer + // module is added to this phase. + if( context->evaluationTimer ) + context->evaluationTimer->pause(); + + if( RManBenchmark > 0 ) + context->transferTimer = new RMTimer("ServerComm", "transfer time "); +#endif + + // In case of an error or the result set is empty, no endTransfer() + // is called by the client. + // Therefore, some things have to be release here. + if( returnValue >= 2) + { +#ifdef RMANBENCHMARK + Tile::o2Timer.stop(); + Tile::opTimer.stop(); + Tile::relTimer.stop(); + Tile::opTimer.stop(); + ZLibCompression::decompTimer.stop(); + ZLibCompression::compTimer.stop(); + if( context->evaluationTimer ) + delete context->evaluationTimer; + context->evaluationTimer = 0; + + if( context->transferTimer ) + delete context->transferTimer; + context->transferTimer = 0; + + RMTimer* releaseTimer = 0; + + if( RManBenchmark > 0 ) + releaseTimer = new RMTimer("ServerComm", "release time "); +#endif + + // release transfer collection/iterator + context->releaseTransferStructures(); + +#ifdef RMANBENCHMARK + if( releaseTimer ) + delete releaseTimer; +#endif + } + context->release(); + } + else + { + RMInit::logOut << "Error: client not registered." << std::endl; + returnValue = 3; + } + + RMDBGEXIT( 4, RMDebug::module_servercomm, "ServerComm", "executeQuery" ) + return returnValue; +} + + + +unsigned short +ServerComm::initExecuteUpdate( unsigned long callingClientId ) +{ + RMDBGENTER( 4, RMDebug::module_servercomm, "ServerComm", "initExecuteUpdate" ) + unsigned short returnValue = 0; + + RMInit::logOut << "Request: initExecuteUpdate..." << std::flush; + + ClientTblElt* context = getClientContext( callingClientId ); + + if( context != 0 ) + { + // delete old transfer structures + context->releaseTransferStructures(); + + MDDType* mddType = new MDDType( "tmp" ); + SetType* setType = new SetType( "tmp", mddType ); + + TypeFactory::addTempType( mddType ); + TypeFactory::addTempType( setType ); + + // create a transient collection for storing MDD constants + context->transferColl = new MDDColl( setType ); + + context->release(); + + RMInit::logOut << MSG_OK << std::endl; + } + else + { + RMInit::logOut << "Error: client not registered." << std::endl; + returnValue = 1; + } + + RMDBGEXIT( 4, RMDebug::module_servercomm, "ServerComm", "initExecuteUpdate" ) + return returnValue; +} + + + +unsigned short +ServerComm::startInsertTransMDD( unsigned long callingClientId, + r_Minterval &domain, unsigned long typeLength, + const char* typeName ) +{ + RMDBGENTER( 4, RMDebug::module_servercomm, "ServerComm", "startInsertTransMDD" ) + unsigned short returnValue = 0; + + RMInit::logOut << "Request: startInsertTransMDD type '" + << typeName <<"', domain " << domain << ", cell length " << typeLength << ", " + << domain.cell_count()*typeLength << " bytes..." << std::flush; + + ClientTblElt* context = getClientContext( callingClientId ); + + if( context != 0 ) + { + + RMDBGMIDDLE(1, RMDebug::module_servercomm, "ServerComm", "startInsertTransMDD(...) TRANSFER " << context->transferFormat << ", EXACT " << (bool)context->exactFormat); + + // Determine the type of the MDD to be inserted. + const MDDType* mddType = TypeFactory::mapMDDType( typeName ); + + if( mddType ) + { + if( mddType->getSubtype() != MDDType::MDDONLYTYPE ) + { + MDDBaseType* mddBaseType = (MDDBaseType*)mddType; + + if( !mddType->compatibleWithDomain( &domain ) ) + { + // return error + returnValue = 3; + RMInit::logOut << "Error: MDD type incompatible wrt. domain: " << domain << std::endl; + + context->release(); + + return returnValue; + } + + // create for further insertions + context->transferMDD = new MDDObj( mddBaseType, domain ); + + RMInit::logOut << MSG_OK << std::endl; + } + else + { + RMInit::logOut << "Error: MDD type has no base type." << std::endl; + returnValue = 2; + } + } + else + { + RMInit::logOut << "Error: MDD type not found." << std::endl; + returnValue = 2; + } + + context->release(); + } + else + { + RMInit::logOut << "Error: client not registered." << std::endl; + returnValue = 1; + } + + RMDBGEXIT( 4, RMDebug::module_servercomm, "ServerComm", "startInsertTransMDD" ) + return returnValue; +} + + + +unsigned short +ServerComm::endInsertMDD( unsigned long callingClientId, + int isPersistent ) +{ + RMDBGENTER( 4, RMDebug::module_servercomm, "ServerComm", "endInsertMDD" ) + unsigned short returnValue = 0; + + RMInit::logOut << "Request: endInsertMDD..." << std::flush; + + ClientTblElt* context = getClientContext( callingClientId ); + + if( context != 0 ) + { + if( isPersistent ) + { + // we are finished with this MDD Object, so insert it into the collection + context->transferColl->insert( context->assembleMDD ); + + // reset assembleMDD, because otherwise it is tried to be freed + context->assembleMDD = 0; + + // free transfer structure + context->releaseTransferStructures(); + + // old: context->transferColl->releaseAll(); caused a crash because releaseAll() is not idempotent + } + else + { + // we are finished with this MDD Object, so insert it into the collection + context->transferColl->insert( context->transferMDD ); + + // reset transferMDD + context->transferMDD = 0; + + // Do not delete the transfer structure because the transient set + // of MDD objects will be used as constants for executeUpdate(). + } + + RMInit::logOut << MSG_OK << std::endl; + + context->release(); + } + else + { + RMInit::logOut << "Error: client not registered." << std::endl; + returnValue = 1; + } + + RMDBGEXIT( 4, RMDebug::module_servercomm, "ServerComm", "endInsertMDD" ) + return returnValue; +} + + + +unsigned short +ServerComm::executeUpdate( unsigned long callingClientId, + const char* query, + ExecuteUpdateRes &returnStructure ) +{ + RMDBGENTER( 4, RMDebug::module_servercomm, "ServerComm", "executeUpdate" ) + + RMInit::logOut << "Request: executeUpdate query '" << query << "'..." << std::flush; + +#ifdef RMANBENCHMARK + RMTimer* queryOptimizationTimer = 0; + Tile::relTimer.start(); + Tile::relTimer.pause(); + Tile::opTimer.start(); + Tile::opTimer.pause(); + ZLibCompression::decompTimer.start(); + ZLibCompression::decompTimer.pause(); + ZLibCompression::compTimer.start(); + ZLibCompression::compTimer.pause(); + + if( RManBenchmark > 0 ) + RMInit::bmOut << "Query (update): " << query << std::endl; +#endif + + unsigned short returnValue = 0; + + ClientTblElt* context = getClientContext( callingClientId ); + + if( context != 0 ) + { +#ifdef PURIFY + purify_printf( "%s\n", query ); +#endif + + // + // execute the query + // + +#ifdef RMANBENCHMARK + if( RManBenchmark > 0 ) + queryOptimizationTimer = new RMTimer("ServerComm", "optimization time"); +#endif + + QueryTree* qtree = new QueryTree(); // create a query tree object... + parseQueryTree = qtree; // ...and assign it to the global parse query tree pointer; + + mddConstants = context->transferColl; // assign the mdd constants collection to the global pointer (temporary) + currentClientTblElt = context; // assign current client table element (temporary) + + int ppRet = 0; + udfEnabled = false; // forced for RNP tests + if(udfEnabled) + { + // + // preprocess + // + RMInit::logOut << "preprocessing..." << std::flush; + ppInBuf = (char *)query; + ppreset(); + ppRet = ppparse(); + + if(ppOutBuf) + RMInit::dbgOut << "new query: '" << ppOutBuf << "'" << std::endl; + else + RMInit::dbgOut << "new query: empty." << std::endl; + + // initialize the input string parameters + beginParseString = ppOutBuf; + iterParseString = ppOutBuf; + } + else + { + beginParseString = (char*)query; + iterParseString = (char*)query; + } + + yyreset(); + + RMInit::logOut << "parsing..." << std::flush; + + if( ppRet == 0 && yyparse(0) == 0 ) + { + try + { + RMDBGIF(1, RMDebug::module_servercomm, "ServerComm::executeUpdate", \ + qtree->printTree( 2, RMInit::logOut ); + ); + + RMInit::logOut << "checking semantics..." << std::flush; + + qtree->checkSemantics(); + + // coman: Why is disabled optimization for update query? + // unsigned int localOptimizationLevel = globalOptimizationLevel < qtree->getOptimizationLevel() ? + // globalOptimizationLevel : qtree->getOptimizationLevel(); + + unsigned int localOptimizationLevel = 0; + + RMInit::logOut << "optimizing (level " << localOptimizationLevel << ")..." << std::flush; + qtree->optimize( localOptimizationLevel ); + + RMDBGIF(1, RMDebug::module_servercomm, "ServerComm::executeUpdate", \ + qtree->printTree( 2, RMInit::logOut ); + ); + +#ifdef RMANBENCHMARK + if( queryOptimizationTimer ) + { + delete queryOptimizationTimer; + queryOptimizationTimer = 0; + } + + if( RManBenchmark > 0 ) + context->evaluationTimer = new RMTimer("ServerComm", "evaluation time "); +#endif + + RMInit::logOut << "evaluating..." << std::flush; + qtree->evaluateUpdate(); + + // release data + context->releaseTransferStructures(); + + RMInit::logOut << MSG_OK << std::endl; + } + catch( ParseInfo& info ) + { + // release data + context->releaseTransferStructures(); + + returnValue = 3; // evaluation error + + // set the error values of the return structure + returnStructure.errorNo = info.getErrorNo(); + returnStructure.lineNo = info.getLineNo(); + returnStructure.columnNo = info.getColumnNo(); + returnStructure.token = strdup( info.getToken().c_str() ); + + RMInit::logOut << "Error: cannot parse query (1)." << std::endl; + info.printStatus( RMInit::logOut ); + } + catch(r_Error &err) + { + context->releaseTransferStructures(); + context->release(); + RMInit::logOut << "Error: " << err.get_errorno() << " " << err.what() << std::endl; + throw; + } + } + else + { + if(ppRet) + { + RMInit::logOut << MSG_OK << std::endl; + returnValue = 0; + } + else // parse error + { + if( parseError ) + { + returnStructure.errorNo = parseError->getErrorNo(); + returnStructure.lineNo = parseError->getLineNo(); + returnStructure.columnNo = parseError->getColumnNo(); + returnStructure.token = strdup( parseError->getToken().c_str() ); + + delete parseError; + + RMInit::logOut << "Error: cannot parse query (2)." << std::endl; + parseError = 0; + } + else + { + returnStructure.errorNo = 309; + RMInit::logOut << "Error: unspecific internal parser error." << endl; + } + + yyreset(); // reset the input buffer of the scanner + + returnValue = 2; + } + } + + parseQueryTree = 0; + mddConstants = 0; + currentClientTblElt = 0; + delete qtree; + + // delete set of mdd constants + context->releaseTransferStructures(); + + // + // done + // + + context->release(); + } + else + { + RMInit::logOut << "Error: client not registered." << std::endl; + returnValue = 1; + } + + +#ifdef RMANBENCHMARK + if( queryOptimizationTimer ) + delete queryOptimizationTimer; + + // stop evaluation timer + if( context->evaluationTimer ) + { + delete context->evaluationTimer; + context->evaluationTimer = 0; + } + + Tile::opTimer.stop(); + Tile::relTimer.stop(); + ZLibCompression::decompTimer.stop(); + ZLibCompression::compTimer.stop(); +#endif + + RMDBGEXIT( 4, RMDebug::module_servercomm, "ServerComm", "executeUpdate" ) + return returnValue; +} + + + +unsigned short +ServerComm::getCollByName( unsigned long callingClientId, + const char* collName, + char* &typeName, + char* &typeStructure, + r_OId &oid ) +{ + RMDBGENTER( 4, RMDebug::module_servercomm, "ServerComm", "ServerComm::getCollByName" ) + + RMInit::logOut << "Request: getCollByName '" << collName << "'..." << std::flush; + + unsigned short returnValue=0; + + ClientTblElt* context = getClientContext( callingClientId ); + + if( context != 0 ) + { + // + // create the actual transfer collenction + // + + // delete old transfer collection/iterator + context->releaseTransferStructures(); + + // create the transfer collection + try + { + context->transferColl = MDDColl::getMDDCollection( collName ); + RMDBGMIDDLE( 4, RMDebug::module_server, "ServerComm", "retrieved mdd collection" ) + } + catch(std::bad_alloc) + { + RMInit::logOut << "Error: cannot allocate memory." << std::endl; + context->release(); //!!! + throw; + } + catch (r_Error& err) + { + RMInit::logOut << "Error " << err.get_errorno() << " " << err.what() << std::endl; + context->release(); //!!! + throw; + } + catch(...) + { + returnValue = 2; // collection name invalid + RMInit::logOut << "Error: unspecific exception." << std::endl; + } + + if( returnValue == 0 ) + { + // create the transfer iterator + context->transferCollIter = context->transferColl->createIterator(); + context->transferCollIter->reset(); + + // set typeName and typeStructure + CollectionType* collectionType = (CollectionType*) context->transferColl->getCollectionType(); + + if( collectionType ) + { + typeName = strdup( collectionType->getTypeName() ); + typeStructure = collectionType->getTypeStructure(); // no copy !!! + + // set oid in case of a persistent collection + if( context->transferColl->isPersistent() ) + { + EOId eOId; + if (context->transferColl->isPersistent()) + { + if (context->transferColl->getEOId(eOId) == true) + oid = r_OId( eOId.getSystemName(), eOId.getBaseName(), eOId.getOId() ); + } + } + RMInit::logOut << MSG_OK << std::endl; + } + else + { + RMInit::logOut << "Warning: cannot obtain collection type information." << endl; + typeName = strdup(""); + typeStructure = strdup(""); + } + + if( !context->transferCollIter->notDone() ) + { + RMInit::logOut << MSG_OK << ", result empty."; + returnValue = 1; + + // delete transfer collection/iterator + context->releaseTransferStructures(); + } + } + + // + // done + // + + context->release(); + } + else + { + RMInit::logOut << "Error: client not registered." << std::endl; + returnValue = 3; + } + + RMDBGEXIT( 4, RMDebug::module_servercomm, "ServerComm", " ServerComm::getCollByName" ) + + return returnValue; +} + + + +unsigned short +ServerComm::getCollByOId( unsigned long callingClientId, + r_OId &oid, + char* &typeName, + char* &typeStructure, + char* &collName ) +{ + RMDBGENTER( 4, RMDebug::module_servercomm, "ServerComm", " ServerComm::getCollByOId" ) + + RMInit::logOut << "Request: getCollByOId " << oid << "..." << std::flush; + + unsigned short returnValue=0; + + ClientTblElt* context = getClientContext( callingClientId ); + + if( context != 0 ) + { + // delete old transfer collection/iterator + context->releaseTransferStructures(); + + // check type and existence of oid + OId oidIf( oid.get_local_oid() ); + OId::OIdType objType = oidIf.getType(); + + if( objType == OId::MDDCOLLOID ) + { + // + // get collection + // + + try + { + RMDBGMIDDLE( 4, RMDebug::module_server, "ServerComm", + std::endl << " execute new PersMDDColl(" << oid << ")" ) + context->transferColl = MDDColl::getMDDCollection(oidIf); + RMDBGMIDDLE( 4, RMDebug::module_server, "ServerComm", " ok" ) + } + catch(std::bad_alloc) + { + RMInit::logOut << "Error: cannot allocate memory." << std::endl; + throw; + } + catch (r_Error& err) + { + RMInit::logOut << "Error " << err.get_errorno() << " " << err.what() << std::endl; + throw; + } + catch(...) // not found (?) + { + returnValue = 2; + RMInit::logOut << "Error: unspecific exception." << endl; + } + + // + // create the actual transfer collenction + // + + if( returnValue == 0 ) + { + // get collection name + collName = strdup(context->transferColl->getName()); + + // create the transfer iterator + context->transferCollIter = context->transferColl->createIterator(); + context->transferCollIter->reset(); + + // set typeName and typeStructure + CollectionType* collectionType = (CollectionType*) context->transferColl->getCollectionType(); + + if( collectionType ) + { + typeName = strdup( collectionType->getTypeName() ); + typeStructure = collectionType->getTypeStructure(); // no copy !!! + + // set oid in case of a persistent collection + if( context->transferColl->isPersistent() ) + { + EOId eOId; + + if (context->transferColl->getEOId(eOId) == true) + oid = r_OId( eOId.getSystemName(), eOId.getBaseName(), eOId.getOId() ); + } + RMInit::logOut << MSG_OK << std::endl; + } + else + { + RMInit::logOut << MSG_OK << ", but warning: cannot obtain type information." << endl; + typeName = strdup(""); + typeStructure = strdup(""); + } + + if( !context->transferCollIter->notDone() ) + { + RMInit::logOut << MSG_OK << ", result empty." << endl; + returnValue = 1; + + // delete transfer collection/iterator + context->releaseTransferStructures(); + } + } + + } + else + { + returnValue = 2; // oid does not belong to a collection object + RMInit::logOut << "Error: oid does not belong to a collection object." << std::endl; + } + + // + // done + // + + context->release(); + } + else + { + RMInit::logOut << "Error: client not registered." << std::endl; + returnValue = 3; + } + + RMDBGEXIT( 4, RMDebug::module_servercomm, "ServerComm", " ServerComm::getCollByOId" ) + + return returnValue; +} + + + +unsigned short +ServerComm::getCollOIdsByName( unsigned long callingClientId, + const char* collName, + char* &typeName, + char* &typeStructure, + r_OId &oid, + RPCOIdEntry* &oidTable, + unsigned int &oidTableSize ) +{ + RMDBGENTER( 4, RMDebug::module_servercomm, "ServerComm", "getCollOIdsByName" ) + + RMInit::logOut << "Request: getCollOIdsByName '" << collName << "'..." << std::flush; + + unsigned short returnValue=0; + + ClientTblElt* context = getClientContext( callingClientId ); + + if( context != 0 ) + { + // + // get collection + // + + MDDColl* coll = 0; + MDDColl* almost = 0; + + try + { + RMDBGMIDDLE( 4, RMDebug::module_server, "ServerComm", "retrieving collection " << collName) + almost = MDDColl::getMDDCollection( collName ); + RMDBGMIDDLE( 4, RMDebug::module_server, "ServerComm", "retrieved collection " << collName) + if (!almost->isPersistent()) + { + RMDBGMIDDLE( 4, RMDebug::module_server, "ServerComm", "retrieved system collection") + RMInit::logOut << "Error: trying to get oid of system collection: " << collName << std::endl; + throw r_Error(SYSTEM_COLLECTION_HAS_NO_OID); + } + else + { + RMDBGMIDDLE( 4, RMDebug::module_server, "ServerComm", "retrieved persistent collection") + coll = (MDDColl*)almost; + } + } + catch(std::bad_alloc) + { + RMInit::logOut << "Error: cannot allocate memory." << std::endl; + throw; + } + catch (r_Error& err) + { + RMDBGMIDDLE( 4, RMDebug::module_server, "ServerComm", "caught exception") + RMInit::logOut << "Error " << err.get_errorno() << ": " << err.what() << std::endl; + returnValue = 2; // collection name invalid + } + catch(...) + { + RMDBGMIDDLE( 4, RMDebug::module_server, "ServerComm", "caught exception") + returnValue = 2; // collection name invalid + RMInit::logOut << "Error: unspecific exception." << std::endl; + } + RMDBGMIDDLE( 4, RMDebug::module_server, "ServerComm", "after exception catching") + + if( returnValue == 0 ) + { + // set typeName and typeStructure + CollectionType* collectionType = (CollectionType*) coll->getCollectionType(); + + if( collectionType ) + { + typeName = strdup( collectionType->getTypeName() ); + typeStructure = collectionType->getTypeStructure(); // no copy !!! + + // set oid + EOId eOId; + + if (coll->getEOId(eOId) == true) + oid = r_OId( eOId.getSystemName(), eOId.getBaseName(), eOId.getOId() ); + } + else + { + RMInit::logOut << "Warning: no type information available..." << std::flush; + typeName = strdup(""); + typeStructure = strdup(""); + } + + if( coll->getCardinality() ) + { + // create iterator + MDDCollIter* collIter = coll->createIterator(); + int i; + + oidTableSize = coll->getCardinality(); + oidTable = (RPCOIdEntry*) mymalloc( sizeof(RPCOIdEntry) * oidTableSize ); + + TALK( oidTableSize << " elements..." ); + + for( collIter->reset(), i=0; collIter->notDone(); collIter->advance(), i++ ) + { + MDDObj* mddObj = collIter->getElement(); + + if( mddObj->isPersistent() ) + { + EOId eOId; + + if( ((MDDObj*)mddObj)->getEOId( &eOId ) == 0 ) + oidTable[i].oid = strdup( r_OId( eOId.getSystemName(), eOId.getBaseName(), eOId.getOId() ).get_string_representation() ); + else + oidTable[i].oid = strdup(""); + mddObj = 0; + } + else + oidTable[i].oid = strdup(""); + } + + delete collIter; + + RMInit::logOut << MSG_OK << ", " << coll->getCardinality() << " result(s)." << endl; + } + else + { + RMInit::logOut << MSG_OK << ", result empty." << endl; + returnValue = 1; + } + + delete coll; + } + + // + // done + // + + context->release(); + } + else + { + RMInit::logOut << "Error: client not registered." << std::endl; + returnValue = 3; + } + + RMDBGEXIT( 4, RMDebug::module_servercomm, "ServerComm", "getCollOIdsByName " << returnValue) + + return returnValue; +} + + +unsigned short +ServerComm::getCollOIdsByOId( unsigned long callingClientId, + r_OId &oid, + char* &typeName, + char* &typeStructure, + RPCOIdEntry* &oidTable, + unsigned int &oidTableSize, + char* &collName ) +{ + RMDBGENTER( 4, RMDebug::module_servercomm, "ServerComm", "getCollOIdsByOId" ) + + RMInit::logOut << "Request: getCollOIdsByOId " << oid << "..." << std::flush; + + unsigned short returnValue=0; + + ClientTblElt* context = getClientContext( callingClientId ); + + if( context != 0 ) + { + // check type and existence of oid + OId oidIf( oid.get_local_oid() ); + OId::OIdType objType = oidIf.getType(); + + if( objType == OId::MDDCOLLOID ) + { + // + // get collection + // + + MDDColl* coll = 0; + + try + { + RMDBGMIDDLE( 4, RMDebug::module_server, "ServerComm", "get mdd coll by oid " << oidIf) + coll = MDDColl::getMDDCollection(oidIf); + RMDBGMIDDLE( 4, RMDebug::module_server, "ServerComm", "retrieved mdd coll" ) + } + catch(std::bad_alloc) + { + RMInit::logOut << "Error: cannot allocate memory." << std::endl; + throw; + } + catch (r_Error& err) + { + RMInit::logOut << "Error " << err.get_errorno() << ": " << err.what() << std::endl; + returnValue = 2; // collection name invalid + if (err.get_kind() != r_Error::r_Error_RefNull) + throw; + } + catch(...) + { + returnValue = 2; // collection name invalid + RMInit::logOut << "Error: unknown collection name." << std::endl; + } + + if( returnValue == 0 ) + { + // get collection name + collName = strdup(coll->getName()); + + // set typeName and typeStructure + CollectionType* collectionType = (CollectionType*) coll->getCollectionType(); + + if( collectionType ) + { + typeName = strdup( collectionType->getTypeName() ); + typeStructure = collectionType->getTypeStructure(); // no copy !!! + + // set oid + EOId eOId; + + if (coll->getEOId(eOId) == true) + oid = r_OId( eOId.getSystemName(), eOId.getBaseName(), eOId.getOId() ); + } + else + { + RMInit::logOut << "Warning: no type information available..." << std::flush; + typeName = strdup(""); + typeStructure = strdup(""); + } + + if( coll->getCardinality() ) + { + // create iterator + MDDCollIter* collIter = coll->createIterator(); + int i; + + oidTableSize = coll->getCardinality(); + oidTable = (RPCOIdEntry*) mymalloc( sizeof(RPCOIdEntry) * oidTableSize ); + + TALK( oidTableSize << " elements..." ); + + for( collIter->reset(), i=0; collIter->notDone(); collIter->advance(), i++ ) + { + MDDObj* mddObj = collIter->getElement(); + + if( mddObj->isPersistent() ) + { + EOId eOId; + + if( ((MDDObj*)mddObj)->getEOId( &eOId ) == 0 ) + oidTable[i].oid = strdup( r_OId( eOId.getSystemName(), eOId.getBaseName(), eOId.getOId() ).get_string_representation() ); + else + oidTable[i].oid = strdup(""); + } + else + oidTable[i].oid = strdup(""); + } + + delete collIter; + //coll->releaseAll(); + + RMInit::logOut << MSG_OK << ", " << coll->getCardinality() << " result(s)." << endl; + } + else + { + RMInit::logOut << MSG_OK << ", result empty." << endl; + returnValue = 1; + } + + delete coll; + } + } + else + { + returnValue = 2; // oid does not belong to a collection object + RMInit::logOut << "Error: not a collection oid: " << oid << std::endl; + } + + // + // done + // + + context->release(); + } + else + { + RMInit::logOut << "Error: client not registered." << std::endl; + returnValue = 3; + } + + RMDBGEXIT( 4, RMDebug::module_servercomm, "ServerComm", "getCollOIdsByOId " << returnValue) + return returnValue; +} + + + +unsigned short +ServerComm::getNextMDD( unsigned long callingClientId, + r_Minterval &mddDomain, + char* &typeName, + char* &typeStructure, + r_OId &oid, + unsigned short ¤tFormat ) +{ + RMDBGENTER( 4, RMDebug::module_servercomm, "ServerComm", "getNextMDD" ) + + RMInit::logOut << "Request: getNextMDD..." << std::flush; + + unsigned short returnValue = 0; + + ClientTblElt* context = getClientContext( callingClientId ); + + if( context != 0 ) + { + try + { + if( context->transferData && context->transferDataIter && *(context->transferDataIter) != context->transferData->end() ) + { + // + // convert the mdd to transfer to rpc data structures + // + + // get the MDD object to be transfered + QtMDD* mddData = (QtMDD*) **(context->transferDataIter); + MDDObj* mddObj = mddData->getMDDObject(); + + // initialize mddDomain to give it back + mddDomain = mddData->getLoadDomain(); + + TALK( "domain " << mddDomain ); + + // + // initialize tiles to transfer + // + +#ifdef RMANBENCHMARK + // pause transfer timer and resume evaluation timer + if( context->transferTimer ) + context->transferTimer->pause(); + if( context->evaluationTimer ) + context->evaluationTimer->resume(); +#endif + + if( mddObj->getCurrentDomain() == mddData->getLoadDomain() ) + context->transTiles = mddObj->getTiles(); + else + { + // If the load domain is different from the current domain, we have + // a persitent MDD object. The border tiles have to be cut (and + // therefore copied) in order to be ready for transfering them. + // These temporary border tiles are added to the deletableTiles list + // which is deleted at the end. + + context->transTiles = mddObj->intersect( mddData->getLoadDomain() ); + + // iterate over the tiles + for( vector::iterator iter = context->transTiles->begin(); iter != context->transTiles->end(); iter++ ) + { + // get relevant area of source tile + r_Minterval sourceTileDomain( mddData->getLoadDomain().create_intersection( (*iter)->getDomain() ) ); + + if( sourceTileDomain != (*iter)->getDomain() ) + { + // create a new transient tile and copy the transient data + Tile* newTransTile = new Tile( sourceTileDomain, mddObj->getCellType() ); + newTransTile->copyTile( sourceTileDomain, *iter, sourceTileDomain ); + + // replace the tile in the list with the new one + *iter = newTransTile; + + // add the new tile to deleteableTiles + if( !(context->deletableTiles) ) + context->deletableTiles = new vector(); + context->deletableTiles->push_back( newTransTile ); + } + } + } + +#ifdef RMANBENCHMARK + // In order to be sure that reading tiles from disk is done + // in the evaluation phase, the contents pointers of each tile + // are got. + char* benchmarkPointer; + + for( vector::iterator benchmarkIter = context->transTiles->begin(); + benchmarkIter != context->transTiles->end(); benchmarkIter++ ) + benchmarkPointer = (*benchmarkIter)->getContents(); + + // pause evaluation timer and resume transfer timer + if( context->evaluationTimer ) + context->evaluationTimer->pause(); + if( context->transferTimer ) + context->transferTimer->resume(); +#endif + + // initialize tile iterator + context->tileIter = new vector::iterator; + *(context->tileIter) = context->transTiles->begin(); + + const BaseType* baseType = mddObj->getCellType(); + + TALK( "cell length " << baseType->getSize() ); + + // + // set typeName and typeStructure + // + // old: typeName = strdup( mddObj->getCellTypeName() ); not known for the moment being + typeName = strdup(""); + + // create a temporary mdd type for the moment being + r_Minterval typeDomain( mddData->getLoadDomain() ); + MDDType* mddType = new MDDDomainType( "tmp", (BaseType*)baseType, typeDomain ); + TypeFactory::addTempType( mddType ); + + typeStructure = mddType->getTypeStructure(); // no copy !!! + + // I'm not sure about this code... +#if 0 + // determine data format from the 1st tile + if( context->transTiles->size() && (*(context->transTiles))[0]->getDataFormat() == r_TIFF ) + currentFormat = r_TIFF; + else + currentFormat = r_Array; +#else + if (context->transTiles->size()) + currentFormat = (*(context->transTiles))[0]->getDataFormat(); + else + currentFormat = r_Array; +#endif + + // set oid in case of persistent MDD objects + if( mddObj->isPersistent() ) + { + EOId eOId; + + if( ((MDDObj*)mddObj)->getEOId( &eOId ) == 0 ) + oid = r_OId( eOId.getSystemName(), eOId.getBaseName(), eOId.getOId() ); + } + + // + // + // + + if( context->transTiles->size() > 0 ) + { + RMInit::logOut << MSG_OK << ", " << context->transTiles->size() << " more tile(s)" << endl; + } + else // context->transTiles->size() == 0 + { + returnValue = 2; + RMInit::logOut << "Error: no tiles in MDD object." << std::endl; + } + + context->totalTransferedSize = 0; + context->totalRawSize = 0; + } + else + { + if( context->transferDataIter && *(context->transferDataIter) == context->transferData->end() ) + { + returnValue = 1; // nothing left in the collection + RMInit::logOut << MSG_OK << ", no more tiles." << std::endl; + context->releaseTransferStructures(); + } + else + { + returnValue = 2; // no actual transfer collection + RMInit::logOut << "Error: no transfer collection. " << std::endl; + } + } + + // + // done + // + + context->release(); + } + catch( r_Ebase_dbms& myErr ) + { + RMInit::logOut << "Error: base DBMS exception (kind " << myErr.get_kind() << ", errno " << myErr.get_errorno() << ") " << myErr.what() << std::endl; + returnValue = 42; + throw; + } + catch( r_Error& myErr ) + { + RMInit::logOut << "Error: (kind " << myErr.get_kind() << ", errno " << myErr.get_errorno() << ") " << myErr.what() << std::endl; + throw; + } + catch(std::bad_alloc) + { + RMInit::logOut << "Error: cannot allocate memory." << std::endl; + throw; + } + catch(...) + { + RMInit::logOut << "Error: unspecified exception." << std::endl; + } + } + else + { + RMInit::logOut << "Error: client not registered." << std::endl; + returnValue = 2; + } + + + RMDBGEXIT( 4, RMDebug::module_servercomm, "ServerComm", "getNextMDD" ) + + return returnValue; +} + + +unsigned short +ServerComm::getNextElement( unsigned long callingClientId, + char* &buffer, + unsigned int &bufferSize) +{ + RMDBGENTER(1, RMDebug::module_servercomm, "ServerComm", "getNextElement(...)") + + RMInit::logOut << "Request: getNextElement..." << std::flush; + + unsigned short returnValue = 0; + + ClientTblElt* context = getClientContext( callingClientId ); + + if( context != 0 ) + { + RMDBGMIDDLE(1, RMDebug::module_servercomm, "ServerComm", "getNextElement(...) TRANSFER " << context->transferFormat << ", EXACT " << (bool)context->exactFormat); + + if( context->transferData && context->transferDataIter && + *(context->transferDataIter) != context->transferData->end() ) + { + + // + // convert data element to rpc data structures + // + // Buffer is allocated and has to be freed by the caller using free(). + + // get next object to be transfered + try + { + QtData* dataObj = **(context->transferDataIter); + + switch( dataObj->getDataType() ) + { + case QT_STRING: + { + QtStringData* stringDataObj = (QtStringData*)dataObj; + bufferSize = stringDataObj->getStringData().length(); + buffer = (char*)mymalloc( bufferSize ); + memcpy( (void*)buffer, (void*)stringDataObj->getStringData().c_str(), bufferSize ); + } + break; + case QT_INTERVAL: + { + QtIntervalData* intervalDataObj = (QtIntervalData*)dataObj; + char* stringData = intervalDataObj->getIntervalData().get_string_representation(); + bufferSize = strlen( stringData ); + buffer = (char*)mymalloc( bufferSize ); + memcpy( (void*)buffer, (void*)stringData, bufferSize ); + free( stringData ); + } + break; + case QT_MINTERVAL: + { + QtMintervalData* mintervalDataObj = (QtMintervalData*)dataObj; + char* stringData = mintervalDataObj->getMintervalData().get_string_representation(); + bufferSize = strlen( stringData ); + buffer = (char*)mymalloc( bufferSize ); + memcpy( (void*)buffer, (void*)stringData, bufferSize ); + free( stringData ); + } + break; + case QT_POINT: + { + QtPointData* pointDataObj = (QtPointData*)dataObj; + char* stringData = pointDataObj->getPointData().get_string_representation(); + bufferSize = strlen( stringData ); + buffer = (char*)mymalloc( bufferSize ); + memcpy( (void*)buffer, (void*)stringData, bufferSize ); + + free( stringData ); + } + break; + default: + if( dataObj->isScalarData() ) + { + QtScalarData* scalarDataObj = (QtScalarData*)dataObj; + bufferSize = scalarDataObj->getValueType()->getSize(); + buffer = (char*)mymalloc( bufferSize ); + memcpy( (void*)buffer, (void*)scalarDataObj->getValueBuffer(), bufferSize ); + // server endianess + r_Endian::r_Endianness serverEndian = r_Endian::get_endianness(); + + // change endianess if necessary + // currently only one client is active at one time + // if((context->clientId == 1) && (strcmp(context->clientIdText, ServerComm::HTTPCLIENT) == 0) && (serverEndian != r_Endian::r_Endian_Big)) + if( (strcmp(context->clientIdText, ServerComm::HTTPCLIENT) == 0) && (serverEndian != r_Endian::r_Endian_Big)) + { + RMInit::logOut << "changing endianness..." << std::flush; + // calling client is a http-client(java -> always BigEndian) and server has LittleEndian + switch(scalarDataObj->getDataType()) + { + case QT_USHORT: + { + r_UShort tmp = *(r_UShort*)buffer; + *(r_UShort*)buffer = r_Endian::swap(tmp); + } + break; + + case QT_SHORT: + { + r_Short tmp = *(r_Short*)buffer; + *(r_Short*)buffer = r_Endian::swap(tmp); + } + break; + + case QT_LONG: + { + r_Long tmp = *(r_Long*)buffer; + *(r_Long*)buffer = r_Endian::swap(tmp); + } + break; + + case QT_ULONG: + { + r_ULong tmp = *(r_ULong*)buffer; + *(r_ULong*)buffer = r_Endian::swap(tmp); + } + break; + + case QT_FLOAT: + { + r_Float tmp = *(r_Float*)buffer; + *(r_Float*)buffer = r_Endian::swap(tmp); + } + break; + + case QT_DOUBLE: + { + r_Double tmp = *(r_Double*)buffer; + *(r_Double*)buffer = r_Endian::swap(tmp); + } + break; + + default: + { + RMDBGENTER( 0, RMDebug::module_servercomm, "ServerComm", "getNextElement(...) bad dataType " << scalarDataObj->getDataType()); + } + break; + } + } + } + break; + } + } + catch( r_Ebase_dbms& myErr) + { + RMInit::logOut << "Error: base BMS exception (kind " << myErr.get_kind() << ", errno " << myErr.get_errorno() << ") " << myErr.what() << std::endl; + throw; + } + catch (r_Error& err) + { + RMInit::logOut << "Error: exception (kind " << err.get_kind() << ", errno " << err.get_errorno() << ") " << err.what() << std::endl; + throw; + } + + // increment list iterator + (*(context->transferDataIter))++; + + if( *(context->transferDataIter) != context->transferData->end() ) + { + returnValue = 0; + RMInit::logOut << MSG_OK << ", some more tile(s) left." << endl; + } + else + { + returnValue = 1; + RMInit::logOut << MSG_OK << ", no more tiles." << std::endl; + } + } + else + { + if( context->transferDataIter && *(context->transferDataIter) == context->transferData->end() ) + { + returnValue = 1; // nothing left in the collection + TALK( "nothing left..." << MSG_OK ); + context->releaseTransferStructures(); + } + else + { + returnValue = 2; // no actual transfer collection + RMInit::logOut << "Error: no transfer collection." << endl; + } + } + + // + // done + // + + context->release(); + } + else + { + RMInit::logOut << "Error: client not registered." << std::endl; + returnValue = 2; + } + + RMDBGEXIT(1, RMDebug::module_servercomm, "ServerComm", "getNextElement(...)") + return returnValue; +} + + + +unsigned short +ServerComm::getMDDByOId( unsigned long callingClientId, + r_OId &oid, + r_Minterval &mddDomain, + char* &typeName, + char* &typeStructure, + unsigned short ¤tFormat ) +{ + RMDBGENTER( 4, RMDebug::module_servercomm, "ServerComm", "getMDDByOId" ) + + RMInit::logOut << "Request: getMDDByOId with oid " << oid << "..." << std::flush; + + unsigned short returnValue = 0; + + ClientTblElt* context = getClientContext( callingClientId ); + + if( context != 0 ) + { + // delete old transfer collection/iterator + context->releaseTransferStructures(); + + // check type and existence of oid + OId oidIf( oid.get_local_oid() ); + OId::OIdType objType = oidIf.getType(); + + if( objType == OId::MDDOID ) + { + // get MDD object + try + { + context->transferMDD = new MDDObj( oidIf ); + } + catch(std::bad_alloc) + { + RMInit::logOut << "Error: cannot allocate memory." << std::endl; + context->release(); + throw; + } + catch (r_Error& err) + { + RMInit::logOut << "Error: (kind " << err.get_kind() << ", errno " << err.get_errorno() << ") " << err.what() << std::endl; + context->release(); + throw; + } + catch(...) + { + returnValue = 2; + RMInit::logOut << "Error: unspecified exception." << endl; + } + + if( !returnValue ) + { + // + // convert the mdd to transfer to rpc data structures + // + + // initialize mddDomain to give it back + mddDomain = context->transferMDD->getCurrentDomain(); + + TALK( "domain " << mddDomain ); + + // initialize context fields + context->transTiles = context->transferMDD->getTiles(); + context->tileIter = new vector::iterator; + *(context->tileIter) = context->transTiles->begin(); + + const BaseType* baseType = context->transferMDD->getCellType(); + + TALK( "cell length " << baseType->getSize() ); + + // + // set typeName and typeStructure + // + // old: typeName = strdup( context->transferMDD->getCellTypeName() ); not known for the moment being + + typeName = strdup(""); + + // create a temporary mdd type for the moment being + MDDType* mddType = new MDDDomainType( "tmp", (BaseType*)baseType, context->transferMDD->getCurrentDomain() ); + TypeFactory::addTempType( mddType ); + + typeStructure = mddType->getTypeStructure(); // no copy !!! + + // I'm not sure about this code either +#if 0 + // determine data format from the 1st tile + if( context->transTiles->size() && (*(context->transTiles))[0]->getDataFormat() == r_TIFF ) + currentFormat = r_TIFF; + else + currentFormat = r_Array; +#else + if (context->transTiles->size()) + currentFormat = (*(context->transTiles))[0]->getDataFormat(); + else + currentFormat = r_Array; +#endif + + // set oid in case of persistent MDD objects + if( context->transferMDD->isPersistent() ) + { + EOId eOId; + + if( ((MDDObj*)(context->transferMDD))->getEOId( &eOId ) == 0 ) + oid = r_OId( eOId.getSystemName(), eOId.getBaseName(), eOId.getOId() ); + } + + // + // + // + + if( context->transTiles->size() > 0 ) + { + RMInit::logOut << MSG_OK << ", got " << context->transTiles->size() << " tile(s)." << endl; + } + else // context->transTiles->size() == 0 + { + returnValue = 3; + RMInit::logOut << "Error: no tiles in MDD object." << endl; + } + } + } + else + { + returnValue = 2; // oid does not belong to an MDD object + RMInit::logOut << "Error: oid does not belong to an MDD object." << endl; + } + + // + // done + // + + context->release(); + } + else + { + RMInit::logOut << "Error: client not registered." << std::endl; + returnValue = 1; // client context not found + } + + context->totalRawSize = 0; + context->totalTransferedSize = 0; + + RMDBGEXIT( 4, RMDebug::module_servercomm, "ServerComm", "getMDDByOId" ) + + return returnValue; +} + + + +unsigned short +ServerComm::getNextTile( unsigned long callingClientId, + RPCMarray** rpcMarray ) +{ + RMDBGENTER( 4, RMDebug::module_servercomm, "ServerComm", "getNextTile" ) + + RMInit::logOut << "Request: getNextTile..." << std::flush; + + unsigned long transOffset = 0; + unsigned long transSize = 0; + unsigned short statusValue = 0; + unsigned short returnValue = 0; + + // initialize the result parameter for failure cases + *rpcMarray = 0; + + ClientTblElt* context = getClientContext( callingClientId ); + + if( context != 0 ) + { + if( context->transTiles && context->tileIter ) + { + Tile* resultTile = **(context->tileIter); + r_Minterval mddDomain = resultTile->getDomain(); + void* useTransData; + unsigned long totalSize; + + // allocate memory for the output parameter rpcMarray + *rpcMarray = (RPCMarray*)mymalloc( sizeof( RPCMarray ) ); + + if ( context->bytesToTransfer == 0 ) + { + // free old data + if (context->encodedData != NULL) + { + free(context->encodedData); + context->encodedData = NULL; + context->encodedSize = 0; + } + + // Transfer compression... + // we have to repack the data if ((tf != cf) && exact) or ((tf != Array) && (cf == Array)) + // where tf = transferFormat, cf = currentFormat. + r_Data_Format tf = context->transferFormat; + r_Data_Format cf = resultTile->getDataFormat(); + RMDBGMIDDLE( 4, RMDebug::module_servercomm, "ServerComm", "getNextTile CURRENT " << cf << ", TRANSFER " << tf << ", EXACT " << (bool)context->exactFormat); + if (((tf != cf) && (context->exactFormat != 0)) || ((tf != r_Array) && (cf == r_Array))) + { + RMDBGMIDDLE(4, RMDebug::module_servercomm, "ServerComm", "repack data from " << cf << " to " << tf) + + //the cast from const char* to char* is okay because the owner flag is set correctly + char *encData = (char*)resultTile->getCompressedContents(); + unsigned long encSize = resultTile->getCompressedSize(); + //the data format could have changed during compression + cf = resultTile->getDataFormat(); + // this method actually repacks everything as needed + if(ensureTileFormat(cf, tf, mddDomain, resultTile->getType(), + encData, encSize, 1, 0, context->transferFormatParams) != ENSURE_TILE_FORMAT_OK) + { + //FIXME returnValue + returnValue = 4; + + RMInit::logOut << "Error: tile format mismatch while fetching next tile." << endl; + (*rpcMarray)->domain = mddDomain.get_string_representation(); + + // allocate memory for the output parameter data and assign its fields + (*rpcMarray)->data.confarray_len = 0; + (*rpcMarray)->data.confarray_val = NULL; + // 3. store cell type length + (*rpcMarray)->cellTypeLength = 0; + + context->release(); + return returnValue; + } + // if the new format is bigger than the old one and we're allowed to, revert to the old one + if ((encSize >= resultTile->getCompressedSize()) && (context->exactFormat == 0)) + { + free( encData ); + } + else + { + context->encodedData = encData; + context->encodedSize = encSize; + } + } + } + + // note: transfer compression affects the current format, not the storage format. + if ( context->encodedData == NULL ) + { + totalSize = resultTile->getCompressedSize(); + //this is bad because useTransData is char* although it is not modified + useTransData = (char*)resultTile->getCompressedContents(); + (*rpcMarray)->currentFormat = resultTile->getDataFormat(); + RMDBGMIDDLE(4, RMDebug::module_servercomm, "ServerComm", "using tile format " << (r_Data_Format)(*rpcMarray)->currentFormat) + } + else + { + totalSize = context->encodedSize; + useTransData = context->encodedData; + (*rpcMarray)->currentFormat = context->transferFormat; + //FILE *fp = fopen("trans_data.raw", "wb"); fwrite(useTransData, 1, totalSize, fp); fclose(fp); + RMDBGMIDDLE(4, RMDebug::module_servercomm, "ServerComm", "using transfer format " << (r_Data_Format)(*rpcMarray)->currentFormat) + } + // Preserve storage format + (*rpcMarray)->storageFormat = resultTile->getDataFormat(); + RMDBGMIDDLE(4, RMDebug::module_servercomm, "ServerComm", "rpc storage " << (r_Data_Format)(*rpcMarray)->storageFormat) + RMDBGMIDDLE(4, RMDebug::module_servercomm, "ServerComm", "rpc current " << (r_Data_Format)(*rpcMarray)->currentFormat) + + transSize = totalSize; + + if( totalSize > maxTransferBufferSize ) + { + // if there is the rest of a tile to transfer, do it! + if( context->bytesToTransfer ) + { + TALK( " resuming block transfer..." ); + transOffset = totalSize - context->bytesToTransfer; + if( context->bytesToTransfer > maxTransferBufferSize ) + { + transSize = maxTransferBufferSize; + statusValue = 1; + } + else + { + transSize = context->bytesToTransfer; + statusValue = 2; + } + + context->bytesToTransfer -= transSize; + } + else // transfer first block of too large tile + { + TALK( " has to be split..." ); + transSize = maxTransferBufferSize; + context->bytesToTransfer = totalSize - transSize; + statusValue = 1; + } + } + else // resultTile->getSize() <= maxTransferBufferSize + statusValue = 3; + + context->totalTransferedSize += transSize; + + // 1. convert domain + (*rpcMarray)->domain = mddDomain.get_string_representation(); + + // 2. copy data pointers + TALK( " domain " << mddDomain << ", " << transSize << " bytes" ); + + // allocate memory for the output parameter data and assign its fields + (*rpcMarray)->data.confarray_len = (unsigned int)transSize; + (*rpcMarray)->data.confarray_val = ((char*)useTransData) + transOffset; + + // 3. store cell type length + (*rpcMarray)->cellTypeLength = resultTile->getType()->getSize(); + + // increment iterator only if tile is transferred completely + if( statusValue > 1 ) + { + context->totalRawSize += resultTile->getSize(); + (*context->tileIter)++; + } + + // delete tile vector and increment transfer collection iterator if tile iterator is exhausted + if( (*context->tileIter) == context->transTiles->end() ) + { + + // delete tile vector transTiles (tiles are deleted when the object is deleted) + if( context->transTiles ) + { + delete context->transTiles; + context->transTiles = 0; + } + + // delete tile iterator + if( context->tileIter ) + { + delete context->tileIter; + context->tileIter = 0; + } + + if( context->transferDataIter ) + { + (*(context->transferDataIter))++; + + if( *(context->transferDataIter) != context->transferData->end() ) + { + returnValue = 1; + TALK( " some MDDs left..." ); + RMInit::logOut << MSG_OK << ", some MDD(s) left." << endl; + } + else + { + // no elements left -> delete collection and iterator + + // Memory of last tile is still needed for the last byte transfer, + // therefore, do not release memory now, but with any next RPC call. + // context->releaseTransferStructures(); + + returnValue = 0; + RMInit::logOut << MSG_OK << ", all MDDs fetched." << endl; + } + } + else + { + returnValue = 0; + RMInit::logOut << MSG_OK << ", MDD transfer complete." << endl; + } + + if ((context->totalTransferedSize != context->totalRawSize) && (context->totalRawSize != 0)) + { + TALK( "(compressed using " << context->transferFormat << " to " << ((r_Double)(100 * context->totalTransferedSize)) / context->totalRawSize << "%) " ); + } + } + else + { + if( statusValue == 1 ) // at least one block in actual tile is left + { + RMInit::logOut << MSG_OK << ", some block(s) left." << endl; + returnValue = 3; + } + else // tiles left in actual MDD + { + RMInit::logOut << MSG_OK << ", some tile(s) left." << endl; + returnValue = 2; + } + } + } + else // no actual transfer collection or nothing left in the collection + { + returnValue = 4; + RMInit::logOut << "Error: no transfer collection or nothing left in collection." << endl; + } + + context->release(); + } + else + { // client context not found + RMInit::logOut << "Error: client not registered." << std::endl; + returnValue = 4; + } + + RMDBGEXIT( 4, RMDebug::module_servercomm, "ServerComm", "getNextTile" ) + return returnValue; +} + + +unsigned short +ServerComm::endTransfer( unsigned long client ) +{ + RMDBGENTER( 4, RMDebug::module_servercomm, "ServerComm", "endTransfer" ) + unsigned short returnValue = 0; + + RMInit::logOut << "Client " << client << " called: endTransfer..." << std::flush; + + ClientTblElt* context = getClientContext( client ); + + if( context ) + { +#ifdef RMANBENCHMARK + Tile::relTimer.stop(); + Tile::opTimer.stop(); + ZLibCompression::decompTimer.stop(); + ZLibCompression::compTimer.stop(); + if( context->evaluationTimer ) delete context->evaluationTimer; + context->evaluationTimer = 0; + if( context->transferTimer ) delete context->transferTimer; + context->transferTimer = 0; + RMTimer* releaseTimer = 0; + + if( RManBenchmark > 0 ) + releaseTimer = new RMTimer("ServerComm", "release time "); +#endif + // release transfer collection/iterator + context->releaseTransferStructures(); + +#ifdef RMANBENCHMARK + if( releaseTimer ) delete releaseTimer; +#endif + + context->release(); + + RMInit::logOut << MSG_OK << std::endl; + } + else + { + RMInit::logOut << "Error: client not registered." << std::endl; + returnValue = 1; + } + + RMDBGEXIT( 4, RMDebug::module_servercomm, "ServerComm", "endTransfer" ) + return returnValue; +} + + + +/************************************************************************* + * Method name...: aliveSignal( unsigned long client ) + ************************************************************************/ +unsigned short +ServerComm::aliveSignal( unsigned long client ) +{ + unsigned short returnValue = 0; + + RMInit::logOut << "Client " << client << " called: endTransfer..." << std::flush; + + ClientTblElt* context = getClientContext( client ); + + if( context ) + { + // set the time of the client's last action to now + context->lastActionTime = time( NULL ); + + returnValue = 1; + + context->release(); + + RMInit::logOut << MSG_OK << std::endl; + } + else + { + RMInit::logOut << "Error: client not registered." << std::endl; + } + + return returnValue; +} + + + +unsigned short +ServerComm::getNewOId( unsigned long callingClientId, + unsigned short objType, + r_OId& oid ) +{ + RMDBGENTER( 4, RMDebug::module_servercomm, "ServerComm", "getNewOId" ) + unsigned short returnValue = 0; + + RMInit::logOut << "Request: getNewOId of type " + << (objType==1?"MDD":"collection") << "..." << std::flush; + + ClientTblElt* context = getClientContext( callingClientId ); + + if( context != 0 ) + { + EOId eOId; + + if( objType == 1 ) + EOId::allocateEOId( eOId, OId::MDDOID ); + else // objType == 2 + EOId::allocateEOId( eOId, OId::MDDCOLLOID ); + + RMDBGMIDDLE( 4, RMDebug::module_servercomm, "ServerComm", "allocated " << eOId) + oid = r_OId( eOId.getSystemName(), eOId.getBaseName(), eOId.getOId() ); + + context->release(); + + RMInit::logOut << MSG_OK << std::endl; + } + else + { + RMInit::logOut << "Error: client not registered." << std::endl; + returnValue = 1; + } + + RMDBGEXIT( 4, RMDebug::module_servercomm, "ServerComm", "getNewOId " << returnValue) + return returnValue; +} + + + +unsigned short +ServerComm::getObjectType( unsigned long callingClientId, + r_OId& oid, + unsigned short &objType ) +{ + RMDBGENTER( 4, RMDebug::module_servercomm, "ServerComm", "getObjectType" ) + unsigned short returnValue = 0; + + RMInit::logOut << "Request: getObjectType by oid " << oid << "..." << std::flush; + + ClientTblElt* context = getClientContext( callingClientId ); + + if( context != 0 ) + { + OId oidIf( oid.get_local_oid() ); + + objType = oidIf.getType(); + + if( objType == OId::INVALID ) + { + // oid not found + RMInit::logOut << "Error: no type for this oid." << std::endl; + returnValue = 2; + } + else + { + RMInit::logOut << "type is " << (objType==1?"MDD":"collection") << "..." << MSG_OK << endl; + } + + context->release(); + } + else + { + RMInit::logOut << "Error: client not registered." << endl; + returnValue = 1; + } + + RMDBGEXIT( 4, RMDebug::module_servercomm, "ServerComm", "getObjectType" ) + return returnValue; +} + + + +unsigned short +ServerComm::getTypeStructure( unsigned long callingClientId, + const char* typeName, + unsigned short typeType, + char* &typeStructure ) +{ + RMDBGENTER( 4, RMDebug::module_servercomm, "ServerComm", "getTypeStructure" ) + unsigned short returnValue = 0; + + RMInit::logOut << "Request: getTypeStructure of type '" << typeName << "'..." << std::flush; + + ClientTblElt* context = getClientContext( callingClientId ); + if (context == 0) + { + RMInit::logOut << "Error: client not registered." << std::endl; + returnValue = 1; + } + + if (returnValue==0 && !transactionActive) + { + RMInit::logOut << "Error: no transaction open." << endl; + returnValue = 1; + } + + if (returnValue==0) + { + if( typeType == 1 ) + { + // get collection type + CollectionType* collType = (CollectionType*)TypeFactory::mapSetType( (char*)typeName ); + + if( collType ) + typeStructure = collType->getTypeStructure(); // no copy + else + returnValue = 2; + } + else if( typeType == 2 ) + { + // get MDD type + const MDDType* mddType = TypeFactory::mapMDDType( typeName ); + + if( mddType ) + typeStructure = mddType->getTypeStructure(); // no copy + else + returnValue = 2; + } + else // base type not implemented + { + returnValue = 2; + } + + if( returnValue == 2 ) + RMInit::logOut << "Error: unknown type." << endl; + else + RMInit::logOut << MSG_OK << endl; + + context->release(); + } + + RMDBGEXIT( 4, RMDebug::module_servercomm, "ServerComm", "getTypeStructure" ) + return returnValue; +} + + +unsigned short +ServerComm::setTransferMode( unsigned long callingClientId, + unsigned short format, + const char* formatParams ) +{ + RMDBGENTER(4, RMDebug::module_servercomm, "ServerComm", "setTransferMode(" << callingClientId << ", " << format << ", " << formatParams) + + RMInit::logOut << "Request: setTransferMode..." << std::flush; + + unsigned short retval = 1; + + ClientTblElt* context = getClientContext( callingClientId ); + + if (context != 0) + { + r_Data_Format fmt = (r_Data_Format)format; + // query compression module whether this is a legal format for transfer compression + if (r_Tile_Compression::check_data_format(fmt) == r_Tile_Compression::INVALID) + { + RMInit::logOut << "Error: invalid transfer compression format: " << format << std::endl; + retval = 2; + } + else + { + if (context->transferFormatParams != NULL) + { + delete [] context->transferFormatParams; + context->transferFormatParams = NULL; + // revert the transfer format strictness + context->exactFormat = 0; + } + if (formatParams != NULL) + { + context->transferFormatParams = new char[strlen(formatParams)+1]; + strcpy(context->transferFormatParams, formatParams); + // extract any occurrences of ``exactformat'' + context->clientParams->process(context->transferFormatParams); + } + context->transferFormat = fmt; + + RMInit::logOut << MSG_OK << std::endl; + retval = 0; + } + context->release(); + RMDBGMIDDLE(1, RMDebug::module_servercomm, "ServerComm", "setTransferMode(...) current transfer format :" << context->transferFormat) + RMDBGMIDDLE(1, RMDebug::module_servercomm, "ServerComm", "setTransferMode(...)current transfer params :" << context->transferFormatParams ); + } + else + { + RMInit::logOut << "Error: client not registered." << std::endl; + retval = 1; + } + + RMDBGEXIT(4, RMDebug::module_servercomm, "ServerComm", "setTransferMode " << retval) + return retval; +} + +unsigned short +ServerComm::setStorageMode( unsigned long callingClientId, + unsigned short format, + const char* formatParams ) +{ + RMDBGENTER(4, RMDebug::module_servercomm, "ServerComm", "setStorageMode(" << callingClientId << ", " << format << ", " << formatParams) + + RMInit::logOut << "Request: setStorageMode..." << std::flush; + + unsigned short retval = 1; + + ClientTblElt* context = getClientContext( callingClientId ); + + if (context != 0) + { + r_Data_Format fmt = (r_Data_Format)format; + if (r_Tile_Compression::check_data_format(fmt) == r_Tile_Compression::INVALID) + { + RMInit::logOut << "Error: invalid storage compression format: " << format << std::endl; + retval = 2; + } + else + { + if (context->storageFormatParams != NULL) + { + delete [] context->storageFormatParams; + context->storageFormatParams = NULL; + } + if (formatParams != NULL) + { + context->storageFormatParams = new char[strlen(formatParams)+1]; + strcpy(context->storageFormatParams, formatParams); + } + context->storageFormat = fmt; + + RMInit::logOut << MSG_OK << std::endl; + retval = 0; + } + context->release(); + RMDBGMIDDLE(1, RMDebug::module_servercomm, "ServerComm", "setStorageMode(...) current storage format :" << context->storageFormat) + RMDBGMIDDLE(1, RMDebug::module_servercomm, "ServerComm", "setStorageMode(...) current storage params :" << context->storageFormatParams) + } + else + { + RMInit::logOut << "Error: client not registered." << std::endl; + retval = 1; + } + + RMDBGEXIT(4, RMDebug::module_servercomm, "ServerComm", "setStorageMode " << retval) + return retval; +} + +int +ServerComm::ensureTileFormat( r_Data_Format &hasFmt, + r_Data_Format needFmt, + const r_Minterval &dom, + const BaseType *type, + char *&data, + unsigned long &size, + int repack, + int owner, + const char *params ) +{ + RMDBGENTER(2, RMDebug::module_servercomm, "ServerComm", "ensureTileFormat(" << hasFmt << ", " << needFmt << ", " << dom << ", " << type->getName() << ", data, " << size << ", repack " << repack << ", owner " << owner << ", params " << params << ")") + int status = ENSURE_TILE_FORMAT_OK; + + RMDBGMIDDLE(2, RMDebug::module_servercomm, "ServerComm", "ensureTileFormat(...) #Size 1=" << size); + + // convert to storage format (this should go into a separate function) + if (hasFmt != needFmt) + { + r_Tile_Compression *engine = NULL; + r_ULong newSize=0; + char *newData=NULL; + + char *tpstruct = type->getTypeStructure(); + r_Base_Type *compType = (r_Base_Type*)(r_Type::get_any_type(tpstruct)); + + // decompress + try + { + engine = r_Tile_Compression::create(hasFmt, dom, compType); + // decompression doesn't change the size variable! + newData = (char*)(engine->decompress(data, size, params)); + delete engine; + + if (newData == NULL) + { + RMInit::logOut << "Error: decompression failed for format '" << hasFmt << "'." << endl; + size = dom.cell_count() * compType->size(); + newData = (char*)mymalloc(size * sizeof(char)); + memset(newData, 0, size); + // FIXME: make it an error!! --PB 2005-aug-25 + RMInit::logOut << "Error fixed by returning empty data of length " << size << " bytes." << std::endl; + } + // do I actually own the data? + if (owner != 0) + free(data); + data = newData; + size = dom.cell_count() * (compType->size()); + hasFmt = r_Array; + } + catch (r_Error &err) + { + RMInit::logOut << "Error: cannot decompress format '" << hasFmt << "' (" << err.get_errorno() << "): " << err.what() << endl; + RMDBGEXIT(2, RMDebug::module_servercomm, "ServerComm", "ensureTileFormat(...) " << ENSURE_TILE_FORMAT_BAD) + status = ENSURE_TILE_FORMAT_BAD; + } + + RMDBGMIDDLE(2, RMDebug::module_servercomm, "ServerComm", "ensureTileFormat(...) #Size 2=" << size); + + if ((status == ENSURE_TILE_FORMAT_OK) && (repack != 0) && (needFmt != r_Array)) + { + try + { + newSize = size; + engine = r_Tile_Compression::create(needFmt, dom, compType); + newData = (char*)(engine->compress(data, newSize, params)); + delete engine; + + if (newData == NULL) + { + RMInit::logOut << "Error: recompression failed for format '" << needFmt << "'." << endl; + RMDBGEXIT(2, RMDebug::module_servercomm, "ServerComm", "ensureTileFormat(...) " << ENSURE_TILE_FORMAT_BAD) + status = ENSURE_TILE_FORMAT_BAD; + } + else + { + free(data); + data = newData; size = newSize; + hasFmt = needFmt; + RMDBGMIDDLE(2, RMDebug::module_servercomm, "ServerComm", "recompressed to " << hasFmt << " size " << size) + } + } + catch (r_Error &err) + { + RMInit::logOut << "Error: unsupported tile format '" << needFmt << "' (" << err.get_errorno() << "): " << err.what() << endl; + RMDBGEXIT(2, RMDebug::module_servercomm, "ServerComm", "ensureTileFormat " << ENSURE_TILE_FORMAT_BAD) + status = ENSURE_TILE_FORMAT_BAD; + } + } + else + { + RMDBGMIDDLE(2, RMDebug::module_servercomm, "ServerComm", "ensureTileFormat(...) do not recompress") + } + delete compType; + free(tpstruct); + } + + RMDBGMIDDLE(2, RMDebug::module_servercomm, "ServerComm", "ensureTileFormat(...) #Size 3=" << size); + RMDBGEXIT(2, RMDebug::module_servercomm, "ServerComm", "ensureTileFormat(...) " << ENSURE_TILE_FORMAT_OK) + return status; +} diff --git a/servercomm/test/Makefile b/servercomm/test/Makefile new file mode 100644 index 0000000..bf035b4 --- /dev/null +++ b/servercomm/test/Makefile @@ -0,0 +1,117 @@ +# -*-Makefile-*- +# +# This file is part of rasdaman community. +# +# Rasdaman community is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Rasdaman community 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with rasdaman community. If not, see . +# +# Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +# rasdaman GmbH. +# +# For more information please see +# or contact Peter Baumann via . # Top Level makefile. This points to the various modules that have to be build +# and/or deployed +# +# MAKEFILE FOR: +# test programs of module servercomm +######################### Definitions ############################ + +# standard include with general options +include $(RMANBASE)/Makefile.inc + +# all test programs +SRCCXX = test_servercomm.cc test_dbcontent.cc test_oid.cc +OBJS = ${SRCCXX:%.cc=%.o} +ALLTESTS = ${SRCCXX:%.cc=%} + +# some additional flags for compiling and linking + +CXXFLAGS += -I$(RMANBASE)/servercomm +LDFLAGS := -I$(RMANBASE)/servercomm $(LDFLAGS) + +# add communication flags +CXXFLAGS += $(COMMCXXFLAGS) +LDFLAGS += $(COMMLDFLAGS) + +# add compile and link options for STL +CXXFLAGS += $(STLCXXFLAGS) +LDFLAGS += $(STLLDFLAGS) + +########################### Targets ############################## + +# test target for servercomm +.PHONY : servercomm +servercomm: adminif raslib test_module test_servercomm + +# test target for dbcontent +.PHONY : dbcontent +dbcontent: adminif raslib test_module test_dbcontent + +.PHONY : adminif +adminif: + cd $(RMANBASE)/reladminif; $(MAKE) + +.PHONY : raslib +raslib: + cd $(RMANBASE)/raslib; $(MAKE) + +.PHONY : qlparser +qlparser: + cd $(RMANBASE)/qlparser; $(MAKE) + +.PHONY : test_module +test_module: + cd $(RMANBASE)/servercomm; $(MAKE) + +test_servercomm: test_servercomm.o \ + $(QLPARSER) \ + $(SERVERCOMM) \ + $(CACHETAMGR) \ + $(MDDIF) \ + $(CATALOGIF) \ + $(INDEXIF) \ + $(INDEXMGR) \ + $(BLOBIF) \ + $(ADMINIF) \ + $(PREPROCESSOR) \ + $(STORAGEMGR) \ + $(TILEMGR) \ + $(RASLIB) \ + $(RELINDEX) \ + $(INDEXIF) + $(CXX) $(LDFLAGS) -o $@ $^ $(QLPARSER) $(INDEXMGR) $(CACHETAMGR) $(RELINDEX) $(RELSTORAGEIF) $(RELADMINIF) $(STORAGEMGR) $(CONVERSION) $(INDEXIF) $(RELCATALOGIF) /usr/local/pgsql/lib/libpq.a /usr/local/pgsql/lib/libecpg.a -lssl -lcrypt /usr/local/pgsql/lib/libpgtypes.a -lm -ljpeg -ltiff -lpng -lmfhdf -ldf -lz -ldl -lcrypto + +test_dbcontent: test_dbcontent.o \ + $(QLPARSER) \ + $(SERVERCOMM) \ + $(RASLIB) \ + $(CACHETAMGR) \ + $(MDDIF) \ + $(CATALOGIF) \ + $(INDEXIF) \ + $(INDEXMGR) \ + $(BLOBIF) \ + $(ADMINIF) + $(CXX) $(LDFLAGS) $(O2LDFLAGS) -o $@ $^ $(QLPARSER) $(INDEXMGR) $(CACHETAMGR) -lm + +test_oid: test_oid.o $(RASLIB) $(CACHETAMGR)\ + $(MDDIF) $(CATALOGIF) $(INDEXIF) $(INDEXMGR) $(BLOBIF) $(ADMINIF) + $(PURIFY) $(CXX) $(O2LDFLAGS) $(LDFLAGS) -o $@ $^ \ + $(RASLIB) -lm -L$(SUPPORT_BASE)/lib -lz + + +# general rules +include $(RMANBASE)/Makefile.rel + +# automatically created dependencies +include Makefile.dep diff --git a/servercomm/test/template_inst.hh b/servercomm/test/template_inst.hh new file mode 100644 index 0000000..569c0bf --- /dev/null +++ b/servercomm/test/template_inst.hh @@ -0,0 +1,138 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see . +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see +* or contact Peter Baumann via . +*/ + +//for rb_tree, select1st +#include +#include +#include +#include +#include + +#if(__GNUC__==2 &&__GNUC_MINOR__==95) +using std::rb_tree; +using std::select1st; +#else +using __gnu_cxx::rb_tree; +using __gnu_cxx::select1st; +#endif + +using std::vector; +using std::pair; + +// commented by Constantin Jucovschi (gcc 3.4+ no longer supports __default_alloc_template) +//using std::__default_alloc_template; +using std::fill_n; + +#include "qlparser/symtab.hh" + +#include "raslib/attribute.hh" +#include "raslib/itertype.hh" +#include "raslib/dlist.hh" + +#include "tile.hh" + +#include "indexmgr/keyobject.hh" + +#include "reladminif/dbref.hh" +#include "reladminif/dbobjectiditerator.hh" + +#include "relblobif/blobtile.hh" +#include "relblobif/dbtile.hh" +#include "relblobif/inlinetile.hh" + +#include "relcatalogif/typeiterator.hh" +#include "relcatalogif/settype.hh" +#include "relcatalogif/structtype.hh" +#include "relcatalogif/mddtype.hh" +#include "relcatalogif/inlineminterval.hh" +#include "relcatalogif/dbminterval.hh" + +#include "relindexif/dbtcindex.hh" +#include "relindexif/hierindex.hh" +#include "relindexif/dbrcindexds.hh" + +#include "relmddif/dbmddobj.hh" +#include "relmddif/dbmddset.hh" + +#include "relstorageif/dbudfds.hh" +#include "relstorageif/dbudfpackageds.hh" +#include "relstorageif/dbstoragelayout.hh" + +template class SymbolTable; + +template class r_IterType; + +template class DBRef; +template class DBRef; +template class DBRef; +template class DBRef; +template class DBRef; +template class DBRef; +template class DBRef; +template class DBRef; +template class DBRef; +template class DBRef; +template class DBRef; +//template class DBRef; +// template bool operator< (const DBRef&, const DBRef&); + +//template TypeIterator; +//template TypeIterator; +template class TypeIterator; +template class DBRef; +template class DBRef; + +template class DBObjectIdIterator; +template class DBObjectIterator; +template class DBObjectIterator; +template class DBObjectIterator; +template class DBObjectIterator; +template class DBRef; +template class DBRef; +template class DBRef; + +template std::ostream& operator<< (const vector&, std::ostream&); +template std::ostream& operator<< (std::ostream &, const vector&); +template std::ostream& operator << (std::ostream& os, const std::vector& list); +template std::ostream& operator << (std::ostream& os, const std::vector& list); + +template class rb_tree, select1st >, less >; +template class rb_tree, select1st >, less >; +template class rb_tree >, select1st > >, less >; +template class rb_tree, select1st >, less >; +template class rb_tree, select1st >, less >; +template class rb_tree, select1st >, less >; +template class rb_tree, select1st >, less >; +template class rb_tree, select1st >, less >; +template class rb_tree, select1st >, less >; +template class vector; +template class vector; +template class vector; +template class vector; +template class vector; +template class vector; +template class vector; +template class vector; + +template class Tile ** fill_n(Tile **, unsigned int, Tile * const &); + diff --git a/servercomm/test/test_dbcontent.cc b/servercomm/test/test_dbcontent.cc new file mode 100644 index 0000000..a0bd6da --- /dev/null +++ b/servercomm/test/test_dbcontent.cc @@ -0,0 +1,161 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see . +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see +* or contact Peter Baumann via . +/ +/** + * SOURCE: test_dbcontent.cc + * + * MODULE: test + * + * PURPOSE: + * Reads the contents of the specified collection and prints + * it on the screen. + * + * COMMENTS: + * none + * +*/ + +#include +#include + +#include "o2template_CC.hxx" // declaration of O2 ref and coll classes + +#include "ulongtype.hh" + +#include "cachetamgr/persmddcoll.hh" +#include "cachetamgr/persmddobj.hh" +#include "cachetamgr/perstile.hh" + +#include "cachetamgr/persmddcolliter.hh" + +#include "adminif.hh" +#include "databaseif.hh" +#include "transactionif.hh" + +#include + +extern char* myExecArgv0 = ""; + +static void testAccessing( const char* collName ); + +/************************************************************* + * Function name.: int main( int argc, char** argv) + * + * Arguments.....: + * argc: number of arguments given to program + * argv: array of char* with arguments + * Return value..: exit status + * Description...: none + ************************************************************/ + +void +main( int ac, char** av) +{ + char baseName[255]; + char collName[255]; + + if( ac > 1 ) + strcpy( baseName, av[1] ); + else + strcpy( baseName, "RasDaBase" ); + + if( ac > 2 ) + strcpy( collName, av[2] ); + else + strcpy( collName, "Images" ); + + pid_t cpid; + cout << "Parent process id is " << getpid() << endl; + + cpid = fork(); + + if( !cpid ) + { + cout << "Child process id is " << getpid() << endl; + // sleep(1); + } + + // variables representing O2 database, ta and session + DatabaseIf database; + TransactionIf ta; + + // don't forget to initialize before using AdminIf! + myExecArgv0 = av[0]; + AdminIf* myAdmin = AdminIf::instance(); + + // connect to the database + cout << getpid() << " Connecting to database " << baseName << "..." << endl; + database.open( baseName ); + + // read coll and print contents + cout << getpid() << " Read collection and print contents..." << endl; + ta.begin(); + testAccessing( collName ); + ta.commit(); + + cout << getpid() << " closing db ... "; cout.flush(); + database.close(); + cout << getpid() << " OK" << endl; + + cout << getpid() << " Ending O2 session..." << endl; + delete myAdmin; + cout << getpid() << " OK" << endl; + + return; +} + + + +/************************************************************* + * Function......: testAccessing() + * + * Arguments.....: none + * Return value..: none + * Description...: reads DirTilesIx's and shows contents + ************************************************************/ + +static void testAccessing( const char* collName ) +{ + PersMDDObj* accessedObj; + + cout << getpid() << " ....testAccessing" << endl; + /* + PersMDDColl objsSet( collName ); + + // To test PersMDDColl::printStatus( ) + objsSet.printStatus( ); + + // To test PersMDDObj::printStatus( ), MDDCollIter::createIterator( ) and + // MDDCollIter methods : + MDDCollIter* objsIt = objsSet.createIterator( ); + + for( int i = 1 ; objsIt->notDone( ); i++, objsIt->advance( )) + { + accessedObj = (PersMDDObj*) objsIt->getElement(); + cout << " --"<printStatus(); + } + delete objsIt; + */ +} + + diff --git a/servercomm/test/test_oid.cc b/servercomm/test/test_oid.cc new file mode 100644 index 0000000..280b499 --- /dev/null +++ b/servercomm/test/test_oid.cc @@ -0,0 +1,229 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see . +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see +* or contact Peter Baumann via . +/ +/** + * SOURCE: test_dbcontent.cc + * + * MODULE: test + * + * PURPOSE: + * Reads the contents of the specified collection and prints + * it on the screen. + * + * COMMENTS: + * none + * +*/ + +#include +#include + +#include "o2template_CC.hxx" // declaration of O2 ref and coll classes + +#include "ulongtype.hh" + +#include "cachetamgr/persmddcoll.hh" +#include "cachetamgr/persmddobj.hh" +#include "cachetamgr/perstile.hh" + +#include "cachetamgr/persmddcolliter.hh" + +#include "adminif.hh" +#include "databaseif.hh" +#include "transactionif.hh" + +#include + +RMINITGLOBALS('C') + +extern char* myExecArgv0 = ""; + +static void +insertObj( char* dbName, OId o, char* cn ) +{ + MDDBaseType* mddType = (MDDBaseType*)TypeFactory::mapMDDType( "GreyImage" ); + r_Minterval domain( "[0:9,0:9]" ); + + // cout << " " << o << "," << cn << " ... " << flush; + + PersMDDObj* obj = new PersMDDObj( mddType, domain, dbName, o ); + PersMDDColl objsSet( cn ); + objsSet.insert( obj ); + delete obj; +} + + +static void removeObj( char* dbName, char* collName, OId o ) +{ + // open collection + PersMDDColl* coll = 0; + + char answer = 'n'; + cout << endl << "SCAN (y/n) ?" << flush; + cin >> answer; + + if( answer == 'y' ) + { + coll = new PersMDDColl( collName ); + MDDCollIter* collIter = coll->createIterator(); + MDDObj* mddObj; + for( collIter->reset(); collIter->notDone(); collIter->advance() ) + { + mddObj = collIter->getElement(); + /* + if( mddObj->isPersistent() ) + { + EOId eOId; + ((PersMDDObj*)mddObj)->getEOId( &eOId ); + + cout << "MDD " << eOId.getOId() << flush; + } + */ + + } + /* + cout << endl << "PRINT OBJECT (y/n) ?" << flush; + cin >> answer; + if( answer == 'y' ) + mddObj->printStatus( ); + */ + delete collIter; + + coll->releaseAll(); + delete coll; + + cout << endl << "SCAN end" << endl; + } + + coll = new PersMDDColl( collName ); + + if( coll ) + { + cout <<"o == " << o << " dbName == " << dbName << endl; + coll->remove( o, dbName ); + + coll->releaseAll(); + delete coll; + } +} + + +int checkArguments( int argc, char** argv, const char* searchText, int& optionValueIndex ) +{ + int found = 0; + int i=1; + + while( !found && i. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see +* or contact Peter Baumann via . +/ +/** + * SOURCE: test_servercomm.cc + * + * MODULE: servercomm + * + * COMMENTS: + * None +*/ + + + +#include + +#define __EXECUTABLE__ +#define EARLY_TEMPLATE +#define DEBUG_MAIN +#define DEBUG +#include "debug.hh" +#include "template_inst.hh" + +#include "raslib/rmdebug.hh" +#include "qlparser/qtscalardata.hh" + +#include "servercomm/servercomm.hh" + + +extern char* myExecArgv0 = ""; +extern int tiling = 1; +extern unsigned long maxTransferBufferSize = 4000000; +extern int globalOptimizationLevel = 4; +extern char* dbSchema = 0; +extern int noTimeOut = 0; +char globalConnectId[255] = {0}; +bool udfEnabled = true; + +RMINITGLOBALS('C'); + +#include + + +int checkArguments( int argc, char** argv, const char* searchText, int& optionValueIndex ) +{ + int found = 0; + int i=1; + + while( !found && i::iterator i; + /* for (i=r->transferData->begin(); i!=r->transferData->end(); ++i) { + // t = (QtScalarData*)(*i); + // t->printStatus(); + }*/ + } + catch ( r_Error& errorObj ) + { + cerr << errorObj.what() << endl; + return -1; + } + catch ( ... ) + { + cerr << "Unknown exception caught in main." << endl; + return -1; + } + + return 0; +} -- cgit