NetSDS::App(3pm)

πŸ“¦
version 1.301 Β· 2 versions
, in debian bookworm, debian trixie, ubuntu jammy, ubuntu noble, ubuntu resolute

πŸ’– NetSDS::App β€” the NetSDS::App(3pm) tea πŸ’–

okay bestie, gather round, because this is the superclass β€” the mother of all NetSDS apps. Perl docs, User Contributed, and she is iconic. ✨


✨ NAME ✨

NetSDS::App β€” the common application superclass 🫢


🌸 SYNOPSIS 🌸

#!/usr/bin/env perl

use 5.8.0;
use warnings;
use strict;

MyApp->run(
        conf_file => '/etc/NetSDS/myapp.conf', # default place for config search
        daemon => 1,      # run in daemon mode
        use_pidfile => 1, # write PID file to avoid double processing
        verbose => 0,     # no verbosity
);

1;

# Application logic here
package MyApp;

use base 'NetSDS::App';

# Startup hook
sub start {
        my ($self) = @_;

        # Use configuration
        $self->{listen_port} = $self->conf->{listen_port};

        # Use logging subsystem
        $self->log("info", "Application successfully started with PID=".$self->pid);
}

# Main processing hook
sub process {
        my ($self) = @_;
        print "Hello!";

        # Use verbose output
        $self->speak("Trying to be more verbose");

}

like. look at her. she's so clean. πŸ’…


πŸ¦‹ DESCRIPTION πŸ¦‹

NetSDS::App is the base class for NetSDS applications. She carries all the shared, grown-up, "everybody needs this" functionality so you don't have to write it 400 times. Which is:

  • initialization
  • configuration file processing
  • command line parameters processing
  • application workflow
  • daemonization
  • PID file processing
  • logging
  • event detail records writing
  • default signal handling

A new application should inherit from the NetSDS::App class directly, or through child classes if you need something more specific β€” think CGI, AGI, SMPP, that whole vibe. πŸ‘‘

The common application workflow is giving very much this diagram, no cap:

App->run(%params)
   |
initialize()
   |
   ----------
   |        |
start()     |
   |        |
process()   --- main_loop()
   |        |
stop()      |
   |        |
   ----------
   |
finalize()

So when the application starts, initialize() is invoked first. It handles all the common startup stuff β€” CLI parameter processing, daemonization, reading the configuration. The initialize() method may be overwritten in more specific frameworks if you need to change the default behaviour for some application type. it's giving customizable. ✨

Then main_loop() gets invoked to process the main application logic. This method gives you three redefinable hooks: start(), process() and stop(). These hooks are the ones you should overwrite to implement whatever logic you actually need:

  • start() β€” start time hook πŸŒ…
  • process() β€” process iteration hook πŸ”
  • stop() β€” finish time hook πŸŒ™

Depending on the infinite flag, main_loop() may call the process() hook in an infinite loop, or only once. literally one and done.

The main_loop() workflow may be redefined in an inherited framework if you want to implement some other process flow logic. your app, your rules. πŸ’–

On the very last step, finalize() is invoked to do the necessary finalization actions on the framework level. clean exit, bestie. πŸŽ€


🌈 STARTUP PARAMETERS 🌈

Your application class can be handed a bunch of parameters that let you manage how she behaves. Like... a config file, daemonization mode, debugging flag. the whole little toolkit. 🧰

These parameters get passed to the run() method as a hash:

MyApp->run(
        has_conf => 1,
        conf_file => '/etc/sample/file.conf',
        daemon => 1,
        use_pidfile => 1,
);

Here's the full rundown, no skips:

  • has_conf β€” 1 if a configuration file is required (default: yes)

Mostly our applications need config files, but some of them genuinely don't need any configuration at all (small utilities and such). Set the has_conf parameter to 0 to avoid the search for a configuration file. she can be low-maintenance. πŸ“

  • conf_file β€” default path to the configuration file (default: autodetect)

This one lets you explicitly set the path to the config file. By default it's determined from the application name and it looks like /etc/NetSDS/{name}.conf.

  • name β€” application name (default: autodetect)

This name is used for config and PID file names, and for logging. By default it's automatically detected from the executable script name. 🏷️

  • debug β€” 1 for the debugging flag (default: no)
  • daemon β€” 1 for daemon mode (default: no)
  • verbose β€” 1 for verbose mode (default: no)
  • use_pidfile β€” 1 to use PID files (default: no)
  • pid_dir β€” path to the PID files catalog (default: '/var/run/NetSDS')
  • auto_features β€” 1 for auto features inclusion (default: no)

Set this parameter to 1 if you plan to use automatically plugged application features. Go read the "PLUGGABLE APPLICATION FEATURES" section below. πŸ‘€

  • infinite β€” 1 for infinite loop (default: yes)
  • edr_file β€” EDR (event detail records) file name (default: undef)

πŸ’… COMMAND LINE PARAMETERS πŸ’…

Command line parameters can be passed to a NetSDS application to override the defaults. yes bestie, CLI girlies stay winning.

  • --conf β€” path to config file
  • --[no]debug β€” set debug mode
  • --[no]daemon β€” set daemon/foreground mode
  • --[no]verbose β€” set verbosity mode
  • --name β€” set application name

These CLI options override the conf_file, debug, daemon, verbose and name default parameters that are passed in the run() method. periodt. ✨

Examples:

# Debugging in foreground mode
./application --config=/etc/myapp.conf --nodaemon --debug

# Set application name explicitly
./application --name=myapp

🫢 heads up bestie β€” this is only part 1, there's more coming, don't go anywhere πŸ’–

πŸ’– CLASS API πŸ’–

new([%params]) β€” class constructor

Okay so this is the constructor, and normally you're not the one calling it β€” run() does it for you 🫢. It makes the application object and sets up its initial properties from the params you hand over as a hash.

The standard parameters are:

  • name β€” application name
  • debug β€” set to 1 for debugging
  • daemon β€” set to 1 for daemonization
  • verbose β€” set to 1 for more verbosity
  • use_pidfile β€” set to 1 for PID files processing
  • pid_dir β€” path to PID files catalog
  • conf_file β€” path to configuration file
  • has_conf β€” set to 1 if configuration file is necessary
  • auto_features β€” set to 1 for auto features inclusion
  • infinite β€” set to 1 for infinite loop

run(%parameters) β€” application launcher

This one calls the class constructor and then bounces straight into main_loop(). ✨

Every parameter you pass gets transparently forwarded to the application constructor. No filtering, no secret remapping, just vibes and passing it along πŸ’…

#!/usr/bin/env perl

use 5.8.0;
use warnings;
use strict;

MyApp->run(
        conf_file => '/etc/myapp.conf',
        daemon => 1,
        use_pidfile => 1,
);

1;

# **********************************
# Logic of application

package MyApp;
use base 'NetSDS::App';
1;

name([$name]) β€” application name

An accessor for the application name β€” read it, or set a brand new one. Main character energy fr fr πŸ‘‘

print "My name is " . $self->name;

pid() β€” PID of application process

Read-only access to the process identifier (PID). Look but don't touch bestie πŸ™ˆ

print "My PID is " . $self->pid;

debug() β€” debugging flag

Accessor to the debugging flag. If the application was called with the --debug option, this hands you back a TRUE value. πŸ›

if ($self->debug) {
        print "Debug info: " . $debug_data;
}

verbose() β€” verbosity flag

Accessor to the verbosity flag.

You can use it to crank the application's verbosity level up whenever you need to. πŸ“’

if ($self->verbose) {
        print "I'm working!";
};
NOTE: this flag is for normal operations. If you need to implement debug output or other development/testing functionality β€” use debug() instead. Don't mix them, that's not the vibe. πŸ’€

logger() β€” accessor to logger

Accessor to the logger β€” which is an object of the NetSDS::Logger class.

NOTE: there's literally no need to use this method directly in your application. Peep the log() method description to understand how the logging features work. πŸͺ΅

conf() β€” accessor to configuration

Accessor to the application configuration β€” it's represented as a hash reference returned by the NetSDS::Conf module.

Configuration sample:

------------------------
content_dir /var/lib/content

<kannel>
        send_url http://127.0.0.1:13013/
        login netsds
        passwd topsecret
</kannel>
------------------------

Code sample:

# Retrieve configuration
my $content_dir = $self->conf->{content_dir};
my $kannel_url = $self->conf->{kannel}->{send_url};

use_pidfile(BOOL) β€” PID file checking flag

Parameters: TRUE if PID file checking is required. πŸŽ€


pid_dir([$directory]) β€” PID files storage

Parameters: directory name.

$app->pid_dir("/var/run");

daemon(BOOL) β€” daemonization flag

Parameters: TRUE if the application should be a daemon.

if ($self->daemon()) {
        $self->log("info", "Yeah! I'm daemon!");
};

auto_features() β€” auto features flag

Automatic features inclusion is allowed if TRUE. πŸ¦‹


infinite([$bool]) β€” is application in infinite loop

Example:

# Switch to infinite loop mode
$app->infinite(1);

edr_file([$file_name]) β€” accessor to EDR file name

Parameters: EDR file path. πŸ“


initialize() β€” common application initialization

The whole getting-ready ritual, in order:

  1. Reading config if necessary.
  2. Daemonize application.
  3. Check PID file for already running application instances.
  4. Start logger.
  5. Prepare default signal handlers.

use_auto_features() β€” add features to application

This method implements automatic features inclusion by the application configuration file β€” check out the feature sections. ✨


add_feature($name, $class, $config, %params) β€” add feature

Parameters: feature name, class name, parameters (optional).

Returns: feature object.

$self->add_feature('kannel','NetSDS::Feature::Kannel', $self->conf->{feature}->{kannel});
$self->kannel->send(.....);

finalize() β€” switch to finalization stage

This method gets called when we need to finish the application. That's it, that's the whole thing 🫑


start() β€” user defined initialization hook

Abstract method for postinitialization procedures execution.

Arguments and return are defined in the inherited classes. This method should be overwritten in the exact application.

Remember that the start() method is invoked after initialize() β€” order matters bestie. πŸ”₯


process() β€” main loop iteration hook

Abstract method for main loop iteration procedures execution.

Arguments and return are defined in the inherited classes.

This method should be overwritten in the exact application.


stop() β€” post processing hook

This method should be rewritten in the target class to contain the real post processing routines. πŸ›‘


main_loop() β€” main loop algorithm

This method provides the default main loop algorithm implementation, and it may be rewritten for alternative logic. Go off, customise it πŸ’…


🌈 LOGGING AND ERROR HANDLING 🌈

log($level, $message) β€” write message to log

This method gives you the ability to write log messages to syslog. πŸ“¬

Example:

$self->log("info", "New message arrived with id=$msg_id");

error($message) β€” return error with logging

This method extends the inherited method functionality with automatically logging this message to syslog.

Example:

if (!$dbh->ping) {
        return $self->error("We have problem with DBMS");
}

speak(@strs) β€” verbose output

Parameters: list of strings to be written as verbose output.

This method implements verbose output to STDOUT. πŸ—£οΈ

$self->speak("Do something");

edr($record [,$record..]) β€” write EDR

Parameters: list of EDR records to write.

$app->edr({
        event => "call",
        status => "rejected",
});

config_file($file_name) β€” determine full configuration file name

That's it, it figures out the full name of the configuration file for you. 🎯


🧚 PLUGGABLE APPLICATION FEAUTURES 🧚

To add more flexibility to application development, the NetSDS::App framework allows you to add pluggable features. An application feature is a class dynamically loaded into the application using configuration file parameters. ✨

To use application features, a developer should do the following:

  • set auto_features run() parameter
  • create feature sections in application as described
  • create feature classes inherited from NetSDS::Feature

🌸 EXAMPLES 🌸

See samples/app.pl πŸ‘€


πŸ’€ BUGS πŸ’€

This module is a one bug itself :-)


πŸ¦‹ SEE ALSO πŸ¦‹

NetSDS, NetSDS::Class::Abstract, NetSDS::Logger


πŸ‘‘ TODO πŸ‘‘

Fix and cleanup!


πŸ’œ AUTHOR πŸ’œ

Valentyn Solomko <val@pere.org.ua>

Michael Bochkaryov <misha@rattler.kiev.ua>


πŸŽ€ LICENSE πŸŽ€

Copyright (C) 2008-2009 Net Style Ltd.

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA


and that's a wrap bestie!! 🫢 this lil page was served up by perl v5.32.1, dated 2021-12-26, and yes it's literally called NetSDS::App(3pm) πŸ’–βœ¨ go forth and daemonize responsibly πŸŽ€