summaryrefslogtreecommitdiff
path: root/cpp/src/IceGrid/IceGridNode.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/src/IceGrid/IceGridNode.cpp')
-rw-r--r--cpp/src/IceGrid/IceGridNode.cpp851
1 files changed, 851 insertions, 0 deletions
diff --git a/cpp/src/IceGrid/IceGridNode.cpp b/cpp/src/IceGrid/IceGridNode.cpp
new file mode 100644
index 00000000000..bdfefe0a9de
--- /dev/null
+++ b/cpp/src/IceGrid/IceGridNode.cpp
@@ -0,0 +1,851 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2011 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.
+//
+// **********************************************************************
+
+#include <IceUtil/UUID.h>
+#include <IceUtil/Timer.h>
+#include <IceUtil/StringUtil.h>
+#include <IceUtil/FileUtil.h>
+#include <Ice/Ice.h>
+#include <Ice/Locator.h>
+#include <Ice/Service.h>
+#include <IceGrid/Activator.h>
+#include <IceGrid/NodeServerAdminRouter.h>
+#include <IceGrid/RegistryI.h>
+#include <IceGrid/FileUserAccountMapperI.h>
+#include <IceGrid/NodeI.h>
+#include <IceGrid/NodeSessionManager.h>
+#include <IceGrid/TraceLevels.h>
+#include <IceGrid/DescriptorParser.h>
+#include <IceGrid/Util.h>
+#include <IcePatch2/Util.h>
+
+#ifdef _WIN32
+# include <direct.h>
+# include <sys/types.h>
+# include <winsock2.h>
+#endif
+
+using namespace std;
+using namespace Ice;
+using namespace IceGrid;
+
+namespace
+{
+
+class ProcessI : public Process
+{
+public:
+
+ ProcessI(const ActivatorPtr&, const ProcessPtr&);
+
+ virtual void shutdown(const Current&);
+ virtual void writeMessage(const std::string&, Int, const Current&);
+
+private:
+
+ ActivatorPtr _activator;
+ ProcessPtr _origProcess;
+};
+
+
+class NodeService : public Service
+{
+public:
+
+ NodeService();
+ ~NodeService();
+
+ virtual bool shutdown();
+
+protected:
+
+ virtual bool start(int, char*[], int&);
+ bool startImpl(int, char*[], int&);
+ virtual void waitForShutdown();
+ virtual bool stop();
+ virtual CommunicatorPtr initializeCommunicator(int&, char*[], const InitializationData&);
+
+private:
+
+ void usage(const std::string&);
+
+ ActivatorPtr _activator;
+ IceUtil::TimerPtr _timer;
+ RegistryIPtr _registry;
+ NodeIPtr _node;
+ NodeSessionManager _sessions;
+ Ice::ObjectAdapterPtr _adapter;
+};
+
+class CollocatedRegistry : public RegistryI
+{
+public:
+
+ CollocatedRegistry(const CommunicatorPtr&, const ActivatorPtr&, bool, bool);
+ virtual void shutdown();
+
+private:
+
+ ActivatorPtr _activator;
+};
+
+
+class DefaultServantLocator : public Ice::ServantLocator
+{
+public:
+
+ DefaultServantLocator(const ObjectPtr& servant) :
+ _servant(servant)
+ {
+ }
+
+ virtual ObjectPtr locate(const Current& c, LocalObjectPtr&)
+ {
+ return _servant;
+ }
+
+ virtual void finished(const Current&, const ObjectPtr&, const LocalObjectPtr&)
+ {
+ }
+
+ virtual void deactivate(const string&)
+ {
+ }
+
+private:
+ ObjectPtr _servant;
+};
+
+#ifdef _WIN32
+void
+setNoIndexingAttribute(const string& pa)
+{
+ wstring path = IceUtil::stringToWstring(pa);
+ DWORD attrs = GetFileAttributesW(path.c_str());
+ if(attrs == INVALID_FILE_ATTRIBUTES)
+ {
+ FileException ex(__FILE__, __LINE__);
+ ex.path = pa;
+ ex.error = IceInternal::getSystemErrno();
+ throw ex;
+ }
+ if(!SetFileAttributesW(path.c_str(), attrs | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED))
+ {
+ FileException ex(__FILE__, __LINE__);
+ ex.path = pa;
+ ex.error = IceInternal::getSystemErrno();
+ throw ex;
+ }
+}
+#endif
+
+}
+
+
+CollocatedRegistry::CollocatedRegistry(const CommunicatorPtr& com,
+ const ActivatorPtr& activator,
+ bool nowarn,
+ bool readonly) :
+ RegistryI(com, new TraceLevels(com, "IceGrid.Registry"), nowarn, readonly),
+ _activator(activator)
+{
+}
+
+void
+CollocatedRegistry::shutdown()
+{
+ _activator->shutdown();
+}
+
+ProcessI::ProcessI(const ActivatorPtr& activator, const ProcessPtr& origProcess) :
+ _activator(activator),
+ _origProcess(origProcess)
+{
+}
+
+void
+ProcessI::shutdown(const Current&)
+{
+ _activator->shutdown();
+}
+
+void
+ProcessI::writeMessage(const string& message, Int fd, const Current& current)
+{
+ _origProcess->writeMessage(message, fd, current);
+}
+
+NodeService::NodeService()
+{
+}
+
+NodeService::~NodeService()
+{
+}
+
+
+bool
+NodeService::shutdown()
+{
+ assert(_activator);
+ _activator->shutdown();
+ _sessions.terminate(); // Unblock the main thread if it's blocked on waitForCreate()
+ return true;
+}
+
+bool
+NodeService::start(int argc, char* argv[], int& status)
+{
+ try
+ {
+ if(!startImpl(argc, argv, status))
+ {
+ stop();
+ return false;
+ }
+ }
+ catch(...)
+ {
+ stop();
+ throw;
+ }
+ return true;
+}
+
+bool
+NodeService::startImpl(int argc, char* argv[], int& status)
+{
+ bool nowarn = false;
+ bool readonly = false;
+ string desc;
+ vector<string> targets;
+ for(int i = 1; i < argc; ++i)
+ {
+ if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0)
+ {
+ usage(argv[0]);
+ status = EXIT_SUCCESS;
+ return false;
+ }
+ else if(strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0)
+ {
+ print(ICE_STRING_VERSION);
+ status = EXIT_SUCCESS;
+ return false;
+ }
+ else if(strcmp(argv[i], "--nowarn") == 0)
+ {
+ nowarn = true;
+ }
+ else if(strcmp(argv[i], "--readonly") == 0)
+ {
+ readonly = true;
+ }
+ else if(strcmp(argv[i], "--deploy") == 0)
+ {
+ if(i + 1 >= argc)
+ {
+ error("missing descriptor argument for option `" + string(argv[i]) + "'");
+ usage(argv[0]);
+ return false;
+ }
+
+ desc = argv[++i];
+
+ while(i + 1 < argc && argv[++i][0] != '-')
+ {
+ targets.push_back(argv[i]);
+ }
+ }
+ else
+ {
+ error("invalid option: `" + string(argv[i]) + "'");
+ usage(argv[0]);
+ return false;
+ }
+ }
+
+ PropertiesPtr properties = communicator()->getProperties();
+
+ //
+ // Disable server idle time. Otherwise, the adapter would be
+ // shutdown prematurely and the deactivation would fail.
+ // Deactivation of the node relies on the object adapter
+ // to be active since it needs to terminate servers.
+ //
+ // TODO: implement Ice.ServerIdleTime in the activator
+ // termination listener instead?
+ //
+ properties->setProperty("Ice.ServerIdleTime", "0");
+
+ //
+ // Warn the user that setting Ice.ThreadPool.Server isn't useful.
+ //
+ if(!nowarn && properties->getPropertyAsIntWithDefault("Ice.ThreadPool.Server.Size", 0) > 0)
+ {
+ Warning out(communicator()->getLogger());
+ out << "setting `Ice.ThreadPool.Server.Size' is not useful, ";
+ out << "you should set individual adapter thread pools instead.";
+ }
+
+ setupThreadPool(properties, "IceGrid.Node.ThreadPool", 1, 100);
+
+ //
+ // Create the activator.
+ //
+ TraceLevelsPtr traceLevels = new TraceLevels(communicator(), "IceGrid.Node");
+ _activator = new Activator(traceLevels);
+
+ //
+ // Collocate the IceGrid registry if we need to.
+ //
+ if(properties->getPropertyAsInt("IceGrid.Node.CollocateRegistry") > 0)
+ {
+ _registry = new CollocatedRegistry(communicator(), _activator, nowarn, readonly);
+ if(!_registry->start())
+ {
+ return false;
+ }
+
+ //
+ // Set the default locator property to point to the collocated
+ // locator (this property is passed by the activator to each
+ // activated server). The default locator is also needed by
+ // the node session manager.
+ //
+ if(properties->getProperty("Ice.Default.Locator").empty())
+ {
+ Identity locatorId;
+ locatorId.category = properties->getPropertyWithDefault("IceGrid.InstanceName", "IceGrid");
+ locatorId.name = "Locator";
+ string endpoints = properties->getProperty("IceGrid.Registry.Client.Endpoints");
+ string locatorPrx = "\"" + communicator()->identityToString(locatorId) + "\" :" + endpoints;
+ communicator()->setDefaultLocator(Ice::LocatorPrx::uncheckedCast(communicator()->stringToProxy(locatorPrx)));
+ properties->setProperty("Ice.Default.Locator", locatorPrx);
+ }
+ }
+ else if(properties->getProperty("Ice.Default.Locator").empty())
+ {
+ error("property `Ice.Default.Locator' is not set");
+ return false;
+ }
+
+ //
+ // Initialize the database environment (first setup the directory structure if needed).
+ //
+ string dataPath = properties->getProperty("IceGrid.Node.Data");
+ string dbPath;
+ if(dataPath.empty())
+ {
+ error("property `IceGrid.Node.Data' is not set");
+ return false;
+ }
+ else
+ {
+ if(!IceUtilInternal::directoryExists(dataPath))
+ {
+ ostringstream os;
+ FileException ex(__FILE__, __LINE__);
+ ex.path = dataPath;
+ ex.error = IceInternal::getSystemErrno();
+ os << ex;
+ error("property `IceGrid.Node.Data' is set to an invalid path:\n" + os.str());
+ return false;
+ }
+
+ //
+ // Creates subdirectories.
+ //
+ if(dataPath[dataPath.length() - 1] != '/')
+ {
+ dataPath += "/";
+ }
+
+ IcePatch2::createDirectory(dataPath + "servers");
+ IcePatch2::createDirectory(dataPath + "tmp");
+ IcePatch2::createDirectory(dataPath + "distrib");
+
+#ifdef _WIN32
+ //
+ // Make sure these directories are not indexed by the Windows
+ // indexing service (which can cause random "Access Denied"
+ // errors if indexing runs at the same time as the node is
+ // creating/deleting files).
+ //
+ try
+ {
+ setNoIndexingAttribute(dataPath + "servers");
+ setNoIndexingAttribute(dataPath + "tmp");
+ setNoIndexingAttribute(dataPath + "distrib");
+ }
+ catch(const FileException& ex)
+ {
+ if(!nowarn)
+ {
+ Warning out(communicator()->getLogger());
+ out << "couldn't disable file indexing:\n" << ex;
+ }
+ }
+#endif
+ }
+
+ //
+ // Check that required properties are set and valid.
+ //
+ if(properties->getProperty("IceGrid.Node.Endpoints").empty())
+ {
+ error("property `IceGrid.Node.Endpoints' is not set");
+ return false;
+ }
+
+ string name = properties->getProperty("IceGrid.Node.Name");
+ if(name.empty())
+ {
+ error("property `IceGrid.Node.Name' is not set");
+ return false;
+ }
+
+ //
+ // Setup the Freeze database environment home directory. The name of the database
+ // environment for the IceGrid node is the name of the node.
+ //
+ properties->setProperty("Freeze.DbEnv." + name + ".DbHome", dbPath);
+
+ //
+ // Create the node object adapter.
+ //
+ _adapter = communicator()->createObjectAdapter("IceGrid.Node");
+
+ //
+ // Setup the user account mapper if configured.
+ //
+ string mapperProperty = "IceGrid.Node.UserAccountMapper";
+ string mapperPropertyValue = properties->getProperty(mapperProperty);
+ UserAccountMapperPrx mapper;
+ if(!mapperPropertyValue.empty())
+ {
+ try
+ {
+ mapper = UserAccountMapperPrx::uncheckedCast(communicator()->propertyToProxy(mapperProperty));
+ }
+ catch(const Ice::LocalException& ex)
+ {
+ ostringstream os;
+ os << "user account mapper `" << mapperProperty << "' is invalid:\n" << ex;
+ error(os.str());
+ return false;
+ }
+ }
+ else
+ {
+ string userAccountFileProperty = properties->getProperty("IceGrid.Node.UserAccounts");
+ if(!userAccountFileProperty.empty())
+ {
+ try
+ {
+ Ice::ObjectPrx object = _adapter->addWithUUID(new FileUserAccountMapperI(userAccountFileProperty));
+ object = object->ice_collocationOptimized(true);
+ mapper = UserAccountMapperPrx::uncheckedCast(object);
+ }
+ catch(const std::string& msg)
+ {
+ error(msg);
+ return false;
+ }
+ }
+ }
+
+ //
+ // Create a new timer to handle server activation/deactivation timeouts.
+ //
+ _timer = new IceUtil::Timer();
+
+ //
+ // The IceGrid instance name.
+ //
+ const string instanceName = communicator()->getDefaultLocator()->ice_getIdentity().category;
+
+ //
+ // Create the server factory. The server factory creates persistent objects
+ // for the server and server adapter. It also takes care of installing the
+ // evictors and object factories necessary to store these objects.
+ //
+ Identity id = communicator()->stringToIdentity(instanceName + "/Node-" + name);
+ NodePrx nodeProxy = NodePrx::uncheckedCast(_adapter->createProxy(id));
+ _node = new NodeI(_adapter, _sessions, _activator, _timer, traceLevels, nodeProxy, name, mapper);
+ _adapter->add(_node, nodeProxy->ice_getIdentity());
+
+ _adapter->addServantLocator(new DefaultServantLocator(new NodeServerAdminRouter(_node)), _node->getServerAdminCategory());
+
+ //
+ // Start the platform info thread if needed.
+ //
+ _node->getPlatformInfo().start();
+
+ //
+ // Ensures that the locator is reachable.
+ //
+ if(!nowarn)
+ {
+ try
+ {
+ communicator()->getDefaultLocator()->ice_timeout(1000)->ice_ping();
+ }
+ catch(const Ice::LocalException& ex)
+ {
+ Warning out(communicator()->getLogger());
+ out << "couldn't reach the IceGrid registry (this is expected ";
+ out << "if it's down, otherwise please check the value of the ";
+ out << "Ice.Default.Locator property):\n" << ex;
+ }
+ }
+
+ //
+ // Create the node sessions with the registries.
+ //
+ _sessions.create(_node);
+
+ //
+ // In some tests, we deploy icegridnodes using IceGrid:
+ //
+ if(properties->getProperty("Ice.Admin.Endpoints") != "")
+ {
+ //
+ // Replace Process facet and create Admin object
+ //
+ try
+ {
+ ProcessPtr origProcess = ProcessPtr::dynamicCast(communicator()->removeAdminFacet("Process"));
+ communicator()->addAdminFacet(new ProcessI(_activator, origProcess), "Process");
+ communicator()->getAdmin();
+ }
+ catch(const Ice::NotRegisteredException&)
+ {
+ //
+ // Some plug-in removed the Process facet, so we don't replace it.
+ // (unlikely error though)
+ //
+ }
+ }
+
+ //
+ // Start the activator.
+ //
+ _activator->start();
+
+ //
+ // Activate the adapter.
+ //
+ _adapter->activate();
+
+ //
+ // Notify the node session manager that the node can start
+ // accepting incoming connections.
+ //
+ _sessions.activate();
+
+ string bundleName = properties->getProperty("IceGrid.Node.PrintServersReady");
+ if(!bundleName.empty() || !desc.empty())
+ {
+ enableInterrupt();
+ if(!_sessions.waitForCreate())
+ {
+ //
+ // Create was interrupted, return true as if the service was
+ // correctly initiliazed to make sure it's properly stopped.
+ //
+ return true;
+ }
+ disableInterrupt();
+ }
+
+ //
+ // Deploy application if a descriptor is passed as a command-line option.
+ //
+ if(!desc.empty())
+ {
+ try
+ {
+ Ice::Identity registryId;
+ registryId.category = instanceName;
+ registryId.name = "Registry";
+
+ RegistryPrx registry = RegistryPrx::checkedCast(
+ communicator()->stringToProxy("\"" + communicator()->identityToString(registryId) + "\""));
+ if(!registry)
+ {
+ throw "invalid registry";
+ }
+
+ registry = registry->ice_preferSecure(true); // Use SSL if available.
+
+ IceGrid::AdminSessionPrx session;
+ if(communicator()->getProperties()->getPropertyAsInt("IceGridAdmin.AuthenticateUsingSSL"))
+ {
+ session = registry->createAdminSessionFromSecureConnection();
+ }
+ else
+ {
+ string id = communicator()->getProperties()->getProperty("IceGridAdmin.Username");
+ string password = communicator()->getProperties()->getProperty("IceGridAdmin.Password");
+ while(id.empty())
+ {
+ cout << "user id: " << flush;
+ getline(cin, id);
+ id = IceUtilInternal::trim(id);
+ }
+
+ if(password.empty())
+ {
+ cout << "password: " << flush;
+ getline(cin, password);
+ password = IceUtilInternal::trim(password);
+ }
+
+ session = registry->createAdminSession(id, password);
+ }
+ assert(session);
+
+ AdminPrx admin = session->getAdmin();
+ map<string, string> vars;
+ ApplicationDescriptor app = DescriptorParser::parseDescriptor(desc, targets, vars, communicator(), admin);
+
+ try
+ {
+ admin->syncApplication(app);
+ }
+ catch(const ApplicationNotExistException&)
+ {
+ admin->addApplication(app);
+ }
+ }
+ catch(const DeploymentException& ex)
+ {
+ ostringstream ostr;
+ ostr << "failed to deploy application `" << desc << "':\n" << ex << ": " << ex.reason;
+ warning(ostr.str());
+ }
+ catch(const AccessDeniedException& ex)
+ {
+ ostringstream ostr;
+ ostr << "failed to deploy application `" << desc << "':\n"
+ << "registry database is locked by `" << ex.lockUserId << "'";
+ warning(ostr.str());
+ }
+ catch(const std::exception& ex)
+ {
+ ostringstream ostr;
+ ostr << "failed to deploy application `" << desc << "':\n" << ex.what();
+ warning(ostr.str());
+ }
+ catch(const string& reason)
+ {
+ ostringstream ostr;
+ ostr << "failed to deploy application `" << desc << "':\n" << reason;
+ warning(ostr.str());
+ }
+ }
+
+ if(!bundleName.empty())
+ {
+ print(bundleName + " ready");
+ }
+
+ return true;
+}
+
+void
+NodeService::waitForShutdown()
+{
+ //
+ // Wait for the activator shutdown. Once the run method returns
+ // all the servers have been deactivated.
+ //
+ enableInterrupt();
+ _activator->waitForShutdown();
+ disableInterrupt();
+}
+
+bool
+NodeService::stop()
+{
+ if(_activator)
+ {
+ try
+ {
+ _activator->shutdown();
+ _activator->destroy();
+ }
+ catch(...)
+ {
+ assert(false);
+ }
+ _activator = 0;
+ }
+
+ if(_timer)
+ {
+ //
+ // The timer must be destroyed after the activator and before the
+ // communicator is shutdown.
+ //
+ try
+ {
+ _timer->destroy();
+ }
+ catch(...)
+ {
+ assert(false);
+ }
+ _timer = 0;
+ }
+
+ //
+ // Deactivate the node object adapter.
+ //
+ if(_adapter)
+ {
+ try
+ {
+ _adapter->deactivate();
+ _adapter = 0;
+ }
+ catch(const Ice::LocalException& ex)
+ {
+ ostringstream ostr;
+ ostr << "unexpected exception while shutting down node:\n" << ex;
+ warning(ostr.str());
+ }
+ }
+
+ //
+ // Terminate the node sessions with the registries.
+ //
+ _sessions.destroy();
+
+ //
+ // Stop the platform info thread.
+ //
+ if(_node)
+ {
+ _node->getPlatformInfo().stop();
+ }
+
+ //
+ // We can now safely shutdown the communicator.
+ //
+ try
+ {
+ communicator()->shutdown();
+ communicator()->waitForShutdown();
+ }
+ catch(const Ice::LocalException& ex)
+ {
+ ostringstream ostr;
+ ostr << "unexpected exception while shutting down node:\n" << ex;
+ warning(ostr.str());
+ }
+
+ //
+ // Break cylic reference counts.
+ //
+ if(_node)
+ {
+ _node->shutdown();
+ _node = 0;
+ }
+
+ //
+ // And shutdown the collocated registry.
+ //
+ if(_registry)
+ {
+ _registry->stop();
+ _registry = 0;
+ }
+
+ return true;
+}
+
+CommunicatorPtr
+NodeService::initializeCommunicator(int& argc, char* argv[],
+ const InitializationData& initializationData)
+{
+ InitializationData initData = initializationData;
+ initData.properties = createProperties(argc, argv, initData.properties);
+
+ //
+ // Make sure that IceGridNode doesn't use collocation optimization
+ //
+ initData.properties->setProperty("Ice.Default.CollocationOptimized", "0");
+
+ //
+ // Delay creation of Admin object:
+ //
+ initData.properties->setProperty("Ice.Admin.DelayCreation", "1");
+
+ //
+ // Default backend database plugin is Freeze if none is specified.
+ //
+ if(initData.properties->getPropertyAsInt("IceGrid.Node.CollocateRegistry") > 0 &&
+ initData.properties->getProperty("Ice.Plugin.DB").empty())
+ {
+ initData.properties->setProperty("Ice.Plugin.DB", "IceGridFreezeDB:createFreezeDB");
+ }
+
+ //
+ // Setup the client thread pool size.
+ //
+ setupThreadPool(initData.properties, "Ice.ThreadPool.Client", 1, 100);
+
+ return Service::initializeCommunicator(argc, argv, initData);
+}
+
+void
+NodeService::usage(const string& appName)
+{
+ string options =
+ "Options:\n"
+ "-h, --help Show this message.\n"
+ "-v, --version Display the Ice version.\n"
+ "--nowarn Don't print any security warnings.\n"
+ "--readonly Start the collocated master registry in read-only mode."
+ "\n"
+ "--deploy DESCRIPTOR [TARGET1 [TARGET2 ...]]\n"
+ " Add or update descriptor in file DESCRIPTOR, with\n"
+ " optional targets.\n";
+#ifndef _WIN32
+ options.append(
+ "\n"
+ "\n"
+ "--daemon Run as a daemon.\n"
+ "--noclose Do not close open file descriptors.\n"
+ "--nochdir Do not change the current working directory.\n"
+ "--pidfile FILE Write process ID into FILE."
+ );
+#endif
+ print("Usage: " + appName + " [options]\n" + options);
+}
+
+//COMPILERFIX: Borland C++ 2010 doesn't support wmain for console applications.
+#if defined(_WIN32 ) && !defined(__BCPLUSPLUS__)
+
+int
+wmain(int argc, wchar_t* argv[])
+
+#else
+
+int
+main(int argc, char* argv[])
+
+#endif
+{
+ NodeService svc;
+ return svc.main(argc, argv);
+}