# 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;
}

#--------------------------------------------------------------------------------------------------