Class: Jettywrapper

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

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(params = {}) ⇒ Jettywrapper

configure the singleton with some defaults



28
29
30
31
32
33
34
35
36
# File 'lib/jettywrapper.rb', line 28

def initialize(params = {})
  if defined?(Rails.root)
    @base_path = Rails.root
  else
    @base_path = "."
  end

  logger.debug 'Initializing jettywrapper'
end

Instance Attribute Details

#base_pathObject

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



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

def base_path
  @base_path
end

#java_optsObject

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



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

def java_opts
  @java_opts
end

#jetty_homeObject

Where is jetty located?



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

def jetty_home
  @jetty_home
end

#portObject

What port should jetty start on? Default is 8888



19
20
21
# File 'lib/jettywrapper.rb', line 19

def port
  @port
end

#quietObject

Keep quiet about jetty output?



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

def quiet
  @quiet
end

#solr_homeObject

Where is solr located? Default is jetty_home/solr



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

def solr_home
  @solr_home
end

#startup_waitObject

After jetty starts, how long to wait until starting the tests?



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

def startup_wait
  @startup_wait
end

Class Method Details

.configure(params = {}) ⇒ Object

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

Parameters:

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

    Required. Where is jetty located?

  • :jetty_port (Symbol)

    What port should jetty start on? Default is 8888

  • :startup_wait (Symbol)

    After jetty starts, how long to wait before running tests? If you don’t let jetty start all the way before running the tests, they’ll fail because they can’t reach jetty.

  • :solr_home (Symbol)

    Where is solr? Default is jetty_home/solr

  • :quiet (Symbol)

    Keep quiet about jetty output? Default is true.

  • :java_opts (Symbol)

    A list of options to pass to the jvm



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

def configure(params = {})
  hydra_server = self.instance
  hydra_server.reset_process!
  hydra_server.quiet = params[:quiet].nil? ? true : params[:quiet]
  if defined?(Rails.root)
   base_path = Rails.root
  elsif defined?(APP_ROOT)
   base_path = APP_ROOT
  else
   raise "You must set either Rails.root, APP_ROOT or pass :jetty_home as a parameter so I know where jetty is" unless params[:jetty_home]
  end
  hydra_server.jetty_home = params[:jetty_home] || File.expand_path(File.join(base_path, 'jetty'))
  hydra_server.solr_home = params[:solr_home]  || File.join( hydra_server.jetty_home, "solr")
  hydra_server.port = params[:jetty_port] || 8888
  hydra_server.startup_wait = params[:startup_wait] || 5
  hydra_server.java_opts = params[:java_opts] || []
  return hydra_server
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)


157
158
159
160
161
162
# File 'lib/jettywrapper.rb', line 157

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)


201
202
203
204
205
206
207
# File 'lib/jettywrapper.rb', line 201

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)


181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/jettywrapper.rb', line 181

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_configObject



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/jettywrapper.rb', line 42

def load_config
  if defined? Rails 
    config_name =  Rails.env 
    app_root = Rails.root
  else 
    config_name =  ENV['environment']
    app_root = ENV['APP_ROOT']
    app_root ||= '.'
  end
  filename = "#{app_root}/config/jetty.yml"
  begin
    file = YAML.load_file(filename)
  rescue Exception => e
    logger.warn "Didn't find expected jettywrapper config file at #{filename}, using default file instead."
    file ||= YAML.load_file(File.join(File.dirname(__FILE__),"../config/jetty.yml"))
    #raise "Unable to load: #{file}" unless file
  end
  config = file.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]



169
170
171
172
173
174
# File 'lib/jettywrapper.rb', line 169

def pid(params)
  Jettywrapper.configure(params)
  pid = Jettywrapper.instance.pid
  return nil unless pid
  pid
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



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

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:



146
147
148
149
150
# File 'lib/jettywrapper.rb', line 146

def stop(params)
   Jettywrapper.configure(params)
   Jettywrapper.instance.stop
   return Jettywrapper.instance
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
  }
  error = Jettywrapper.wrap(jetty_params) do   
    Rake::Task["rake:spec"].invoke 
    Rake::Task["rake:cucumber"].invoke 
  end 
  raise "test failures: #{error}" if error
end


110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/jettywrapper.rb', line 110

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

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

  return error
end

Instance Method Details

#java_variablesObject



217
218
219
220
# File 'lib/jettywrapper.rb', line 217

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

#jetty_commandObject

What command is being run to invoke jetty?



213
214
215
# File 'lib/jettywrapper.rb', line 213

def jetty_command
  ["java", java_variables, java_opts, "-jar", "start.jar"].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



321
322
323
324
325
326
327
328
# File 'lib/jettywrapper.rb', line 321

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



343
344
345
# File 'lib/jettywrapper.rb', line 343

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



331
332
333
# File 'lib/jettywrapper.rb', line 331

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

#pid_fileObject

The file where the process ID will be written



313
314
315
# File 'lib/jettywrapper.rb', line 313

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



337
338
339
340
# File 'lib/jettywrapper.rb', line 337

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

#pid_pathObject

The fully qualified path to the pid_file



307
308
309
310
# File 'lib/jettywrapper.rb', line 307

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



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

def process
  @process ||= begin
     process = ChildProcess.build(*jetty_command)
     if self.quiet
       process.io.stderr = File.open("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



280
281
282
# File 'lib/jettywrapper.rb', line 280

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


230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
# File 'lib/jettywrapper.rb', line 230

def start
  logger.debug "Starting jetty with these values: "
  logger.debug "jetty_home: #{@jetty_home}"
  logger.debug "solr_home: #{@solr_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
    if Jettywrapper.is_port_in_use?(@jetty_port)
      raise("Port #{self.jetty_port} is already in use.")
    end
  end
  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}"
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


289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
# File 'lib/jettywrapper.rb', line 289

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