///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoMain.cc
// -----------
// Cego main module
//
// Design and Implementation by Bjoern Lemke               
//                                                         
// (C)opyright 2000-2010 Bjoern Lemke                        
//
// This program 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 2, or (at your option)
// any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; see the file COPYING.  If not, write to
// the Free Software Foundation, 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.
//
// IMPLEMENTATION MODULE
//
// Class: Main
// 
// Description: Main module for the cego database program. The program can be invoked in different modes.
//
// Status: QG-2.6
//
///////////////////////////////////////////////////////////////////////////////

#include <locale.h>
#include <stdlib.h>

#include <lfc/Sleeper.h>
#include <lfc/Process.h>
#include <lfc/GetLongOpt.h>
#include <lfc/File.h>
#include <lfc/Timer.h>
#include <lfc/Tokenizer.h>
#include <lfc/Exception.h>
#include <lfc/Host.h>
#include <lfc/AESCrypt.h>
#include <lfc/SigHandler.h>

#include <lfc/XMLSuite.h>

#include "CegoDbThreadPool.h"
#include "CegoLogThreadPool.h"
#include "CegoAdminThreadPool.h"

#include "CegoDatabaseManager.h"
#include "CegoDistManager.h"
#include "CegoAction.h"
#include "CegoXMLdef.h"
#include "CegoBeatThread.h"
#include "CegoMediatorThread.h"
#include "CegoBeatConnection.h"
#include "CegoXPorter.h"
#include "CegoXMLdef.h"
#include "CegoModule.h"
#include "CegoCheckpoint.h"

#define USAGE "Usage:\n\n\
  Info:               cego [ --version ] [ --help ]\n\
\n\
  Initialization:     cego --mode=init --dbxml=<db xml> --dbname=<dbname>\n\
                           [ --hostname=<hostname> ] [ --pgsize=<page size> ]\n\
                           [ --dbport=<data port> ] [ --admport=<admin port> ] [ --logport=<log port ]\n\
                           [ --pidfile=<pid file> ] [ --loglevel=<log level> ]\n\
\n\
  User setup:         cego --mode=adduser --user=<user>/<password>\n\
                           --role=<role> --dbxml=<db xml>\n\
\n\
  Role setup:         cego --mode=addrole --role=<role> --dbxml=<db xml>\n\
\n\
  Permission setup:   cego --mode=addperm --role=<role> --permid=<permid>\n\
                           --tableset=<tableset> --filter=<object filter>\n\
                           --perm=<permission> --dbxml=<db xml>\n\
\n\
  Define Mode:        cego --mode=define --tsdef=<db attr string> --tableset=<tableset>\n\
                           --dbxml=<db xml>\n\
\n\
  Creation Mode:      cego --mode=create --tableset=<tableset> --dbxml=<db xml>\n\
\n\
  Export Mode:        cego --mode=<xmlexport|binexport|plainexport> --expfile=<exportfile>\n\
                           --tableset=<tableset> --dbxml=<db xml> --user=<user/pwd>\n\
\n\
  Import Mode:        cego --mode=<xmlimport|binimport|plainimport> --impfile=<importfile>\n\
                            --tableset=<tableset> --dbxml=<db xml> --user=<user/pwd> [ --noLogging ]\n\
\n\
  Batch Mode:         cego --mode=batch --batchfile=<batchfile> --tableset=<tableset>\n\
                           --dbxml=<db xml> --user=<user/pwd> [ --poolsize=<poolsize> ] [ --ignore ]\n\
\n\
  Daemon Mode:        cego --mode=daemon --dbxml=<db xml> [--tableset=<tableset>,<tableset>,... ]\n\
                           [ --cleanup ] [ --forceload ] [ --nolockstat ]\n\
                           [ --numdbthread=<numdb> ] [ --numadminthread=<numadm> ] [ --numlogthread=<numlog> ]\n\
                           [ --poolsize=<poolsize> ]  [ --pidfile=<pidfile> ] [ --protocol={serial|xml}]\n\
                           [ --master=<host:port:user:passwd> ]\n\
\n\
  General Options:    [ --logfile=<logfile> ] [ --lockfile=<lockfile> ]\n"

// DEFINES
#define DEFAULTLOGFILE "cego.log"
#ifdef HAVE_MINGW32
#define DEFAULTLOCKFILE "c:/windows/temp/cego.lock"
#else
#define DEFAULTLOCKFILE "/tmp/cego.lock"
#endif
#define DEFAULTPROTOCOL "xml"
#define DEFAULTPOOLSIZE 1000
#define DEFAULTDBTHREADNUM 10
#define DEFAULTADMINTHREADNUM 5
#define DEFAULTLOGTHREADNUM 3
#define DEFAULTBEATPORT 2000
#define DEFAULTBEATUSER "cgadm"
#define DEFAULTBEATPWD "cgadm"
#define DEFAULTROLE XML_ROLE_ALL_VALUE
#define DEFAULTTMPSIZE 100
#define DEFAULTSYSSIZE 100
#define DEFAULTLOGNUM 3
#define DEFAULTLOGSIZE 1000000
#define DEFAULTORDERSIZE 10000000
#define DEFAULTTSROOT "./"
#define DEFAULTPRIMARY "localhost"
#define DEFAULTSECONDARY "localhost"
#define DEFAULTTSTICKET "tsticket.xml"
#define DEFAULTAPPFILENAME "./data01.dbf"
#define DEFAULTAPPFILESIZE 1000

#define DEFAULTHOST "localhost"
#define DEFAULTPIDFILE "./pid"
#define DEFAULTPAGESIZE 32768
#define DEFAULTDBPORT 2200
#define DEFAULTADMPORT 2000
#define DEFAULTLOGPORT 3000
#define DEFAULTLOGLEVEL "NOTICE"

#define MAXCMDLEN 10000

#define LANGUAGE_ENV "LANG"
#define LOCALE_CATEGORY LC_TIME

extern char __lfcVersionString[];
extern char __lfcxmlVersionString[];
extern char __caseSensitiveFlag;
extern char __quoteEscapeFlag;
extern Chain __dateFormatString;
extern bool __lockStatOn;

enum XPortFormat { BINARY, XML, PLAIN };

int init(GetLongOpt& longOpt);
int addUser(GetLongOpt& longOpt);
int addRole(GetLongOpt& longOpt);
int addPerm(GetLongOpt& longOpt);
int defineTableSet(GetLongOpt& longOpt);
int createTableSet(GetLongOpt& longOpt);
int importTableSet(GetLongOpt& longOpt, XPortFormat format);
int exportTableSet(GetLongOpt& longOpt, XPortFormat format);
int runBatch(GetLongOpt& longOpt);
int runDaemon(GetLongOpt& longOpt);
bool processBatchFile(CegoDatabaseManager *pDBMng, CegoAction *pAction, const Chain& batchFileName, bool ignoreError, bool consoleOut, Chain& errorMsg);

int main(int argc, char **argv)
{	

    GetLongOpt longOpt(argc, argv);
    
    longOpt.addOpt("version");
    longOpt.addOpt("help");
    longOpt.addOpt("mode");
    longOpt.addOpt("user");
    longOpt.addOpt("dbxml");
    longOpt.addOpt("dbname");

    longOpt.addOpt("hostname", DEFAULTHOST);
    longOpt.addOpt("pidfile", DEFAULTPIDFILE);
    longOpt.addOpt("pgsize", Chain(DEFAULTPAGESIZE));
    longOpt.addOpt("dbport", Chain(DEFAULTDBPORT));
    longOpt.addOpt("admport", Chain(DEFAULTADMPORT));
    longOpt.addOpt("logport", Chain(DEFAULTLOGPORT));
    longOpt.addOpt("loglevel", Chain(DEFAULTLOGLEVEL));

    longOpt.addOpt("tableset");
    longOpt.addOpt("cleanup");
    longOpt.addOpt("forceload");
    longOpt.addOpt("nolockstat");
    longOpt.addOpt("role", DEFAULTROLE);
    longOpt.addOpt("filter");
    longOpt.addOpt("perm");
    longOpt.addOpt("permid");
    longOpt.addOpt("tsdef");
    longOpt.addOpt("nologging");
    longOpt.addOpt("expfile");
    longOpt.addOpt("impfile");
    longOpt.addOpt("batchfile");
    longOpt.addOpt("poolsize", Chain(DEFAULTPOOLSIZE));
    longOpt.addOpt("ignore");
    longOpt.addOpt("daemon");
    longOpt.addOpt("numdbthread", Chain(DEFAULTDBTHREADNUM));
    longOpt.addOpt("numadminthread", Chain(DEFAULTADMINTHREADNUM));
    longOpt.addOpt("numlogthread", Chain(DEFAULTLOGTHREADNUM));
    longOpt.addOpt("master");
    longOpt.addOpt("logfile", DEFAULTLOGFILE);
    longOpt.addOpt("lockfile", DEFAULTLOCKFILE);
    longOpt.addOpt("pidfile");
    longOpt.addOpt("protocol", DEFAULTPROTOCOL);
    
    try
    {
	longOpt.parseOpt(); 
    }
    catch ( Exception e )
    {
	Chain msg;
	e.pop(msg);
	cerr << msg << endl;
	cerr << USAGE << endl;
	exit(1);	
    }
    
    // set localization
    char* lang = 0;
    if ( ( lang = getenv(LANGUAGE_ENV) ) != 0)
    {
	if ( setlocale(LOCALE_CATEGORY, lang) == 0)
	{
	    Chain msg = Chain("Cannot set locale ") + Chain(lang);
	    cerr << msg << endl;
	    exit(1);
	}
    }
    // for random generation in CegoFunction
    int seed = 5 * (time(NULL) % 100000); 	
    srand( seed  ); 

    if ( longOpt.isSet( Chain("help") ) )
    {	
	cerr << USAGE << endl;
	exit(0);
    }

    if ( longOpt.isSet( Chain("version") ) )
    {
	cout << CEGO_PRODUCT << " (" << sizeof(long) * 8 << " bit), Version " << CEGO_VERSION 
	     << " [ lfc : " << __lfcVersionString  
	     << ", lfcxml : " <<  __lfcxmlVersionString << " ]" << endl;
	cout << CEGO_COPYRIGHT << endl;
	exit(0);
    }

    if ( longOpt.isSet("nolockstat") )
	__lockStatOn = false;
    
    Chain mode = longOpt.getOptValue("mode");
    
    int exitCode = 0;

    if ( mode == Chain("init") )
    {
	exitCode = init(longOpt);
    }
    else if ( mode == Chain("adduser") )
    {
	exitCode = addUser(longOpt);
    }
    else if ( mode == Chain("addrole") )
    {
	exitCode = addRole(longOpt);
    }
    else if ( mode == Chain("addperm") )
    {
	exitCode = addPerm(longOpt);
    }
    else if ( mode == Chain("define") )
    {
	exitCode = defineTableSet(longOpt);
    }
    else if ( mode == Chain("create") )
    {
	exitCode = createTableSet(longOpt);
    }
    else if ( mode == Chain("xmlimport")  )
    {
	exitCode = importTableSet(longOpt, XML);
    }
    else if ( mode == Chain("binimport") )
    {
	exitCode = importTableSet(longOpt, BINARY);
    }
    else if ( mode == Chain("plainimport") )
    {
	exitCode = importTableSet(longOpt, PLAIN);
    }
    else if ( mode == Chain("xmlexport") )
    {
	exitCode = exportTableSet(longOpt, XML);
    }
    else if ( mode == Chain("binexport") )
    {
	exitCode = exportTableSet(longOpt, BINARY);
    }
    else if ( mode == Chain("plainexport") )
    {
	exitCode = exportTableSet(longOpt, PLAIN);
    }
    else if ( mode == Chain("batch") )
    {
	exitCode = runBatch(longOpt);
    }
    else if ( mode == Chain("daemon") )
    {
	exitCode = runDaemon(longOpt);
    }
    else
    {
	cerr << USAGE << endl;
	exit(0);	
    }
    
    exit(exitCode);
}


int init(GetLongOpt& longOpt)
{
    
    int exitCode = 0;

    CegoDatabaseManager* pDBMng = 0;

    try {
	
	Chain logFile = longOpt.getOptValue("logfile");	
	Chain lockFile = longOpt.getOptValue("lockfile");	
	Chain dbXML = longOpt.getOptValue("dbxml");
	Chain dbName = longOpt.getOptValue("dbname");

	Chain hostname = longOpt.getOptValue("hostname");
	Chain pidfile = longOpt.getOptValue("pidfile");
	Chain pgsize = longOpt.getOptValue("pgsize");
	Chain dbport = longOpt.getOptValue("dbport");
	Chain admport = longOpt.getOptValue("admport");
	Chain logport = longOpt.getOptValue("logport");
	Chain logLevel = longOpt.getOptValue("loglevel");


	if ( dbXML.length() == 0 || dbXML == Chain("") )
	{
	    throw Exception(EXLOC, "No dbxml set");
	}

	if ( dbName.length() == 0 || dbName == Chain("") )
	{
	    throw Exception(EXLOC, "No database name set");
	}

	if ( logLevel != Chain("ERROR") 
	     && logLevel != Chain("NOTICE")
	     && logLevel != Chain("DEBUG") 
	     && logLevel != Chain("NONE") )
	{
	    throw Exception(EXLOC, "Invalid log level");
	}

	pDBMng = new CegoDatabaseManager(dbXML, lockFile, logFile);
	
	pDBMng->initXml(dbName, pgsize.asInteger(), hostname, dbport.asInteger(), admport.asInteger(), logport.asInteger(), pidfile, logLevel);
	
	delete pDBMng;
    }
    catch ( Exception e)
    {

	if ( pDBMng ) 
	    delete pDBMng;

	Chain msg;
	e.pop(msg);
	cerr << msg << endl;
	exitCode = 1;
    }
    return exitCode;
}


int addUser(GetLongOpt& longOpt)
{
    
    int exitCode = 0;

    CegoDatabaseManager* pDBMng = 0;

    try {
	
	Chain logFile = longOpt.getOptValue("logfile");	
	Chain lockFile = longOpt.getOptValue("lockfile");	
	Chain dbXML = longOpt.getOptValue("dbxml");
	Chain userName = longOpt.getOptValue("user");
	Chain userRole = longOpt.getOptValue("role");

	Tokenizer userTok(userName, Chain("/")); 
	
	Chain user;
	Chain password;
	
	userTok.nextToken(user);
	userTok.nextToken(password);
	
	if ( dbXML.length() == 0 || dbXML == Chain("") )
	{
	    throw Exception(EXLOC, "No dbxml set");
	}
	if ( user.length() == 0 || user == Chain("") )
	{
	    throw Exception(EXLOC, "No user set");
	}
	if ( password.length() == 0 || password == Chain("") )
	{
	    throw Exception(EXLOC, "No password set");
	}
	
	pDBMng = new CegoDatabaseManager(dbXML, lockFile, logFile);
	
	pDBMng->xml2Doc();
	pDBMng->configureLogger();
	
	AESCrypt aescrypt(CEGOAESKEY, CEGOAESKEYLEN);
	pDBMng->addUser(user, aescrypt.encrypt(password));
	pDBMng->assignUserRole(user, userRole);
	
	pDBMng->doc2Xml();
	
	delete pDBMng;
    }
    catch ( Exception e)
    {

	if ( pDBMng ) 
	    delete pDBMng;

	Chain msg;
	e.pop(msg);
	cerr << msg << endl;
	exitCode = 1;
    }
    return exitCode;
}

int addRole(GetLongOpt& longOpt)
{
    int exitCode = 0;

    CegoDatabaseManager* pDBMng = 0;

    try {

	Chain logFile = longOpt.getOptValue("logfile");
	Chain lockFile = longOpt.getOptValue("lockfile");
	Chain dbXML = longOpt.getOptValue("dbxml");

	Chain role = longOpt.getOptValue("role");
	Chain filter = longOpt.getOptValue("filter");
	Chain tableset = longOpt.getOptValue("tableset");
	
	
	if ( dbXML.length() == 0 || dbXML == Chain("") )
	{
	    throw Exception(EXLOC, "No dbxml set");
	}
	
	pDBMng = new CegoDatabaseManager(dbXML, lockFile, logFile);
	
	pDBMng->xml2Doc();
	pDBMng->configureLogger();
	
	pDBMng->createRole(role);
	
	pDBMng->doc2Xml();
	
	delete pDBMng;
	
    }
    catch ( Exception e)
    {
	if ( pDBMng ) 
	    delete pDBMng;

	Chain msg;
	e.pop(msg);
	cerr << msg << endl;
	exitCode = 1;
    }

    return exitCode;
}

int addPerm(GetLongOpt& longOpt)
{
    int exitCode = 0;

    CegoDatabaseManager* pDBMng = 0;

    try {

	Chain logFile = longOpt.getOptValue("logfile");
	Chain lockFile = longOpt.getOptValue("lockfile");
	Chain dbXML = longOpt.getOptValue("dbxml");

	Chain role = longOpt.getOptValue("role");
	Chain filter = longOpt.getOptValue("filter");
	Chain tableset = longOpt.getOptValue("tableset");
	Chain perm = longOpt.getOptValue("perm");
	Chain permid = longOpt.getOptValue("permid");
	

	if ( dbXML.length() == 0 || dbXML == Chain("") )
	{
	    throw Exception(EXLOC, "No dbxml set");
	}
	
	pDBMng = new CegoDatabaseManager(dbXML, lockFile, logFile);
	
	pDBMng->xml2Doc();
	pDBMng->configureLogger();
	
	pDBMng->setPerm(role, permid, tableset, filter, perm);
	
	pDBMng->doc2Xml();
	
	delete pDBMng;
	
    }
    catch ( Exception e)
    {
	if ( pDBMng ) 
	    delete pDBMng;

	Chain msg;
	e.pop(msg);
	cerr << msg << endl;
	exitCode = 1;
    }

    return exitCode;
}


int defineTableSet(GetLongOpt& longOpt)
{

    int exitCode = 0;

    CegoDatabaseManager* pDBMng = 0;

    try {
	
	Chain logFile = longOpt.getOptValue("logfile");
	Chain lockFile = longOpt.getOptValue("lockfile"); 
	Chain dbXML = longOpt.getOptValue("dbxml");
	Chain tableSet = longOpt.getOptValue("tableset");
	Chain tsDef = longOpt.getOptValue("tsdef");
	
	if ( dbXML.length() == 0  || dbXML == Chain("") )
	{
	    throw Exception(EXLOC, "No dbxml set");
	}
	if ( tableSet.length() == 0 || tableSet == Chain("") )
	{
	    throw Exception(EXLOC, "No tableSet set");
	}
	if ( tsDef.length() == 0 || tsDef == Chain("") )
	{
	    throw Exception(EXLOC, "No tsdef set");
	}
	       
	pDBMng = new CegoDatabaseManager(dbXML, lockFile, logFile);
	
	pDBMng->xml2Doc();
	pDBMng->configureLogger();
	
	Chain tsRoot(DEFAULTTSROOT);
	Chain tsTicket(DEFAULTTSTICKET);
	
	int sysSize = DEFAULTSYSSIZE;
	int tmpSize = DEFAULTTMPSIZE;
	int logFileSize = DEFAULTLOGSIZE;
	int logFileNum = DEFAULTLOGNUM;
	
	Chain appFileName = DEFAULTAPPFILENAME;
	int appFileSize = DEFAULTAPPFILESIZE;
	long orderSize = DEFAULTORDERSIZE;

	Tokenizer atok(tsDef, Chain(","));
	
	Chain attrTup;
	while ( atok.nextToken(attrTup) )
	{
	    Tokenizer vtok(attrTup, Chain(":"));
	    Chain attr;
	    Chain value;
	    vtok.nextToken(attr);
	    vtok.nextToken(value);
	    if ( attr == Chain("syssize"))
		sysSize = value.asInteger();
	    else if ( attr == Chain("tmpsize"))
		tmpSize = value.asInteger();
	    else if ( attr == Chain("logfilesize"))
		logFileSize = value.asInteger();
	    else if ( attr == Chain("logfilenum"))
		logFileNum = value.asInteger();
	    else if ( attr == Chain("tsticket"))
		tsTicket = value;
	    else if ( attr == Chain("tsroot"))
		tsRoot = value;
	    else if ( attr == Chain("appfile"))
		appFileName = value;
	    else if ( attr == Chain("appsize"))
		appFileSize = value.asInteger();
	    else if ( attr == Chain("ordersize"))
		orderSize = value.asLong();

	}
	
	Host h;
	int sysFid = pDBMng->nextTSID();
	int tmpFid = pDBMng->nextFID();
	
	pDBMng->addTableSetDef(tableSet, 
			       tsRoot, 
			       tsTicket,
			       h.getName(), 
			       h.getName(), 
			       h.getName(), 
			       sysFid, 
			       tmpFid, 
			       sysSize, 
			       tmpSize,
			       logFileSize, 
			       logFileNum,
			       orderSize); 
	
	
	int appFid = pDBMng->nextFID();
	pDBMng->addDataFile(tableSet, Chain(XML_APPFILE_VALUE), appFid, appFileName, appFileSize);
	
	pDBMng->doc2Xml();
	
	delete pDBMng;
	
    }
    catch (Exception e)
    {

	if ( pDBMng ) 
	    delete pDBMng;

	Chain msg;
	e.pop(msg);
	cerr << msg << endl;
	exitCode = 1;
    }
    
    return exitCode;
}

int createTableSet(GetLongOpt& longOpt)
{

    int exitCode = 0;

    CegoDatabaseManager* pDBMng = 0;

    try {
	
	Chain logFile = longOpt.getOptValue("logfile");
	Chain lockFile = longOpt.getOptValue("lockfile");	
	Chain dbXML = longOpt.getOptValue("dbxml");
	Chain tableSet = longOpt.getOptValue("tableset");
	Chain poolSize = longOpt.getOptValue("poolsize");
	
	if ( dbXML.length() == 0  || dbXML == Chain("") )
	{
	    throw Exception(EXLOC, "No dbxml set");
	}
	if ( tableSet.length() == 0 || tableSet == Chain("") )
	{
	    throw Exception(EXLOC, "No tableSet set");
	}
		
	pDBMng = new CegoDatabaseManager(dbXML, lockFile, logFile);
		
	pDBMng->initPool(poolSize.asInteger());
	pDBMng->configureLogger();
	unsigned long modId = pDBMng->getModId("CegoMain");
	
	pDBMng->log(modId, Logger::NOTICE, Chain("Creating tableset ") + tableSet + Chain(" ..."));
	
	CegoLockHandler* pLockHandler = new CegoLockHandler(pDBMng);
	
	pLockHandler->initLocks();	    
	
	CegoTableManager tabMng(pDBMng);
	
	tabMng.createTableSet(tableSet);
	
	pDBMng->log(modId, Logger::NOTICE, Chain("Tableset ") + tableSet + Chain(" created"));
	
	pLockHandler->deleteLocks();
	delete pDBMng;
	delete pLockHandler;
	
    }
    catch (Exception e)
    {
	if ( pDBMng ) 
	    delete pDBMng;

	Chain msg;
	e.pop(msg);
	cerr << msg << endl;
	exitCode = 1;
    }

    return exitCode;
}


int importTableSet(GetLongOpt& longOpt, XPortFormat format)
{
	
    Chain errorMsg;
    int exitCode=0;
	
    CegoDatabaseManager* pDBMng = 0;
    
    try 
    {
	
	Chain logFile = longOpt.getOptValue("logfile");
	Chain lockFile = longOpt.getOptValue("lockfile");
	Chain dbXML = longOpt.getOptValue("dbxml");
	Chain tableSet = longOpt.getOptValue("tableset");
	Chain poolSize = longOpt.getOptValue("poolsize");
	Chain impFileName = longOpt.getOptValue("impfile");

	bool doLogging = true;
	if ( longOpt.isSet("nologging") )
	    doLogging=false;

	Chain userName = longOpt.getOptValue("user");

	Tokenizer userTok(userName, Chain("/")); 
	
	Chain user;
	Chain password;
	
	userTok.nextToken(user);
	userTok.nextToken(password);
	
	if ( dbXML.length() == 0  || dbXML == Chain("") )
	{
	    throw Exception(EXLOC, "No dbxml set");
	}
	if ( tableSet.length() == 0 || tableSet == Chain("") )
	{
	    throw Exception(EXLOC, "No tableSet set");
	}
	if ( impFileName.length() == 0 || impFileName == Chain("") )
	{
	    throw Exception(EXLOC, "No impfile set");
	}
	
	pDBMng = new CegoDatabaseManager(dbXML, lockFile, logFile);

	pDBMng->initPool(poolSize.asInteger());
	pDBMng->configureLogger();
	unsigned long modId = pDBMng->getModId("CegoMain");
	
	CegoLockHandler* pLockHandler = 0;
	CegoLogThreadPool* pLogPool = 0;
	CegoDistManager* pTabMng = 0;
	
	try
	{

	    if ( pDBMng->isCaseSensitiveMode() )
		__caseSensitiveFlag = 1;
	    else
		__caseSensitiveFlag = 0;
	    
	    if ( pDBMng->isQuoteEscapeMode() )	       
		__quoteEscapeFlag = 1;
	    else
		__quoteEscapeFlag = 0;


	    Chain dtFormat = pDBMng->getDateFormatString();
	    
	    if ( dtFormat != Chain("") )
		__dateFormatString=dtFormat;
	    
	    pLockHandler = new CegoLockHandler(pDBMng);
	    
	    pLockHandler->initLocks();
	    
	    if ( pDBMng->isArchiveMode(tableSet) )
	    {
#ifdef CGDEBUG	
		pDBMng->log(modId, Logger::DEBUG, Chain("Creating log threadpool ..."));
#endif
		pLogPool = new CegoLogThreadPool(pDBMng);
		pLogPool->start(0);
	    }
	    
	    pTabMng = new CegoDistManager(pDBMng);

	    pTabMng->setActiveUser(tableSet, user, password);
	    
#ifdef CGDEBUG	
	    pDBMng->log(modId, Logger::DEBUG, Chain("Starting tableset ") + tableSet + Chain(" ..."));
#endif	    
	    
	    Host h;
	    pTabMng->startDistTableSet(tableSet, h.getName(), false);
	    
	    CegoXPorter xp(pTabMng);
	    
	    if ( format == XML )
	    {
		cout << "Starting XML import of file " << impFileName << endl;
		xp.xmlImportTableSet(tableSet, false, impFileName, doLogging);
	    }
	    else if ( format == BINARY )
	    {
		cout << "Starting binary import of file " << impFileName << endl;
		xp.binImportTableSet(tableSet, false, impFileName, doLogging, false);
	    }
	    else if ( format == PLAIN )
	    {
		cout << "Starting plain import of file " << impFileName << endl;
		xp.binImportTableSet(tableSet, false, impFileName, true, true);
	    }

	    pTabMng->stopDistTableSet(tableSet, true);
	    
	    pDBMng->doc2Xml();
	    
	}
	catch ( Exception e )
	{
	    Chain msg;
	    Chain module;
	    int line;
	    
	    Chain exep;
	    while ( e.pop(module, line, msg) )
	    {
		exep += Chain("\n\t") + module + Chain("(") +  Chain(line) + Chain(") : ") + msg;
	    }
	    pDBMng->log(modId, Logger::LOGERR, Chain("Import Thread : ") + exep);
	    exitCode=1;
	    errorMsg=Chain("Import failed, see log for details");		
	}
	
	if ( pLogPool )
	    delete pLogPool;
	
	if ( pTabMng )
	    delete pTabMng;
	
	if ( pLockHandler )
	{
	    pLockHandler->deleteLocks();
	    delete pLockHandler;
	}
	
	delete pDBMng;
	pDBMng = 0;
    }
    catch ( Exception e )
    {	    
	if ( pDBMng ) 
	    delete pDBMng;
	
	e.pop(errorMsg);
	exitCode=1;
    }
    
    if ( exitCode == 0 )
    {
	cout << "Import done" << endl;
    }
    else
    {
	cerr << "ERROR: " << errorMsg << endl;
    }

    return exitCode;
}

int exportTableSet(GetLongOpt& longOpt, XPortFormat format)
{
    
    Chain errorMsg;
    int exitCode=0;
    
    CegoDatabaseManager* pDBMng = 0;
    
    try 
    {

	Chain logFile = longOpt.getOptValue("logfile");
	Chain lockFile = longOpt.getOptValue("lockfile");
	Chain dbXML = longOpt.getOptValue("dbxml");
	Chain tableSet = longOpt.getOptValue("tableset");
	Chain poolSize = longOpt.getOptValue("poolsize");
	Chain expFileName = longOpt.getOptValue("expfile");  
	Chain userName = longOpt.getOptValue("user");

	Tokenizer userTok(userName, Chain("/")); 
	
	Chain user;
	Chain password;
	
	userTok.nextToken(user);
	userTok.nextToken(password);

	
	if ( dbXML.length() == 0  || dbXML == Chain("") )
	{
	    throw Exception(EXLOC, "No dbxml set");
	}
	if ( tableSet.length() == 0 || tableSet == Chain("") )
	{
	    throw Exception(EXLOC, "No tableSet set");
	}
	if ( expFileName.length() == 0 || expFileName == Chain("") )
	{
	    throw Exception(EXLOC, "No expfile set");
	}
	
	pDBMng = new CegoDatabaseManager(dbXML, lockFile, logFile);
	
	pDBMng->initPool(poolSize.asInteger());	   
	pDBMng->configureLogger();
	unsigned long modId = pDBMng->getModId("CegoMain");
		
	CegoLockHandler* pLockHandler = 0;
	CegoLogThreadPool* pLogPool = 0;
	CegoDistManager* pTabMng = 0;
	
	try
	{

	    if ( pDBMng->isCaseSensitiveMode() )
		__caseSensitiveFlag = 1;
	    else
		__caseSensitiveFlag = 0;
	    
	    if ( pDBMng->isQuoteEscapeMode() )	       
		__quoteEscapeFlag = 1;
	    else
		__quoteEscapeFlag = 0;

	    Chain dtFormat = pDBMng->getDateFormatString();
	    
	    if ( dtFormat != Chain("") )
		__dateFormatString=dtFormat;
	    	    
	    pLockHandler = new CegoLockHandler(pDBMng);

	    pLockHandler->initLocks();
	    	    
	    if ( pDBMng->isArchiveMode(tableSet) )
	    {
#ifdef CGDEBUG	
		pDBMng->log(modId, Logger::DEBUG, Chain("Creating log threadpool ..."));
#endif

		pLogPool = new CegoLogThreadPool(pDBMng);
		pLogPool->start(0);
	    }
	    
	    pTabMng = new CegoDistManager(pDBMng);
	    
#ifdef CGDEBUG	
	    pDBMng->log(modId, Logger::DEBUG, Chain("Starting tableset ") + tableSet + Chain(" ..."));    	
#endif    
	    Host h;
	    pTabMng->startDistTableSet(tableSet, h.getName(), false);
	    
	    CegoXPorter xp(pTabMng);
	    
	    if ( format == XML )
	    {
		cout << "Starting XML export of file " << expFileName << endl;
		xp.xmlExportTableSet(tableSet, false, expFileName);
	    }
	    else if ( format == BINARY )
	    {
		cout << "Starting binary export of file " << expFileName << endl;
		xp.binExportTableSet(tableSet, false, expFileName, false);
	    }
	    else if ( format == PLAIN )
	    {
		cout << "Starting plain export of file " << expFileName << endl;
		xp.binExportTableSet(tableSet, false, expFileName, true);
	    }

	    
	    pTabMng->stopDistTableSet(tableSet, true);
	    
	    pDBMng->doc2Xml();
	    
	}
	catch ( Exception e )
	{
	    Chain msg;
	    Chain module;
	    int line;
	    
	    Chain exep;
	    while ( e.pop(module, line, msg) )
	    {
		exep += Chain("\n\t") + module + Chain("(") +  Chain(line) + Chain(") : ") + msg;
	    }
	    pDBMng->log(modId, Logger::LOGERR, Chain("Import Thread : ") + exep);
	    exitCode=1;
	    errorMsg=Chain("Import failed, see log for details");		
	}
	
	if ( pLogPool )
	    delete pLogPool;
	
	if ( pTabMng )
	    delete pTabMng;
	
	if ( pLockHandler )
	{
	    pLockHandler->deleteLocks();
	    delete pLockHandler;
	}
	
	delete pDBMng;
	pDBMng = 0;
    }
    catch ( Exception e )
    {	    
	if ( pDBMng ) 
	    delete pDBMng;
	
	e.pop(errorMsg);
	exitCode=1;
    }
    
    if ( exitCode == 0 )
    {
	cout << "Export done" << endl;
    }
    else
    {
	cerr << "ERROR: " << errorMsg << endl;
	
    }	
    
    return exitCode;
}

int runBatch(GetLongOpt& longOpt)
{

    Chain errorMsg;
    int exitCode=0;
    
    CegoDatabaseManager* pDBMng = 0;
    
    try 
    {

	Chain logFile = longOpt.getOptValue("logfile");
	Chain lockFile = longOpt.getOptValue("lockfile");
	Chain dbXML = longOpt.getOptValue("dbxml");
	Chain tableSet = longOpt.getOptValue("tableset");
	Chain poolSize = longOpt.getOptValue("poolsize");
	Chain batchFileName = longOpt.getOptValue("batchfile");  
	bool ignoreError = longOpt.isSet("ignore");
	Chain userName = longOpt.getOptValue("user");

	Tokenizer userTok(userName, Chain("/")); 
	
	Chain user;
	Chain password;
	
	userTok.nextToken(user);
	userTok.nextToken(password);
	

	if ( dbXML.length() == 0  || dbXML == Chain("") )
	{
	    throw Exception(EXLOC, "No dbxml set");
	}
	if ( tableSet.length() == 0 || tableSet == Chain("") )
	{
	    throw Exception(EXLOC, "No tableSet set");
	}
	    	    	
	pDBMng = new CegoDatabaseManager(dbXML, lockFile, logFile);
	
	pDBMng->initPool(poolSize.asInteger());
	pDBMng->configureLogger();
	unsigned long modId = pDBMng->getModId("CegoMain");
		
	CegoLockHandler* pLockHandler = 0;
	CegoLogThreadPool* pLogPool = 0;
	CegoDistManager* pTabMng = 0;
	CegoAction* pAction = 0;
	
	try {
	    
	    pLockHandler = new CegoLockHandler(pDBMng);
	    
	    
	    if ( pDBMng->isCaseSensitiveMode() )
		__caseSensitiveFlag = 1;
	    else
		__caseSensitiveFlag = 0;
	    
	    if ( pDBMng->isQuoteEscapeMode() )	       
		__quoteEscapeFlag = 1;
	    else
		__quoteEscapeFlag = 0;
	    
	    Chain dtFormat = pDBMng->getDateFormatString();
	    if ( dtFormat != Chain("") )
		__dateFormatString=dtFormat;
	    
	    
	    pLockHandler->initLocks();
	    
	    if ( pDBMng->isArchiveMode(tableSet) )
	    {
#ifdef CGDEBUG	
		pDBMng->log(modId, Logger::DEBUG, Chain("Creating log threadpool ..."));
#endif

		pLogPool = new CegoLogThreadPool(pDBMng);
		pLogPool->start(0);
	    }
	    
	    
	    pTabMng = new CegoDistManager(pDBMng);
	    pTabMng->setActiveUser(tableSet, user, password);
	    pTabMng->setThreadId(1);

#ifdef CGDEBUG	
	    pDBMng->log(modId, Logger::DEBUG, Chain("Starting tableset ") + tableSet + Chain(" ..."));
#endif	    

	    Host h;
	    pTabMng->startDistTableSet(tableSet, h.getName(), false);
	    	    
#ifdef CGDEBUG	
	    pDBMng->log(modId, Logger::DEBUG, Chain("Tableset ") + tableSet + Chain(" completed"));
#endif	    

	    pAction = new CegoAction( pTabMng);

	    pAction->setTableSet(tableSet);
	    
	    int tabSetId = pDBMng->getTabSetId(tableSet);
	    
	    if ( processBatchFile(pDBMng, pAction, batchFileName, ignoreError, true, errorMsg) == false )
		exitCode = 1;

	    pTabMng->stopDistTableSet(tableSet, false);
	    
	    pDBMng->doc2Xml();
	    
	}    
	catch ( Exception e)
	{	   
	    exitCode = 1;
	    e.pop(errorMsg);	    
	}

	if ( pAction )
	    delete pAction;
	
	if ( pLogPool )
	    delete pLogPool;
	
	if ( pTabMng )
	    delete pTabMng;
	
	if ( pLockHandler )
	{
	    pLockHandler->deleteLocks();
	    delete pLockHandler;
	}

	delete pDBMng;
	
    }
    catch ( Exception e )
    {	    
	if ( pDBMng ) 
	    delete pDBMng;
	
	e.pop(errorMsg);
	exitCode=1;
    }
    
    if ( exitCode == 0 )
    {
	cout << "Batch done" << endl;
    }
    else
    {
	cerr << "ERROR: " << errorMsg << endl;
	
    }	

    
    return exitCode;

}

int runDaemon(GetLongOpt& longOpt)
{

    int exitCode = 0;

    CegoDatabaseManager* pDBMng = 0;
    unsigned long modId = 0;

    try {
    
	Chain logFile = longOpt.getOptValue("logfile");
	Chain lockFile = longOpt.getOptValue("lockfile");
	Chain pidFile = longOpt.getOptValue("pidfile");
	Chain dbXML = longOpt.getOptValue("dbxml");
	Chain tableSetList = longOpt.getOptValue("tableset");

	Chain poolSize = longOpt.getOptValue("poolsize");
	Chain numDbThread = longOpt.getOptValue("numdbthread");
	Chain numAdminThread = longOpt.getOptValue("numadminthread");
	Chain numLogThread = longOpt.getOptValue("numlogthread");
	Chain master = longOpt.getOptValue("master");
	Chain protocol = longOpt.getOptValue("protocol");
	bool cleanIt = longOpt.isSet("cleanup");
	bool forceload = longOpt.isSet("forceload");

	
	CegoMediatorThread *pMedThread = 0;
    	
	if ( dbXML.length() == 0  || dbXML == Chain("") )
	{
	    throw Exception(EXLOC, "No dbxml set");
	}

	CegoDbHandler::ProtocolType protType;
	if ( protocol == Chain("serial") )
	{
	    protType = CegoDbHandler::SERIAL;
	}
	else if ( protocol == Chain("xml") )
	{
	    protType = CegoDbHandler::XML;
	}
	else
	{
	    throw Exception(EXLOC, "Invalid protocol");
	}

	
	pDBMng = new CegoDatabaseManager(dbXML, lockFile, logFile, protType);
	pMedThread = new CegoMediatorThread(pDBMng);
	    
	modId = pDBMng->getModId("CegoMain");
	
	if ( master.length() != 0  && master != Chain("") )
	{

	    Tokenizer tok(master, Chain(":"));
	    
	    Chain host;
	    Chain port;
	    Chain user;
	    Chain passwd;
	    
	    tok.nextToken(host);
	    tok.nextToken(port);
	    tok.nextToken(user);
	    tok.nextToken(passwd);

#ifdef CGDEBUG	
	    pDBMng->log(modId, Logger::DEBUG, Chain("Getting db spec from master host ") + host);
#endif	

	    AESCrypt aescrypt(CEGOAESKEY, CEGOAESKEYLEN);	    

	    CegoBeatConnection beat;
	    beat = CegoBeatConnection(host, port.asInteger(), user, aescrypt.encrypt(passwd), pDBMng);
	    
	    pDBMng->configureLogger();
	    pMedThread->getDbSpec(dbXML, beat.getHostName(), beat.getPortNo(), beat.getUser(), beat.getPasswd());
	    pDBMng->setXmlDef(dbXML);

	    pDBMng->xml2Doc();	   
	    Host h;
	    pDBMng->setDBHost(h.getName());
	    pDBMng->doc2Xml();	   
	}

	pDBMng->initPool(poolSize.asInteger());
	pDBMng->configureLogger();	     	
	

#ifdef CGDEBUG	
	pDBMng->log(modId, Logger::DEBUG, "Initializing page and record locks ...");
#endif	

	CegoLockHandler* pLockHandler = new CegoLockHandler(pDBMng);
	
	if ( pDBMng->isCaseSensitiveMode() )       
	    __caseSensitiveFlag = 1;
	else
	    __caseSensitiveFlag = 0;
	
	if ( pDBMng->isQuoteEscapeMode() )	       
	    __quoteEscapeFlag = 1;
	else
	    __quoteEscapeFlag = 0;
	
	Chain dtFormat = pDBMng->getDateFormatString();
	if ( dtFormat != Chain("") )
	    __dateFormatString=dtFormat;
	
	pLockHandler->initLocks();
	
#ifdef CGDEBUG	
	pDBMng->log(modId, Logger::DEBUG, "Creating db threadpool ...");
#endif

	CegoDbThreadPool* pDbPool = new CegoDbThreadPool(numDbThread.asInteger(), pDBMng, protType);

#ifdef CGDEBUG	
	pDBMng->log(modId, Logger::DEBUG, "Sync db pool to ready ...");	
#endif

	pDbPool->syncToReady();	

#ifdef CGDEBUG	
	pDBMng->log(modId, Logger::DEBUG, "DB Pool is ready");
#endif

#ifdef CGDEBUG		
	pDBMng->log(modId, Logger::DEBUG, "Creating log threadpool ...");
#endif

	CegoLogThreadPool* pLogPool = new CegoLogThreadPool(numLogThread.asInteger(), pDBMng);

#ifdef CGDEBUG		
	pDBMng->log(modId, Logger::DEBUG, "Creating admin threadpool ...");
#endif

	CegoAdminThreadPool *pAdminPool = new CegoAdminThreadPool(numAdminThread.asInteger(), pDBMng, pDbPool, pLogPool);
	
	pDBMng->setThreadInfo(numDbThread.asInteger(), numAdminThread.asInteger(), numLogThread.asInteger());
		
	CegoBeatThread *pBeatThread = new CegoBeatThread(pDBMng);
	
	pMedThread->start(0);
	
	pDbPool->start(0);
	pLogPool->start(0);
	pAdminPool->start(0);
	
	// write pidfile

	// is pidfile set in command line ?
	if ( pidFile.length() == 0  || pidFile == Chain("") )
	{

	    // if not, get pid file from xml
	    pDBMng->getPidFile(pidFile);
	    

	}

	if ( pidFile != Chain("") )
	{
	    File pf(pidFile);
	    pf.open(File::WRITE);
	    Process p;
	    int pid = p.getPid();
	    pf.writeChain(Chain(pid));
	    pf.close();
	}
	else
	{
	    throw Exception(EXLOC, "No pidfilename set");
	}
	
	Host h;
	
	// correct online entries 
	ListT<Chain> corList;
	pDBMng->getActiveTableSet(h.getName(), corList);
	
	if ( corList.Size() > 0 )
	{
	    CegoDistManager tabMng(pDBMng);
	    Chain *pCor = corList.First();
	    while ( pCor )
	    {
		
#ifdef CGDEBUG	
		pDBMng->log(modId, Logger::DEBUG, Chain("Correcting tableset ") + *pCor + Chain(" runstate"));
#endif

		pDBMng->setTableSetRunState(*pCor, XML_OFFLINE_VALUE);
		pCor = corList.Next();
	    }
	    
	}

	bool startTableSet = true;
	if ( tableSetList.length() == 0 || tableSetList == Chain("") )
	{
	    startTableSet = false;
	}

	if ( startTableSet )
	{
	    
	    Tokenizer tok(tableSetList, Chain(","));

	    CegoDistManager* pTabMng = new CegoDistManager(pDBMng);		

	    // we have to allow the main thread to access all objects
	    pTabMng->disableAuth();

	    Chain tableSet;

	    try
	    {

		while ( tok.nextToken(tableSet) )
		{

		    Chain secondary = pDBMng->getSecondary(tableSet);
		    
#ifdef CGDEBUG	
		    pDBMng->log(modId, Logger::DEBUG, Chain("Starting tableset ") + tableSet);
#endif		
    
		    Chain runState = pDBMng->getTableSetRunState(tableSet);
		    if ( runState == Chain(XML_DEFINED_VALUE) )
		    {		   
			Chain msg = Chain("Cannot start tableset ") + tableSet + Chain(", not yet created"); 
			throw Exception(EXLOC, msg);
		    }
		    
		    pTabMng->startDistTableSet(tableSet, secondary, cleanIt);
		    
		    if ( forceload )
		    {
#ifdef CGDEBUG	
			pDBMng->log(modId, Logger::DEBUG, Chain("Forced loading object for ") + tableSet + Chain("..."));
#endif

			int tabSetId = pDBMng->getTabSetId(tableSet);
			pDbPool->loadObjects(tabSetId);	
#ifdef CGDEBUG		
			pDBMng->log(modId, Logger::DEBUG, Chain("Objects for ") + tableSet + Chain(" loaded"));
#endif

		    }
		    
		    Chain batchFileName = pDBMng->getTSInitFile(tableSet);
		    
		    File batchFile(batchFileName);

		    if ( batchFile.exists() ) 
		    {
			
			CegoAction* pAction = new CegoAction(pTabMng);

			try
			{
			    pAction->setTableSet(tableSet);
			    
			    Chain errorMsg;
			    if ( processBatchFile(pDBMng, pAction, batchFileName, false, false, errorMsg) == false )
				throw Exception(EXLOC, errorMsg);			    
			    
			}
			catch ( Exception e )
			{
			    delete pAction;
			    throw Exception(EXLOC, Chain("Execution of init file ") + batchFileName + Chain(" failed"), e);
			}
			
			delete pAction;
		    }
		}
	    }
	    catch ( Exception e )
	    {
		delete pTabMng;
		throw Exception(EXLOC, Chain("Cannot start tableset  ") + tableSet, e);
	    }
	    delete pTabMng;
	}
    	
	cout << "Cego daemon up and running ..." << endl;

	CegoCheckpoint cpc;

	while ( ! pBeatThread->isTerminated() 
		&& ! pDbPool->isTerminated() 
		&& ! pLogPool->isTerminated() 
		&& ! pAdminPool->isTerminated() )
	{
	    
	    
	    pBeatThread->beat();
	    
	    ListT<Chain> actList;

	    // we get all active tableset, except the tablesets which actually are recovered
	    pDBMng->getActiveTableSet(h.getName(), actList, false);
	    
	    if ( actList.Size() > 0 )
	    {
		
		Chain *pAct = actList.First();
		while ( pAct )
		{
#ifdef CGDEBUG	
		    pDBMng->log(modId, Logger::DEBUG, Chain("Checking tableset ") + *pAct + Chain(" ..."));
#endif

		    CegoDistManager tabMng(pDBMng);
		    
		    try 
		    {
			tabMng.cleanTableSet(*pAct);
			int cpi = pDBMng->getCheckpointInterval(*pAct);
			if ( cpc.checkpointReached(*pAct, cpi) )
			{
			    pDBMng->log(modId, Logger::NOTICE, Chain("Checkpoint reached for tableset ") + *pAct);
			    tabMng.writeCheckPoint(*pAct, true, true);
			}
		    }
		    catch ( Exception e )
		    {
			Chain msg;
			Chain module;
			int line;
			
			Chain exep;
			while ( e.pop(module, line, msg) )
			{
			    exep += Chain("\n\t") + module + Chain("(") +  Chain(line) + Chain(") : ") + msg;
			}
			
			pDBMng->log(modId, Logger::LOGERR, Chain("Main Thread : ") + exep);
		    }
#ifdef CGDEBUG	
		    pDBMng->log(modId, Logger::DEBUG, Chain("Tableset ") + *pAct + Chain(" checked"));
#endif

		    
		    pAct = actList.Next();
		}
		
	    }

	    pDBMng->beat();
	    
	    Sleeper s;
	    s.secSleep(NETMNG_BEATDELAY);
	}
	
	cout << "Terminating ..." << endl;
	
	pDBMng->log(modId, Logger::NOTICE, Chain("Shutting database down .."));
	
	ListT<Chain> atsList;
	
	pDBMng->getActiveTableSet(h.getName(), atsList);
	
	if ( atsList.Size() > 0 )
	{
	    CegoDistManager tabMng(pDBMng);
	    Chain *pATS = atsList.First();
	    while ( pATS )
	    {
		
		pDBMng->log(modId, Logger::NOTICE, Chain("Syncing active tableset ") + *pATS + Chain("..."));
		
		tabMng.writeCheckPoint(*pATS, true, true);
		pDBMng->setTableSetRunState(*pATS, XML_OFFLINE_VALUE);
		pATS = atsList.Next();
	    }
	}
	
	// anyway sync the xml file
	pDBMng->doc2Xml();
	
	// because admin threads are using dbthreadpool and logthread pool, we hove to terminate admin thread pool first
#ifdef CGDEBUG	
	pDBMng->log(modId, Logger::DEBUG, Chain("Deleting admin threadpool ..."));
#endif

	delete pAdminPool;

#ifdef CGDEBUG	
	pDBMng->log(modId, Logger::DEBUG, Chain("Deleting log threadpool ..."));
#endif

	delete pLogPool;

#ifdef CGDEBUG	
	pDBMng->log(modId, Logger::DEBUG, Chain("Deleting db threadpool ..."));
#endif

	delete pDbPool;

#ifdef CGDEBUG	
	pDBMng->log(modId, Logger::DEBUG, Chain("Deleting beat thread ..."));
#endif

	delete pBeatThread;

#ifdef CGDEBUG	
	pDBMng->log(modId, Logger::DEBUG, Chain("Deleting med thread ..."));
#endif

	delete pMedThread;
	
	pLockHandler->deleteLocks();
	delete pLockHandler;
    }
    catch ( Exception e)
    {

	Chain msg;
	Chain module;
	int line;
	
	bool log2Console=true;

	if ( pDBMng ) 
	{	
	    if ( pDBMng->isLoggerConfigured() )
	    {
		Chain exep;
		while ( e.pop(module, line, msg) )
		{
		    exep += Chain("\n\t") + module + Chain("(") +  Chain(line) + Chain(") : ") + msg;
		}		
		pDBMng->log(modId, Logger::LOGERR, Chain("Main Thread : ") + exep);	    

		cerr << "Cego daemon failed, see log for detailed information" << endl;
		log2Console=false;
	    }
	}
	
	if ( log2Console )
	{
	    cerr << "Cego daemon failed :";
	    Chain exep;
	    while ( e.pop(module, line, msg) )
	    {
		exep += Chain("\n\t") + module + Chain("(") +  Chain(line) + Chain(") : ") + msg;	    
	    }
	    cerr << exep << endl;
	}
	exitCode = 1;
    }
    
    if ( pDBMng )
    {
	pDBMng->log(modId, Logger::NOTICE, Chain("Shutdown finished"));
	delete pDBMng;
    }

    if ( exitCode == 0 )
    {
	cout << "Cego daemon is terminated." << endl;
    }

    return exitCode;
}


bool processBatchFile(CegoDatabaseManager *pDBMng, CegoAction *pAction, const Chain& batchFileName, bool ignoreError, bool consoleOut, Chain& errorMsg)
{
    File batchFile(batchFileName);
    batchFile.open(File::READ);

    int lineNo=0;

    Chain cmd;
    Chain line;

    bool disableDelimiter=false;

    unsigned long modId = pDBMng->getModId("CegoMain");

#ifdef CGDEBUG	
    pDBMng->log(modId, Logger::DEBUG, Chain("Processing batchfile ") + batchFileName);
#endif

    while (batchFile.readLine(line, MAXCMDLEN))
    {
	lineNo++;
	line = line.cutTrailing(" \t");
	
	bool isCommentLine = false;
	
	if ( line.length() >= 2 )
	{		    
	    if (line.cutTrailing(" \t").subChain(1, 2) == Chain("--"))
	    {
		isCommentLine = true;
	    }
	}
	
	if ( ! isCommentLine )
	{
	    if ( line == Chain("@") )
	    {
		if ( disableDelimiter == false )
		    disableDelimiter=true;
		else
		    disableDelimiter=false;
	    }
	    else
	    {			
		cmd = cmd + Chain(" ") + line;
		cmd = cmd.cutTrailing(" \t");
	    }
	    
	    if ( cmd.length() > 0 )
	    {
		
		if (cmd.subChain(cmd.length()-1, cmd.length()) == Chain(";") 
		    && disableDelimiter==false)
		{
		    
#ifdef CGDEBUG	
		    pDBMng->log(modId, Logger::DEBUG, Chain("Processing command <<<") + cmd + Chain(">>>"));	
#endif		
    
		    pAction->cleanUp();
		    pAction->setCommandChain(cmd);
		    
		    try {

			Timer t(6,3);
			t.start();			
			pAction->parse();
			t.stop();

			if ( consoleOut )
			    cout << "ok ( " << t << " s )" << endl;
		    }
		    catch ( Exception e)
		    {
			
			    			    
			Chain msg;
			Chain module;
			int line;
			
			Chain exep;
			while ( e.pop(module, line, msg) )
			{
			    exep += Chain("\n\t") + module + Chain("(") +  Chain(line) + Chain(") : ") + msg;
			}
			
			pDBMng->log(modId, Logger::LOGERR, Chain("Batch Thread : ") + exep);

			errorMsg=e.getBaseMsg();			
			
			if ( ignoreError )
			{		     
			    
			    if ( consoleOut )
			    {
				cerr << Chain("Error ignored at line number ") + Chain(lineNo) + " : ";
				cerr << errorMsg << endl;
			    }
			}
			else
			{
			    if ( consoleOut )
			    {
				cerr << Chain("Line Number ") + Chain(lineNo) + Chain(" : ");
				cerr << errorMsg << endl;
			    }

			    return false;
			   
			}
		    }
		    
		    cmd = Chain();
		    
		}
	    }
	}
    }
    if ( cmd.length() > 1 && consoleOut)
    {
	cout << "incomplete command ..<" << cmd << ">" << endl;
    }
    
    batchFile.close();

    return true;
    
}
