Reputation: 33
I am writing a ruby
script to send files to a server as soon as they are added in a folder.
The script needs to be run as a service on a MS windows machine and uses win32/daemon gem
to make it a service. But I am not able to start the script.
The filename is check.rb
and, on running ruby check.rb
via cmd
(as Administrator) on Windows, I get this error:
Service_Main thread exited abnormally
Here are some screenshots of the run script and the error that I get:
Here is the code file:
require 'rubygems'
require 'win32/daemon'
require "csv"
require "rest_client"
require "colorize"
require 'logger'
include Win32
def working_dir
# '/home/manav/Desktop/work/tripbeam'
'C:\\AIR'
end
def sub_dir_seperator
# '/'
'\\'
end
$log = Logger.new(working_dir + sub_dir_seperator + 'logs.txt')
def print_it(message)
puts message
$log.debug(message)
end
def get_files
begin
Dir.entries(working_dir).select { |f| !File.directory? f }.select { |f| f =~ /AIR\d{5}.Txt/ }.sort_by{|f| File.ctime(working_dir + sub_dir_seperator + f)}
rescue Exception => e
print_it "Error getting files in directory: #{working_dir}"
print_it "Err class #{e.class}, message: #{e.message}"
end
end
def add_record_to_csv_with_fail(filename)
begin
print_it "Adding #{filename} to records.csv"
CSV.open(working_dir + sub_dir_seperator + "records.csv", "a") do |csv|
created_time = File.ctime(working_dir + sub_dir_seperator + filename).to_s
csv << [filename, created_time, "", "fail"]
end
print_it "Added #{filename} to records.csv with status fail"
rescue => e
print_it "Error adding #{filename} to records.csv"
print_it "Err class #{e.class}, message: #{e.message}"
end
end
def add_record_to_csv_with_success(filename)
begin
print_it "Adding #{filename} to records.csv"
CSV.open(working_dir + sub_dir_seperator + "records.csv", "a") do |csv|
created_time = File.ctime(working_dir + sub_dir_seperator + filename).to_s
csv << [filename, created_time, "", "success"]
end
print_it "Added #{filename} to records.csv with success"
rescue => e
print_it "Error adding #{filename} to records.csv"
print_it "Err class #{e.class}, message: #{e.message}"
end
end
def send_file_to_server(url, params, headers, filename)
print_it "sending #{filename} to server..."
begin
response = RestClient.post(url, params, headers)
responsebody = eval(response.body)
print_it "Response from Server: " + responsebody[:message]
if responsebody[:message].eql?("Success")
print_it "Sent file to server"
add_record_to_csv_with_success(filename)
else
add_record_to_csv_with_fail(filename)
end
rescue Exception => e
print_it "Can not send the file to server"
print_it "Err class #{e.class}, message: #{e.message}"
add_record_to_csv_with_fail(filename)
ensure
$number_of_files += 1
end
end
def api_url
'https://tripbeam.us/api/v1/files'
# 'http://192.168.1.18:3002/api/v1/files'
end
def delete_files_from_records
current_files = get_files
records = CSV.read(working_dir + sub_dir_seperator + "records.csv", headers:true)
print_it "Updating records.csv"
records.each do |row|
if !(current_files.include? row['name'])
#update delete column
row['deleted'] = Time.now.to_s
end
end
#save record
CSV.open(working_dir + sub_dir_seperator + "records.csv", "w") do |csv_out|
csv_out << ["name", "created", "deleted", "sent_status"]
records.each do |row|
csv_out << row
end
end
print_it "Updated records.csv"
$number_of_files = get_files.length
end
def add_files_added_while_program_not_running
current_files = get_files
records = CSV.read(working_dir + sub_dir_seperator + 'records.csv', headers:true)
recorded_files = records.by_col[0]
added_files = current_files - recorded_files
if added_files.length != 0
print_it "#{added_files.length} file(s) were added while the program was shut down."
end
added_files.each do |added_file|
file_to_send = File.new(working_dir + sub_dir_seperator + added_file)
params = {:attachment => file_to_send}
headers = {:"Accept" => 'application/vnd.tripbeam.v1'}
send_file_to_server(api_url, params, headers, added_file)
end
end
begin
class DemoDaemon < Daemon
def service_init
print_it "Initializing background service..."
10.times{ |i|
print_it i.to_s
sleep 1
}
print_it "Initialized background service..."
end
def service_main(*args)
#before listen
$number_of_files = get_files.length
current_files = get_files
records = CSV.read(working_dir + sub_dir_seperator + 'records.csv', headers:true)
recorded_files_names = records.by_col[0]
#check which files were added aftrt program shut down
add_files_added_while_program_not_running()
#Listen for new files
$number_of_files = get_files.length
print_it "Number of files in dir: #{working_dir} : #{$number_of_files}"
# While we're in here the daemon is running.
while running?
if state == RUNNING
#listen here....
while true
begin
sleep(1)
current_files = get_files
if $number_of_files > current_files.length
#file deleted
number_of_files_deleted = $number_of_files - current_files.length
print_it "#{number_of_files_deleted} file(s) deleted"
#update records.csv
delete_files_from_records
elsif $number_of_files < current_files.length
#file added
number_of_files_added = current_files.length - $number_of_files
print_it "#{number_of_files_added} file(s) added"
#send to server
files_added = current_files[($number_of_files - current_files.length)..current_files.length]
files_added.each do |added_file|
file_to_send = File.new(working_dir + sub_dir_seperator + added_file)
params = {:attachment => file_to_send}
headers = {:"Accept" => 'application/vnd.tripbeam.v1'}
send_file_to_server(api_url, params, headers, added_file)
end
end
rescue Exception => e
print_it "Error while listening, #{e.class}, message: #{e.message}"
end
end
else # PAUSED or IDLE
print_it "Somebody has paused the service..."
sleep 100
end
end
msg = 'service_main left at: ' + Time.now.to_s
print_it msg
end
def service_stop
msg = 'Received stop signal at: ' + Time.now.to_s
print_it msg
end
# This event triggers when the service receives a signal to pause.
#
def service_pause
msg = 'Received pause signal at: ' + Time.now.to_s
print_it msg
end
# This event triggers when the service receives a signal to resume
# from a paused state.
#
def service_resume
msg = 'Received resume signal at: ' + Time.now.to_s
print_it msg
end
end
# Create an instance of the Daemon and put it into a loop. I borrowed the
# method name 'mainloop' from Tk, btw.
#
DemoDaemon.mainloop
rescue Exception => err
print_it "service failure: #{err.class} .... #{err.message}"
raise
end
Upvotes: 3
Views: 248
Reputation: 2122
I am writing a ruby script to send files to a server as soon as they are added in a folder.
First of all, a loop in your code (in method service_main
) re-reads the disk every second, 24/7. (That's all day, and all night—and on weekends, too!) That would wear out your client's disk, quickly and unexpectedly. :(
Ultimately, an alternative to that bad technique is to ask Windows' operating system services for a notification, whenever any file in the directory has been changed.
You can web-search the following:
directory notification windows ruby
That yields (for example) the program WatchDirectory, which can be run as a Windows service.
Using an off-the-shelf program is better, because your team would have less code to maintain.
WatchDirectory can invoke a custom batch file whenever anything changes in a target directory. "There is also a special plugin that can start a .bat file script giving you a 100% flexibility." So the batch file can start a Ruby program (which you can write). The Ruby program can compare the directory's contents and perform any other desired functionality.
Or WatchDirectory may already include plugins for everything you need (which would be better), without writing (and maintaining) any Ruby program. "Tasks are performed by running a plugin. An example of a task would be copying the new file to another directory." (From the help page.)
That's possible. Again, this way would be better. Remember, a line of code not written is a line of code that needn't be maintained.
For alternatives to using WatchDirectory, you can web-search the following:
watchdirectory alternatives
One result is a page on AlternativeTo's website: Alternatives to WatchDirectory (for Windows).
If you prefer to write your own code (creating a polylingual solution with Ruby and a bit of C++, rather than employing an off-the-shelf program, such as WatchDirectory), a link to some useful sample C++ code from Microsoft (which monitors folders) also resulted from the first web-search I mentioned above.
The MS windows machine is used by many people, so we dont want anyone to close the cmd terminal by mistake.
If you wish for any (non-Windows service) program always to be hidden from the taskbar (for any reason), you can web-search the following:
windows program hide
One result is Best Ways to Quickly Hide Windows Applications, which yields the configurable and apparently useful "Windows Hide Tool" and "ClickyGone."
Upvotes: 1