lyber-core 0.9.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +20 -0
- data/README.rdoc +58 -0
- data/lib/dlss_service.rb +82 -0
- data/lib/dor/base.rb +18 -0
- data/lib/dor/suri_service.rb +28 -0
- data/lib/dor/workflow_service.rb +111 -0
- data/lib/dor_service.rb +535 -0
- data/lib/lyber_core.rb +14 -0
- data/lib/lyber_core/connection.rb +97 -0
- data/lib/lyber_core/destroyer.rb +74 -0
- data/lib/lyber_core/exceptions/empty_queue.rb +9 -0
- data/lib/lyber_core/log.rb +105 -0
- data/lib/lyber_core/rake/dlss_release.rb +126 -0
- data/lib/lyber_core/robots/robot.rb +214 -0
- data/lib/lyber_core/robots/work_item.rb +103 -0
- data/lib/lyber_core/robots/work_queue.rb +154 -0
- data/lib/lyber_core/robots/workflow.rb +104 -0
- data/lib/lyber_core/robots/workspace.rb +77 -0
- data/lib/lyber_core/utils.rb +4 -0
- data/lib/lyber_core/utils/bagit_bag.rb +100 -0
- data/lib/lyber_core/utils/checksum_validate.rb +65 -0
- data/lib/lyber_core/utils/file_utilities.rb +168 -0
- data/lib/roxml_models/identity_metadata/dublin_core.rb +46 -0
- data/lib/roxml_models/identity_metadata/identity_metadata.rb +118 -0
- data/lib/tasks/rdoc.rake +32 -0
- metadata +371 -0
data/lib/lyber_core.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'dlss_service'
|
2
|
+
require 'dor/suri_service'
|
3
|
+
require 'dor/workflow_service'
|
4
|
+
require 'dor/base'
|
5
|
+
require 'lyber_core/connection'
|
6
|
+
require 'lyber_core/destroyer'
|
7
|
+
require 'lyber_core/log'
|
8
|
+
require 'lyber_core/robots/robot'
|
9
|
+
require 'lyber_core/robots/workflow'
|
10
|
+
require 'lyber_core/robots/workspace'
|
11
|
+
require 'lyber_core/robots/work_queue'
|
12
|
+
require 'lyber_core/robots/work_item'
|
13
|
+
require 'lyber_core/exceptions/empty_queue'
|
14
|
+
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'net/https'
|
2
|
+
require 'uri'
|
3
|
+
require 'cgi'
|
4
|
+
|
5
|
+
module LyberCore
|
6
|
+
class Connection
|
7
|
+
def Connection.get_https_connection(url)
|
8
|
+
LyberCore::Log.debug("Establishing connection to #{url.host} on port #{url.port}")
|
9
|
+
https = Net::HTTP.new(url.host, url.port)
|
10
|
+
if(url.scheme == 'https')
|
11
|
+
https.use_ssl = true
|
12
|
+
LyberCore::Log.debug("Using SSL")
|
13
|
+
https.cert = OpenSSL::X509::Certificate.new( File.read(LyberCore::CERT_FILE) )
|
14
|
+
LyberCore::Log.debug("Using cert file #{LyberCore::CERT_FILE}")
|
15
|
+
https.key = OpenSSL::PKey::RSA.new( File.read(LyberCore::KEY_FILE), LyberCore::KEY_PASS )
|
16
|
+
LyberCore::Log.debug("Using key file #{LyberCore::KEY_FILE} with pass #{LyberCore::KEY_PASS}")
|
17
|
+
https.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
18
|
+
LyberCore::Log.debug("https.verify_mode = #{https.verify_mode} (should eql #{OpenSSL::SSL::VERIFY_NONE})")
|
19
|
+
end
|
20
|
+
https
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns body of the HTTP response, or passes the response to the block if it's passed in
|
24
|
+
#
|
25
|
+
# == Required Parameters
|
26
|
+
# - <b>full_url</b> - A string containing the full url to the resource you're trying to connect to
|
27
|
+
# - <b>method</b> - Recognizes the following symbols which correspond to an HTTP verb. The convenience methods take care of this
|
28
|
+
# :get for HTTP GET
|
29
|
+
# :post for HTTP POST
|
30
|
+
# :put for HTTP PUT
|
31
|
+
# - <b>body</b> The body of your request. Can be nil if you don't have one.
|
32
|
+
#
|
33
|
+
# == Options
|
34
|
+
# - <b>:auth_user</b> for basic HTTP authentication. :auth_user and :auth_password must both be set if using authentication
|
35
|
+
# - <b>:auth_password</b> for basic HTTP authentication. :auth_user and :auth_password must both be set if using authentication
|
36
|
+
# - <b>:content_type</b> if not passed in as an option, then it is set to 'application/xml'
|
37
|
+
#
|
38
|
+
# == Block
|
39
|
+
# By default, this method returns the body of the response, Net::HTTPResponse.body . If you want to work with the Net::HTTPResponse
|
40
|
+
# object, you can pass in a block, and the response will be passed to it.
|
41
|
+
#
|
42
|
+
# == Exceptions
|
43
|
+
# Any exceptions thrown while trying to connect should be handled by the caller
|
44
|
+
def Connection.connect(full_url, method, body, options = {}, &block)
|
45
|
+
url = URI.parse(full_url)
|
46
|
+
case method
|
47
|
+
when :get
|
48
|
+
req = Net::HTTP::Get.new(url.request_uri)
|
49
|
+
when :post
|
50
|
+
req = Net::HTTP::Post.new(url.request_uri)
|
51
|
+
when :put
|
52
|
+
req = Net::HTTP::Put.new(url.request_uri)
|
53
|
+
end
|
54
|
+
req.body = body unless(body.nil?)
|
55
|
+
if(options.include?(:content_type))
|
56
|
+
req.content_type = options[:content_type]
|
57
|
+
else
|
58
|
+
req.content_type = 'application/xml'
|
59
|
+
end
|
60
|
+
|
61
|
+
if(options.include?(:auth_user))
|
62
|
+
req.basic_auth options[:auth_user], options[:auth_password]
|
63
|
+
end
|
64
|
+
|
65
|
+
res = Connection.get_https_connection(url).start {|http| http.request(req) }
|
66
|
+
case res
|
67
|
+
when Net::HTTPSuccess
|
68
|
+
if(block_given?)
|
69
|
+
block.call(res)
|
70
|
+
else
|
71
|
+
return res.body
|
72
|
+
end
|
73
|
+
else
|
74
|
+
raise res.error!
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
# Convenience method for performing an HTTP GET using Connection.connect
|
82
|
+
def Connection.get(full_url, options = {}, &b)
|
83
|
+
Connection.connect(full_url, :get, nil, options, &b)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Convenience method for performing an HTTP POST using Connection.connect
|
87
|
+
def Connection.post(full_url, body, options = {}, &b)
|
88
|
+
Connection.connect(full_url, :post, body, options, &b)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Convenience method for performing an HTTP PUT using Connection.connect
|
92
|
+
def Connection.put(full_url, body, options = {}, &b)
|
93
|
+
Connection.connect(full_url, :put, body, options, &b)
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module LyberCore
|
2
|
+
|
3
|
+
class LyberCore::Destroyer
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'active-fedora'
|
7
|
+
require 'open-uri'
|
8
|
+
|
9
|
+
attr_reader :repository
|
10
|
+
attr_reader :workflow
|
11
|
+
attr_reader :registration_robot
|
12
|
+
attr_reader :druid_list
|
13
|
+
attr_reader :current_druid
|
14
|
+
|
15
|
+
# Given a repository, a workflow and the name of a robot that registers objects in fedora
|
16
|
+
# we can generate a list of all the druids that were created by that robot
|
17
|
+
def initialize(repository, workflow, registration_robot)
|
18
|
+
@repository = repository
|
19
|
+
@workflow = workflow
|
20
|
+
@registration_robot = registration_robot
|
21
|
+
@druid_list = self.get_druid_list
|
22
|
+
end
|
23
|
+
|
24
|
+
def get_druid_list
|
25
|
+
begin
|
26
|
+
druid_list = []
|
27
|
+
url_string = "#{WORKFLOW_URI}/workflow_queue?repository=#{@repository}&workflow=#{@workflow}&completed=#{@registration_robot}"
|
28
|
+
LyberCore::Log.info("Fetching druids from #{url_string}")
|
29
|
+
doc = Nokogiri::XML(open(url_string))
|
30
|
+
doc.xpath("//objects/object/@id").each do |id|
|
31
|
+
druid_list << id.to_s
|
32
|
+
end
|
33
|
+
return druid_list
|
34
|
+
rescue Exception => e
|
35
|
+
raise e, "Couldn't fetch druid list from #{url_string}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Iterate through the druids in @druid_list and delete each of them from FEDORA
|
40
|
+
def delete_druids
|
41
|
+
begin
|
42
|
+
connect_to_fedora
|
43
|
+
@druid_list.each do |druid|
|
44
|
+
@current_druid = druid
|
45
|
+
LyberCore::Log.info("Deleting #{@current_druid}")
|
46
|
+
begin
|
47
|
+
obj = ActiveFedora::Base.load_instance(@current_druid)
|
48
|
+
obj.delete
|
49
|
+
rescue ActiveFedora::ObjectNotFoundError
|
50
|
+
LyberCore::Log.info("#{@current_druid} does not exist in this repository")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
rescue Exception => e
|
54
|
+
raise e
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def connect_to_fedora
|
59
|
+
begin
|
60
|
+
if @repository == "dor"
|
61
|
+
repo_url = FEDORA_URI
|
62
|
+
elsif @repository == "sdr"
|
63
|
+
repo_url = SEDORA_URI
|
64
|
+
else
|
65
|
+
raise "I don't know how to connect to repository #{@repository}"
|
66
|
+
end
|
67
|
+
LyberCore::Log.info("connecting to #{repo_url}")
|
68
|
+
Fedora::Repository.register(repo_url)
|
69
|
+
rescue Errno::ECONNREFUSED => e
|
70
|
+
raise e, "Can't connect to Fedora at url #{repo_url}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
|
2
|
+
module LyberCore
|
3
|
+
|
4
|
+
class LyberCore::Log
|
5
|
+
require 'logger'
|
6
|
+
|
7
|
+
# Default values
|
8
|
+
DEFAULT_LOGFILE = "/tmp/lybercore_log.log"
|
9
|
+
DEFAULT_LOG_LEVEL = Logger::INFO
|
10
|
+
DEFAULT_FORMATTER = proc{|s,t,p,m|"%5s [%s] (%s) %s :: %s\n" % [s,
|
11
|
+
t.strftime("%Y-%m-%d %H:%M:%S"), $$, p, m]}
|
12
|
+
|
13
|
+
# Initial state
|
14
|
+
@@logfile = DEFAULT_LOGFILE
|
15
|
+
@@log ||= Logger.new(@@logfile)
|
16
|
+
# $stdout.reopen(@@logfile)
|
17
|
+
# $stderr.reopen(@@logfile)
|
18
|
+
@@log.level = DEFAULT_LOG_LEVEL
|
19
|
+
@@log.formatter = DEFAULT_FORMATTER
|
20
|
+
|
21
|
+
# Restore LyberCore::Log to its default state
|
22
|
+
def Log.restore_defaults
|
23
|
+
@@log.level = DEFAULT_LOG_LEVEL
|
24
|
+
Log.set_logfile(DEFAULT_LOGFILE)
|
25
|
+
@@log.formatter = DEFAULT_FORMATTER
|
26
|
+
end
|
27
|
+
|
28
|
+
# The current location of the logfile
|
29
|
+
def Log.logfile
|
30
|
+
return @@logfile
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
|
35
|
+
# Accepts a filename as an argument, and checks to see whether that file can be
|
36
|
+
# opened for writing. If it can be opened, it closes the existing Logger object
|
37
|
+
# and re-opens it with the new logfile location. It raises an exception if it
|
38
|
+
# cannot write to the specified logfile.
|
39
|
+
def Log.set_logfile(new_logfile)
|
40
|
+
begin
|
41
|
+
current_log_level = @@log.level
|
42
|
+
current_formatter = @@log.formatter
|
43
|
+
@@log = Logger.new(new_logfile)
|
44
|
+
@@logfile = new_logfile
|
45
|
+
@@log.level = current_log_level
|
46
|
+
@@log.formatter = current_formatter
|
47
|
+
rescue Exception => e
|
48
|
+
raise e, "Couldn't initialize logfile #{new_logfile}: #{e.backtrace}"
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
# Set the log level.
|
54
|
+
# See http://ruby-doc.org/core/classes/Logger.html for more info.
|
55
|
+
# Possible values are:
|
56
|
+
# Logger::FATAL (4): an unhandleable error that results in a program crash
|
57
|
+
# Logger::ERROR (3): a handleable error condition
|
58
|
+
# Logger::WARN (2): a warning
|
59
|
+
# Logger::INFO (1): generic (useful) information about system operation
|
60
|
+
# Logger::DEBUG (0): low-level information for developers
|
61
|
+
def Log.set_level(loglevel)
|
62
|
+
begin
|
63
|
+
if [0,1,2,3,4].contains? loglevel
|
64
|
+
@@log.level = loglevel
|
65
|
+
@@log.debug "Setting LyberCore::Log.level to #{loglevel}"
|
66
|
+
else
|
67
|
+
@@log.warn "I received an invalid option for log level. I expected a number between 0 and 4 but I got #{loglevel}"
|
68
|
+
@@log.warn "I'm setting the loglevel to 0 (debug) because you seem to be having trouble."
|
69
|
+
@@log.level = 0
|
70
|
+
end
|
71
|
+
rescue Exception => e
|
72
|
+
raise e, "Couldn't set log level: #{e.backtrace}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Return the current log level
|
77
|
+
def Log.level
|
78
|
+
@@log.level
|
79
|
+
end
|
80
|
+
|
81
|
+
def Log.fatal(msg)
|
82
|
+
@@log.add(Logger::FATAL) { msg }
|
83
|
+
end
|
84
|
+
|
85
|
+
def Log.error(msg)
|
86
|
+
@@log.add(Logger::ERROR) { msg }
|
87
|
+
end
|
88
|
+
|
89
|
+
def Log.warn(msg)
|
90
|
+
@@log.add(Logger::WARN) { msg }
|
91
|
+
end
|
92
|
+
|
93
|
+
def Log.info(msg)
|
94
|
+
@@log.add(Logger::INFO) { msg }
|
95
|
+
end
|
96
|
+
|
97
|
+
def Log.debug(msg)
|
98
|
+
@@log.add(Logger::DEBUG) { msg }
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'rake/tasklib'
|
2
|
+
require 'pony'
|
3
|
+
|
4
|
+
module LyberCore
|
5
|
+
|
6
|
+
# A rake task that will tag, build and publish your gem to the DLSS gemserver
|
7
|
+
#
|
8
|
+
# == Usage
|
9
|
+
# Include the following two lines in your <tt>Rakefile</tt>:
|
10
|
+
# require 'lyber_core/rake/dlss_release'
|
11
|
+
# LyberCore::DlssRelease.new
|
12
|
+
#
|
13
|
+
# To build and release the gem, run the following AFTER pushing your code to the git repository on AFS:
|
14
|
+
# rake dlss_release
|
15
|
+
#
|
16
|
+
# == Requirements
|
17
|
+
# - You need <b>ONE</b> <tt>your-gemname.gemspec</tt> file in the root of your project. See the example in <tt>lyber-core.gemspec</tt> or look at http://yehudakatz.com/2010/04/02/using-gemspecs-as-intended/
|
18
|
+
# - Inside the <tt>Gem::Specification</tt> block of the <tt>your-gemname.gemspec</tt> file, you must set the version number. For example
|
19
|
+
# Gem::Specification.new do |s|
|
20
|
+
# s.name = "your-gemname"
|
21
|
+
# ...
|
22
|
+
# s.version = "0.9.6"
|
23
|
+
# ...
|
24
|
+
# end
|
25
|
+
# - You need to have access to the account on the DLSS gemserver, usually by adding your sunetid to the .k5login. You also need git installed.
|
26
|
+
class DlssRelease < Rake::TaskLib
|
27
|
+
# Name of the rake task. Defaults to :dlss_release
|
28
|
+
attr_accessor :name
|
29
|
+
|
30
|
+
# Name of the gem
|
31
|
+
attr_accessor :gemname
|
32
|
+
|
33
|
+
# Version of the gem being released
|
34
|
+
attr_accessor :version
|
35
|
+
|
36
|
+
def initialize(name=:dlss_release)
|
37
|
+
@name = name
|
38
|
+
@gemname = determine_gemname
|
39
|
+
yield self if block_given?
|
40
|
+
raise "Gemname must be set" if @gemname.nil?
|
41
|
+
define
|
42
|
+
end
|
43
|
+
|
44
|
+
# Tries to parse the gemname from the prefix of the <tt>your-gemname.gemspec</tt> file.
|
45
|
+
def determine_gemname
|
46
|
+
files = Dir.glob("*.gemspec")
|
47
|
+
raise "Unable to find project gemspec file.\nEither it doesn't exist or there are too many files ending in .gemspec" if(files.size != 1)
|
48
|
+
|
49
|
+
files.first =~ /(.*)\.gemspec/
|
50
|
+
$1
|
51
|
+
end
|
52
|
+
|
53
|
+
def send_release_announcement
|
54
|
+
Pony.mail(:to => 'dlss-developers@lists.stanford.edu',
|
55
|
+
:from => 'dlss-developers@lists.stanford.edu',
|
56
|
+
:subject => "Release #{@version} of the #{@gemname} gem",
|
57
|
+
:body => "The #{@gemname}-#{@version}.gem has been released to the DLSS gemserver",
|
58
|
+
:via => :smtp,
|
59
|
+
:via_options => {
|
60
|
+
:address => 'smtp.stanford.edu',
|
61
|
+
:port => '25',
|
62
|
+
:enable_starttls_auto => true,
|
63
|
+
:authentication => :plain, # :plain, :login, :cram_md5, no auth by default
|
64
|
+
:domain => "localhost.localdomain" # the HELO domain provided by the client to the server
|
65
|
+
}
|
66
|
+
)
|
67
|
+
end
|
68
|
+
|
69
|
+
def define
|
70
|
+
desc "Tag, build, and release DLSS specified gem"
|
71
|
+
task @name do
|
72
|
+
IO.foreach(File.join(Rake.original_dir, "#{@gemname}.gemspec")) do |line|
|
73
|
+
if(line =~ /\.version.*=.*"(.*)"/)
|
74
|
+
@version = $1
|
75
|
+
break
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
if(@version.nil?)
|
80
|
+
raise "Unable to find version number in #{@gemname}.gemspec"
|
81
|
+
end
|
82
|
+
created_gem = "#{@gemname}-#{@version}.gem"
|
83
|
+
|
84
|
+
puts "Make sure:"
|
85
|
+
puts " 1) Version #{@version} of #{@gemname} has not been tagged and released previously"
|
86
|
+
puts " 2) All of the tests pass"
|
87
|
+
puts " 3) You've pushed the code to the git repository on AFS (usually 'git push origin master')"
|
88
|
+
puts "Type 'yes' to continue if all of these statements are true"
|
89
|
+
|
90
|
+
resp = STDIN.gets.chomp
|
91
|
+
unless(resp =~ /yes/ )
|
92
|
+
raise "Please complete the prerequisites"
|
93
|
+
end
|
94
|
+
|
95
|
+
puts "Releasing version #{@version} of the #{@gemname} gem"
|
96
|
+
|
97
|
+
begin
|
98
|
+
tags = `git tag`.split("\n")
|
99
|
+
raise "tag v#{@version} already exists. Change the version number in the #{@gemname}.gemspec file" if tags.include?("v#{@version}")
|
100
|
+
|
101
|
+
puts "...Tagging release"
|
102
|
+
sh "git tag -a v#{@version} -m 'Gem version #{@version}'"
|
103
|
+
sh "git push origin --tags"
|
104
|
+
|
105
|
+
puts "...Building gem"
|
106
|
+
sh "gem build #{@gemname}.gemspec"
|
107
|
+
|
108
|
+
puts "...Publishing gem to sulair-rails-dev DLSS gemserver"
|
109
|
+
sh "scp #{created_gem} webteam@sulair-rails-dev.stanford.edu:/var/www/html/gems"
|
110
|
+
sh "ssh webteam@sulair-rails-dev.stanford.edu gem generate_index -d /var/www/html"
|
111
|
+
|
112
|
+
puts "Done!!!!! A local copy of the gem is in the pkg directory"
|
113
|
+
FileUtils.mkdir("pkg") unless File.exists?("pkg")
|
114
|
+
FileUtils.mv("#{created_gem}", "pkg")
|
115
|
+
rescue Exception => e
|
116
|
+
FileUtils.rm("#{created_gem}") if(File.exists?("#{created_gem}"))
|
117
|
+
raise e
|
118
|
+
end
|
119
|
+
|
120
|
+
puts "Sending release announcement to DLSS developers list"
|
121
|
+
send_release_announcement
|
122
|
+
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,214 @@
|
|
1
|
+
# == Usage
|
2
|
+
# ruby_cl_skeleton [options] source_file
|
3
|
+
#
|
4
|
+
# For help use: ruby_cl_skeleton -h
|
5
|
+
|
6
|
+
module LyberCore
|
7
|
+
module Robots
|
8
|
+
require 'optparse'
|
9
|
+
require 'ostruct'
|
10
|
+
|
11
|
+
# ===== Usage
|
12
|
+
# User defined robots should derive from this class and override the #process_item method
|
13
|
+
class Robot
|
14
|
+
attr_accessor :workflow_name
|
15
|
+
attr_accessor :workflow_step
|
16
|
+
|
17
|
+
# A LyberCore::Robots::Workflow object
|
18
|
+
attr_accessor :workflow
|
19
|
+
attr_accessor :collection_name
|
20
|
+
attr_accessor :workspace
|
21
|
+
attr_accessor :args
|
22
|
+
attr_accessor :options
|
23
|
+
|
24
|
+
|
25
|
+
# Available options
|
26
|
+
# - :collection_name - The collection this workflow should work with.
|
27
|
+
# Defined as a subdirectory within ROBOT_ROOT/config/workflows/your_workflow/your_collection
|
28
|
+
# - :workspace - Full path of where to find content for a particular workflow
|
29
|
+
# - :logfile - Where to write log messages
|
30
|
+
# - :loglevel - Level of logging from 0 - 4 where 0 = DEBUG and 4 = FATAL
|
31
|
+
def initialize(workflow_name, workflow_step, args = {})
|
32
|
+
@workflow_name = workflow_name
|
33
|
+
@workflow_step = workflow_step
|
34
|
+
@collection_name = args[:collection_name]
|
35
|
+
@opts = args
|
36
|
+
|
37
|
+
if args[:logfile]
|
38
|
+
LyberCore::Log.set_logfile(args[:logfile])
|
39
|
+
else
|
40
|
+
FileUtils.mkdir(File.join(ROBOT_ROOT, 'log')) unless(File.exists?(File.join(ROBOT_ROOT, 'log')))
|
41
|
+
robot_logfile = File.join(ROBOT_ROOT,'log',workflow_step+'.log')
|
42
|
+
LyberCore::Log.set_logfile(robot_logfile)
|
43
|
+
end
|
44
|
+
|
45
|
+
LyberCore::Log.set_level(args[:loglevel]) if args[:loglevel]
|
46
|
+
|
47
|
+
# Set defaults
|
48
|
+
@options = OpenStruct.new
|
49
|
+
self.parse_options
|
50
|
+
self.create_workflow
|
51
|
+
self.set_workspace
|
52
|
+
end
|
53
|
+
|
54
|
+
# Some workflows require a directory where their content lives
|
55
|
+
# If a robot is invoked with a :workspace => true option, its @workspace
|
56
|
+
# should be set from the value in
|
57
|
+
def set_workspace
|
58
|
+
if(@opts[:workspace])
|
59
|
+
@workspace = LyberCore::Robots::Workspace.new(@workflow_name, @collection_name)
|
60
|
+
LyberCore::Log.debug("workspace = #{workspace.inspect}")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Create the workflow at instantiation, not when we start running the robot.
|
65
|
+
# That way we can do better error checking and ensure that everything is going
|
66
|
+
# to run okay before we actually start things.
|
67
|
+
def create_workflow
|
68
|
+
|
69
|
+
unless defined?(WORKFLOW_URI)
|
70
|
+
LyberCore::Log.fatal "FATAL: WORKFLOW_URI is not defined"
|
71
|
+
LyberCore::Log.fatal "Usually this is a value like 'http://lyberservices-dev.stanford.edu/workflow'"
|
72
|
+
LyberCore::Log.fatal "Usually you load it by setting ROBOT_ENVIRONMENT when you invoke your robot"
|
73
|
+
raise "WORKFLOW_URI is not set! Do you need to set your ROBOT_ENVIRONMENT value?"
|
74
|
+
end
|
75
|
+
LyberCore::Log.debug("About to instatiate a Workflow object
|
76
|
+
-- LyberCore::Robots::Workflow.new(#{@workflow_name},#{collection_name}")
|
77
|
+
@workflow = LyberCore::Robots::Workflow.new(@workflow_name, {:logger => @logger, :collection_name => @collection_name})
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
# == Create a new workflow
|
82
|
+
def start()
|
83
|
+
|
84
|
+
begin
|
85
|
+
LyberCore::Log.debug("Starting robot...")
|
86
|
+
|
87
|
+
queue = @workflow.queue(@workflow_step)
|
88
|
+
|
89
|
+
# If we have arguments, parse out the parts that indicate druids
|
90
|
+
if(@options.file or @options.druid)
|
91
|
+
queue.enqueue_druids(get_druid_list)
|
92
|
+
else
|
93
|
+
queue.enqueue_workstep_waiting()
|
94
|
+
end
|
95
|
+
process_queue(queue)
|
96
|
+
# TODO: Implement a FatalError class
|
97
|
+
# rescue LyberCore::Exceptions::FatalError => e
|
98
|
+
# LyberCore::Log.fatal("e.msg")
|
99
|
+
# exit
|
100
|
+
rescue LyberCore::Exceptions::EmptyQueue
|
101
|
+
LyberCore::Log.info("Empty queue -- no objects to process")
|
102
|
+
rescue Exception => e
|
103
|
+
LyberCore::Log.error(e.msg)
|
104
|
+
LyberCore::Log.error(e.backtrace.join("\n"))
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Generate a list of druids to process
|
109
|
+
def get_druid_list
|
110
|
+
|
111
|
+
druid_list = Array.new
|
112
|
+
|
113
|
+
# append any druids passed explicitly
|
114
|
+
if(@options.druid)
|
115
|
+
druid_list << @options.druid
|
116
|
+
end
|
117
|
+
|
118
|
+
# identifier list is in a file
|
119
|
+
if (@options.file && File.exist?(@options.file))
|
120
|
+
File.open(@options.file) do |file|
|
121
|
+
file.each_line do |line|
|
122
|
+
druid = line.strip
|
123
|
+
if (druid.length > 0)
|
124
|
+
druid_list << druid
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
return druid_list
|
131
|
+
end
|
132
|
+
|
133
|
+
def process_queue(queue)
|
134
|
+
while work_item = queue.next_item do
|
135
|
+
begin
|
136
|
+
#call overridden method
|
137
|
+
process_item(work_item)
|
138
|
+
work_item.set_success
|
139
|
+
rescue Exception => e
|
140
|
+
# LyberCore::Log.error("Encountered exception processing #{work_item.druid}: #{e.to_s}")
|
141
|
+
# LyberCore::Log.debug("Encountered exception processing #{work_item.druid}: #{e.backtrace.join("\n")}")
|
142
|
+
work_item.set_error(e)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
queue.print_stats()
|
146
|
+
end
|
147
|
+
|
148
|
+
# Override this method in your robot instance. The method in this base class will throw an exception if it is not overriden.
|
149
|
+
def process_item(work_item)
|
150
|
+
#to be overridden by child classes
|
151
|
+
raise 'You must implement this method in your subclass'
|
152
|
+
end
|
153
|
+
|
154
|
+
# ###########################
|
155
|
+
# command line option parsing
|
156
|
+
|
157
|
+
def parse_options
|
158
|
+
|
159
|
+
options = {}
|
160
|
+
|
161
|
+
o = OptionParser.new do |opts|
|
162
|
+
opts.banner = "Usage: example.rb [options]"
|
163
|
+
opts.separator ""
|
164
|
+
|
165
|
+
opts.on("-d DRUID", "--druid DRUID", "Pass in a druid to process") do |d|
|
166
|
+
@options.druid = d
|
167
|
+
end
|
168
|
+
|
169
|
+
opts.on("-f", "--file FILE", "Pass in a file of druids to process") do |f|
|
170
|
+
@options.file = f
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
174
|
+
|
175
|
+
# Parse the command line options and ignore anything not specified above
|
176
|
+
begin
|
177
|
+
o.parse!
|
178
|
+
rescue OptionParser::InvalidOption => e
|
179
|
+
LyberCore::Log.debug("e.inspect")
|
180
|
+
rescue OptionParser::ParseError => e
|
181
|
+
LyberCore::Log.error("Couldn't parse options: #{e.backtrace}")
|
182
|
+
raise e
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
186
|
+
|
187
|
+
# def output_options
|
188
|
+
# puts "Options:\n"
|
189
|
+
#
|
190
|
+
# @options.marshal_dump.each do |name, val|
|
191
|
+
# puts " #{name} = #{val}"
|
192
|
+
# end
|
193
|
+
# end
|
194
|
+
#
|
195
|
+
# def output_help
|
196
|
+
# output_version
|
197
|
+
# RDoc::usage() #exits app
|
198
|
+
# end
|
199
|
+
#
|
200
|
+
# def output_usage
|
201
|
+
# RDoc::usage('usage') # gets usage from comments above
|
202
|
+
# end
|
203
|
+
#
|
204
|
+
# def output_version
|
205
|
+
# puts "#{File.basename(__FILE__)} version #{VERSION}"
|
206
|
+
# end
|
207
|
+
|
208
|
+
# ##################################
|
209
|
+
# end of command line option parsing
|
210
|
+
# ##################################
|
211
|
+
|
212
|
+
end # end of class
|
213
|
+
end # end of Robots module
|
214
|
+
end # end of LyberCore module
|