summaryrefslogtreecommitdiff
path: root/cpp/src/IceGrid/ServerI.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/src/IceGrid/ServerI.cpp')
-rw-r--r--cpp/src/IceGrid/ServerI.cpp807
1 files changed, 807 insertions, 0 deletions
diff --git a/cpp/src/IceGrid/ServerI.cpp b/cpp/src/IceGrid/ServerI.cpp
new file mode 100644
index 00000000000..3d97d2a0302
--- /dev/null
+++ b/cpp/src/IceGrid/ServerI.cpp
@@ -0,0 +1,807 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2005 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+#ifdef __sun
+#define _POSIX_PTHREAD_SEMANTICS
+#endif
+
+#include <IceUtil/UUID.h>
+#include <Ice/Ice.h>
+#include <IceGrid/ServerI.h>
+#include <IceGrid/ServerFactory.h>
+#include <IceGrid/TraceLevels.h>
+#include <IceGrid/Activator.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef _WIN32
+# include <direct.h>
+#else
+# include <unistd.h>
+# include <dirent.h>
+#endif
+
+#include <fstream>
+
+using namespace std;
+using namespace IceGrid;
+
+ServerI::ServerI(const ServerFactoryPtr& factory,
+ const TraceLevelsPtr& traceLevels,
+ const ActivatorPtr& activator,
+ Ice::Int waitTime,
+ const string& serversDir) :
+ _factory(factory),
+ _traceLevels(traceLevels),
+ _activator(activator),
+ _waitTime(waitTime),
+ _serversDir(serversDir),
+ _state(Inactive)
+{
+ assert(_activator);
+}
+
+ServerI::~ServerI()
+{
+}
+
+bool
+ServerI::start(ServerActivation act, const Ice::Current& current)
+{
+ string exe;
+ string wd;
+ Ice::StringSeq opts;
+ Ice::StringSeq evs;
+
+ while(true)
+ {
+ Lock sync(*this);
+ switch(_state)
+ {
+ case Inactive:
+ {
+ if(act < activation)
+ {
+ return false;
+ }
+
+ _state = Activating;
+
+ //
+ // Prevent eviction of the server object once it's not anymore in the inactive state.
+ //
+ _factory->getServerEvictor()->keep(current.id);
+
+ break;
+ }
+ case Activating:
+ case Deactivating:
+ {
+ wait(); // TODO: Timeout?
+ continue;
+ }
+ case Active:
+ {
+ return true; // Raise an exception instead?
+ }
+ case Destroying:
+ case Destroyed:
+ {
+ Ice::ObjectNotExistException ex(__FILE__,__LINE__);
+ ex.id = current.id;
+ throw ex;
+ }
+ }
+
+ if(_traceLevels->server > 2)
+ {
+ Ice::Trace out(_traceLevels->logger, _traceLevels->serverCat);
+ out << "changed server `" << name << "' state to `Activating'";
+ }
+ assert(_state == Activating);
+
+ exe = exePath;
+ wd = pwd;
+ opts = options;
+ evs = envs;
+ break;
+ }
+
+ try
+ {
+ ServerPrx self = ServerPrx::uncheckedCast(current.adapter->createProxy(current.id));
+ bool active = _activator->activate(name, exe, wd, opts, evs, self);
+ setState(active ? Active : Inactive, current);
+ return active;
+ }
+ catch(const Ice::SyscallException& ex)
+ {
+ Ice::Warning out(_traceLevels->logger);
+ out << "activation failed for server `" << name << "':\n";
+ out << ex;
+
+ setState(Inactive, current);
+ return false;
+ }
+}
+
+void
+ServerI::stop(const Ice::Current& current)
+{
+ while(true)
+ {
+ Lock sync(*this);
+ switch(_state)
+ {
+ case Inactive:
+ {
+ return;
+ }
+ case Activating:
+ case Deactivating:
+ {
+ wait(); // TODO: Timeout?
+ continue;
+ }
+ case Active:
+ {
+ _state = Deactivating;
+ break;
+ }
+ case Destroying:
+ case Destroyed:
+ {
+ Ice::ObjectNotExistException ex(__FILE__,__LINE__);
+ ex.id = current.id;
+ throw ex;
+ }
+ }
+
+ if(_traceLevels->server > 2)
+ {
+ Ice::Trace out(_traceLevels->logger, _traceLevels->serverCat);
+ out << "changed server `" << name << "' state to `Deactivating'";
+ }
+ assert(_state == Deactivating);
+ break;
+ }
+
+ stopInternal(current);
+}
+
+void
+ServerI::sendSignal(const string& signal, const Ice::Current& current)
+{
+ _activator->sendSignal(name, signal);
+}
+
+void
+ServerI::writeMessage(const string& message, Ice::Int fd, const Ice::Current& current)
+{
+ IceUtil::Monitor< ::IceUtil::Mutex>::Lock sync(*this);
+ if(_process != 0)
+ {
+ try
+ {
+ _process->writeMessage(message, fd);
+ }
+ catch(const Ice::LocalException&)
+ {
+ }
+ }
+}
+
+void
+ServerI::destroy(const Ice::Current& current)
+{
+ bool stop = false;
+
+ while(true)
+ {
+ Lock sync(*this);
+ switch(_state)
+ {
+ case Inactive:
+ {
+ _state = Destroyed;
+ break;
+ }
+ case Active:
+ {
+ stop = true;
+ _state = Destroying;
+ break;
+ }
+ case Activating:
+ case Deactivating:
+ {
+ wait(); // TODO: Timeout?
+ continue;
+ }
+ case Destroying:
+ case Destroyed:
+ {
+ Ice::ObjectNotExistException ex(__FILE__,__LINE__);
+ ex.id = current.id;
+ throw ex;
+ }
+ }
+
+ assert(_state == Destroyed || _state == Destroying);
+
+ if(_traceLevels->server > 2)
+ {
+ Ice::Trace out(_traceLevels->logger, _traceLevels->serverCat);
+ out << "changed server `" << name << "' state to `";
+ out << (_state == Destroyed ? "Destroyed" : "Destroying") << "'";
+ }
+ break;
+ }
+
+ if(stop)
+ {
+ stopInternal(current);
+ }
+
+
+ _factory->destroy(this, current.id);
+}
+
+void
+ServerI::terminated(const Ice::Current& current)
+{
+ ServerState newState = Inactive; // Initialize to keep the compiler happy.
+ ServerAdapterPrxDict adpts;
+ while(true)
+ {
+ Lock sync(*this);
+ switch(_state)
+ {
+ case Inactive:
+ {
+ assert(false);
+ }
+ case Activating:
+ {
+ wait(); // TODO: Timeout?
+ continue;
+ }
+ case Active:
+ {
+ _state = Deactivating;
+ if(_traceLevels->server > 2)
+ {
+ Ice::Trace out(_traceLevels->logger, _traceLevels->serverCat);
+ out << "changed server `" << name << "' state to `Deactivating'";
+ }
+ newState = Inactive;
+ break;
+ }
+ case Deactivating:
+ {
+ //
+ // Deactivation was initiated by the stop method.
+ //
+ newState = Inactive;
+ break;
+ }
+ case Destroying:
+ {
+ //
+ // Deactivation was initiated by the destroy method.
+ //
+ newState = Destroyed;
+ break;
+ }
+ case Destroyed:
+ {
+ assert(false);
+ }
+ }
+
+ assert(_state == Deactivating || _state == Destroying);
+ adpts = adapters;
+
+ //
+ // Clear the process proxy.
+ //
+ _process = 0;
+ break;
+ }
+
+ if(newState != Destroyed)
+ {
+ //
+ // The server has terminated, set its adapter direct proxies to
+ // null to cause the server re-activation if one of its adapter
+ // direct proxy is requested.
+ //
+ for(ServerAdapterPrxDict::iterator p = adpts.begin(); p != adpts.end(); ++p)
+ {
+ try
+ {
+ p->second->setDirectProxy(0);
+ }
+ catch(const Ice::ObjectNotExistException&)
+ {
+ }
+ }
+ }
+
+ setState(newState, current);
+}
+
+ServerState
+ServerI::getState(const Ice::Current&)
+{
+ Lock sync(*this);
+ return _state;
+}
+
+Ice::Int
+ServerI::getPid(const Ice::Current& current)
+{
+ return _activator->getServerPid(name);
+}
+
+void
+ServerI::setActivationMode(ServerActivation mode, const ::Ice::Current&)
+{
+ Lock sync(*this);
+ activation = mode;
+}
+
+ServerActivation
+ServerI::getActivationMode(const ::Ice::Current&)
+{
+ Lock sync(*this);
+ return activation;
+}
+
+ServerDescriptorPtr
+ServerI::getDescriptor(const Ice::Current&)
+{
+ Lock sync(*this);
+ return descriptor;
+}
+
+void
+ServerI::setExePath(const string& path, const ::Ice::Current&)
+{
+ Lock sync(*this);
+ exePath = path;
+}
+
+void
+ServerI::setPwd(const string& path,const ::Ice::Current&)
+{
+ Lock sync(*this);
+ pwd = path;
+}
+
+void
+ServerI::setEnvs(const Ice::StringSeq& s, const ::Ice::Current&)
+{
+ Lock sync(*this);
+ envs = s;
+}
+
+void
+ServerI::setOptions(const Ice::StringSeq& opts, const ::Ice::Current&)
+{
+ Lock sync(*this);
+ options = opts;
+}
+
+void
+ServerI::addAdapter(const ServerAdapterPrx& adapter, bool registerProcess, const ::Ice::Current&)
+{
+ Lock sync(*this);
+ ServerAdapterPrxDict::const_iterator p = adapters.find(adapter->ice_getIdentity());
+ if(p != adapters.end())
+ {
+ DeploymentException ex;
+ ex.reason = "failed to add adapter because it already exists";
+ throw ex;
+ }
+ adapters[adapter->ice_getIdentity()] = adapter;
+ processRegistered |= registerProcess;
+}
+
+void
+ServerI::removeAdapter(const ServerAdapterPrx& adapter, const ::Ice::Current&)
+{
+ Lock sync(*this);
+ adapters.erase(adapter->ice_getIdentity());
+}
+
+string
+ServerI::addConfigFile(const string& n, const PropertyDescriptorSeq& properties, const ::Ice::Current&)
+{
+ string file = _serversDir + name + "/config/" + n;
+
+ ofstream configfile;
+ configfile.open(file.c_str(), ios::out);
+ if(!configfile)
+ {
+ DeploymentException ex;
+ ex.reason = "couldn't create configuration file: " + file;
+ throw ex;
+ }
+
+ for(PropertyDescriptorSeq::const_iterator p = properties.begin(); p != properties.end(); ++p)
+ {
+ configfile << p->name;
+ if(!p->value.empty())
+ {
+ configfile << "=" << p->value;
+ }
+ configfile << endl;
+ }
+ configfile.close();
+
+ return file;
+}
+
+void
+ServerI::removeConfigFile(const string& n, const ::Ice::Current&)
+{
+ string file = _serversDir + name + "/config/" + n;
+ if(unlink(file.c_str()) != 0)
+ {
+ Ice::Warning out(_traceLevels->logger);
+ out << "couldn't remove configuration file: " + file + ": " + strerror(getSystemErrno());
+ }
+}
+
+string
+ServerI::addDbEnv(const DbEnvDescriptor& dbEnv, const string& path, const ::Ice::Current&)
+{
+ string dir;
+ if(dbEnv.dbHome.empty())
+ {
+ dir = _serversDir + name + "/dbs/" + dbEnv.name;
+ }
+ else
+ {
+ dir = dbEnv.dbHome;
+ }
+
+ //
+ // If no db home directory is specified for this db env, we provide one.
+ //
+ if(dbEnv.dbHome.empty())
+ {
+ //
+ // First, we try to move the given backup if specified, if not successful, we just
+ // create the database environment directory.
+ //
+ if(path.empty() || rename((path + "/" + dbEnv.name).c_str(), dir.c_str()) != 0)
+ {
+ //
+ // Create the database environment directory.
+ //
+#ifdef _WIN32
+ if(_mkdir(dir.c_str()) != 0)
+#else
+ if(mkdir(dir.c_str(), 0755) != 0)
+#endif
+ {
+ DeploymentException ex;
+ ex.reason = "couldn't create directory " + dir + ": " + strerror(getSystemErrno());
+ throw ex;
+ }
+ }
+ }
+
+ string file = dir + "/DB_CONFIG";
+ ofstream configfile;
+ configfile.open(file.c_str(), ios::out);
+ if(!configfile)
+ {
+ rmdir(dir.c_str());
+
+ DeploymentException ex;
+ ex.reason = "couldn't create configuration file: " + file;
+ throw ex;
+ }
+
+ for(PropertyDescriptorSeq::const_iterator p = dbEnv.properties.begin(); p != dbEnv.properties.end(); ++p)
+ {
+ if(!p->name.empty())
+ {
+ configfile << p->name;
+ if(!p->value.empty())
+ {
+ configfile << " " << p->value;
+ }
+ configfile << endl;
+ }
+ }
+ configfile.close();
+
+ return dir;
+}
+
+void
+ServerI::removeDbEnv(const DbEnvDescriptor& dbEnv, const string& moveTo, const ::Ice::Current&)
+{
+ string path;
+ if(dbEnv.dbHome.empty())
+ {
+ path = _serversDir + name + "/dbs/" + dbEnv.name;
+ }
+ else
+ {
+ path = dbEnv.dbHome;
+ }
+
+ if(unlink((path + "/DB_CONFIG").c_str()) != 0)
+ {
+ Ice::Warning out(_traceLevels->logger);
+ out << "couldn't remove file: " + path + "/DB_CONFIG: " + strerror(getSystemErrno());
+ }
+
+ //
+ // If no db home directory was specified for this db env, we provided one. We need to cleanup
+ // this directory now.
+ //
+ if(dbEnv.dbHome.empty())
+ {
+ if(!moveTo.empty())
+ {
+ //
+ // Move the database environment directory to the given directory.
+ //
+ if(rename(path.c_str(), (moveTo + "/" + dbEnv.name).c_str()) != 0)
+ {
+ Ice::Warning out(_traceLevels->logger);
+ out << "couldn't rename directory " + path + " to " + moveTo + "/" + dbEnv.name + ": " +
+ strerror(getSystemErrno());
+ }
+ }
+ else
+ {
+ //
+ // Delete the database environment directory.
+ //
+ Ice::StringSeq files;
+
+#ifdef _WIN32
+ string pattern = path + "/*";
+ WIN32_FIND_DATA data;
+ HANDLE hnd = FindFirstFile(pattern.c_str(), &data);
+ if(hnd == INVALID_HANDLE_VALUE)
+ {
+ // TODO: log a warning, throw an exception?
+ return;
+ }
+
+ do
+ {
+ if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
+ {
+ files.push_back(path + "/" + data.cFileName);
+ }
+ }
+ while(FindNextFile(hnd, &data));
+
+ FindClose(hnd);
+#else
+
+ DIR* dir = opendir(path.c_str());
+ if(dir == 0)
+ {
+ // TODO: log a warning, throw an exception?
+ return;
+ }
+
+ // TODO: make the allocation/deallocation exception-safe
+ struct dirent* entry = static_cast<struct dirent*>(malloc(pathconf(path.c_str(), _PC_NAME_MAX) + 1));
+
+ while(readdir_r(dir, entry, &entry) == 0 && entry != 0)
+ {
+ string name = path + "/" + entry->d_name;
+ struct stat buf;
+
+ if(::stat(name.c_str(), &buf) != 0)
+ {
+ if(errno != ENOENT)
+ {
+ //
+ // TODO: log error
+ //
+ }
+ }
+ else if(S_ISREG(buf.st_mode))
+ {
+ files.push_back(name);
+ }
+ }
+
+ free(entry);
+ closedir(dir);
+#endif
+
+ for(Ice::StringSeq::iterator p = files.begin(); p != files.end(); ++p)
+ {
+ if(unlink(p->c_str()) != 0)
+ {
+ //
+ // TODO: log error
+ //
+ }
+ }
+
+ if(rmdir(path.c_str()) != 0)
+ {
+ Ice::Warning out(_traceLevels->logger);
+ out << "couldn't remove directory: " + path + ": " + strerror(getSystemErrno());
+ }
+ }
+ }
+}
+
+void
+ServerI::setProcess(const ::Ice::ProcessPrx& proc, const ::Ice::Current&)
+{
+ IceUtil::Monitor< ::IceUtil::Mutex>::Lock sync(*this);
+ _process = proc;
+ notifyAll();
+}
+
+void
+ServerI::stopInternal(const Ice::Current& current)
+{
+ Ice::ProcessPrx process;
+ {
+ IceUtil::Monitor< ::IceUtil::Mutex>::Lock sync(*this);
+ if(!_process && processRegistered)
+ {
+ while(!_process)
+ {
+ if(_state == Inactive || _state == Destroyed)
+ {
+ //
+ // State changed to inactive or destroyed, the server
+ // has been correctly deactivated, we can return.
+ //
+ return;
+ }
+
+ //
+ // Wait for the process to be set.
+ //
+ wait(); // TODO: timeout?
+ }
+ }
+ process = _process;
+ }
+
+ try
+ {
+ //
+ // Deactivate the server.
+ //
+ _activator->deactivate(name, process);
+
+ //
+ // Wait for the server to be inactive (the activator monitors
+ // the process and should notify us when it detects the
+ // process termination by calling the terminated() method).
+ //
+ Lock sync(*this);
+
+#ifndef NDEBUG
+ ServerState oldState = _state;
+#endif
+
+ while(true)
+ {
+ if(_state == Inactive || _state == Destroyed)
+ {
+ //
+ // State changed to inactive or destroyed, the server
+ // has been correctly deactivated, we can return.
+ //
+ return;
+ }
+
+ //
+ // Wait for a notification.
+ //
+ bool notify = timedWait(IceUtil::Time::seconds(_waitTime));
+ if(!notify)
+ {
+ assert(oldState == _state);
+ break;
+ }
+ }
+
+ if(_traceLevels->server > 1)
+ {
+ Ice::Trace out(_traceLevels->logger, _traceLevels->serverCat);
+ out << "graceful server shutdown timed out, killing server `" << name << "'";
+ }
+ }
+ catch(const Ice::Exception& ex)
+ {
+ Ice::Warning out(_traceLevels->logger);
+ out << "graceful server shutdown failed, killing server `" << name << "':\n";
+ out << ex;
+ }
+
+
+ //
+ // The server is still not inactive, kill it.
+ //
+ try
+ {
+ _activator->kill(name);
+ }
+ catch(const Ice::SyscallException& ex)
+ {
+ Ice::Warning out(_traceLevels->logger);
+ out << "deactivation failed for server `" << name << "':\n";
+ out << ex;
+
+ setState(Active, current);
+ }
+}
+
+void
+ServerI::setState(ServerState st, const Ice::Current& current)
+{
+ Lock sync(*this);
+
+ //
+ // Allow eviction of an inactive server object.
+ //
+ if(_state != Inactive && st == Inactive)
+ {
+ _factory->getServerEvictor()->release(current.id);
+ }
+
+ _state = st;
+
+ if(_traceLevels->server > 1)
+ {
+ if(_state == Active)
+ {
+ Ice::Trace out(_traceLevels->logger, _traceLevels->serverCat);
+ out << "changed server `" << name << "' state to `Active'";
+ }
+ else if(_state == Inactive)
+ {
+ Ice::Trace out(_traceLevels->logger, _traceLevels->serverCat);
+ out << "changed server `" << name << "' state to `Inactive'";
+ }
+ else if(_state == Destroyed)
+ {
+ Ice::Trace out(_traceLevels->logger, _traceLevels->serverCat);
+ out << "changed server `" << name << "' state to `Destroyed'";
+ }
+ else if(_traceLevels->server > 2)
+ {
+ if(_state == Activating)
+ {
+ Ice::Trace out(_traceLevels->logger, _traceLevels->serverCat);
+ out << "changed server `" << name << "' state to `Activating'";
+ }
+ else if(_state == Deactivating)
+ {
+ Ice::Trace out(_traceLevels->logger, _traceLevels->serverCat);
+ out << "changed server `" << name << "' state to `Deactivating'";
+ }
+ }
+ }
+
+ notifyAll();
+}