Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .bundle/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
BUNDLE_VERSION: "system"
2 changes: 2 additions & 0 deletions examples/camping/.bundle/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
BUNDLE_VERSION: "system"
2 changes: 2 additions & 0 deletions examples/rails8/.bundle/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
BUNDLE_VERSION: "system"
2 changes: 2 additions & 0 deletions examples/sinatra/.bundle/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
BUNDLE_VERSION: "system"
2 changes: 1 addition & 1 deletion gemfiles/.bundle/config
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
BUNDLE_RETRY: "1"
BUNDLE_VERSION: "system"
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@ public void destroy() {
public RackApplication getApplication() throws RackException {
final RuntimeException error = getInitError();
if ( error != null ) {
log(DEBUG, "due a previous initialization failure application instance can not be returned");
throw error; // this is better - we shall never return null here ...
log(DEBUG, "due to a previous initialization failure application instance can not be returned");
throw error;
}
return getApplicationImpl();
}
Expand Down
20 changes: 10 additions & 10 deletions src/spec/ruby/jruby/rack/booter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -294,17 +294,17 @@
@runtime.evalScriptlet("load 'jruby/rack/boot/rack.rb'")

# booter got setup :
should_not_eval_as_nil "defined?(JRuby::Rack.booter)"
should_not_eval_as_nil "JRuby::Rack.booter"
should_eval_as_not_nil "defined?(JRuby::Rack.booter)"
should_eval_as_not_nil "JRuby::Rack.booter"
should_eval_as_eql_to "JRuby::Rack.booter.class.name", 'JRuby::Rack::Booter'

# Booter.boot! run :
should_not_eval_as_nil "ENV['RACK_ENV']"
should_eval_as_not_nil "ENV['RACK_ENV']"
# rack got required :
should_not_eval_as_nil "defined?(Rack::RELEASE)"
should_not_eval_as_nil "defined?(Rack.release)"
should_eval_as_not_nil "defined?(Rack::RELEASE)"
should_eval_as_not_nil "defined?(Rack.release)"
# check if it got loaded correctly :
should_not_eval_as_nil "Rack::Request.new({}) rescue nil"
should_eval_as_not_nil "Rack::Request.new({}) rescue nil"
end

end
Expand Down Expand Up @@ -341,13 +341,13 @@
@runtime.evalScriptlet("load 'jruby/rack/boot/rails.rb'")

# booter got setup :
should_not_eval_as_nil "defined?(JRuby::Rack.booter)"
should_not_eval_as_nil "JRuby::Rack.booter"
should_eval_as_not_nil "defined?(JRuby::Rack.booter)"
should_eval_as_not_nil "JRuby::Rack.booter"
should_eval_as_eql_to "JRuby::Rack.booter.class.name", 'JRuby::Rack::RailsBooter'

# Booter.boot! run :
should_not_eval_as_nil "ENV['RACK_ENV']"
should_not_eval_as_nil "ENV['RAILS_ENV']"
should_eval_as_not_nil "ENV['RACK_ENV']"
should_eval_as_not_nil "ENV['RAILS_ENV']"

# rack not yet required (let bundler decide which rack version to load) :
should_eval_as_nil "defined?(Rack::RELEASE)"
Expand Down
4 changes: 0 additions & 4 deletions src/spec/ruby/jruby/rack/rails_booter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,6 @@
end
end

after :all do
$servlet_context = nil
end

let(:railtie_class) { Class.new(Rails::Railtie) }

it "should have loaded the railtie" do
Expand Down
136 changes: 74 additions & 62 deletions src/spec/ruby/rack/application_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ def it_should_rewind_body

before :each do
@app_factory = DefaultRackApplicationFactory.new
reset_booter
reset_servlet_context_global
end

it "should receive a rackup script via the 'rackup' parameter" do
Expand Down Expand Up @@ -161,11 +163,6 @@ def it_should_rewind_body
expect(input_stream.getMaximumBufferSize).to eq 420
end

before do
reset_booter
JRuby::Rack.context = $servlet_context = nil
end

it "should init and create application object without a rackup script" do
$servlet_context = @servlet_context
# NOTE: a workaround to be able to mock it :
Expand Down Expand Up @@ -303,8 +300,8 @@ def newRuntime()

it "creates a new Ruby runtime with the jruby-rack environment pre-loaded" do
@runtime = app_factory.newRuntime
should_not_eval_as_nil "defined?(::Rack)"
should_not_eval_as_nil "defined?(::Rack::Handler::Servlet)"
should_eval_as_not_nil "defined?(::Rack)"
should_eval_as_not_nil "defined?(::Rack::Handler::Servlet)"
should_eval_as_nil "defined?(Rack::Handler::Bogus)"
end

Expand Down Expand Up @@ -333,14 +330,14 @@ def newRuntime()
app_factory.checkAndSetRackVersion(@runtime)
@runtime.evalScriptlet "require 'rack'"

should_not_eval_as_nil "defined?(Bundler)"
should_eval_as_not_nil "defined?(Bundler)"
should_eval_as_eql_to "Rack.release", '2.2.0'
should_eval_as_eql_to "Gem.loaded_specs['rack'].version.to_s", '2.2.0'
end

it "initializes the $servlet_context global variable" do
@runtime = app_factory.new_runtime
should_not_eval_as_nil "defined?($servlet_context)"
expect(@runtime.evalScriptlet("defined?($servlet_context)")).to be_truthy
end

it "clears environment variables if the configuration ignores the environment" do
Expand Down Expand Up @@ -516,7 +513,7 @@ def reset_config

describe org.jruby.rack.rails.RailsRackApplicationFactory do

java_import org.jruby.rack.rails.RailsRackApplicationFactory
require 'jruby/rack/rails_booter'

before :each do
@app_factory = RailsRackApplicationFactory.new
Expand Down Expand Up @@ -557,8 +554,20 @@ def createRackServletWrapper(runtime, rackup, filename)

describe org.jruby.rack.PoolingRackApplicationFactory do

# Workaround rspec mocks/proxies not being thread-safe which causes occasional failures
class Synchronized
def initialize(obj)
@delegate = obj
@lock = Mutex.new
end

def method_missing(name, *args, &block)
@lock.synchronize { @delegate.send(name, *args, &block) }
end
end

before :each do
@factory = double "factory"
@factory = Synchronized.new(double("factory").as_null_object)
@pooling_factory = org.jruby.rack.PoolingRackApplicationFactory.new @factory
@pooling_factory.context = @rack_context
end
Expand Down Expand Up @@ -608,7 +617,7 @@ def createRackServletWrapper(runtime, rackup, filename)
it "creates applications during initialization according to the jruby.min.runtimes context parameter" do
allow(@factory).to receive(:init)
allow(@factory).to receive(:newApplication) do
app = double "app"
app = Synchronized.new(double("app").as_null_object)
expect(app).to receive(:init)
app
end
Expand Down Expand Up @@ -641,7 +650,7 @@ def createRackServletWrapper(runtime, rackup, filename)
it "forces the maximum size to be greater or equal to the initial size" do
allow(@factory).to receive(:init)
allow(@factory).to receive(:newApplication) do
app = double "app"
app = Synchronized.new(double("app").as_null_object)
expect(app).to receive(:init)
app
end
Expand All @@ -655,17 +664,17 @@ def createRackServletWrapper(runtime, rackup, filename)
end

it "retrieves the error application from the delegate factory" do
app = double("app")
app = double "app"
expect(@factory).to receive(:getErrorApplication).and_return app
expect(@pooling_factory.getErrorApplication).to eq app
end

it "waits till initial runtimes get initialized (with wait set to true)" do
allow(@factory).to receive(:init)
allow(@factory).to receive(:newApplication) do
app = double "app"
app = Synchronized.new(double("app").as_null_object)
allow(app).to receive(:init) do
sleep(0.10)
sleep(0.05)
end
app
end
Expand All @@ -679,15 +688,16 @@ def createRackServletWrapper(runtime, rackup, filename)

it "throws an exception from getApplication when an app failed to initialize " +
"(even when only a single application initialization fails)" do
app_init_secs = 0.05
allow(@factory).to receive(:init)
app_count = java.util.concurrent.atomic.AtomicInteger.new(0)
allow(@factory).to receive(:newApplication) do
app = double "app"
app = Synchronized.new(double("app").as_null_object)
allow(app).to receive(:init) do
if app_count.addAndGet(1) == 2
raise org.jruby.rack.RackInitializationException.new('failed app init')
end
sleep(0.05)
sleep(app_init_secs)
end
app
end
Expand All @@ -701,7 +711,7 @@ def createRackServletWrapper(runtime, rackup, filename)
rescue org.jruby.rack.RackInitializationException
# ignore - sometimes initialization happens fast enough that the init error is thrown already
end
sleep(0.20)
sleep(num_runtimes * app_init_secs + 0.07) # sleep with a buffer

failed = 0
num_runtimes.times do
Expand All @@ -717,28 +727,29 @@ def createRackServletWrapper(runtime, rackup, filename)
end

it "wait until pool is filled when invoking getApplication (with wait set to false)" do
app_init_secs = 0.2
allow(@factory).to receive(:init)
allow(@factory).to receive(:newApplication) do
app = double "app"
allow(app).to receive(:init) { sleep(0.2) }
app = Synchronized.new(double("app").as_null_object)
allow(app).to receive(:init) { sleep(app_init_secs) }
app
end
allow(@rack_config).to receive(:getBooleanProperty).with("jruby.runtime.init.wait").and_return false
expect(@rack_config).to receive(:getInitialRuntimes).and_return 3
expect(@rack_config).to receive(:getMaximumRuntimes).and_return 4

@pooling_factory.init(@rack_context)
millis = java.lang.System.currentTimeMillis
start = java.lang.System.currentTimeMillis
expect(@pooling_factory.getApplication).not_to be nil
millis = java.lang.System.currentTimeMillis - millis
expect(millis).to be >= 150 # getApplication waited ~ 0.2 secs
expect(java.lang.System.currentTimeMillis - start).to be_within(70).of(app_init_secs * 1000) # getApplication waited ~ sleep time
end

it "waits acquire timeout till an application is available from the pool (than raises)" do
app_init_secs = 0.2
allow(@factory).to receive(:init)
expect(@factory).to receive(:newApplication).twice do
app = double "app"
expect(app).to receive(:init) { sleep(0.2) }
app = Synchronized.new(double("app").as_null_object)
expect(app).to receive(:init) { sleep(app_init_secs) }
app
end
allow(@rack_config).to receive(:getBooleanProperty).with("jruby.runtime.init.wait").and_return false
Expand All @@ -747,86 +758,90 @@ def createRackServletWrapper(runtime, rackup, filename)

@pooling_factory.init(@rack_context)
@pooling_factory.acquire_timeout = 1.to_java # second
millis = java.lang.System.currentTimeMillis
start = java.lang.System.currentTimeMillis
expect(@pooling_factory.getApplication).not_to be nil
millis = java.lang.System.currentTimeMillis - millis
expect(millis).to be >= 150 # getApplication waited ~ 0.2 secs
expect(java.lang.System.currentTimeMillis - start).to be_within(70).of(app_init_secs * 1000)

app2 = @pooling_factory.getApplication # now the pool is empty

@pooling_factory.acquire_timeout = 0.1.to_java # second
millis = java.lang.System.currentTimeMillis
timeout_secs = 0.1
@pooling_factory.acquire_timeout = (timeout_secs).to_java
start = java.lang.System.currentTimeMillis
expect { @pooling_factory.getApplication }.to raise_error(org.jruby.rack.AcquireTimeoutException)
millis = java.lang.System.currentTimeMillis - millis
expect(millis).to be >= 90 # waited about ~ 0.1 secs
expect(java.lang.System.currentTimeMillis - start).to be_within(20).of(timeout_secs * 1000)

@pooling_factory.finishedWithApplication(app2) # gets back to the pool
expect(@pooling_factory.getApplication).to eq app2
end

it "gets and initializes new applications until maximum allows to create more" do
app_init_secs = 0.1
allow(@factory).to receive(:init)
expect(@factory).to receive(:newApplication).twice do
app = double "app (new)"
expect(app).to receive(:init) { sleep(0.1) }
app = Synchronized.new(double("app (new)").as_null_object)
expect(app).to receive(:init) { sleep(app_init_secs) }
app
end
allow(@rack_config).to receive(:getBooleanProperty).with("jruby.runtime.init.wait").and_return false
allow(@rack_config).to receive(:getInitialRuntimes).and_return 2
allow(@rack_config).to receive(:getMaximumRuntimes).and_return 4

timeout_secs = 0.1
@pooling_factory.init(@rack_context)
@pooling_factory.acquire_timeout = 0.10.to_java # second
@pooling_factory.acquire_timeout = (timeout_secs).to_java # second

2.times { expect(@pooling_factory.getApplication).not_to be nil }

app_get_secs = 0.15
expect(@factory).to receive(:getApplication).twice do
app = double "app (get)"; sleep(0.15); app
app = Synchronized.new(double("app (get)").as_null_object)
sleep(app_get_secs)
app
end

millis = java.lang.System.currentTimeMillis
start = java.lang.System.currentTimeMillis
2.times { expect(@pooling_factory.getApplication).not_to be nil }
millis = java.lang.System.currentTimeMillis - millis
expect(millis).to be >= 300 # waited about 2 x 0.15 secs
expect(java.lang.System.currentTimeMillis - start).to be_within(70).of(2 * app_get_secs * 1000)

millis = java.lang.System.currentTimeMillis
start = java.lang.System.currentTimeMillis
expect {
@pooling_factory.getApplication
}.to raise_error(org.jruby.rack.AcquireTimeoutException)
millis = java.lang.System.currentTimeMillis - millis
expect(millis).to be >= 90 # waited about ~ 0.10 secs
expect(java.lang.System.currentTimeMillis - start).to be_within(20).of(timeout_secs * 1000)
end

it "initializes initial runtimes in paralel (with wait set to false)" do
it "initializes initial runtimes in parallel (with wait set to false)" do
app_init_secs = 0.15
allow(@factory).to receive(:init)
allow(@factory).to receive(:newApplication) do
app = double "app"
allow(app).to receive(:init) do
sleep(0.15)
end
app = Synchronized.new(double("app").as_null_object)
allow(app).to receive(:init) { sleep(app_init_secs) }
app
end

init_threads = 4
init_runtimes = 6
allow(@rack_config).to receive(:getBooleanProperty).with("jruby.runtime.init.wait").and_return false
allow(@rack_config).to receive(:getInitialRuntimes).and_return 6
allow(@rack_config).to receive(:getRuntimeInitThreads).and_return init_threads
allow(@rack_config).to receive(:getInitialRuntimes).and_return init_runtimes
allow(@rack_config).to receive(:getMaximumRuntimes).and_return 8

expected_initial_init_time = 1.1 * (init_runtimes.to_f / init_threads.to_f).ceil * app_init_secs # 10% margin
@pooling_factory.init(@rack_context)
sleep(0.10)
expect(@pooling_factory.getApplicationPool.size).to be < 6
sleep(0.9)
expect(@pooling_factory.getApplicationPool.size).to be >= 6
sleep(app_init_secs) # wait for at some (but not possibly all) to finish
expect(@pooling_factory.getApplicationPool.size).to be < init_runtimes
sleep(expected_initial_init_time - app_init_secs) # remaining time
expect(@pooling_factory.getApplicationPool.size).to be >= init_runtimes

expect(@pooling_factory.getManagedApplications).to_not be_empty
expect(@pooling_factory.getManagedApplications.size).to eql 6
expect(@pooling_factory.getManagedApplications.size).to eql init_runtimes
end

it "throws from init when application initialization in thread failed" do
app_init_secs = 0.05
allow(@factory).to receive(:init)
allow(@factory).to receive(:newApplication) do
app = double "app"
allow(app).to receive(:init) do
sleep(0.05); raise "app.init raising"
end
app = Synchronized.new(double("app").as_null_object)
allow(app).to receive(:init) { sleep(app_init_secs); raise "app.init raising" }
app
end
allow(@rack_config).to receive(:getInitialRuntimes).and_return 2
Expand All @@ -845,9 +860,6 @@ def createRackServletWrapper(runtime, rackup, filename)

expect { @pooling_factory.init(@rack_context) }.to raise_error org.jruby.rack.RackInitializationException
expect(raise_error_logged).to eql 1 # logs same init exception once

# NOTE: seems it's not such a good idea to return empty on init error
# expect(@pooling_factory.getManagedApplications).to be_empty
end

end
Expand Down
Loading