[C++] POCO learning summary (10): Poco::Util::Application (application framework)

[C++] Guo Laoer’s blog post: C++ directory

1. Poco::Util::Application application framework

1.1 Basic functions of the application

Poco::Util::Application is an application framework implemented by POCO. It supports the following functions:

  • Command line parameter processing
  • Configuration file
  • Initialization and shutdown
  • log

1.2 Command line programs and daemons

POCO supports two applications:

  • Command line application: launched from the command line, POCO supports command line parameter processing
  • Server application: usually runs as a daemon process, such as Linux daemon, Windows service

2. Poco::Util::Subsystem subsystem

2.1 Description

Poco::Util::Application inherits from Poco::Util::Subsystem.
POCO combines different subsystems into one application. Subsystems extend applications in a modular manner.

  • Subsystems abstract unified initialization and shutdown steps.
  • When an application is initialized, all its registered subsystems are also initialized.
  • When an application shuts down, all its registered subsystems also shut down.

2.2 Use

A subsystem is inherited from Poco::Util::Subsystem. Here are some commonly used interfaces:

  • const char* name(): Returns the name of the subsystem;
  • void initialize(Application& app): Initialize subsystem operations;
  • void uninitialize(Application& app): Operation when closing the subsystem;
  • void reinitialize(Application& app): Reconfigure the subsystem (optional; the default implementation calls uninitialize(), then initialize());
  • void defineOptions(OptionSet& options): Subsystem custom command line parameters

3. Command line program

Command line applications are implemented by creating subclasses of Poco::Util::Application. There are several virtual member functions that need to be rewritten:

  • void initialize(Application& self)
  • void reinitialize()
  • void uninitialize()
  • void defineOptions()
  • int main(std::vectorstd::string& args)

Note: The above main is a member function of Poco::Util::Application, and the main logic of the application is written here.
The return value is an enumeration: ExitCode

enum ExitCode
{
    
    
	EXIT_OK          = 0,   成功终止
	EXIT_USAGE	     = 64,  命令行使用错误
	EXIT_DATAERR     = 65,  数据格式错误
	EXIT_NOINPUT     = 66,  无法打开输入
	EXIT_NOUSER      = 67,  地址错误
	EXIT_NOHOST      = 68,  主机名是未知
	EXIT_UNAVAILABLE = 69,  服务不可用
	EXIT_SOFTWARE    = 70,  内部软件错误
	EXIT_OSERR	     = 71,  系统错误 (e.g., can't fork)
	EXIT_OSFILE      = 72,  关键操作系统文件丢失
	EXIT_CANTCREAT   = 73,  不能创建(用户)输出文件
	EXIT_IOERR       = 74,  输入/输出错误
	EXIT_TEMPFAIL    = 75,  临时错误,可以尝试重新运行
	EXIT_PROTOCOL    = 76,  协议中的远程错误
	EXIT_NOPERM      = 77,  没有权限
	EXIT_CONFIG      = 78   配置错误
};

4. Poco::Util::ServerApplication service program

If you want to implement a service class program, you need to inherit from Poco::Util::ServerApplication;
Poco::Util::ServerApplication itself inherits from Poco::Util::Application ;

Service applications can be run from the command line, such as Linux daemons and Windows services.
Typically, service applications work in background threads. Therefore, the main() member function will start the thread and then wait for an external request to terminate the application (see waitForTerminationRequest()).

5. Configuration file

5.1 Description

By default two configurations are created:

  • Writable MapConfiguration, read-only PRIO_APPLICATION
  • SystemConfiguration, PRIO_SYSTEM

5.2 Usage

void MyApplication::initialize(Application& self)
{
 loadConfiguration(); // 加载默认配置文件
 Application::initialize(self);
}

6. Command line parameters

6.1 Description

Applications can define and process command line parameters (options). The command line parameter format varies from system to system:

  • Windows:/option or /option=value
  • Linux:-o, -ovalue, --option or --option:value

6.2 Use

The application options are implemented by overriding the virtual function void defineOptions(OptionSet& options)
"Define option class" OptionSet is used to process options, such as OptionSet::addOption() Functions can add options, examples are as follows:

void defineOptions(OptionSet& options)
{
    
    
	Application::defineOptions(options);
	options.addOption(
		Option("help", "h", "display help information on command line arguments")
			.required(false)
			.repeatable(false)
			.callback(OptionCallback<SampleApp>(this, &SampleApp::handleHelp)));
				
	options.addOption(
		Option("config-file", "f", "load configuration data from a file")
			.required(false)
			.repeatable(true)
			.argument("file")
			.callback(OptionCallback<SampleApp>(this, &SampleApp::handleConfig)));
	options.addOption(
 		Option("bind", "b", "bind option value to test.property")
 			.required(false)
		 	.argument("value")
 			.validator(new IntValidator(0, 100))
 			.binding("test.property"));
}

Print when running:

-h, --help display help 	information on command line arguments
-ffile, --config-file=file 	load configuration data from a file
-bvalue, --bind=value bind 	option value to test.property

Each option Option has the following contents:

  • a full name
  • a character abbreviation (short name)
  • a description
  • A parameter name: argument
  • Is it necessary: ​​required
  • Repeatable: repeatable, it can be given multiple times on the command line
  • Callback function: callback

6.3 Verify option parameters

Option parameters can be automatically validated by specifying a Validator object for the option.

  • IntValidator checks whether the parameter is an integer within a certain range.
  • RegExpValidator Validates whether a parameter matches the given regular expression.

For example, in the above example: .validator(new IntValidator(0, 100))

6.4 Display help information

The Poco::Util::HelpFormatter class can be used to display command line option help information.
When the user requests help information, all further command line processing should be canceled (especially enforcing required options). This can be done by calling stopOptionsProcessing().

void displayHelp()
{
    
    
	HelpFormatter helpFormatter(options());
	helpFormatter.setCommand(commandName());
	helpFormatter.setUsage("OPTIONS");
	helpFormatter.setHeader("Poco::Util::Application类的示例");
	helpFormatter.format(std::cout);
}

void handleHelp(const std::string& name, const std::string& value)
{
    
    
	_helpRequested = true;
	displayHelp();
	stopOptionsProcessing();
}

7. Windows services

Windows service requires registration.
Poco::Util::ServerApplication can implement the registration steps.

  • Registration: Specify the option /registerService when starting from the command line
  • Unregister: Specify /unregisterService option to cancel
  • Set the service name: specified through the option /displayName

8. Linux daemon process

On the Linux platform, after inheriting Poco::Util::ServerApplication, it can be implemented through the command line parameter "-daemon" to run as a daemon process.
A daemon process that, when started, is immediately detached from the background process that performs the actual work. After starting the background process, the foreground process exits.
After initialization is completed, before entering the main() method, the current working directory of the daemon will be changed to the root directory ("/"), which is a common practice for daemons.

An application can determine whether it is running as a daemon by checking the application.runasdaemon configuration property.
As with Windows services, be careful when using relative paths in configuration files because the daemon's current working directory is the root directory.

#include "Poco/Util/ServerApplication.h"
#include "Poco/Util/Option.h"
#include "Poco/Util/OptionSet.h"
#include "Poco/Util/HelpFormatter.h"
#include "Poco/Task.h"
#include "Poco/TaskManager.h"
#include "Poco/DateTimeFormatter.h"
#include <iostream>

using Poco::Util::Application;
using Poco::Util::ServerApplication;
using Poco::Util::Option;
using Poco::Util::OptionSet;
using Poco::Util::OptionCallback;
using Poco::Util::HelpFormatter;
using Poco::Task;
using Poco::TaskManager;
using Poco::DateTimeFormatter;

class SampleTask: public Task
{
    
    
public:
	SampleTask(): Task("SampleTask"){
    
    }

	void runTask()
	{
    
    
		Application& app = Application::instance();
		while (!sleep(5000))
		{
    
    
			Application::instance().logger().information("busy doing nothing... " + DateTimeFormatter::format(app.uptime()));
		}
	}
};

class SampleServer: public ServerApplication
{
    
    
public:
	SampleServer(): _helpRequested(false){
    
    }

	~SampleServer(){
    
    }

protected:
	void initialize(Application& self)
	{
    
    
		loadConfiguration(); // load default configuration files, if present
		ServerApplication::initialize(self);
		logger().information("starting up");
	}

	void uninitialize()
	{
    
    
		logger().information("shutting down");
		ServerApplication::uninitialize();
	}

	void defineOptions(OptionSet& options)
	{
    
    
		ServerApplication::defineOptions(options);
		options.addOption(
			Option("help", "h", "display help information on command line arguments")
				.required(false)
				.repeatable(false)
				.callback(OptionCallback<SampleServer>(this, &SampleServer::handleHelp)));
	}

	void handleHelp(const std::string& name, const std::string& value)
	{
    
    
		_helpRequested = true;
		displayHelp();
		stopOptionsProcessing();
	}

	void displayHelp()
	{
    
    
		HelpFormatter helpFormatter(options());
		helpFormatter.setCommand(commandName());
		helpFormatter.setUsage("OPTIONS");
		helpFormatter.setHeader("A sample server application that demonstrates some of the features of the Util::ServerApplication class.");
		helpFormatter.format(std::cout);
	}

	int main(const ArgVec& args)
	{
    
    
		if (!_helpRequested)
		{
    
    
			TaskManager tm;
			tm.start(new SampleTask);
			waitForTerminationRequest();
			tm.cancelAll();
			tm.joinAll();
		}
		return Application::EXIT_OK;
	}
private:
	bool _helpRequested;
};

POCO_SERVER_MAIN(SampleServer)

9. A simple example

#include "Poco/Util/Application.h"
#include "Poco/Util/Option.h"
#include "Poco/Util/OptionSet.h"
#include "Poco/Util/HelpFormatter.h"
#include "Poco/Util/AbstractConfiguration.h"
#include "Poco/AutoPtr.h"
#include <iostream>
#include <sstream>


using Poco::Util::Application;
using Poco::Util::Option;
using Poco::Util::OptionSet;
using Poco::Util::HelpFormatter;
using Poco::Util::AbstractConfiguration;
using Poco::Util::OptionCallback;
using Poco::AutoPtr;


class SampleApp: public Application
	/// This sample demonstrates some of the features of the Util::Application class,
	/// such as configuration file handling and command line arguments processing.
	///
	/// Try SampleApp --help (on Unix platforms) or SampleApp /help (elsewhere) for
	/// more information.
{
    
    
public:
	SampleApp(): _helpRequested(false)
	{
    
    
	}

protected:
	void initialize(Application& self)
	{
    
    
		loadConfiguration(); // load default configuration files, if present
		Application::initialize(self);
		// add your own initialization code here
	}

	void uninitialize()
	{
    
    
		// add your own uninitialization code here
		Application::uninitialize();
	}

	void reinitialize(Application& self)
	{
    
    
		Application::reinitialize(self);
		// add your own reinitialization code here
	}

	void defineOptions(OptionSet& options)
	{
    
    
		Application::defineOptions(options);

		options.addOption(
			Option("help", "h", "display help information on command line arguments")
				.required(false)
				.repeatable(false)
				.callback(OptionCallback<SampleApp>(this, &SampleApp::handleHelp)));

		options.addOption(
			Option("define", "D", "define a configuration property")
				.required(false)
				.repeatable(true)
				.argument("name=value")
				.callback(OptionCallback<SampleApp>(this, &SampleApp::handleDefine)));

		options.addOption(
			Option("config-file", "f", "load configuration data from a file")
				.required(false)
				.repeatable(true)
				.argument("file")
				.callback(OptionCallback<SampleApp>(this, &SampleApp::handleConfig)));

		options.addOption(
			Option("bind", "b", "bind option value to test.property")
				.required(false)
				.repeatable(false)
				.argument("value")
				.binding("test.property"));
	}

	void handleHelp(const std::string& name, const std::string& value)
	{
    
    
		_helpRequested = true;
		displayHelp();
		stopOptionsProcessing();
	}

	void handleDefine(const std::string& name, const std::string& value)
	{
    
    
		defineProperty(value);
	}

	void handleConfig(const std::string& name, const std::string& value)
	{
    
    
		loadConfiguration(value);
	}

	void displayHelp()
	{
    
    
		HelpFormatter helpFormatter(options());
		helpFormatter.setCommand(commandName());
		helpFormatter.setUsage("OPTIONS");
		helpFormatter.setHeader("A sample application that demonstrates some of the features of the Poco::Util::Application class.");
		helpFormatter.format(std::cout);
	}

	void defineProperty(const std::string& def)
	{
    
    
		std::string name;
		std::string value;
		std::string::size_type pos = def.find('=');
		if (pos != std::string::npos)
		{
    
    
			name.assign(def, 0, pos);
			value.assign(def, pos + 1, def.length() - pos);
		}
		else name = def;
		config().setString(name, value);
	}

	int main(const ArgVec& args)
	{
    
    
		if (!_helpRequested)
		{
    
    
			logger().information("Command line:");
			std::ostringstream ostr;
			for (ArgVec::const_iterator it = argv().begin(); it != argv().end(); ++it)
			{
    
    
				ostr << *it << ' ';
			}
			logger().information(ostr.str());
			logger().information("Arguments to main():");
			for (ArgVec::const_iterator it = args.begin(); it != args.end(); ++it)
			{
    
    
				logger().information(*it);
			}
			logger().information("Application properties:");
			printProperties("");
		}
		return Application::EXIT_OK;
	}

	void printProperties(const std::string& base)
	{
    
    
		AbstractConfiguration::Keys keys;
		config().keys(base, keys);
		if (keys.empty())
		{
    
    
			if (config().hasProperty(base))
			{
    
    
				std::string msg;
				msg.append(base);
				msg.append(" = ");
				msg.append(config().getString(base));
				logger().information(msg);
			}
		}
		else
		{
    
    
			for (AbstractConfiguration::Keys::const_iterator it = keys.begin(); it != keys.end(); ++it)
			{
    
    
				std::string fullKey = base;
				if (!fullKey.empty()) fullKey += '.';
				fullKey.append(*it);
				printProperties(fullKey);
			}
		}
	}

private:
	bool _helpRequested;
};

Print help information

$ ./SampleApp -h
usage: SampleApp OPTIONS
A sample application that demonstrates some of the features of the Poco::Util::Application class.

-h, --help                         display help information on command line arguments
-Dname=value, --define=name=value  define a configuration property
-ffile, --config-file=file         load configuration data from a file
-bvalue, --bind=value              bind option value to test.property

Print information after running

$ ./SampleApp
[Information] Command line:
[Information] ./SampleApp 
[Information] Arguments to main():
[Information] Application properties:
[Information] application.argc = 1
[Information] application.argv[0] = ./SampleApp
[Information] application.baseName = SampleApp
[Information] application.cacheDir = /home/laoer/.cache/SampleApp/
[Information] application.configDir = /home/laoer/git/poco/Util/samples/SampleApp/
[Information] application.dataDir = /home/laoer/.local/share/SampleApp/
[Information] application.dir = /home/laoer/git/poco/Util/samples/SampleApp/bin/Linux/x86_64/
[Information] application.name = SampleApp
[Information] application.path = /home/laoer/git/poco/Util/samples/SampleApp/bin/Linux/x86_64/SampleApp
[Information] application.tempDir = /home/laoer/.local/tmp/SampleApp/
[Information] logging.channels.c1.class = ConsoleChannel
[Information] logging.channels.c1.formatter = f1
[Information] logging.formatters.f1.class = PatternFormatter
[Information] logging.formatters.f1.pattern = [%p] %t
[Information] logging.loggers.app.channel = c1
[Information] logging.loggers.app.name = Application
[Information] logging.loggers.root.channel.class = ConsoleChannel
[Information] system.osName = Linux
[Information] system.osVersion = 6.2.0-37-generic
[Information] system.osArchitecture = x86_64
[Information] system.nodeName = laoer
[Information] system.nodeId = 
[Information] system.currentDir = /home/laoer/git/poco/Util/samples/SampleApp/bin/Linux/x86_64/
[Information] system.homeDir = /home/laoer/
[Information] system.configHomeDir = /home/laoer/.config/
[Information] system.cacheHomeDir = /home/laoer/.cache/
[Information] system.dataHomeDir = /home/laoer/.local/share/
[Information] system.tempHomeDir = /home/laoer/.local/tmp/
[Information] system.tempDir = /tmp/
[Information] system.configDir = /etc/
[Information] system.dateTime = 2023-12-10T14:50:02Z
[Information] system.pid = 4919

Guess you like

Origin blog.csdn.net/u010168781/article/details/134817890