@@ -104,6 +104,8 @@ def it_should_rewind_body
104104
105105 before :each do
106106 @app_factory = DefaultRackApplicationFactory . new
107+ reset_booter
108+ reset_servlet_context_global
107109 end
108110
109111 it "should receive a rackup script via the 'rackup' parameter" do
@@ -161,11 +163,6 @@ def it_should_rewind_body
161163 expect ( input_stream . getMaximumBufferSize ) . to eq 420
162164 end
163165
164- before do
165- reset_booter
166- JRuby ::Rack . context = $servlet_context = nil
167- end
168-
169166 it "should init and create application object without a rackup script" do
170167 $servlet_context = @servlet_context
171168 # NOTE: a workaround to be able to mock it :
@@ -303,8 +300,8 @@ def newRuntime()
303300
304301 it "creates a new Ruby runtime with the jruby-rack environment pre-loaded" do
305302 @runtime = app_factory . newRuntime
306- should_not_eval_as_nil "defined?(::Rack)"
307- should_not_eval_as_nil "defined?(::Rack::Handler::Servlet)"
303+ should_eval_as_not_nil "defined?(::Rack)"
304+ should_eval_as_not_nil "defined?(::Rack::Handler::Servlet)"
308305 should_eval_as_nil "defined?(Rack::Handler::Bogus)"
309306 end
310307
@@ -333,14 +330,14 @@ def newRuntime()
333330 app_factory . checkAndSetRackVersion ( @runtime )
334331 @runtime . evalScriptlet "require 'rack'"
335332
336- should_not_eval_as_nil "defined?(Bundler)"
333+ should_eval_as_not_nil "defined?(Bundler)"
337334 should_eval_as_eql_to "Rack.release" , '2.2.0'
338335 should_eval_as_eql_to "Gem.loaded_specs['rack'].version.to_s" , '2.2.0'
339336 end
340337
341338 it "initializes the $servlet_context global variable" do
342339 @runtime = app_factory . new_runtime
343- should_not_eval_as_nil "defined?($servlet_context)"
340+ expect ( @runtime . evalScriptlet ( "defined?($servlet_context)" ) ) . to be_truthy
344341 end
345342
346343 it "clears environment variables if the configuration ignores the environment" do
@@ -516,7 +513,7 @@ def reset_config
516513
517514describe org . jruby . rack . rails . RailsRackApplicationFactory do
518515
519- java_import org . jruby . rack . rails . RailsRackApplicationFactory
516+ require ' jruby/ rack/rails_booter'
520517
521518 before :each do
522519 @app_factory = RailsRackApplicationFactory . new
@@ -557,8 +554,20 @@ def createRackServletWrapper(runtime, rackup, filename)
557554
558555describe org . jruby . rack . PoolingRackApplicationFactory do
559556
557+ # Workaround rspec mocks/proxies not being thread-safe which causes occasional failures
558+ class Synchronized
559+ def initialize ( obj )
560+ @delegate = obj
561+ @lock = Mutex . new
562+ end
563+
564+ def method_missing ( name , *args , &block )
565+ @lock . synchronize { @delegate . send ( name , *args , &block ) }
566+ end
567+ end
568+
560569 before :each do
561- @factory = double "factory"
570+ @factory = Synchronized . new ( double ( "factory" ) . as_null_object )
562571 @pooling_factory = org . jruby . rack . PoolingRackApplicationFactory . new @factory
563572 @pooling_factory . context = @rack_context
564573 end
@@ -608,7 +617,7 @@ def createRackServletWrapper(runtime, rackup, filename)
608617 it "creates applications during initialization according to the jruby.min.runtimes context parameter" do
609618 allow ( @factory ) . to receive ( :init )
610619 allow ( @factory ) . to receive ( :newApplication ) do
611- app = double "app"
620+ app = Synchronized . new ( double ( "app" ) . as_null_object )
612621 expect ( app ) . to receive ( :init )
613622 app
614623 end
@@ -641,7 +650,7 @@ def createRackServletWrapper(runtime, rackup, filename)
641650 it "forces the maximum size to be greater or equal to the initial size" do
642651 allow ( @factory ) . to receive ( :init )
643652 allow ( @factory ) . to receive ( :newApplication ) do
644- app = double "app"
653+ app = Synchronized . new ( double ( "app" ) . as_null_object )
645654 expect ( app ) . to receive ( :init )
646655 app
647656 end
@@ -655,17 +664,17 @@ def createRackServletWrapper(runtime, rackup, filename)
655664 end
656665
657666 it "retrieves the error application from the delegate factory" do
658- app = double ( "app" )
667+ app = double "app"
659668 expect ( @factory ) . to receive ( :getErrorApplication ) . and_return app
660669 expect ( @pooling_factory . getErrorApplication ) . to eq app
661670 end
662671
663672 it "waits till initial runtimes get initialized (with wait set to true)" do
664673 allow ( @factory ) . to receive ( :init )
665674 allow ( @factory ) . to receive ( :newApplication ) do
666- app = double "app"
675+ app = Synchronized . new ( double ( "app" ) . as_null_object )
667676 allow ( app ) . to receive ( :init ) do
668- sleep ( 0.10 )
677+ sleep ( 0.05 )
669678 end
670679 app
671680 end
@@ -679,15 +688,16 @@ def createRackServletWrapper(runtime, rackup, filename)
679688
680689 it "throws an exception from getApplication when an app failed to initialize " +
681690 "(even when only a single application initialization fails)" do
691+ app_init_secs = 0.05
682692 allow ( @factory ) . to receive ( :init )
683693 app_count = java . util . concurrent . atomic . AtomicInteger . new ( 0 )
684694 allow ( @factory ) . to receive ( :newApplication ) do
685- app = double "app"
695+ app = Synchronized . new ( double ( "app" ) . as_null_object )
686696 allow ( app ) . to receive ( :init ) do
687697 if app_count . addAndGet ( 1 ) == 2
688698 raise org . jruby . rack . RackInitializationException . new ( 'failed app init' )
689699 end
690- sleep ( 0.05 )
700+ sleep ( app_init_secs )
691701 end
692702 app
693703 end
@@ -701,7 +711,7 @@ def createRackServletWrapper(runtime, rackup, filename)
701711 rescue org . jruby . rack . RackInitializationException
702712 # ignore - sometimes initialization happens fast enough that the init error is thrown already
703713 end
704- sleep ( 0.20 )
714+ sleep ( num_runtimes * app_init_secs + 0.07 ) # sleep with a buffer
705715
706716 failed = 0
707717 num_runtimes . times do
@@ -717,28 +727,29 @@ def createRackServletWrapper(runtime, rackup, filename)
717727 end
718728
719729 it "wait until pool is filled when invoking getApplication (with wait set to false)" do
730+ app_init_secs = 0.2
720731 allow ( @factory ) . to receive ( :init )
721732 allow ( @factory ) . to receive ( :newApplication ) do
722- app = double "app"
723- allow ( app ) . to receive ( :init ) { sleep ( 0.2 ) }
733+ app = Synchronized . new ( double ( "app" ) . as_null_object )
734+ allow ( app ) . to receive ( :init ) { sleep ( app_init_secs ) }
724735 app
725736 end
726737 allow ( @rack_config ) . to receive ( :getBooleanProperty ) . with ( "jruby.runtime.init.wait" ) . and_return false
727738 expect ( @rack_config ) . to receive ( :getInitialRuntimes ) . and_return 3
728739 expect ( @rack_config ) . to receive ( :getMaximumRuntimes ) . and_return 4
729740
730741 @pooling_factory . init ( @rack_context )
731- millis = java . lang . System . currentTimeMillis
742+ start = java . lang . System . currentTimeMillis
732743 expect ( @pooling_factory . getApplication ) . not_to be nil
733- millis = java . lang . System . currentTimeMillis - millis
734- expect ( millis ) . to be >= 150 # getApplication waited ~ 0.2 secs
744+ expect ( java . lang . System . currentTimeMillis - start ) . to be_within ( 70 ) . of ( app_init_secs * 1000 ) # getApplication waited ~ sleep time
735745 end
736746
737747 it "waits acquire timeout till an application is available from the pool (than raises)" do
748+ app_init_secs = 0.2
738749 allow ( @factory ) . to receive ( :init )
739750 expect ( @factory ) . to receive ( :newApplication ) . twice do
740- app = double "app"
741- expect ( app ) . to receive ( :init ) { sleep ( 0.2 ) }
751+ app = Synchronized . new ( double ( "app" ) . as_null_object )
752+ expect ( app ) . to receive ( :init ) { sleep ( app_init_secs ) }
742753 app
743754 end
744755 allow ( @rack_config ) . to receive ( :getBooleanProperty ) . with ( "jruby.runtime.init.wait" ) . and_return false
@@ -747,86 +758,90 @@ def createRackServletWrapper(runtime, rackup, filename)
747758
748759 @pooling_factory . init ( @rack_context )
749760 @pooling_factory . acquire_timeout = 1 . to_java # second
750- millis = java . lang . System . currentTimeMillis
761+ start = java . lang . System . currentTimeMillis
751762 expect ( @pooling_factory . getApplication ) . not_to be nil
752- millis = java . lang . System . currentTimeMillis - millis
753- expect ( millis ) . to be >= 150 # getApplication waited ~ 0.2 secs
763+ expect ( java . lang . System . currentTimeMillis - start ) . to be_within ( 70 ) . of ( app_init_secs * 1000 )
754764
755765 app2 = @pooling_factory . getApplication # now the pool is empty
756-
757- @pooling_factory . acquire_timeout = 0.1 . to_java # second
758- millis = java . lang . System . currentTimeMillis
766+ timeout_secs = 0.1
767+ @pooling_factory . acquire_timeout = ( timeout_secs ) . to_java
768+ start = java . lang . System . currentTimeMillis
759769 expect { @pooling_factory . getApplication } . to raise_error ( org . jruby . rack . AcquireTimeoutException )
760- millis = java . lang . System . currentTimeMillis - millis
761- expect ( millis ) . to be >= 90 # waited about ~ 0.1 secs
770+ expect ( java . lang . System . currentTimeMillis - start ) . to be_within ( 20 ) . of ( timeout_secs * 1000 )
762771
763772 @pooling_factory . finishedWithApplication ( app2 ) # gets back to the pool
764773 expect ( @pooling_factory . getApplication ) . to eq app2
765774 end
766775
767776 it "gets and initializes new applications until maximum allows to create more" do
777+ app_init_secs = 0.1
768778 allow ( @factory ) . to receive ( :init )
769779 expect ( @factory ) . to receive ( :newApplication ) . twice do
770- app = double "app (new)"
771- expect ( app ) . to receive ( :init ) { sleep ( 0.1 ) }
780+ app = Synchronized . new ( double ( "app (new)" ) . as_null_object )
781+ expect ( app ) . to receive ( :init ) { sleep ( app_init_secs ) }
772782 app
773783 end
774784 allow ( @rack_config ) . to receive ( :getBooleanProperty ) . with ( "jruby.runtime.init.wait" ) . and_return false
775785 allow ( @rack_config ) . to receive ( :getInitialRuntimes ) . and_return 2
776786 allow ( @rack_config ) . to receive ( :getMaximumRuntimes ) . and_return 4
777787
788+ timeout_secs = 0.1
778789 @pooling_factory . init ( @rack_context )
779- @pooling_factory . acquire_timeout = 0.10 . to_java # second
790+ @pooling_factory . acquire_timeout = ( timeout_secs ) . to_java # second
780791
781792 2 . times { expect ( @pooling_factory . getApplication ) . not_to be nil }
782793
794+ app_get_secs = 0.15
783795 expect ( @factory ) . to receive ( :getApplication ) . twice do
784- app = double "app (get)" ; sleep ( 0.15 ) ; app
796+ app = Synchronized . new ( double ( "app (get)" ) . as_null_object )
797+ sleep ( app_get_secs )
798+ app
785799 end
786800
787- millis = java . lang . System . currentTimeMillis
801+ start = java . lang . System . currentTimeMillis
788802 2 . times { expect ( @pooling_factory . getApplication ) . not_to be nil }
789- millis = java . lang . System . currentTimeMillis - millis
790- expect ( millis ) . to be >= 300 # waited about 2 x 0.15 secs
803+ expect ( java . lang . System . currentTimeMillis - start ) . to be_within ( 70 ) . of ( 2 * app_get_secs * 1000 )
791804
792- millis = java . lang . System . currentTimeMillis
805+ start = java . lang . System . currentTimeMillis
793806 expect {
794807 @pooling_factory . getApplication
795808 } . to raise_error ( org . jruby . rack . AcquireTimeoutException )
796- millis = java . lang . System . currentTimeMillis - millis
797- expect ( millis ) . to be >= 90 # waited about ~ 0.10 secs
809+ expect ( java . lang . System . currentTimeMillis - start ) . to be_within ( 20 ) . of ( timeout_secs * 1000 )
798810 end
799811
800- it "initializes initial runtimes in paralel (with wait set to false)" do
812+ it "initializes initial runtimes in parallel (with wait set to false)" do
813+ app_init_secs = 0.15
801814 allow ( @factory ) . to receive ( :init )
802815 allow ( @factory ) . to receive ( :newApplication ) do
803- app = double "app"
804- allow ( app ) . to receive ( :init ) do
805- sleep ( 0.15 )
806- end
816+ app = Synchronized . new ( double ( "app" ) . as_null_object )
817+ allow ( app ) . to receive ( :init ) { sleep ( app_init_secs ) }
807818 app
808819 end
820+
821+ init_threads = 4
822+ init_runtimes = 6
809823 allow ( @rack_config ) . to receive ( :getBooleanProperty ) . with ( "jruby.runtime.init.wait" ) . and_return false
810- allow ( @rack_config ) . to receive ( :getInitialRuntimes ) . and_return 6
824+ allow ( @rack_config ) . to receive ( :getRuntimeInitThreads ) . and_return init_threads
825+ allow ( @rack_config ) . to receive ( :getInitialRuntimes ) . and_return init_runtimes
811826 allow ( @rack_config ) . to receive ( :getMaximumRuntimes ) . and_return 8
812827
828+ expected_initial_init_time = 1.1 * ( init_runtimes . to_f / init_threads . to_f ) . ceil * app_init_secs # 10% margin
813829 @pooling_factory . init ( @rack_context )
814- sleep ( 0.10 )
815- expect ( @pooling_factory . getApplicationPool . size ) . to be < 6
816- sleep ( 0.9 )
817- expect ( @pooling_factory . getApplicationPool . size ) . to be >= 6
830+ sleep ( app_init_secs ) # wait for at some (but not possibly all) to finish
831+ expect ( @pooling_factory . getApplicationPool . size ) . to be < init_runtimes
832+ sleep ( expected_initial_init_time - app_init_secs ) # remaining time
833+ expect ( @pooling_factory . getApplicationPool . size ) . to be >= init_runtimes
818834
819835 expect ( @pooling_factory . getManagedApplications ) . to_not be_empty
820- expect ( @pooling_factory . getManagedApplications . size ) . to eql 6
836+ expect ( @pooling_factory . getManagedApplications . size ) . to eql init_runtimes
821837 end
822838
823839 it "throws from init when application initialization in thread failed" do
840+ app_init_secs = 0.05
824841 allow ( @factory ) . to receive ( :init )
825842 allow ( @factory ) . to receive ( :newApplication ) do
826- app = double "app"
827- allow ( app ) . to receive ( :init ) do
828- sleep ( 0.05 ) ; raise "app.init raising"
829- end
843+ app = Synchronized . new ( double ( "app" ) . as_null_object )
844+ allow ( app ) . to receive ( :init ) { sleep ( app_init_secs ) ; raise "app.init raising" }
830845 app
831846 end
832847 allow ( @rack_config ) . to receive ( :getInitialRuntimes ) . and_return 2
@@ -845,9 +860,6 @@ def createRackServletWrapper(runtime, rackup, filename)
845860
846861 expect { @pooling_factory . init ( @rack_context ) } . to raise_error org . jruby . rack . RackInitializationException
847862 expect ( raise_error_logged ) . to eql 1 # logs same init exception once
848-
849- # NOTE: seems it's not such a good idea to return empty on init error
850- # expect(@pooling_factory.getManagedApplications).to be_empty
851863 end
852864
853865end
0 commit comments