1//
2// Application.cpp
3//
4// Library: Util
5// Package: Application
6// Module: Application
7//
8// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#include "Poco/Util/Application.h"
16#include "Poco/Util/SystemConfiguration.h"
17#include "Poco/Util/MapConfiguration.h"
18#include "Poco/Util/PropertyFileConfiguration.h"
19#include "Poco/Util/IniFileConfiguration.h"
20#ifndef POCO_UTIL_NO_XMLCONFIGURATION
21#include "Poco/Util/XMLConfiguration.h"
22#endif
23#ifndef POCO_UTIL_NO_JSONCONFIGURATION
24#include "Poco/Util/JSONConfiguration.h"
25#endif
26#include "Poco/Util/LoggingSubsystem.h"
27#include "Poco/Util/Option.h"
28#include "Poco/Util/OptionProcessor.h"
29#include "Poco/Util/Validator.h"
30#include "Poco/Environment.h"
31#include "Poco/Exception.h"
32#include "Poco/NumberFormatter.h"
33#include "Poco/File.h"
34#include "Poco/Path.h"
35#include "Poco/String.h"
36#include "Poco/ConsoleChannel.h"
37#include "Poco/AutoPtr.h"
38#if defined(POCO_OS_FAMILY_WINDOWS)
39#include "Poco/UnWindows.h"
40#endif
41#if defined(POCO_OS_FAMILY_UNIX) && !defined(POCO_VXWORKS)
42#include "Poco/SignalHandler.h"
43#endif
44#include "Poco/UnicodeConverter.h"
45
46
47using Poco::Logger;
48using Poco::Path;
49using Poco::File;
50using Poco::Environment;
51using Poco::SystemException;
52using Poco::ConsoleChannel;
53using Poco::NumberFormatter;
54using Poco::AutoPtr;
55using Poco::icompare;
56
57
58namespace Poco {
59namespace Util {
60
61
62Application* Application::_pInstance = 0;
63
64
65Application::Application():
66 _pConfig(new LayeredConfiguration),
67 _initialized(false),
68 _unixOptions(true),
69 _pLogger(&Logger::get("ApplicationStartup")),
70 _stopOptionsProcessing(false),
71 _loadedConfigs(0)
72{
73 setup();
74}
75
76
77Application::Application(int argc, char* pArgv[]):
78 _pConfig(new LayeredConfiguration),
79 _initialized(false),
80 _unixOptions(true),
81 _pLogger(&Logger::get("ApplicationStartup")),
82 _stopOptionsProcessing(false),
83 _loadedConfigs(0)
84{
85 setup();
86 init(argc, pArgv);
87}
88
89
90Application::~Application()
91{
92 _pInstance = 0;
93}
94
95
96void Application::setup()
97{
98 poco_assert (_pInstance == 0);
99
100 _pConfig->add(new SystemConfiguration, PRIO_SYSTEM, false);
101 _pConfig->add(new MapConfiguration, PRIO_APPLICATION, true);
102
103 addSubsystem(new LoggingSubsystem);
104
105#if defined(POCO_OS_FAMILY_UNIX) && !defined(POCO_VXWORKS)
106 _workingDirAtLaunch = Path::current();
107
108 #if !defined(_DEBUG)
109 Poco::SignalHandler::install();
110 #endif
111#else
112 setUnixOptions(false);
113#endif
114
115 _pInstance = this;
116
117 AutoPtr<ConsoleChannel> pCC = new ConsoleChannel;
118 Logger::setChannel("", pCC);
119}
120
121
122void Application::addSubsystem(Subsystem* pSubsystem)
123{
124 poco_check_ptr (pSubsystem);
125
126 _subsystems.push_back(pSubsystem);
127}
128
129
130void Application::init(int argc, char* pArgv[])
131{
132 setArgs(argc, pArgv);
133 init();
134}
135
136
137#if defined (POCO_OS_FAMILY_WINDOWS) && !defined(POCO_NO_WSTRING)
138void Application::init(int argc, wchar_t* argv[])
139{
140 std::vector<std::string> args;
141 for (int i = 0; i < argc; ++i)
142 {
143 std::string arg;
144 Poco::UnicodeConverter::toUTF8(argv[i], arg);
145 args.push_back(arg);
146 }
147 init(args);
148}
149#endif
150
151
152void Application::init(const ArgVec& args)
153{
154 setArgs(args);
155 init();
156}
157
158
159void Application::init()
160{
161 Path appPath;
162 getApplicationPath(appPath);
163 _pConfig->setString("application.path", appPath.toString());
164 _pConfig->setString("application.name", appPath.getFileName());
165 _pConfig->setString("application.baseName", appPath.getBaseName());
166 _pConfig->setString("application.dir", appPath.parent().toString());
167 _pConfig->setString("application.configDir", Path::configHome() + appPath.getBaseName() + Path::separator());
168 _pConfig->setString("application.cacheDir", Path::cacheHome() + appPath.getBaseName() + Path::separator());
169 _pConfig->setString("application.dataDir", Path::dataHome() + appPath.getBaseName() + Path::separator());
170 processOptions();
171}
172
173
174const char* Application::name() const
175{
176 return "Application";
177}
178
179
180void Application::initialize(Application& self)
181{
182 for (SubsystemVec::iterator it = _subsystems.begin(); it != _subsystems.end(); ++it)
183 {
184 _pLogger->debug(std::string("Initializing subsystem: ") + (*it)->name());
185 (*it)->initialize(self);
186 }
187 _initialized = true;
188}
189
190
191void Application::uninitialize()
192{
193 if (_initialized)
194 {
195 for (SubsystemVec::reverse_iterator it = _subsystems.rbegin(); it != _subsystems.rend(); ++it)
196 {
197 _pLogger->debug(std::string("Uninitializing subsystem: ") + (*it)->name());
198 (*it)->uninitialize();
199 }
200 _initialized = false;
201 }
202}
203
204
205void Application::reinitialize(Application& self)
206{
207 for (SubsystemVec::iterator it = _subsystems.begin(); it != _subsystems.end(); ++it)
208 {
209 _pLogger->debug(std::string("Re-initializing subsystem: ") + (*it)->name());
210 (*it)->reinitialize(self);
211 }
212}
213
214
215void Application::setUnixOptions(bool flag)
216{
217 _unixOptions = flag;
218}
219
220
221int Application::loadConfiguration(int priority)
222{
223 int n = 0;
224 Path appPath;
225 getApplicationPath(appPath);
226 Path confPath;
227 if (findAppConfigFile(appPath.getBaseName(), "properties", confPath))
228 {
229 _pConfig->add(new PropertyFileConfiguration(confPath.toString()), priority, false);
230 ++n;
231 }
232#ifndef POCO_UTIL_NO_INIFILECONFIGURATION
233 if (findAppConfigFile(appPath.getBaseName(), "ini", confPath))
234 {
235 _pConfig->add(new IniFileConfiguration(confPath.toString()), priority, false);
236 ++n;
237 }
238#endif
239#ifndef POCO_UTIL_NO_JSONCONFIGURATION
240 if (findAppConfigFile(appPath.getBaseName(), "json", confPath))
241 {
242 _pConfig->add(new JSONConfiguration(confPath.toString()), priority, false);
243 ++n;
244 }
245#endif
246#ifndef POCO_UTIL_NO_XMLCONFIGURATION
247 if (findAppConfigFile(appPath.getBaseName(), "xml", confPath))
248 {
249 _pConfig->add(new XMLConfiguration(confPath.toString()), priority, false);
250 ++n;
251 }
252#endif
253 if (n > 0 && _loadedConfigs == 0)
254 {
255 if (!confPath.isAbsolute())
256 _pConfig->setString("application.configDir", confPath.absolute().parent().toString());
257 else
258 _pConfig->setString("application.configDir", confPath.parent().toString());
259 }
260 _loadedConfigs += n;
261 return n;
262}
263
264
265void Application::loadConfiguration(const std::string& path, int priority)
266{
267 int n = 0;
268 Path confPath(path);
269 std::string ext = confPath.getExtension();
270 if (icompare(ext, "properties") == 0)
271 {
272 _pConfig->add(new PropertyFileConfiguration(confPath.toString()), priority, false);
273 ++n;
274 }
275#ifndef POCO_UTIL_NO_INIFILECONFIGURATION
276 else if (icompare(ext, "ini") == 0)
277 {
278 _pConfig->add(new IniFileConfiguration(confPath.toString()), priority, false);
279 ++n;
280 }
281#endif
282#ifndef POCO_UTIL_NO_JSONCONFIGURATION
283 else if (icompare(ext, "json") == 0)
284 {
285 _pConfig->add(new JSONConfiguration(confPath.toString()), priority, false);
286 ++n;
287 }
288#endif
289#ifndef POCO_UTIL_NO_XMLCONFIGURATION
290 else if (icompare(ext, "xml") == 0)
291 {
292 _pConfig->add(new XMLConfiguration(confPath.toString()), priority, false);
293 ++n;
294 }
295#endif
296 else throw Poco::InvalidArgumentException("Unsupported configuration file type", ext);
297
298 if (n > 0 && _loadedConfigs == 0)
299 {
300 if (!confPath.isAbsolute())
301 _pConfig->setString("application.configDir", confPath.absolute().parent().toString());
302 else
303 _pConfig->setString("application.configDir", confPath.parent().toString());
304 }
305 _loadedConfigs += n;
306}
307
308
309std::string Application::commandName() const
310{
311 return _pConfig->getString("application.baseName");
312}
313
314
315std::string Application::commandPath() const
316{
317 return _pConfig->getString("application.path");
318}
319
320
321void Application::stopOptionsProcessing()
322{
323 _stopOptionsProcessing = true;
324}
325
326
327int Application::run()
328{
329 int rc = EXIT_CONFIG;
330 initialize(*this);
331
332 try
333 {
334 rc = EXIT_SOFTWARE;
335 rc = main(_unprocessedArgs);
336 }
337 catch (Poco::Exception& exc)
338 {
339 logger().log(exc);
340 }
341 catch (std::exception& exc)
342 {
343 logger().error(exc.what());
344 }
345 catch (...)
346 {
347 logger().fatal("system exception");
348 }
349
350 uninitialize();
351 return rc;
352}
353
354
355int Application::main(const ArgVec& /*args*/)
356{
357 return EXIT_OK;
358}
359
360
361void Application::setArgs(int argc, char* pArgv[])
362{
363 _command = pArgv[0];
364 _pConfig->setInt("application.argc", argc);
365 _unprocessedArgs.reserve(argc);
366 std::string argvKey = "application.argv[";
367 for (int i = 0; i < argc; ++i)
368 {
369 std::string arg(pArgv[i]);
370 _pConfig->setString(argvKey + NumberFormatter::format(i) + "]", arg);
371 _unprocessedArgs.push_back(arg);
372 }
373}
374
375
376void Application::setArgs(const ArgVec& args)
377{
378 poco_assert (!args.empty());
379
380 _command = args[0];
381 _pConfig->setInt("application.argc", (int) args.size());
382 _unprocessedArgs = args;
383 std::string argvKey = "application.argv[";
384 for (int i = 0; i < args.size(); ++i)
385 {
386 _pConfig->setString(argvKey + NumberFormatter::format(i) + "]", args[i]);
387 }
388}
389
390
391void Application::processOptions()
392{
393 defineOptions(_options);
394 OptionProcessor processor(_options);
395 processor.setUnixStyle(_unixOptions);
396 _argv = _unprocessedArgs;
397 _unprocessedArgs.erase(_unprocessedArgs.begin());
398 ArgVec::iterator it = _unprocessedArgs.begin();
399 while (it != _unprocessedArgs.end() && !_stopOptionsProcessing)
400 {
401 std::string argName;
402 std::string value;
403 if (processor.process(*it, argName, value))
404 {
405 if (!argName.empty()) // "--" option to end options processing or deferred argument
406 {
407 handleOption(argName, value);
408 }
409 it = _unprocessedArgs.erase(it);
410 }
411 else ++it;
412 }
413 if (!_stopOptionsProcessing)
414 processor.checkRequired();
415}
416
417
418void Application::getApplicationPath(Poco::Path& appPath) const
419{
420#if defined(POCO_OS_FAMILY_UNIX) && !defined(POCO_VXWORKS)
421 if (_command.find('/') != std::string::npos)
422 {
423 Path path(_command);
424 if (path.isAbsolute())
425 {
426 appPath = path;
427 }
428 else
429 {
430 appPath = _workingDirAtLaunch;
431 appPath.append(path);
432 }
433 }
434 else
435 {
436 if (!Environment::has("PATH") || !Path::find(Environment::get("PATH"), _command, appPath))
437 appPath = Path(_workingDirAtLaunch, _command);
438 appPath.makeAbsolute();
439 }
440#elif defined(POCO_OS_FAMILY_WINDOWS)
441 #if !defined(POCO_NO_WSTRING)
442 wchar_t path[1024];
443 int n = GetModuleFileNameW(0, path, sizeof(path)/sizeof(wchar_t));
444 if (n > 0)
445 {
446 std::string p;
447 Poco::UnicodeConverter::toUTF8(path, p);
448 appPath = p;
449 }
450 else throw SystemException("Cannot get application file name.");
451 #else
452 char path[1024];
453 int n = GetModuleFileNameA(0, path, sizeof(path));
454 if (n > 0)
455 appPath = path;
456 else
457 throw SystemException("Cannot get application file name.");
458 #endif
459#else
460 appPath = _command;
461#endif
462}
463
464
465bool Application::findFile(Poco::Path& path) const
466{
467 if (path.isAbsolute()) return true;
468
469 Path appPath;
470 getApplicationPath(appPath);
471 Path base = appPath.parent();
472 do
473 {
474 Path p(base, path);
475 File f(p);
476 if (f.exists())
477 {
478 path = p;
479 return true;
480 }
481 if (base.depth() > 0) base.popDirectory();
482 }
483 while (base.depth() > 0);
484 return false;
485}
486
487
488bool Application::findAppConfigFile(const std::string& appName, const std::string& extension, Path& path) const
489{
490 poco_assert (!appName.empty());
491
492 Path p(appName);
493 p.setExtension(extension);
494 bool found = findFile(p);
495 if (!found)
496 {
497#if defined(_DEBUG)
498 if (appName[appName.length() - 1] == 'd')
499 {
500 p.setBaseName(appName.substr(0, appName.length() - 1));
501 found = findFile(p);
502 }
503#endif
504 }
505 if (found)
506 path = p;
507 return found;
508}
509
510
511bool Application::findAppConfigFile(const Path& basePath, const std::string& appName, const std::string& extension, Path& path) const
512{
513 poco_assert (!appName.empty());
514
515 Path p(basePath,appName);
516 p.setExtension(extension);
517 bool found = findFile(p);
518 if (!found)
519 {
520#if defined(_DEBUG)
521 if (appName[appName.length() - 1] == 'd')
522 {
523 p.setBaseName(appName.substr(0, appName.length() - 1));
524 found = findFile(p);
525 }
526#endif
527 }
528 if (found)
529 path = p;
530 return found;
531}
532
533
534void Application::defineOptions(OptionSet& rOptions)
535{
536 for (SubsystemVec::iterator it = _subsystems.begin(); it != _subsystems.end(); ++it)
537 {
538 (*it)->defineOptions(rOptions);
539 }
540}
541
542
543void Application::handleOption(const std::string& rName, const std::string& value)
544{
545 const Option& option = _options.getOption(rName);
546 if (option.validator())
547 {
548 option.validator()->validate(option, value);
549 }
550 if (!option.binding().empty())
551 {
552 AbstractConfiguration* pConfig = option.config();
553 if (!pConfig) pConfig = &config();
554 pConfig->setString(option.binding(), value);
555 }
556 if (option.callback())
557 {
558 option.callback()->invoke(rName, value);
559 }
560}
561
562
563void Application::setLogger(Logger& rLogger)
564{
565 _pLogger = &rLogger;
566}
567
568
569} } // namespace Poco::Util
570