require 'fileutils' require 'open-uri' module Itrp module Export module Monitor class Service def initialize @failed_exports = Set.new @missing_export_ids = Set.new @options = Itrp::Export::Monitor.configuration.current @options[:ids] = (@options[:ids] || []) + [@options[:id]].flatten.compact.map(&:to_i) [:root, :ids, :imap_user_name, :imap_password].each do |required_option| raise ::Itrp::Exception.new("Missing required configuration option #{required_option}") if option(required_option).blank? end [:sub_dirs, :csv_row_sep, :csv_col_sep, :csv_quote_char, :csv_value_proc].each do |unzip_dependent_option| raise ::Itrp::Exception.new("Configuration option #{unzip_dependent_option} is only available when unzip is true") unless option(unzip_dependent_option).blank? end unless @options[:unzip] raise ::Itrp::Exception.new("Configuration option csv_quote_char must be 1 character long") unless option(:csv_quote_char).blank? || option(:csv_quote_char).length == 1 @logger = @options[:logger] create_exit_when_idle_timer end # Retrieve an option def option(key) @options[key] end def process(mail) mail = Itrp::Export::Monitor::Mail.new(mail) return if @failed_exports.include?(mail.download_uri) if option(:ids).include?(mail.export_id) begin kill_exit_when_idle_timer @logger.info { "Processing ITRP Export mail:\n Subject: #{mail.original.subject}\n Export ID: #{mail.export_id}\n Token: #{mail.token}\n URI: #{mail.download_uri}" } store_export(mail) rescue ::Exception => ex @failed_exports.add(mail.download_uri) @logger.error { "Processing of mail '#{mail.original.subject}' failed: #{ex.message}\n #{ex.backtrace.join("\n ")}" } handle_exception(ex, mail) mail.ignore # leave mail in the mailbox ensure create_exit_when_idle_timer end else mail.ignore # leave mail in the mailbox unless @missing_export_ids.include?(mail.export_id) @missing_export_ids.add(mail.export_id) @logger.info { mail.export_id ? "Skipping mail. ITRP Export ID #{mail.export_id} not configured for monitoring" : "Skipping mail. Not an ITRP Export mail: #{mail.original.subject}" } end end end # Generate a clacks config file based on the export config def generate_clacks_config clacks_config_filename = "#{dir(:tmp)}/clacks_config.#{monitor_id}.rb" log_device = @logger.instance_variable_get(:@logdev) log_file = log_device.respond_to?(:filename) && log_device.filename ? File.expand_path(log_device.filename) : "#{dir(:log)}/#{monitor_id}.log" File.open(clacks_config_filename, 'w') do |clacks_config| clacks_config.write(< another_exception @logger.error { "Exception occurred in exception handling: #{another_exception.message}\n #{another_exception.backtrace.join("\n ")}" } end end end def kill_exit_when_idle_timer @exit_when_idle_timer.try(:kill) @exit_when_idle_timer = nil end # reset timer to exit when the export monitor has been idle for a given nr of minutes def create_exit_when_idle_timer timeout_in_seconds = option(:exit_when_idle).to_i * 60 return unless timeout_in_seconds > 0 begin kill_exit_when_idle_timer # make sure only 1 timer thread is running at a time @exit_when_idle_timer = Thread.new do sleep timeout_in_seconds stop_signal = (Signal.list.keys & ['QUIT', 'INT']).first Process.kill(stop_signal, Process.pid) end rescue ::Exception => ex @logger.error { "Unable to schedule timer to exit when idle in #{timeout_in_seconds} seconds: #{ex.message}\n #{ex.backtrace.join("\n ")}" } end end end end end end