mirror of
https://github.com/VCMP-SqMod/SqMod.git
synced 2025-02-12 15:57:12 +01:00
Switched to POCO library for unified platform/library interface. Deprecated the external module API. It was creating more problems than solving. Removed most built-in libraries in favor of system libraries for easier maintenance. Cleaned and secured code with help from static analyzers.
555 lines
18 KiB
C++
555 lines
18 KiB
C++
//
|
|
// Application.h
|
|
//
|
|
// Library: Util
|
|
// Package: Application
|
|
// Module: Application
|
|
//
|
|
// Definition of the Application class.
|
|
//
|
|
// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
|
|
// and Contributors.
|
|
//
|
|
// SPDX-License-Identifier: BSL-1.0
|
|
//
|
|
|
|
|
|
#ifndef Util_Application_INCLUDED
|
|
#define Util_Application_INCLUDED
|
|
|
|
|
|
#include "Poco/Util/Util.h"
|
|
#include "Poco/Util/Subsystem.h"
|
|
#include "Poco/Util/LayeredConfiguration.h"
|
|
#include "Poco/Util/OptionSet.h"
|
|
#include "Poco/AutoPtr.h"
|
|
#include "Poco/Logger.h"
|
|
#include "Poco/Path.h"
|
|
#include "Poco/Timestamp.h"
|
|
#include "Poco/Timespan.h"
|
|
#include "Poco/AutoPtr.h"
|
|
#if defined(POCO_VXWORKS)
|
|
#include <cstdarg>
|
|
#endif
|
|
#include <vector>
|
|
#include <typeinfo>
|
|
|
|
|
|
namespace Poco {
|
|
namespace Util {
|
|
|
|
|
|
class OptionSet;
|
|
|
|
|
|
class Util_API Application: public Subsystem
|
|
/// The Application class implements the main subsystem
|
|
/// in a process. The application class is responsible for
|
|
/// initializing all its subsystems.
|
|
///
|
|
/// Subclasses can and should override the following virtual methods:
|
|
/// - initialize() (the one-argument, protected variant)
|
|
/// - uninitialize()
|
|
/// - reinitialize()
|
|
/// - defineOptions()
|
|
/// - handleOption()
|
|
/// - main()
|
|
///
|
|
/// The application's main logic should be implemented in
|
|
/// the main() method.
|
|
///
|
|
/// There may be at most one instance of the Application class
|
|
/// in a process.
|
|
///
|
|
/// The Application class maintains a LayeredConfiguration (available
|
|
/// via the config() member function) consisting of:
|
|
/// - a MapConfiguration (priority -100) storing application-specific
|
|
/// properties, as well as properties from bound command line arguments.
|
|
/// - a SystemConfiguration (priority 100)
|
|
/// - the configurations loaded with loadConfiguration().
|
|
///
|
|
/// The Application class sets a few default properties in
|
|
/// its configuration. These are:
|
|
/// - application.path: the absolute path to application executable
|
|
/// - application.name: the file name of the application executable
|
|
/// - application.baseName: the file name (excluding extension) of the application executable
|
|
/// - application.dir: the path to the directory where the application executable resides
|
|
/// - application.configDir: the path to the directory where user specific configuration files of the application should be stored.
|
|
/// - application.cacheDir: the path to the directory where user specific non-essential data files of the application should be stored.
|
|
/// - application.dataDir: the path to the directory where user specific data files of the application should be stored.
|
|
/// - application.tempDir: the path to the directory where user specific temporary files and other file objects of the application should be stored.
|
|
///
|
|
/// If loadConfiguration() has never been called, application.configDir will be equal to application.dir.
|
|
///
|
|
/// The POCO_APP_MAIN macro can be used to implement main(argc, argv).
|
|
/// POCO_APP_MAIN supports Unicode command line arguments.
|
|
{
|
|
public:
|
|
using ArgVec = std::vector<std::string>;
|
|
using SubsystemPtr = Poco::AutoPtr<Subsystem>;
|
|
using SubsystemVec = std::vector<SubsystemPtr>;
|
|
|
|
enum ExitCode
|
|
/// Commonly used exit status codes.
|
|
/// Based on the definitions in the 4.3BSD <sysexits.h> header file.
|
|
{
|
|
EXIT_OK = 0, /// successful termination
|
|
EXIT_USAGE = 64, /// command line usage error
|
|
EXIT_DATAERR = 65, /// data format error
|
|
EXIT_NOINPUT = 66, /// cannot open input
|
|
EXIT_NOUSER = 67, /// addressee unknown
|
|
EXIT_NOHOST = 68, /// host name unknown
|
|
EXIT_UNAVAILABLE = 69, /// service unavailable
|
|
EXIT_SOFTWARE = 70, /// internal software error
|
|
EXIT_OSERR = 71, /// system error (e.g., can't fork)
|
|
EXIT_OSFILE = 72, /// critical OS file missing
|
|
EXIT_CANTCREAT = 73, /// can't create (user) output file
|
|
EXIT_IOERR = 74, /// input/output error
|
|
EXIT_TEMPFAIL = 75, /// temp failure; user is invited to retry
|
|
EXIT_PROTOCOL = 76, /// remote error in protocol
|
|
EXIT_NOPERM = 77, /// permission denied
|
|
EXIT_CONFIG = 78 /// configuration error
|
|
};
|
|
|
|
enum ConfigPriority
|
|
{
|
|
PRIO_APPLICATION = -100,
|
|
PRIO_DEFAULT = 0,
|
|
PRIO_SYSTEM = 100
|
|
};
|
|
|
|
Application();
|
|
/// Creates the Application.
|
|
|
|
Application(int argc, char* argv[]);
|
|
/// Creates the Application and calls init(argc, argv).
|
|
|
|
void addSubsystem(Subsystem* pSubsystem);
|
|
/// Adds a new subsystem to the application. The
|
|
/// application immediately takes ownership of it, so that a
|
|
/// call in the form
|
|
/// Application::instance().addSubsystem(new MySubsystem);
|
|
/// is okay.
|
|
|
|
void init(int argc, char* argv[]);
|
|
/// Processes the application's command line arguments
|
|
/// and sets the application's properties (e.g.,
|
|
/// "application.path", "application.name", etc.).
|
|
///
|
|
/// Note that as of release 1.3.7, init() no longer
|
|
/// calls initialize(). This is now called from run().
|
|
|
|
#if defined(_WIN32)
|
|
void init(int argc, wchar_t* argv[]);
|
|
/// Processes the application's command line arguments
|
|
/// and sets the application's properties (e.g.,
|
|
/// "application.path", "application.name", etc.).
|
|
///
|
|
/// Note that as of release 1.3.7, init() no longer
|
|
/// calls initialize(). This is now called from run().
|
|
///
|
|
/// This Windows-specific version of init is used for passing
|
|
/// Unicode command line arguments from wmain().
|
|
#endif
|
|
|
|
void init(const ArgVec& args);
|
|
/// Processes the application's command line arguments
|
|
/// and sets the application's properties (e.g.,
|
|
/// "application.path", "application.name", etc.).
|
|
///
|
|
/// Note that as of release 1.3.7, init() no longer
|
|
/// calls initialize(). This is now called from run().
|
|
|
|
bool initialized() const;
|
|
/// Returns true iff the application is in initialized state
|
|
/// (that means, has been initialized but not yet uninitialized).
|
|
|
|
void setUnixOptions(bool flag);
|
|
/// Specify whether command line option handling is Unix-style
|
|
/// (flag == true; default) or Windows/OpenVMS-style (flag == false).
|
|
///
|
|
/// This member function should be called from the constructor of
|
|
/// a subclass to be effective.
|
|
|
|
int loadConfiguration(int priority = PRIO_DEFAULT);
|
|
/// Loads configuration information from a default location.
|
|
///
|
|
/// The configuration(s) will be added to the application's
|
|
/// LayeredConfiguration with the given priority.
|
|
///
|
|
/// The configuration file(s) must be located in the same directory
|
|
/// as the executable or a parent directory of it, and must have the
|
|
/// same base name as the executable, with one of the following extensions:
|
|
/// .properties, .ini or .xml.
|
|
///
|
|
/// The .properties file, if it exists, is loaded first, followed
|
|
/// by the .ini file and the .xml file.
|
|
///
|
|
/// If the application is built in debug mode (the _DEBUG preprocessor
|
|
/// macro is defined) and the base name of the application executable
|
|
/// ends with a 'd', a config file without the 'd' ending its base name is
|
|
/// also found.
|
|
///
|
|
/// Example: Given the application "SampleAppd.exe", built in debug mode.
|
|
/// Then loadConfiguration() will automatically find a configuration file
|
|
/// named "SampleApp.properties" if it exists and if "SampleAppd.properties"
|
|
/// cannot be found.
|
|
///
|
|
/// Returns the number of configuration files loaded, which may be zero.
|
|
///
|
|
/// This method must not be called before init(argc, argv)
|
|
/// has been called.
|
|
|
|
void loadConfiguration(const std::string& path, int priority = PRIO_DEFAULT);
|
|
/// Loads configuration information from the file specified by
|
|
/// the given path. The file type is determined by the file
|
|
/// extension. The following extensions are supported:
|
|
/// - .properties - properties file (PropertyFileConfiguration)
|
|
/// - .ini - initialization file (IniFileConfiguration)
|
|
/// - .xml - XML file (XMLConfiguration)
|
|
///
|
|
/// Extensions are not case sensitive.
|
|
///
|
|
/// The configuration will be added to the application's
|
|
/// LayeredConfiguration with the given priority.
|
|
|
|
template <class C> C& getSubsystem() const;
|
|
/// Returns a reference to the subsystem of the class
|
|
/// given as template argument.
|
|
///
|
|
/// Throws a NotFoundException if such a subsystem has
|
|
/// not been registered.
|
|
|
|
SubsystemVec& subsystems();
|
|
/// Returns a reference to the subsystem list
|
|
|
|
virtual int run();
|
|
/// Runs the application by performing additional (un)initializations
|
|
/// and calling the main() method.
|
|
///
|
|
/// First calls initialize(), then calls main(), and
|
|
/// finally calls uninitialize(). The latter will be called
|
|
/// even if main() throws an exception. If initialize() throws
|
|
/// an exception, main() will not be called and the exception
|
|
/// will be propagated to the caller. If uninitialize() throws
|
|
/// an exception, the exception will be propagated to the caller.
|
|
|
|
std::string commandName() const;
|
|
/// Returns the command name used to invoke the application.
|
|
|
|
std::string commandPath() const;
|
|
/// Returns the full command path used to invoke the application.
|
|
|
|
LayeredConfiguration& config() const;
|
|
/// Returns the application's configuration reference.
|
|
|
|
LayeredConfiguration::Ptr configPtr() const;
|
|
/// Returns the application's configuration smart pointer.
|
|
|
|
Poco::Logger& logger() const;
|
|
/// Returns the application's logger.
|
|
///
|
|
/// Before the logging subsystem has been initialized, the
|
|
/// application's logger is "ApplicationStartup", which is
|
|
/// connected to a ConsoleChannel.
|
|
///
|
|
/// After the logging subsystem has been initialized, which
|
|
/// usually happens as the first action in Application::initialize(),
|
|
/// the application's logger is the one specified by the
|
|
/// "application.logger" configuration property. If that property
|
|
/// is not specified, the logger is "Application".
|
|
|
|
const ArgVec& argv() const;
|
|
/// Returns reference to vector of the application's arguments as
|
|
/// specified on the command line. If user overrides the
|
|
/// Application::main(const ArgVec&) function, it will receive
|
|
/// only the command line parameters that were not processed in
|
|
/// Application::processOptons(). This function returns the
|
|
/// full set of command line parameters as received in
|
|
/// main(argc, argv*).
|
|
|
|
const OptionSet& options() const;
|
|
/// Returns the application's option set.
|
|
|
|
static Application& instance();
|
|
/// Returns a reference to the Application singleton.
|
|
///
|
|
/// Throws a NullPointerException if no Application instance exists.
|
|
|
|
const Poco::Timestamp& startTime() const;
|
|
/// Returns the application start time (UTC).
|
|
|
|
Poco::Timespan uptime() const;
|
|
/// Returns the application uptime.
|
|
|
|
void stopOptionsProcessing();
|
|
/// If called from an option callback, stops all further
|
|
/// options processing.
|
|
///
|
|
/// If called, the following options on the command line
|
|
/// will not be processed, and required options will not
|
|
/// be checked.
|
|
///
|
|
/// This is useful, for example, if an option for displaying
|
|
/// help information has been encountered and no other things
|
|
/// besides displaying help shall be done.
|
|
|
|
const char* name() const;
|
|
|
|
protected:
|
|
void initialize(Application& self);
|
|
/// Initializes the application and all registered subsystems.
|
|
/// Subsystems are always initialized in the exact same order
|
|
/// in which they have been registered.
|
|
///
|
|
/// Overriding implementations must call the base class implementation.
|
|
|
|
void uninitialize();
|
|
/// Uninitializes the application and all registered subsystems.
|
|
/// Subsystems are always uninitialized in reverse order in which
|
|
/// they have been initialized.
|
|
///
|
|
/// Overriding implementations must call the base class implementation.
|
|
|
|
void reinitialize(Application& self);
|
|
/// Re-nitializes the application and all registered subsystems.
|
|
/// Subsystems are always reinitialized in the exact same order
|
|
/// in which they have been registered.
|
|
///
|
|
/// Overriding implementations must call the base class implementation.
|
|
|
|
virtual void defineOptions(OptionSet& options);
|
|
/// Called before command line processing begins.
|
|
/// If a subclass wants to support command line arguments,
|
|
/// it must override this method.
|
|
/// The default implementation does not define any options itself,
|
|
/// but calls defineOptions() on all registered subsystems.
|
|
///
|
|
/// Overriding implementations should call the base class implementation.
|
|
|
|
virtual void handleOption(const std::string& name, const std::string& value);
|
|
/// Called when the option with the given name is encountered
|
|
/// during command line arguments processing.
|
|
///
|
|
/// The default implementation does option validation, bindings
|
|
/// and callback handling.
|
|
///
|
|
/// Overriding implementations must call the base class implementation.
|
|
|
|
void setLogger(Poco::Logger& logger);
|
|
/// Sets the logger used by the application.
|
|
|
|
virtual int main(const std::vector<std::string>& args);
|
|
/// The application's main logic.
|
|
///
|
|
/// Unprocessed command line arguments are passed in args.
|
|
/// Note that all original command line arguments are available
|
|
/// via the properties application.argc and application.argv[<n>].
|
|
///
|
|
/// Returns an exit code which should be one of the values
|
|
/// from the ExitCode enumeration.
|
|
|
|
bool findFile(Poco::Path& path) const;
|
|
/// Searches for the file in path in the application directory.
|
|
///
|
|
/// If path is absolute, the method immediately returns true and
|
|
/// leaves path unchanged.
|
|
///
|
|
/// If path is relative, searches for the file in the application
|
|
/// directory and in all subsequent parent directories.
|
|
/// Returns true and stores the absolute path to the file in
|
|
/// path if the file could be found. Returns false and leaves path
|
|
/// unchanged otherwise.
|
|
|
|
void init();
|
|
/// Common initialization code.
|
|
|
|
~Application();
|
|
/// Destroys the Application and deletes all registered subsystems.
|
|
|
|
private:
|
|
void setup();
|
|
void setArgs(int argc, char* argv[]);
|
|
void setArgs(const ArgVec& args);
|
|
void getApplicationPath(Poco::Path& path) const;
|
|
void processOptions();
|
|
bool findAppConfigFile(const std::string& appName, const std::string& extension, Poco::Path& path) const;
|
|
bool findAppConfigFile(const Path& basePath, const std::string& appName, const std::string& extension, Poco::Path& path) const;
|
|
|
|
typedef LayeredConfiguration::Ptr ConfigPtr;
|
|
typedef Poco::Logger::Ptr LoggerPtr;
|
|
|
|
ConfigPtr _pConfig;
|
|
SubsystemVec _subsystems;
|
|
bool _initialized;
|
|
std::string _command;
|
|
ArgVec _argv;
|
|
ArgVec _unprocessedArgs;
|
|
OptionSet _options;
|
|
bool _unixOptions;
|
|
Logger* _pLogger;
|
|
Poco::Timestamp _startTime;
|
|
bool _stopOptionsProcessing;
|
|
|
|
#if defined(POCO_OS_FAMILY_UNIX) && !defined(POCO_VXWORKS)
|
|
std::string _workingDirAtLaunch;
|
|
#endif
|
|
|
|
static Application* _pInstance;
|
|
|
|
friend class LoggingSubsystem;
|
|
|
|
Application(const Application&);
|
|
Application& operator = (const Application&);
|
|
};
|
|
|
|
|
|
//
|
|
// inlines
|
|
//
|
|
template <class C> C& Application::getSubsystem() const
|
|
{
|
|
for (const auto& pSub: _subsystems)
|
|
{
|
|
const Subsystem* pSS(pSub.get());
|
|
const C* pC = dynamic_cast<const C*>(pSS);
|
|
if (pC) return *const_cast<C*>(pC);
|
|
}
|
|
throw Poco::NotFoundException("The subsystem has not been registered", typeid(C).name());
|
|
}
|
|
|
|
inline Application::SubsystemVec& Application::subsystems()
|
|
{
|
|
return _subsystems;
|
|
}
|
|
|
|
|
|
inline bool Application::initialized() const
|
|
{
|
|
return _initialized;
|
|
}
|
|
|
|
|
|
inline LayeredConfiguration& Application::config() const
|
|
{
|
|
return *const_cast<LayeredConfiguration*>(_pConfig.get());
|
|
}
|
|
|
|
|
|
inline LayeredConfiguration::Ptr Application::configPtr() const
|
|
{
|
|
return _pConfig;
|
|
}
|
|
|
|
|
|
inline Poco::Logger& Application::logger() const
|
|
{
|
|
poco_check_ptr (_pLogger);
|
|
return *_pLogger;
|
|
}
|
|
|
|
|
|
inline const Application::ArgVec& Application::argv() const
|
|
{
|
|
return _argv;
|
|
}
|
|
|
|
|
|
inline const OptionSet& Application::options() const
|
|
{
|
|
return _options;
|
|
}
|
|
|
|
|
|
inline Application& Application::instance()
|
|
{
|
|
poco_check_ptr (_pInstance);
|
|
return *_pInstance;
|
|
}
|
|
|
|
|
|
inline const Poco::Timestamp& Application::startTime() const
|
|
{
|
|
return _startTime;
|
|
}
|
|
|
|
|
|
inline Poco::Timespan Application::uptime() const
|
|
{
|
|
Poco::Timestamp now;
|
|
Poco::Timespan uptime = now - _startTime;
|
|
|
|
return uptime;
|
|
}
|
|
|
|
|
|
} } // namespace Poco::Util
|
|
|
|
|
|
//
|
|
// Macro to implement main()
|
|
//
|
|
#if defined(_WIN32)
|
|
#define POCO_APP_MAIN(App) \
|
|
int wmain(int argc, wchar_t** argv) \
|
|
{ \
|
|
Poco::AutoPtr<App> pApp = new App; \
|
|
try \
|
|
{ \
|
|
pApp->init(argc, argv); \
|
|
} \
|
|
catch (Poco::Exception& exc) \
|
|
{ \
|
|
pApp->logger().log(exc); \
|
|
return Poco::Util::Application::EXIT_CONFIG;\
|
|
} \
|
|
return pApp->run(); \
|
|
}
|
|
#elif defined(POCO_VXWORKS)
|
|
#define POCO_APP_MAIN(App) \
|
|
int pocoAppMain(const char* appName, ...) \
|
|
{ \
|
|
std::vector<std::string> args; \
|
|
args.push_back(std::string(appName)); \
|
|
va_list vargs; \
|
|
va_start(vargs, appName); \
|
|
const char* arg = va_arg(vargs, const char*); \
|
|
while (arg) \
|
|
{ \
|
|
args.push_back(std::string(arg)); \
|
|
arg = va_arg(vargs, const char*); \
|
|
} \
|
|
va_end(vargs); \
|
|
Poco::AutoPtr<App> pApp = new App; \
|
|
try \
|
|
{ \
|
|
pApp->init(args); \
|
|
} \
|
|
catch (Poco::Exception& exc) \
|
|
{ \
|
|
pApp->logger().log(exc); \
|
|
return Poco::Util::Application::EXIT_CONFIG;\
|
|
} \
|
|
return pApp->run(); \
|
|
}
|
|
#else
|
|
#define POCO_APP_MAIN(App) \
|
|
int main(int argc, char** argv) \
|
|
{ \
|
|
Poco::AutoPtr<App> pApp = new App; \
|
|
try \
|
|
{ \
|
|
pApp->init(argc, argv); \
|
|
} \
|
|
catch (Poco::Exception& exc) \
|
|
{ \
|
|
pApp->logger().log(exc); \
|
|
return Poco::Util::Application::EXIT_CONFIG;\
|
|
} \
|
|
return pApp->run(); \
|
|
}
|
|
#endif
|
|
|
|
|
|
#endif // Util_Application_INCLUDED
|