1//
2// Application.h
3//
4// Library: Util
5// Package: Application
6// Module: Application
7//
8// Definition of the Application class.
9//
10// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
11// and Contributors.
12//
13// SPDX-License-Identifier: BSL-1.0
14//
15
16
17#ifndef Util_Application_INCLUDED
18#define Util_Application_INCLUDED
19
20
21#include "Poco/Util/Util.h"
22#include "Poco/Util/Subsystem.h"
23#include "Poco/Util/LayeredConfiguration.h"
24#include "Poco/Util/OptionSet.h"
25#include "Poco/AutoPtr.h"
26#include "Poco/Logger.h"
27#include "Poco/Path.h"
28#include "Poco/Timestamp.h"
29#include "Poco/Timespan.h"
30#include "Poco/AutoPtr.h"
31#if defined(POCO_VXWORKS)
32#include <cstdarg>
33#endif
34#include <vector>
35#include <typeinfo>
36
37
38namespace Poco {
39namespace Util {
40
41
42class OptionSet;
43
44
45class Util_API Application: public Subsystem
46 /// The Application class implements the main subsystem
47 /// in a process. The application class is responsible for
48 /// initializing all its subsystems.
49 ///
50 /// Subclasses can and should override the following virtual methods:
51 /// - initialize() (the one-argument, protected variant)
52 /// - uninitialize()
53 /// - reinitialize()
54 /// - defineOptions()
55 /// - handleOption()
56 /// - main()
57 ///
58 /// The application's main logic should be implemented in
59 /// the main() method.
60 ///
61 /// There may be at most one instance of the Application class
62 /// in a process.
63 ///
64 /// The Application class maintains a LayeredConfiguration (available
65 /// via the config() member function) consisting of:
66 /// - a MapConfiguration (priority -100) storing application-specific
67 /// properties, as well as properties from bound command line arguments.
68 /// - a SystemConfiguration (priority 100)
69 /// - the configurations loaded with loadConfiguration().
70 ///
71 /// The Application class sets a few default properties in
72 /// its configuration. These are:
73 /// - application.path: the absolute path to application executable
74 /// - application.name: the file name of the application executable
75 /// - application.baseName: the file name (excluding extension) of the application executable
76 /// - application.dir: the path to the directory where the application executable resides
77 /// - application.configDir: the path to the directory where user specific configuration files of the application should be stored.
78 /// - application.cacheDir: the path to the directory where user specific non-essential data files of the application should be stored.
79 /// - application.dataDir: the path to the directory where user specific data files of the application should be stored.
80 /// - application.tempDir: the path to the directory where user specific temporary files and other file objects of the application should be stored.
81 ///
82 /// If loadConfiguration() has never been called, application.configDir will be equal to application.dir.
83 ///
84 /// The POCO_APP_MAIN macro can be used to implement main(argc, argv).
85 /// POCO_APP_MAIN supports Unicode command line arguments.
86{
87public:
88 typedef std::vector<std::string> ArgVec;
89 typedef Poco::AutoPtr<Subsystem> SubsystemPtr;
90 typedef std::vector<SubsystemPtr> SubsystemVec;
91
92 enum ExitCode
93 /// Commonly used exit status codes.
94 /// Based on the definitions in the 4.3BSD <sysexits.h> header file.
95 {
96 EXIT_OK = 0, /// successful termination
97 EXIT_USAGE = 64, /// command line usage error
98 EXIT_DATAERR = 65, /// data format error
99 EXIT_NOINPUT = 66, /// cannot open input
100 EXIT_NOUSER = 67, /// addressee unknown
101 EXIT_NOHOST = 68, /// host name unknown
102 EXIT_UNAVAILABLE = 69, /// service unavailable
103 EXIT_SOFTWARE = 70, /// internal software error
104 EXIT_OSERR = 71, /// system error (e.g., can't fork)
105 EXIT_OSFILE = 72, /// critical OS file missing
106 EXIT_CANTCREAT = 73, /// can't create (user) output file
107 EXIT_IOERR = 74, /// input/output error
108 EXIT_TEMPFAIL = 75, /// temp failure; user is invited to retry
109 EXIT_PROTOCOL = 76, /// remote error in protocol
110 EXIT_NOPERM = 77, /// permission denied
111 EXIT_CONFIG = 78 /// configuration error
112 };
113
114 enum ConfigPriority
115 {
116 PRIO_APPLICATION = -100,
117 PRIO_DEFAULT = 0,
118 PRIO_SYSTEM = 100
119 };
120
121 Application();
122 /// Creates the Application.
123
124 Application(int argc, char* argv[]);
125 /// Creates the Application and calls init(argc, argv).
126
127 void addSubsystem(Subsystem* pSubsystem);
128 /// Adds a new subsystem to the application. The
129 /// application immediately takes ownership of it, so that a
130 /// call in the form
131 /// Application::instance().addSubsystem(new MySubsystem);
132 /// is okay.
133
134 void init(int argc, char* argv[]);
135 /// Processes the application's command line arguments
136 /// and sets the application's properties (e.g.,
137 /// "application.path", "application.name", etc.).
138 ///
139 /// Note that as of release 1.3.7, init() no longer
140 /// calls initialize(). This is now called from run().
141
142#if defined (POCO_OS_FAMILY_WINDOWS) && !defined(POCO_NO_WSTRING)
143 void init(int argc, wchar_t* argv[]);
144 /// Processes the application's command line arguments
145 /// and sets the application's properties (e.g.,
146 /// "application.path", "application.name", etc.).
147 ///
148 /// Note that as of release 1.3.7, init() no longer
149 /// calls initialize(). This is now called from run().
150 ///
151 /// This Windows-specific version of init is used for passing
152 /// Unicode command line arguments from wmain().
153#endif
154
155 void init(const ArgVec& args);
156 /// Processes the application's command line arguments
157 /// and sets the application's properties (e.g.,
158 /// "application.path", "application.name", etc.).
159 ///
160 /// Note that as of release 1.3.7, init() no longer
161 /// calls initialize(). This is now called from run().
162
163 bool initialized() const;
164 /// Returns true iff the application is in initialized state
165 /// (that means, has been initialized but not yet uninitialized).
166
167 void setUnixOptions(bool flag);
168 /// Specify whether command line option handling is Unix-style
169 /// (flag == true; default) or Windows/OpenVMS-style (flag == false).
170 ///
171 /// This member function should be called from the constructor of
172 /// a subclass to be effective.
173
174 int loadConfiguration(int priority = PRIO_DEFAULT);
175 /// Loads configuration information from a default location.
176 ///
177 /// The configuration(s) will be added to the application's
178 /// LayeredConfiguration with the given priority.
179 ///
180 /// The configuration file(s) must be located in the same directory
181 /// as the executable or a parent directory of it, and must have the
182 /// same base name as the executable, with one of the following extensions:
183 /// .properties, .ini or .xml.
184 ///
185 /// The .properties file, if it exists, is loaded first, followed
186 /// by the .ini file and the .xml file.
187 ///
188 /// If the application is built in debug mode (the _DEBUG preprocessor
189 /// macro is defined) and the base name of the application executable
190 /// ends with a 'd', a config file without the 'd' ending its base name is
191 /// also found.
192 ///
193 /// Example: Given the application "SampleAppd.exe", built in debug mode.
194 /// Then loadConfiguration() will automatically find a configuration file
195 /// named "SampleApp.properties" if it exists and if "SampleAppd.properties"
196 /// cannot be found.
197 ///
198 /// Returns the number of configuration files loaded, which may be zero.
199 ///
200 /// This method must not be called before init(argc, argv)
201 /// has been called.
202
203 void loadConfiguration(const std::string& path, int priority = PRIO_DEFAULT);
204 /// Loads configuration information from the file specified by
205 /// the given path. The file type is determined by the file
206 /// extension. The following extensions are supported:
207 /// - .properties - properties file (PropertyFileConfiguration)
208 /// - .ini - initialization file (IniFileConfiguration)
209 /// - .xml - XML file (XMLConfiguration)
210 ///
211 /// Extensions are not case sensitive.
212 ///
213 /// The configuration will be added to the application's
214 /// LayeredConfiguration with the given priority.
215
216 template <class C> C& getSubsystem() const;
217 /// Returns a reference to the subsystem of the class
218 /// given as template argument.
219 ///
220 /// Throws a NotFoundException if such a subsystem has
221 /// not been registered.
222
223 SubsystemVec& subsystems();
224 /// Returns a reference to the subsystem list
225
226 virtual int run();
227 /// Runs the application by performing additional (un)initializations
228 /// and calling the main() method.
229 ///
230 /// First calls initialize(), then calls main(), and
231 /// finally calls uninitialize(). The latter will be called
232 /// even if main() throws an exception. If initialize() throws
233 /// an exception, main() will not be called and the exception
234 /// will be propagated to the caller. If uninitialize() throws
235 /// an exception, the exception will be propagated to the caller.
236
237 std::string commandName() const;
238 /// Returns the command name used to invoke the application.
239
240 std::string commandPath() const;
241 /// Returns the full command path used to invoke the application.
242
243 LayeredConfiguration& config() const;
244 /// Returns the application's configuration reference.
245
246 LayeredConfiguration::Ptr configPtr() const;
247 /// Returns the application's configuration smart pointer.
248
249 Poco::Logger& logger() const;
250 /// Returns the application's logger.
251 ///
252 /// Before the logging subsystem has been initialized, the
253 /// application's logger is "ApplicationStartup", which is
254 /// connected to a ConsoleChannel.
255 ///
256 /// After the logging subsystem has been initialized, which
257 /// usually happens as the first action in Application::initialize(),
258 /// the application's logger is the one specified by the
259 /// "application.logger" configuration property. If that property
260 /// is not specified, the logger is "Application".
261
262 const ArgVec& argv() const;
263 /// Returns reference to vector of the application's arguments as
264 /// specified on the command line. If user overrides the
265 /// Application::main(const ArgVec&) function, it will receive
266 /// only the command line parameters that were not processed in
267 /// Application::processOptons(). This function returns the
268 /// full set of command line parameters as received in
269 /// main(argc, argv*).
270
271 const OptionSet& options() const;
272 /// Returns the application's option set.
273
274 static Application& instance();
275 /// Returns a reference to the Application singleton.
276 ///
277 /// Throws a NullPointerException if no Application instance exists.
278
279 const Poco::Timestamp& startTime() const;
280 /// Returns the application start time (UTC).
281
282 Poco::Timespan uptime() const;
283 /// Returns the application uptime.
284
285 void stopOptionsProcessing();
286 /// If called from an option callback, stops all further
287 /// options processing.
288 ///
289 /// If called, the following options on the command line
290 /// will not be processed, and required options will not
291 /// be checked.
292 ///
293 /// This is useful, for example, if an option for displaying
294 /// help information has been encountered and no other things
295 /// besides displaying help shall be done.
296
297 const char* name() const;
298
299protected:
300 void initialize(Application& self);
301 /// Initializes the application and all registered subsystems.
302 /// Subsystems are always initialized in the exact same order
303 /// in which they have been registered.
304 ///
305 /// Overriding implementations must call the base class implementation.
306
307 void uninitialize();
308 /// Uninitializes the application and all registered subsystems.
309 /// Subsystems are always uninitialized in reverse order in which
310 /// they have been initialized.
311 ///
312 /// Overriding implementations must call the base class implementation.
313
314 void reinitialize(Application& self);
315 /// Re-nitializes the application and all registered subsystems.
316 /// Subsystems are always reinitialized in the exact same order
317 /// in which they have been registered.
318 ///
319 /// Overriding implementations must call the base class implementation.
320
321 virtual void defineOptions(OptionSet& options);
322 /// Called before command line processing begins.
323 /// If a subclass wants to support command line arguments,
324 /// it must override this method.
325 /// The default implementation does not define any options itself,
326 /// but calls defineOptions() on all registered subsystems.
327 ///
328 /// Overriding implementations should call the base class implementation.
329
330 virtual void handleOption(const std::string& name, const std::string& value);
331 /// Called when the option with the given name is encountered
332 /// during command line arguments processing.
333 ///
334 /// The default implementation does option validation, bindings
335 /// and callback handling.
336 ///
337 /// Overriding implementations must call the base class implementation.
338
339 void setLogger(Poco::Logger& logger);
340 /// Sets the logger used by the application.
341
342 virtual int main(const std::vector<std::string>& args);
343 /// The application's main logic.
344 ///
345 /// Unprocessed command line arguments are passed in args.
346 /// Note that all original command line arguments are available
347 /// via the properties application.argc and application.argv[<n>].
348 ///
349 /// Returns an exit code which should be one of the values
350 /// from the ExitCode enumeration.
351
352 bool findFile(Poco::Path& path) const;
353 /// Searches for the file in path in the application directory.
354 ///
355 /// If path is absolute, the method immediately returns true and
356 /// leaves path unchanged.
357 ///
358 /// If path is relative, searches for the file in the application
359 /// directory and in all subsequent parent directories.
360 /// Returns true and stores the absolute path to the file in
361 /// path if the file could be found. Returns false and leaves path
362 /// unchanged otherwise.
363
364 void init();
365 /// Common initialization code.
366
367 ~Application();
368 /// Destroys the Application and deletes all registered subsystems.
369
370private:
371 void setup();
372 void setArgs(int argc, char* argv[]);
373 void setArgs(const ArgVec& args);
374 void getApplicationPath(Poco::Path& path) const;
375 void processOptions();
376 bool findAppConfigFile(const std::string& appName, const std::string& extension, Poco::Path& path) const;
377 bool findAppConfigFile(const Path& basePath, const std::string& appName, const std::string& extension, Poco::Path& path) const;
378
379 typedef LayeredConfiguration::Ptr ConfigPtr;
380 typedef Poco::Logger::Ptr LoggerPtr;
381
382 ConfigPtr _pConfig;
383 SubsystemVec _subsystems;
384 bool _initialized;
385 std::string _command;
386 ArgVec _argv;
387 ArgVec _unprocessedArgs;
388 OptionSet _options;
389 bool _unixOptions;
390 Logger* _pLogger;
391 Poco::Timestamp _startTime;
392 bool _stopOptionsProcessing;
393 int _loadedConfigs;
394
395#if defined(POCO_OS_FAMILY_UNIX) && !defined(POCO_VXWORKS)
396 std::string _workingDirAtLaunch;
397#endif
398
399 static Application* _pInstance;
400
401 friend class LoggingSubsystem;
402
403 Application(const Application&);
404 Application& operator = (const Application&);
405};
406
407
408//
409// inlines
410//
411template <class C> C& Application::getSubsystem() const
412{
413 for (SubsystemVec::const_iterator it = _subsystems.begin(); it != _subsystems.end(); ++it)
414 {
415 const Subsystem* pSS(it->get());
416 const C* pC = dynamic_cast<const C*>(pSS);
417 if (pC) return *const_cast<C*>(pC);
418 }
419 throw Poco::NotFoundException("The subsystem has not been registered", typeid(C).name());
420}
421
422inline Application::SubsystemVec& Application::subsystems()
423{
424 return _subsystems;
425}
426
427
428inline bool Application::initialized() const
429{
430 return _initialized;
431}
432
433
434inline LayeredConfiguration& Application::config() const
435{
436 poco_assert(!_pConfig.isNull());
437 return *const_cast<LayeredConfiguration*>(_pConfig.get());
438}
439
440
441inline LayeredConfiguration::Ptr Application::configPtr() const
442{
443 return _pConfig;
444}
445
446
447inline Poco::Logger& Application::logger() const
448{
449 poco_check_ptr (_pLogger);
450 return *_pLogger;
451}
452
453
454inline const Application::ArgVec& Application::argv() const
455{
456 return _argv;
457}
458
459
460inline const OptionSet& Application::options() const
461{
462 return _options;
463}
464
465
466inline Application& Application::instance()
467{
468 poco_check_ptr (_pInstance);
469 return *_pInstance;
470}
471
472
473inline const Poco::Timestamp& Application::startTime() const
474{
475 return _startTime;
476}
477
478
479inline Poco::Timespan Application::uptime() const
480{
481 Poco::Timestamp now;
482 Poco::Timespan ret = now - _startTime;
483
484 return ret;
485}
486
487
488} } // namespace Poco::Util
489
490
491//
492// Macro to implement main()
493//
494#if defined(POCO_OS_FAMILY_WINDOWS) && !defined(POCO_NO_WSTRING)
495 #define POCO_APP_MAIN(App) \
496 int wmain(int argc, wchar_t** argv) \
497 { \
498 Poco::AutoPtr<App> pApp = new App; \
499 try \
500 { \
501 pApp->init(argc, argv); \
502 } \
503 catch (Poco::Exception& exc) \
504 { \
505 pApp->logger().log(exc); \
506 return Poco::Util::Application::EXIT_CONFIG;\
507 } \
508 return pApp->run(); \
509 } \
510 POCO_WMAIN_WRAPPER()
511#elif defined(POCO_VXWORKS)
512 #define POCO_APP_MAIN(App) \
513 int pocoAppMain(const char* appName, ...) \
514 { \
515 std::vector<std::string> args; \
516 args.push_back(std::string(appName)); \
517 va_list vargs; \
518 va_start(vargs, appName); \
519 const char* arg = va_arg(vargs, const char*); \
520 while (arg) \
521 { \
522 args.push_back(std::string(arg)); \
523 arg = va_arg(vargs, const char*); \
524 } \
525 va_end(vargs); \
526 Poco::AutoPtr<App> pApp = new App; \
527 try \
528 { \
529 pApp->init(args); \
530 } \
531 catch (Poco::Exception& exc) \
532 { \
533 pApp->logger().log(exc); \
534 return Poco::Util::Application::EXIT_CONFIG;\
535 } \
536 return pApp->run(); \
537 }
538#else
539 #define POCO_APP_MAIN(App) \
540 int main(int argc, char** argv) \
541 { \
542 Poco::AutoPtr<App> pApp = new App; \
543 try \
544 { \
545 pApp->init(argc, argv); \
546 } \
547 catch (Poco::Exception& exc) \
548 { \
549 pApp->logger().log(exc); \
550 return Poco::Util::Application::EXIT_CONFIG;\
551 } \
552 return pApp->run(); \
553 }
554#endif
555
556
557#endif // Util_Application_INCLUDED
558