@@ -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
@@ -534,7 +531,7 @@ def reset_config
534531
535532describe org . jruby . rack . rails . RailsRackApplicationFactory do
536533
537- java_import org . jruby . rack . rails . RailsRackApplicationFactory
534+ require ' jruby/ rack/rails_booter'
538535
539536 before :each do
540537 @app_factory = RailsRackApplicationFactory . new
@@ -575,8 +572,20 @@ def createRackServletWrapper(runtime, rackup, filename)
575572
576573describe org . jruby . rack . PoolingRackApplicationFactory do
577574
575+ # Workaround rspec mocks/proxies not being thread-safe which causes occasional failures
576+ class Synchronized
577+ def initialize ( obj )
578+ @delegate = obj
579+ @lock = Mutex . new
580+ end
581+
582+ def method_missing ( name , *args , &block )
583+ @lock . synchronize { @delegate . send ( name , *args , &block ) }
584+ end
585+ end
586+
578587 before :each do
579- @factory = double "factory"
588+ @factory = Synchronized . new ( double ( "factory" ) . as_null_object )
580589 @pooling_factory = org . jruby . rack . PoolingRackApplicationFactory . new @factory
581590 @pooling_factory . context = @rack_context
582591 end
@@ -626,7 +635,7 @@ def createRackServletWrapper(runtime, rackup, filename)
626635 it "creates applications during initialization according to the jruby.min.runtimes context parameter" do
627636 allow ( @factory ) . to receive ( :init )
628637 allow ( @factory ) . to receive ( :newApplication ) do
629- app = double "app"
638+ app = Synchronized . new ( double ( "app" ) . as_null_object )
630639 expect ( app ) . to receive ( :init )
631640 app
632641 end
@@ -659,7 +668,7 @@ def createRackServletWrapper(runtime, rackup, filename)
659668 it "forces the maximum size to be greater or equal to the initial size" do
660669 allow ( @factory ) . to receive ( :init )
661670 allow ( @factory ) . to receive ( :newApplication ) do
662- app = double "app"
671+ app = Synchronized . new ( double ( "app" ) . as_null_object )
663672 expect ( app ) . to receive ( :init )
664673 app
665674 end
@@ -673,17 +682,17 @@ def createRackServletWrapper(runtime, rackup, filename)
673682 end
674683
675684 it "retrieves the error application from the delegate factory" do
676- app = double ( "app" )
685+ app = double "app"
677686 expect ( @factory ) . to receive ( :getErrorApplication ) . and_return app
678687 expect ( @pooling_factory . getErrorApplication ) . to eq app
679688 end
680689
681690 it "waits till initial runtimes get initialized (with wait set to true)" do
682691 allow ( @factory ) . to receive ( :init )
683692 allow ( @factory ) . to receive ( :newApplication ) do
684- app = double "app"
693+ app = Synchronized . new ( double ( "app" ) . as_null_object )
685694 allow ( app ) . to receive ( :init ) do
686- sleep ( 0.10 )
695+ sleep ( 0.05 )
687696 end
688697 app
689698 end
@@ -697,15 +706,16 @@ def createRackServletWrapper(runtime, rackup, filename)
697706
698707 it "throws an exception from getApplication when an app failed to initialize " +
699708 "(even when only a single application initialization fails)" do
709+ app_init_secs = 0.05
700710 allow ( @factory ) . to receive ( :init )
701711 app_count = java . util . concurrent . atomic . AtomicInteger . new ( 0 )
702712 allow ( @factory ) . to receive ( :newApplication ) do
703- app = double "app"
713+ app = Synchronized . new ( double ( "app" ) . as_null_object )
704714 allow ( app ) . to receive ( :init ) do
705715 if app_count . addAndGet ( 1 ) == 2
706716 raise org . jruby . rack . RackInitializationException . new ( 'failed app init' )
707717 end
708- sleep ( 0.05 )
718+ sleep ( app_init_secs )
709719 end
710720 app
711721 end
@@ -719,7 +729,7 @@ def createRackServletWrapper(runtime, rackup, filename)
719729 rescue org . jruby . rack . RackInitializationException
720730 # ignore - sometimes initialization happens fast enough that the init error is thrown already
721731 end
722- sleep ( 0.20 )
732+ sleep ( num_runtimes * app_init_secs + 0.07 ) # sleep with a buffer
723733
724734 failed = 0
725735 num_runtimes . times do
@@ -735,28 +745,29 @@ def createRackServletWrapper(runtime, rackup, filename)
735745 end
736746
737747 it "wait until pool is filled when invoking getApplication (with wait set to false)" do
748+ app_init_secs = 0.2
738749 allow ( @factory ) . to receive ( :init )
739750 allow ( @factory ) . to receive ( :newApplication ) do
740- app = double "app"
741- allow ( app ) . to receive ( :init ) { sleep ( 0.2 ) }
751+ app = Synchronized . new ( double ( "app" ) . as_null_object )
752+ allow ( 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
745756 expect ( @rack_config ) . to receive ( :getInitialRuntimes ) . and_return 3
746757 expect ( @rack_config ) . to receive ( :getMaximumRuntimes ) . and_return 4
747758
748759 @pooling_factory . init ( @rack_context )
749- millis = java . lang . System . currentTimeMillis
760+ start = java . lang . System . currentTimeMillis
750761 expect ( @pooling_factory . getApplication ) . not_to be nil
751- millis = java . lang . System . currentTimeMillis - millis
752- expect ( millis ) . to be >= 150 # getApplication waited ~ 0.2 secs
762+ expect ( java . lang . System . currentTimeMillis - start ) . to be_within ( 70 ) . of ( app_init_secs * 1000 ) # getApplication waited ~ sleep time
753763 end
754764
755765 it "waits acquire timeout till an application is available from the pool (than raises)" do
766+ app_init_secs = 0.2
756767 allow ( @factory ) . to receive ( :init )
757768 expect ( @factory ) . to receive ( :newApplication ) . twice do
758- app = double "app"
759- expect ( app ) . to receive ( :init ) { sleep ( 0.2 ) }
769+ app = Synchronized . new ( double ( "app" ) . as_null_object )
770+ expect ( app ) . to receive ( :init ) { sleep ( app_init_secs ) }
760771 app
761772 end
762773 allow ( @rack_config ) . to receive ( :getBooleanProperty ) . with ( "jruby.runtime.init.wait" ) . and_return false
@@ -765,86 +776,90 @@ def createRackServletWrapper(runtime, rackup, filename)
765776
766777 @pooling_factory . init ( @rack_context )
767778 @pooling_factory . acquire_timeout = 1 . to_java # second
768- millis = java . lang . System . currentTimeMillis
779+ start = java . lang . System . currentTimeMillis
769780 expect ( @pooling_factory . getApplication ) . not_to be nil
770- millis = java . lang . System . currentTimeMillis - millis
771- expect ( millis ) . to be >= 150 # getApplication waited ~ 0.2 secs
781+ expect ( java . lang . System . currentTimeMillis - start ) . to be_within ( 70 ) . of ( app_init_secs * 1000 )
772782
773783 app2 = @pooling_factory . getApplication # now the pool is empty
774-
775- @pooling_factory . acquire_timeout = 0.1 . to_java # second
776- millis = java . lang . System . currentTimeMillis
784+ timeout_secs = 0.1
785+ @pooling_factory . acquire_timeout = ( timeout_secs ) . to_java
786+ start = java . lang . System . currentTimeMillis
777787 expect { @pooling_factory . getApplication } . to raise_error ( org . jruby . rack . AcquireTimeoutException )
778- millis = java . lang . System . currentTimeMillis - millis
779- expect ( millis ) . to be >= 90 # waited about ~ 0.1 secs
788+ expect ( java . lang . System . currentTimeMillis - start ) . to be_within ( 20 ) . of ( timeout_secs * 1000 )
780789
781790 @pooling_factory . finishedWithApplication ( app2 ) # gets back to the pool
782791 expect ( @pooling_factory . getApplication ) . to eq app2
783792 end
784793
785794 it "gets and initializes new applications until maximum allows to create more" do
795+ app_init_secs = 0.1
786796 allow ( @factory ) . to receive ( :init )
787797 expect ( @factory ) . to receive ( :newApplication ) . twice do
788- app = double "app (new)"
789- expect ( app ) . to receive ( :init ) { sleep ( 0.1 ) }
798+ app = Synchronized . new ( double ( "app (new)" ) . as_null_object )
799+ expect ( app ) . to receive ( :init ) { sleep ( app_init_secs ) }
790800 app
791801 end
792802 allow ( @rack_config ) . to receive ( :getBooleanProperty ) . with ( "jruby.runtime.init.wait" ) . and_return false
793803 allow ( @rack_config ) . to receive ( :getInitialRuntimes ) . and_return 2
794804 allow ( @rack_config ) . to receive ( :getMaximumRuntimes ) . and_return 4
795805
806+ timeout_secs = 0.1
796807 @pooling_factory . init ( @rack_context )
797- @pooling_factory . acquire_timeout = 0.10 . to_java # second
808+ @pooling_factory . acquire_timeout = ( timeout_secs ) . to_java # second
798809
799810 2 . times { expect ( @pooling_factory . getApplication ) . not_to be nil }
800811
812+ app_get_secs = 0.15
801813 expect ( @factory ) . to receive ( :getApplication ) . twice do
802- app = double "app (get)" ; sleep ( 0.15 ) ; app
814+ app = Synchronized . new ( double ( "app (get)" ) . as_null_object )
815+ sleep ( app_get_secs )
816+ app
803817 end
804818
805- millis = java . lang . System . currentTimeMillis
819+ start = java . lang . System . currentTimeMillis
806820 2 . times { expect ( @pooling_factory . getApplication ) . not_to be nil }
807- millis = java . lang . System . currentTimeMillis - millis
808- expect ( millis ) . to be >= 300 # waited about 2 x 0.15 secs
821+ expect ( java . lang . System . currentTimeMillis - start ) . to be_within ( 70 ) . of ( 2 * app_get_secs * 1000 )
809822
810- millis = java . lang . System . currentTimeMillis
823+ start = java . lang . System . currentTimeMillis
811824 expect {
812825 @pooling_factory . getApplication
813826 } . to raise_error ( org . jruby . rack . AcquireTimeoutException )
814- millis = java . lang . System . currentTimeMillis - millis
815- expect ( millis ) . to be >= 90 # waited about ~ 0.10 secs
827+ expect ( java . lang . System . currentTimeMillis - start ) . to be_within ( 20 ) . of ( timeout_secs * 1000 )
816828 end
817829
818- it "initializes initial runtimes in paralel (with wait set to false)" do
830+ it "initializes initial runtimes in parallel (with wait set to false)" do
831+ app_init_secs = 0.15
819832 allow ( @factory ) . to receive ( :init )
820833 allow ( @factory ) . to receive ( :newApplication ) do
821- app = double "app"
822- allow ( app ) . to receive ( :init ) do
823- sleep ( 0.15 )
824- end
834+ app = Synchronized . new ( double ( "app" ) . as_null_object )
835+ allow ( app ) . to receive ( :init ) { sleep ( app_init_secs ) }
825836 app
826837 end
838+
839+ init_threads = 4
840+ init_runtimes = 6
827841 allow ( @rack_config ) . to receive ( :getBooleanProperty ) . with ( "jruby.runtime.init.wait" ) . and_return false
828- allow ( @rack_config ) . to receive ( :getInitialRuntimes ) . and_return 6
842+ allow ( @rack_config ) . to receive ( :getRuntimeInitThreads ) . and_return init_threads
843+ allow ( @rack_config ) . to receive ( :getInitialRuntimes ) . and_return init_runtimes
829844 allow ( @rack_config ) . to receive ( :getMaximumRuntimes ) . and_return 8
830845
846+ expected_initial_init_time = 1.1 * ( init_runtimes . to_f / init_threads . to_f ) . ceil * app_init_secs # 10% margin
831847 @pooling_factory . init ( @rack_context )
832- sleep ( 0.10 )
833- expect ( @pooling_factory . getApplicationPool . size ) . to be < 6
834- sleep ( 0.9 )
835- expect ( @pooling_factory . getApplicationPool . size ) . to be >= 6
848+ sleep ( app_init_secs ) # wait for at some (but not possibly all) to finish
849+ expect ( @pooling_factory . getApplicationPool . size ) . to be < init_runtimes
850+ sleep ( expected_initial_init_time - app_init_secs ) # remaining time
851+ expect ( @pooling_factory . getApplicationPool . size ) . to be >= init_runtimes
836852
837853 expect ( @pooling_factory . getManagedApplications ) . to_not be_empty
838- expect ( @pooling_factory . getManagedApplications . size ) . to eql 6
854+ expect ( @pooling_factory . getManagedApplications . size ) . to eql init_runtimes
839855 end
840856
841857 it "throws from init when application initialization in thread failed" do
858+ app_init_secs = 0.05
842859 allow ( @factory ) . to receive ( :init )
843860 allow ( @factory ) . to receive ( :newApplication ) do
844- app = double "app"
845- allow ( app ) . to receive ( :init ) do
846- sleep ( 0.05 ) ; raise "app.init raising"
847- end
861+ app = Synchronized . new ( double ( "app" ) . as_null_object )
862+ allow ( app ) . to receive ( :init ) { sleep ( app_init_secs ) ; raise "app.init raising" }
848863 app
849864 end
850865 allow ( @rack_config ) . to receive ( :getInitialRuntimes ) . and_return 2
@@ -863,9 +878,6 @@ def createRackServletWrapper(runtime, rackup, filename)
863878
864879 expect { @pooling_factory . init ( @rack_context ) } . to raise_error org . jruby . rack . RackInitializationException
865880 expect ( raise_error_logged ) . to eql 1 # logs same init exception once
866-
867- # NOTE: seems it's not such a good idea to return empty on init error
868- # expect(@pooling_factory.getManagedApplications).to be_empty
869881 end
870882
871883end
0 commit comments