# Software- und Organisations-Service GmbH, 2005, www.sos-berlin.com
# Andreas Püschel, 2005-05-30, andreas.pueschel@sos-berlin.com
# Sample script for directory processing with the Job Scheduler API
#
# This script simply moves files from an input directory to an output directory,
# and demonstrates the usage of the Job Scheduler API methods for logging and error handling:
# API methods implementing the following method names are called automatically by the Job Scheduler if they exist
# - spooler_init() is called by the Job Scheduler on job start
# - spooler_open() is called by the Job Scheduler when spooler_init() returned true;
# it is used to create a list of input files from a monitored directory
# - spooler_process() is called once when spooler_open() returned true and
# subsequently as long as previous calls to this method returned true;
# the method implements a processing step for a single file from the list,
# i.e. the file is moved to the destination directory
# - spooler_close() is called when spooler_process() returned false
# - spooler_exit() is called after spooler_close() to terminate processing
# - spooler_on_success() is called after spooler_exit() in case of success
# - spooler_on_error() is called after spooler_exit() if errors occurred
#-----------------------------------------------------------------------------------------------use
package main;
use strict;
use warnings FATAL => "all";
use vars qw($spooler $spooler_job $spooler_log $spooler_task);
use File::Basename;
use File::Copy;
use File::Path;
#---------------------------------------------------------------------------configuration variables
our $profile = $spooler->ini_path;
# settings referencing these variables may be put here or in the section [job sample_api_notification] of factory.ini
our $output_dir = $spooler_task->params->var("output_directory"); # [job sample_api_notification] output_dir=
our $input_repeat = 5; # [job sample_api_notification] input_repeat= # repeat job should the input file not be accessible
#----------------------------------------------------------------------------------global variables
my @input_files; # array of input files from changed directories
my $input_files_index; # index for array of input files
my %config; # hash array for sections contained in configuration file $profile
#---------------------------------------------------------------------------------------------trim
sub trim
{
my $str = shift;
if( defined $str )
{
$str =~ s/^\s*//;
$str =~ s/\s*\r?\n?$//;
}
return $str;
}
#--------------------------------------------------------------------------------------load_profile
sub load_profile
{
my $section;
open INI, $profile or die "could not open configuration file: $profile";
while( my $line = <INI> )
{
$line = trim $line;
if( $line =~ /^\[(.+)\]/ )
{
$section = lc $1;
}
elsif( defined $section && $line =~ /=/ )
{
chomp $line;
( my $entry, my $value ) = split( /=/, $line );
$config{ "[$section]" . lc(trim($entry)) } = trim( $value ) if defined $value;
# $spooler_log->debug9("settings entry found: ".lc(trim($entry))." = ".trim($value)) if defined $value;
}
}
close INI;
}
#--------------------------------------------------------------------------------------read_profile
sub read_profile
{
my $section = shift;
my $entry = shift;
my $default = shift;
my $result = $config{ "[" . lc($section) . "]" . lc($entry) };
return $result if defined $result;
if( !defined $default ) { die "entry $entry= not found for section [$section] in configuration file $profile"; }
return $default;
}
#--------------------------------------------------------------------------------------spooler_init
sub spooler_init
{
eval
{
# add your initialization code here
$profile = $main::profile if defined $main::profile;
load_profile();
# read settings from xml configuration (scheduler.xml) or profile (factory.ini)
$output_dir = read_profile( "job ".$spooler_job->name, "output_dir", $output_dir );
$output_dir = $main::output_dir if defined $main::output_dir;
$input_repeat = read_profile( "job ".$spooler_job->name, "input_repeat", $input_repeat );
$input_repeat = $main::input_repeat if defined $main::input_repeat;
};
if( $@ ) # do not pass 'die' to the Job Scheduler, PerlScript could show weird errors
{
chomp $@;
$spooler_log->error( $@ );
}
return 1;
}
#--------------------------------------------------------------------------------------spooler_open
sub spooler_open
{
my $result = 0;
eval
{
# process all files from changed input directories;
# this array might be of zero length if input files were added or modified
if (!$spooler_task->changed_directories) {
$spooler_log->info("no changed files found in input directories");
return 0;
}
$input_files_index = 0;
my @input_directories = split(/;/, $spooler_task->changed_directories);
for(my $i=0; $i<@input_directories; $i++) {
push(@input_files, <$input_directories[$i]/*>);
$input_files_index += @input_files;
}
$result = $input_files_index;
};
if( $@ ) # do not pass 'die' to the Job Scheduler, PerlScript could show weird errors
{
chomp $@;
$spooler_log->error( $@ );
}
return ($result > 0);
}
#-----------------------------------------------------------------------------------spooler_process
sub spooler_process
{
my $result = 0;
eval
{
# terminate processing if no more files are present
if ($input_files_index le 0) { return 0; }
my $my_input_filename = $input_files[$input_files_index-1]; # temporarily renamed input file
$my_input_filename =~ s/(\.[^.]*)$/~$1/; # "filename.ext" -> "filename~.ext"
# .. rename filename.ext to filename~.ext to ensure the file is accessible
my $ok = move $input_files[$input_files_index-1], $my_input_filename;
if( !$ok )
{
$spooler_job->{state_text} = "could not rename ".$input_files[$input_files_index-1].": $!";
# repeat job execution after the given interval of seconds in order to wait for the input file to become accessible
$spooler_task->{repeat} = $input_repeat;
return 0;
}
$spooler_log->info("processing file: ".$input_files[$input_files_index-1]);
# .. do some processing with this file, put your code here
# .. create output directory
$ok = mkpath $output_dir if (! -d $output_dir);
if( !$ok )
{
$spooler_job->{state_text} = "could not create directory: ".$output_dir.": $!";
$spooler_log->warn("could not create directory: ".$output_dir.": $!");
return 0;
}
# .. move file to destination directory
my $my_output_filename = $output_dir."/".basename($input_files[$input_files_index-1]);
$ok = move $my_input_filename, $my_output_filename;
if( !$ok )
{
$spooler_job->{state_text} = "could not move file ".$input_files[$input_files_index-1].": $!";
$spooler_log->warn("could not move file ".$input_files[$input_files_index-1].": $!");
return 0;
}
# .. state text is shown in the Job Scheduler Web Interface
$spooler_job->{state_text} = "file successfully processed: ".$input_files[$input_files_index-1]." => ".$my_output_filename;
$spooler_log->info("file successfully processed: ".$input_files[$input_files_index-1]." => ".$my_output_filename);
$input_files_index--;
$result = ($input_files_index > 0);
};
if( $@ ) # do not pass 'die' to the Job Scheduler, PerlScript could show weird errors
{
chomp $@;
$spooler_log->error( $@ );
}
return $result;
}
#----------------------------------------------------------------------------------spooler_on_error
sub spooler_on_error
{
eval
{
# add your code for error handling
# $spooler_job->{state_text} = "";
0;
};
if( $@ )
{
chomp $@ if defined $@;
$spooler_log->error( $@ ) if defined $@;
}
return 0;
}
#--------------------------------------------------------------------------------spooler_on_success
sub spooler_on_success
{
eval
{
# add your code for successful termination
# $spooler_job->{state_text} = "";
0;
};
if( $@ )
{
chomp $@ if defined $@;
$spooler_log->error( $@ ) if defined $@;
}
return 0;
}
#--------------------------------------------------------------------------------------------------