Class: Jettywrapper

Inherits:
Object
  • Object
show all
Includes:
ActiveSupport::Benchmarkable
Defined in:
lib/jettywrapper.rb,
lib/jettywrapper/version.rb

Constant Summary collapse

VERSION =
"2.0.5"

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(params = {}) ⇒ Jettywrapper

Returns a new instance of Jettywrapper.



32
33
34
35
# File 'lib/jettywrapper.rb', line 32

def initialize(params = {})
  self.base_path = self.class.app_root
  configure(params)
end

Class Attribute Details

.envObject



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/jettywrapper.rb', line 145

def env
  @env ||= begin
    case
    when ENV['JETTYWRAPPER_ENV']
      ENV['JETTYWRAPPER_ENV']
    when defined?(Rails) && Rails.respond_to?(:env)
      Rails.env
    when ENV['RAILS_ENV']
      ENV['RAILS_ENV']
    when ENV['environment']
      ENV['environment']
    else
      default_environment
    end
  end
end

.hydra_jetty_versionObject



55
56
57
# File 'lib/jettywrapper.rb', line 55

def hydra_jetty_version
  @hydra_jetty_version ||= 'master'
end

.jetty_dirObject



72
73
74
# File 'lib/jettywrapper.rb', line 72

def jetty_dir
  @jetty_dir ||= 'jetty'
end

.tmp_dirObject



64
65
66
# File 'lib/jettywrapper.rb', line 64

def tmp_dir
  @tmp_dir ||= 'tmp'
end

.urlObject



59
60
61
62
# File 'lib/jettywrapper.rb', line 59

def url
  @url ||= defined?(ZIP_URL) ? ZIP_URL : "https://github.com/projecthydra/hydra-jetty/archive/#{hydra_jetty_version}.zip"
  @url
end

Instance Attribute Details

#base_pathObject

The root of the application. Used for determining where log files and PID files should go.



26
27
28
# File 'lib/jettywrapper.rb', line 26

def base_path
  @base_path
end

#java_commandObject

Path to the Java executable



28
29
30
# File 'lib/jettywrapper.rb', line 28

def java_command
  @java_command
end

#java_optsObject

Options to pass to java (ex. [“-Xmx512mb”, “-Xms128mb”])



27
28
29
# File 'lib/jettywrapper.rb', line 27

def java_opts
  @java_opts
end

#java_versionObject

Minimum java version



29
30
31
# File 'lib/jettywrapper.rb', line 29

def java_version
  @java_version
end

#jetty_homeObject

Jetty’s home directory



21
22
23
# File 'lib/jettywrapper.rb', line 21

def jetty_home
  @jetty_home
end

#jetty_optsObject

Options to pass to jetty (ex. [“etc/my_jetty.xml”, “etc/other.xml”] as in wiki.eclipse.org/Jetty/Reference/jetty.xml_usage



30
31
32
# File 'lib/jettywrapper.rb', line 30

def jetty_opts
  @jetty_opts
end

#portObject

Jetty’s port. Default is 8888. Note that attribute is named port, but params passed in expect :jetty_port



22
23
24
# File 'lib/jettywrapper.rb', line 22

def port
  @port
end

#quietObject

true (default) to reduce Jetty’s output



24
25
26
# File 'lib/jettywrapper.rb', line 24

def quiet
  @quiet
end

#solr_homeObject

Solr’s home directory. Default is jetty_home/solr



25
26
27
# File 'lib/jettywrapper.rb', line 25

def solr_home
  @solr_home
end

#startup_waitObject

How many seconds to wait for jetty to spin up. Default is 5.



23
24
25
# File 'lib/jettywrapper.rb', line 23

def startup_wait
  @startup_wait
end

Class Method Details

.app_rootObject



138
139
140
141
142
143
# File 'lib/jettywrapper.rb', line 138

def app_root
  return @app_root if @app_root
  @app_root = Rails.root if defined?(Rails) and defined?(Rails.root)
  @app_root ||= APP_ROOT if defined?(APP_ROOT)
  @app_root ||= '.'
end

.check_java_version!(java_path, required_java_version) ⇒ Object



335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
# File 'lib/jettywrapper.rb', line 335

def check_java_version! java_path, required_java_version
  Tempfile.open("java-version-output") do |f|
    process = ChildProcess.build(java_path, "-version")
    process.io.stderr = f
    process.start

    begin
      process.poll_for_exit(10)
    rescue ChildProcess::TimeoutError
      process.stop # tries increasingly harsher methods to kill the process.
    end

    f.rewind
    err = f.read

    java_version = if version = err.match(/java version "([^"]+)"/)
      version[1]
    else
      raise "Java not found, or an error was encountered when running `#{java_path} -version`: #{err}"
    end

    unless Gem::Dependency.new('', required_java_version.gsub("_", ".")).match?('', java_version.gsub("_", "."))
      raise "Java #{required_java_version} is required to run Jetty. Found Java #{java_version} when running `#{java_path} -version`."
    end
  end

  true
end

.cleanObject



126
127
128
129
# File 'lib/jettywrapper.rb', line 126

def clean
  FileUtils.remove_dir(jetty_dir,true)
  unzip
end

.configure(params = {}) ⇒ Object

Set the jetty parameters. It accepts a Hash of symbols.

Parameters:

  • params (Hash<Symbol>) (defaults to: {})

    :jetty_home Required. Jetty’s home direcotry :jetty_port Jetty’s port. Default is 8888. Note that attribute is named port, but params passed in expect :jetty_port :startup_wait How many seconds to wait for jetty to spin up. Default is 5. If jetty doesn’t finish spinning up, tests can fail because they can’t reach jetty. :solr_home Solr’s home directory. Default is jetty_home/solr :quiet Keep True(default) to reduce jetty’s output :java_opts options to pass to the jvm (ex. [“-Xmx512mb”, “-Xms128mb”]) :jetty_opts options to pass to jetty (ex. [“etc/my_jetty.xml”, “etc/other.xml”] as in wiki.eclipse.org/Jetty/Reference/jetty.xml_usage



204
205
206
207
208
209
# File 'lib/jettywrapper.rb', line 204

def configure(params = {})
  jetty_server = self.instance
  jetty_server.reset_process!
  jetty_server.configure params
  return jetty_server
end

.default_environmentObject



162
163
164
# File 'lib/jettywrapper.rb', line 162

def default_environment
  'development'
end

.download(url = nil) ⇒ Object



76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/jettywrapper.rb', line 76

def download(url = nil)
  return if File.exists? zip_file
  self.url = url if url
  logger.info "Downloading jetty at #{self.url} ..."
  FileUtils.mkdir tmp_dir unless File.exists? tmp_dir

  begin
    open(self.url) do |io|
      IO.copy_stream(io,zip_file)
    end
  rescue Exception => e
    abort "Unable to download jetty from #{self.url} #{e.message}"
  end
end

.expanded_zip_dir(tmp_save_dir) ⇒ Object



120
121
122
123
124
# File 'lib/jettywrapper.rb', line 120

def expanded_zip_dir(tmp_save_dir)
  # This old way is more specific, but won't work for blacklight-jetty
  #expanded_dir = Dir[File.join(tmp_save_dir, "hydra-jetty-*")].first
  Dir[File.join(tmp_save_dir, "*")].first
end

.instanceObject



211
212
213
# File 'lib/jettywrapper.rb', line 211

def instance
  @instance ||= Jettywrapper.new
end

.is_jetty_running?(params) ⇒ Boolean

Determine whether the jetty at the given jetty_home is running

Examples:

Jettywrapper.is_jetty_running?(:jetty_home => '/path/to/jetty')

Parameters:

  • params: (Hash)

    :jetty_home is required. Which jetty do you want to check the status of?

Returns:

  • (Boolean)


283
284
285
286
287
288
# File 'lib/jettywrapper.rb', line 283

def is_jetty_running?(params)
  Jettywrapper.configure(params)
  pid = Jettywrapper.instance.pid
  return false unless pid
  true
end

.is_pid_running?(pid) ⇒ Boolean

Check to see if the pid is actually running. This only works on unix.

Returns:

  • (Boolean)


327
328
329
330
331
332
333
# File 'lib/jettywrapper.rb', line 327

def is_pid_running?(pid)
  begin
    return Process.getpgid(pid) != -1
  rescue Errno::ESRCH
    return false
  end
end

.is_port_in_use?(port) ⇒ Boolean

Check to see if the port is open so we can raise an error if we have a conflict

Examples:

Jettywrapper.is_port_open?(8983)

Parameters:

  • port (Fixnum)

    the port to check

Returns:

  • (Boolean)


307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
# File 'lib/jettywrapper.rb', line 307

def is_port_in_use?(port)
  begin
    Timeout::timeout(1) do
      begin
        s = TCPSocket.new('127.0.0.1', port)
        s.close
        return true
      rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
        return false
      rescue
        return false
      end
    end
  rescue Timeout::Error
  end

  return false
end

.load_config(config_name = env) ⇒ Object



166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/jettywrapper.rb', line 166

def load_config(config_name = env)
  @env = config_name
  jetty_file = "#{app_root}/config/jetty.yml"

  unless File.exists?(jetty_file)
    logger.warn "Didn't find expected jettywrapper config file at #{jetty_file}, using default file instead."
    jetty_file = File.expand_path("../config/jetty.yml", File.dirname(__FILE__))
  end

  begin
    jetty_erb = ERB.new(IO.read(jetty_file)).result(binding)
  rescue
    raise("jetty.yml was found, but could not be parsed with ERB. \n#{$!.inspect}")
  end

  begin
    jetty_yml = YAML::load(jetty_erb)
  rescue
    raise("jetty.yml was found, but could not be parsed.\n")
  end

  if jetty_yml.nil? || !jetty_yml.is_a?(Hash)
    raise("jetty.yml was found, but was blank or malformed.\n")
  end

  config = jetty_yml.with_indifferent_access
  config[config_name] || config['default'.freeze]
end

.loggerObject

If ::Rails.logger is defined and is not nil, it will be returned. If no logger has been defined, a new STDOUT Logger will be created.



370
371
372
# File 'lib/jettywrapper.rb', line 370

def logger
  @@logger ||= defined?(::Rails) && Rails.logger ? ::Rails.logger : ::Logger.new(STDOUT)
end

.logger=(logger) ⇒ Object



364
365
366
# File 'lib/jettywrapper.rb', line 364

def logger=(logger)
  @@logger = logger
end

.pid(params) ⇒ Fixnum

Return the pid of the specified jetty, or return nil if it isn’t running

Examples:

Jettywrapper.pid(:jetty_home => '/path/to/jetty')

Parameters:

  • params: (Hash)

    :jetty_home is required.

Returns:

  • (Fixnum)

    or [nil]



295
296
297
298
299
300
# File 'lib/jettywrapper.rb', line 295

def pid(params)
  Jettywrapper.configure(params)
  pid = Jettywrapper.instance.pid
  return nil unless pid
  pid
end

.reset_configObject



131
132
133
134
135
136
# File 'lib/jettywrapper.rb', line 131

def reset_config
  @app_root = nil
  @env = nil
  @url = nil
  @hydra_jetty_version = nil
end

.start(params) ⇒ Object

Convenience method for configuring and starting jetty with one command

Examples:

Jettywrapper.start(:jetty_home => '/path/to/jetty', :jetty_port => '8983')

Parameters:

  • params: (Hash)

    The configuration to use for starting jetty



258
259
260
261
262
263
# File 'lib/jettywrapper.rb', line 258

def start(params)
  unzip unless File.exists? jetty_dir
  Jettywrapper.configure(params)
  Jettywrapper.instance.start
  return Jettywrapper.instance
end

.stop(params) ⇒ Jettywrapper.instance

Convenience method for configuring and starting jetty with one command. Note that for stopping, only the :jetty_home value is required (including other values won’t hurt anything, though).

Examples:

Jettywrapper.stop_with_params(:jetty_home => '/path/to/jetty')

Parameters:

  • params: (Hash)

    The jetty_home to use for stopping jetty

Returns:



272
273
274
275
276
# File 'lib/jettywrapper.rb', line 272

def stop(params)
   Jettywrapper.configure(params)
   Jettywrapper.instance.stop
   return Jettywrapper.instance
end

.unzipObject



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/jettywrapper.rb', line 91

def unzip
  download unless File.exists? zip_file
  logger.info "Unpacking #{zip_file}..."
  tmp_save_dir = File.join tmp_dir, 'jetty_generator'
  begin
    Zip::File.open(zip_file) do |zip_file|
      # Handle entries one by one
      zip_file.each do |entry|
        dest_file = File.join(tmp_save_dir,entry.name)
        FileUtils.remove_entry(dest_file,true)
        entry.extract(dest_file)
      end
    end
  rescue Exception => e
    abort "Unable to unzip #{zip_file} into tmp_save_dir/ #{e.message}"
  end

  # Remove the old jetty directory if it exists
  FileUtils.remove_dir(jetty_dir,true)

  # Move the expanded zip file into the final destination.
  expanded_dir = expanded_zip_dir(tmp_save_dir)
  begin
    FileUtils.mv(expanded_dir, jetty_dir)
  rescue Exception => e
    abort "Unable to move #{expanded_dir} into #{jetty_dir}/ #{e.message}"
  end
end

.wrap(params) ⇒ Object

Wrap the tests. Startup jetty, yield to the test task, capture any errors, shutdown jetty, and return the error.

Examples:

Using this method in a rake task

require 'jettywrapper'
desc "Spin up jetty and run tests against it"
task :newtest do
  jetty_params = {
    :jetty_home => "/path/to/jetty",
    :quiet => false,
    :jetty_port => 8983,
    :startup_wait => 30,
    :jetty_opts => "/etc/jetty.xml"
  }
  error = Jettywrapper.wrap(jetty_params) do
    Rake::Task["rake:spec"].invoke
    Rake::Task["rake:cucumber"].invoke
  end
  raise "test failures: #{error}" if error
end


234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/jettywrapper.rb', line 234

def wrap(params)
  error = false
  jetty_server = self.configure(params)

  begin
    jetty_server.start
    yield
  rescue
    error = $!
    logger.error "*** Error starting jetty: #{error}"
  ensure
    # puts "stopping jetty server"
    jetty_server.stop
  end

  raise error if error

  return error
end

.zip_fileObject



68
69
70
# File 'lib/jettywrapper.rb', line 68

def zip_file
  ENV['JETTY_ZIP'] || File.join(tmp_dir, url.split('/').last)
end

Instance Method Details

#check_java_version!Object



390
391
392
393
394
# File 'lib/jettywrapper.rb', line 390

def check_java_version!
  if java_version
    @checked_java_version ||= Jettywrapper.check_java_version!(java_command, java_version)
  end
end

#configure(params) ⇒ Object



37
38
39
40
41
42
43
44
45
46
47
# File 'lib/jettywrapper.rb', line 37

def configure params
  self.quiet        = params[:quiet].nil? ? true : params[:quiet]
  self.jetty_home   = params[:jetty_home  ] || File.expand_path(File.join(self.base_path, 'jetty'))
  self.solr_home    = params[:solr_home   ] || File.join( self.jetty_home, "solr")
  self.port         = params[:jetty_port  ] || 8888
  self.startup_wait = params[:startup_wait] || 5
  self.java_opts    = params[:java_opts   ] || []
  self.java_command = params[:java_command] || default_java_command
  self.java_version = params[:java_version]
  self.jetty_opts   = params[:jetty_opts  ] || []
end

#java_variablesObject



385
386
387
388
# File 'lib/jettywrapper.rb', line 385

def java_variables
  ["-Djetty.port=#{@port}",
   "-Dsolr.solr.home=#{Shellwords.escape(@solr_home)}"]
end

#jetty_commandObject

What command is being run to invoke jetty?



381
382
383
# File 'lib/jettywrapper.rb', line 381

def jetty_command
  [java_command, java_variables, java_opts, "-jar", "start.jar", jetty_opts].flatten
end

#jetty_home_to_pid_file(jetty_home) ⇒ String

Take the @jetty_home value and transform it into a legal filename

Examples:

/usr/local/jetty1 => _usr_local_jetty1.pid

Returns:

  • (String)

    the name of the pid_file



510
511
512
513
514
515
516
# File 'lib/jettywrapper.rb', line 510

def jetty_home_to_pid_file(jetty_home)
  begin
    jetty_home.gsub(/\//,'_') << "_#{self.class.env}" << ".pid"
  rescue Exception => e
    raise "Couldn't make a pid file for jetty_home value #{jetty_home}\n  Caused by: #{e}"
  end
end

#loggerObject

end of class << self



376
377
378
# File 'lib/jettywrapper.rb', line 376

def logger
  self.class.logger
end

#pidObject

the process id of the currently running jetty instance



530
531
532
# File 'lib/jettywrapper.rb', line 530

def pid
  File.open( pid_path ) { |f| return f.gets.to_i } if File.exist?(pid_path)
end

#pid_dirObject

The directory where the pid_file will be written



519
520
521
# File 'lib/jettywrapper.rb', line 519

def pid_dir
  File.expand_path(File.join(base_path,'tmp','pids'))
end

#pid_fileObject

The file where the process ID will be written



502
503
504
# File 'lib/jettywrapper.rb', line 502

def pid_file
  jetty_home_to_pid_file(@jetty_home)
end

#pid_file?Boolean

Check to see if there is a pid file already

Returns:

  • (Boolean)

    true if the file exists, otherwise false



525
526
527
# File 'lib/jettywrapper.rb', line 525

def pid_file?
  File.exist?(pid_path)
end

#pid_pathObject

The fully qualified path to the pid_file



496
497
498
499
# File 'lib/jettywrapper.rb', line 496

def pid_path
  #need to memoize this, becasuse the base path could be relative and the cwd can change in the yield block of wrap
  @path ||= File.join(pid_dir, pid_file)
end

#processObject



453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
# File 'lib/jettywrapper.rb', line 453

def process
  @process ||= begin
     process = ChildProcess.build(*jetty_command)
     if self.quiet
       process.io.stderr = File.open(File.expand_path("jettywrapper.log"), "w+")
       process.io.stdout = process.io.stderr
       logger.warn "Logging jettywrapper stdout to #{File.expand_path(process.io.stderr.path)}"
     else
       process.io.inherit!
     end
     process.detach = true

     process
   end
end

#reset_process!Object



469
470
471
# File 'lib/jettywrapper.rb', line 469

def reset_process!
  @process = nil
end

#startObject

Start the jetty server. Check the pid file to see if it is running already, and stop it if so. After you start jetty, write the PID to a file. This is the instance start method. It must be called on Jettywrapper.instance You’re probably better off using Jettywrapper.start(:jetty_home => “/path/to/jetty”)



404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
# File 'lib/jettywrapper.rb', line 404

def start
  logger.debug "Starting jetty with these values: "
  logger.debug "jetty_home: #{@jetty_home}"
  logger.debug "jetty_command: #{jetty_command.join(' ')}"

  check_java_version!

  # Check to see if we can start.
  # 1. If there is a pid, check to see if it is really running
  # 2. Check to see if anything is blocking the port we want to use
  if pid
    if Jettywrapper.is_pid_running?(pid)
      raise("Server is already running with PID #{pid}")
    else
      logger.warn "Removing stale PID file at #{pid_path}"
      File.delete(pid_path)
    end
  end
  if Jettywrapper.is_port_in_use?(self.port)
    raise("Port #{self.port} is already in use.")
  end
  benchmark "Started jetty" do
    Dir.chdir(@jetty_home) do
      process.start
    end
    FileUtils.makedirs(pid_dir) unless File.directory?(pid_dir)
    begin
      f = File.new(pid_path,  "w")
    rescue Errno::ENOENT, Errno::EACCES
      f = File.new(File.join(base_path,'tmp',pid_file),"w")
    end
    f.puts "#{process.pid}"
    f.close
    logger.debug "Wrote pid file to #{pid_path} with value #{process.pid}"
    startup_wait!
  end
end

#startup_wait!Object

Wait for the jetty server to start and begin listening for requests



443
444
445
446
447
448
449
450
451
# File 'lib/jettywrapper.rb', line 443

def startup_wait!
  begin
  Timeout::timeout(startup_wait) do
    sleep 1 until (Jettywrapper.is_port_in_use? self.port)
  end
  rescue Timeout::Error
    logger.warn "Waited #{startup_wait} seconds for jetty to start, but it is not yet listening on port #{self.port}. Continuing anyway."
  end
end

#stopObject

Instance stop method. Must be called on Jettywrapper.instance You’re probably better off using Jettywrapper.stop(:jetty_home => “/path/to/jetty”)



479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
# File 'lib/jettywrapper.rb', line 479

def stop
  logger.debug "Instance stop method called for pid '#{pid}'"
  if pid
    if @process
      @process.stop
    else
      Process.kill("KILL", pid) rescue nil
    end

    begin
      File.delete(pid_path)
    rescue
    end
  end
end