Class: Jettywrapper

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

Overview

Jettywrapper is a Singleton class, so you can only create one jetty instance at a time.

Constant Summary collapse

VERSION =
"1.5.0"

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(params = {}) ⇒ Jettywrapper

configure the singleton with some defaults



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

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

Class Attribute Details

.hydra_jetty_versionObject



43
44
45
# File 'lib/jettywrapper.rb', line 43

def hydra_jetty_version
  @hydra_jetty_version ||= 'v7.0.0'
end

.tmp_dirObject



52
53
54
# File 'lib/jettywrapper.rb', line 52

def tmp_dir
  @tmp_dir ||= 'tmp'
end

.urlObject



47
48
49
50
# File 'lib/jettywrapper.rb', line 47

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.



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

def base_path
  @base_path
end

#java_optsObject

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



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

def java_opts
  @java_opts
end

#jetty_homeObject

Jetty’s home directory



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

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



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

def port
  @port
end

#quietObject

true (default) to reduce Jetty’s output



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

def quiet
  @quiet
end

#solr_homeObject

Solr’s home directory. Default is jetty_home/solr



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

def solr_home
  @solr_home
end

#startup_waitObject

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



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

def startup_wait
  @startup_wait
end

Class Method Details

.app_rootObject



105
106
107
108
109
110
# File 'lib/jettywrapper.rb', line 105

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

.cleanObject



94
95
96
97
# File 'lib/jettywrapper.rb', line 94

def clean
  system "rm -rf #{jetty_dir}"
  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



156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/jettywrapper.rb', line 156

def configure(params = {})
  jetty_server = self.instance
  jetty_server.reset_process!
  jetty_server.quiet = params[:quiet].nil? ? true : params[:quiet]

  jetty_server.jetty_home = params[:jetty_home] || File.expand_path(File.join(app_root, 'jetty'))
  jetty_server.solr_home = params[:solr_home]  || File.join( jetty_server.jetty_home, "solr")
  jetty_server.port = params[:jetty_port] || 8888
  jetty_server.startup_wait = params[:startup_wait] || 5
  jetty_server.java_opts = params[:java_opts] || []
  jetty_server.jetty_opts = params[:jetty_opts] || []
  return jetty_server
end

.download(url = nil) ⇒ Object



64
65
66
67
68
69
70
# File 'lib/jettywrapper.rb', line 64

def download(url = nil)
  self.url = url if url
  logger.info "Downloading jetty at #{self.url} ..."
  FileUtils.mkdir tmp_dir unless File.exists? tmp_dir
  system "curl -L #{self.url} -o #{zip_file}"
  abort "Unable to download jetty from #{self.url}" unless $?.success?
end

.expanded_zip_dir(tmp_save_dir) ⇒ Object



88
89
90
91
92
# File 'lib/jettywrapper.rb', line 88

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

.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)


238
239
240
241
242
243
# File 'lib/jettywrapper.rb', line 238

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)


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

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)


262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/jettywrapper.rb', line 262

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

.jetty_dirObject



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

def jetty_dir
  'jetty'
end

.load_configObject



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/jettywrapper.rb', line 112

def load_config
  if defined? Rails 
    config_name =  Rails.env 
  else 
    config_name =  ENV['environment']
  end

  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 Exception => e
    raise("jetty.yml was found, but could not be parsed with ERB. \n#{$!.inspect}")
  end

  begin
    @jetty_yml = YAML::load(@jetty_erb)
  rescue StandardError => e
    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]
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]



250
251
252
253
254
255
# File 'lib/jettywrapper.rb', line 250

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

.reset_configObject



99
100
101
102
103
# File 'lib/jettywrapper.rb', line 99

def reset_config
  @app_root = 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



214
215
216
217
218
# File 'lib/jettywrapper.rb', line 214

def start(params)
   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:



227
228
229
230
231
# File 'lib/jettywrapper.rb', line 227

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

.unzipObject



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/jettywrapper.rb', line 72

def unzip
  download unless File.exists? zip_file
  logger.info "Unpacking jetty..."
  tmp_save_dir = File.join tmp_dir, 'jetty_generator'
  system "unzip -d #{tmp_save_dir} -qo #{zip_file}"
  abort "Unable to unzip #{zip_file} into tmp_save_dir/" unless $?.success?

  # Remove the old jetty directory if it exists
  system "rm -r #{jetty_dir}" if File.directory?(jetty_dir)

  # Move the expanded zip file into the final destination.
  expanded_dir = expanded_zip_dir(tmp_save_dir)
  system "mv #{expanded_dir} #{jetty_dir}"
  abort "Unable to move #{expanded_dir} into #{jetty_dir}/" unless $?.success?
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


190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/jettywrapper.rb', line 190

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



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

def zip_file
  File.join tmp_dir, url.split('/').last
end

Instance Method Details

#java_variablesObject



298
299
300
301
# File 'lib/jettywrapper.rb', line 298

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

#jetty_commandObject

What command is being run to invoke jetty?



294
295
296
# File 'lib/jettywrapper.rb', line 294

def jetty_command
  ["java", 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



415
416
417
418
419
420
421
422
# File 'lib/jettywrapper.rb', line 415

def jetty_home_to_pid_file(jetty_home)
  begin
    jetty_home.gsub(/\//,'_') << ".pid"
  rescue
    raise "Couldn't make a pid file for jetty_home value #{jetty_home}"
    raise $!
  end
end

#pidObject

the process id of the currently running jetty instance



437
438
439
# File 'lib/jettywrapper.rb', line 437

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



425
426
427
# File 'lib/jettywrapper.rb', line 425

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

#pid_fileObject

The file where the process ID will be written



407
408
409
# File 'lib/jettywrapper.rb', line 407

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



431
432
433
434
# File 'lib/jettywrapper.rb', line 431

def pid_file?
   return true if File.exist?(pid_path)
   false
end

#pid_pathObject

The fully qualified path to the pid_file



401
402
403
404
# File 'lib/jettywrapper.rb', line 401

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



358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
# File 'lib/jettywrapper.rb', line 358

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



374
375
376
# File 'lib/jettywrapper.rb', line 374

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”)

Examples:

Jettywrapper.configure(params)
Jettywrapper.instance.start
return Jettywrapper.instance


311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
# File 'lib/jettywrapper.rb', line 311

def start
  logger.debug "Starting jetty with these values: "
  logger.debug "jetty_home: #{@jetty_home}"
  logger.debug "jetty_command: #{jetty_command.join(' ')}"
  
  # 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



348
349
350
351
352
353
354
355
356
# File 'lib/jettywrapper.rb', line 348

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”)

Examples:

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


383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
# File 'lib/jettywrapper.rb', line 383

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