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 | |
38 | namespace Poco { |
39 | namespace Util { |
40 | |
41 | |
42 | class OptionSet; |
43 | |
44 | |
45 | class 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 | { |
87 | public: |
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 | |
299 | protected: |
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 | |
370 | private: |
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 | // |
411 | template <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 | |
422 | inline Application::SubsystemVec& Application::subsystems() |
423 | { |
424 | return _subsystems; |
425 | } |
426 | |
427 | |
428 | inline bool Application::initialized() const |
429 | { |
430 | return _initialized; |
431 | } |
432 | |
433 | |
434 | inline LayeredConfiguration& Application::config() const |
435 | { |
436 | poco_assert(!_pConfig.isNull()); |
437 | return *const_cast<LayeredConfiguration*>(_pConfig.get()); |
438 | } |
439 | |
440 | |
441 | inline LayeredConfiguration::Ptr Application::configPtr() const |
442 | { |
443 | return _pConfig; |
444 | } |
445 | |
446 | |
447 | inline Poco::Logger& Application::logger() const |
448 | { |
449 | poco_check_ptr (_pLogger); |
450 | return *_pLogger; |
451 | } |
452 | |
453 | |
454 | inline const Application::ArgVec& Application::argv() const |
455 | { |
456 | return _argv; |
457 | } |
458 | |
459 | |
460 | inline const OptionSet& Application::options() const |
461 | { |
462 | return _options; |
463 | } |
464 | |
465 | |
466 | inline Application& Application::instance() |
467 | { |
468 | poco_check_ptr (_pInstance); |
469 | return *_pInstance; |
470 | } |
471 | |
472 | |
473 | inline const Poco::Timestamp& Application::startTime() const |
474 | { |
475 | return _startTime; |
476 | } |
477 | |
478 | |
479 | inline 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 | |