From 1732b73380d40d82b53e0ad79f383a84b8f6b3b0 Mon Sep 17 00:00:00 2001 From: Nick Sieger Date: Mon, 21 Mar 2011 12:27:01 -0500 Subject: [PATCH] Further refactor DRAF to use ScriptingContainer for runtime creation --- .../rack/DefaultRackApplicationFactory.java | 101 +++++++++++++----- src/spec/ruby/rack/application_spec.rb | 29 +++-- 2 files changed, 88 insertions(+), 42 deletions(-) diff --git a/src/main/java/org/jruby/rack/DefaultRackApplicationFactory.java b/src/main/java/org/jruby/rack/DefaultRackApplicationFactory.java index add0e9d0..2bea08ea 100644 --- a/src/main/java/org/jruby/rack/DefaultRackApplicationFactory.java +++ b/src/main/java/org/jruby/rack/DefaultRackApplicationFactory.java @@ -12,7 +12,6 @@ import org.jruby.exceptions.RaiseException; import org.jruby.embed.ScriptingContainer; import org.jruby.embed.LocalContextScope; -import org.jruby.javasupport.JavaEmbedUtils; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.util.ClassCache; @@ -34,17 +33,15 @@ public class DefaultRackApplicationFactory implements RackApplicationFactory { private String rackupScript, rackupLocation; private RackContext rackContext; + private RubyInstanceConfig defaultConfig; private ClassCache classCache; private RackApplication errorApplication; public void init(RackContext rackContext) { this.rackContext = rackContext; this.rackupScript = findRackupScript(); - this.classCache = JavaEmbedUtils.createClassCache( - Thread.currentThread().getContextClassLoader()); - if (errorApplication == null) { - errorApplication = newErrorApplication(); - } + this.defaultConfig = createDefaultConfig(); + rackContext.log(defaultConfig.getVersionString()); } public RackApplication newApplication() throws RackInitializationException { @@ -65,7 +62,10 @@ public void finishedWithApplication(RackApplication app) { app.destroy(); } - public RackApplication getErrorApplication() { + public synchronized RackApplication getErrorApplication() { + if (errorApplication == null) { + errorApplication = newErrorApplication(); + } return errorApplication; } @@ -200,32 +200,86 @@ private interface ApplicationObjectFactory { IRubyObject create(Ruby runtime); } - private ScriptingContainer newContainer() { - RubyInstanceConfig config = createRuntimeConfig(); + private RubyInstanceConfig createDefaultConfig() { + setupJRubyManagement(); + RubyInstanceConfig config = new RubyInstanceConfig(); + config.setLoader(Thread.currentThread().getContextClassLoader()); - ScriptingContainer container = new ScriptingContainer(LocalContextScope.SINGLETHREAD); + if (rackContext.getConfig().getCompatVersion() != null) { + config.setCompatVersion(rackContext.getConfig().getCompatVersion()); + } - container.setClassLoader(Thread.currentThread().getContextClassLoader()); - container.setHomeDirectory(config.getJRubyHome()); - container.setEnvironment(config.getEnvironment()); - container.setLoadPaths(config.loadPaths()); + try { // try to set jruby home to jar file path + URL resource = RubyInstanceConfig.class.getResource("/META-INF/jruby.home"); + if (resource.getProtocol().equals("jar")) { + String home; + try { // http://weblogs.java.net/blog/2007/04/25/how-convert-javaneturl-javaiofile + home = resource.toURI().getSchemeSpecificPart(); + } catch (URISyntaxException urise) { + home = resource.getPath(); + } - if (rackContext.getConfig().getCompatVersion() != null) { - container.setCompatVersion(config.getCompatVersion()); + // Trim trailing slash. It confuses OSGi containers... + if (home.endsWith("/")) { + home = home.substring(0, home.length() - 1); + } + config.setJRubyHome(home); + } + } catch (Exception e) { } + + // Process arguments, namely any that might be in RUBYOPT + config.processArguments(new String[0]); + return config; + } + + private void configureContainer(ScriptingContainer container) { + container.setClassLoader(defaultConfig.getLoader()); + container.setClassCache(defaultConfig.getClassCache()); + container.setCompatVersion(defaultConfig.getCompatVersion()); + container.setHomeDirectory(defaultConfig.getJRubyHome()); + container.setEnvironment(defaultConfig.getEnvironment()); + container.setLoadPaths(defaultConfig.loadPaths()); + container.getProvider().getRubyInstanceConfig().requiredLibraries().addAll(defaultConfig.requiredLibraries()); + } + + private void initializeContainer(ScriptingContainer container) throws RackInitializationException { + try { + container.put("$servlet_context", rackContext); + if (rackContext.getConfig().isIgnoreEnvironment()) { + container.runScriptlet("ENV.clear"); + } + container.runScriptlet("require 'rack/handler/servlet'"); + } catch (RaiseException re) { + throw new RackInitializationException(re); } + } + /** This method is only public for unit tests */ + public ScriptingContainer newContainer() throws RackInitializationException { + ScriptingContainer container = new ScriptingContainer(LocalContextScope.SINGLETHREAD); + configureContainer(container); + initializeContainer(container); return container; } + private Ruby getRuntimeFrom(ScriptingContainer container) { + Ruby runtime = (Ruby) rackContext.getAttribute("jruby.runtime"); + if (runtime == null) { + return container.getProvider().getRuntime(); + } else { + return runtime; + } + } + private RackApplication createApplication(final ApplicationObjectFactory appfact) throws RackInitializationException { try { - final Ruby runtime = newRuntime(); + final ScriptingContainer container = newContainer(); return new DefaultRackApplication() { @Override public void init() throws RackInitializationException { try { - setApplication(appfact.create(runtime)); + setApplication(appfact.create(getRuntimeFrom(container))); } catch (RaiseException re) { captureMessage(re); throw new RackInitializationException(re); @@ -233,7 +287,7 @@ public void init() throws RackInitializationException { } @Override public void destroy() { - JavaEmbedUtils.terminate(runtime); + container.terminate(); } }; } catch (RackInitializationException rie) { @@ -340,12 +394,9 @@ private String findRackupScript() { return rackup; } - /** Used only for testing; not part of the public API. */ - public String verify(Ruby runtime, String script) { - try { - return runtime.evalScriptlet(script).toString(); - } catch (Exception e) { - return e.getMessage(); + private void setupJRubyManagement() { + if (!"false".equalsIgnoreCase(System.getProperty("jruby.management.enabled"))) { + System.setProperty("jruby.management.enabled", "true"); } } diff --git a/src/spec/ruby/rack/application_spec.rb b/src/spec/ruby/rack/application_spec.rb index 35d32571..e1a832df 100644 --- a/src/spec/ruby/rack/application_spec.rb +++ b/src/spec/ruby/rack/application_spec.rb @@ -91,35 +91,30 @@ end end - describe "newRuntime" do - it "should create a new Ruby runtime with the rack environment pre-loaded" do - runtime = app_factory.newRuntime - lazy_string = proc {|v| "(begin; #{v}; rescue Exception => e; e.class; end).name"} - @app_factory.verify(runtime, lazy_string.call("Rack")).should == "Rack" - @app_factory.verify(runtime, lazy_string.call("Rack::Handler::Servlet") - ).should == "Rack::Handler::Servlet" - @app_factory.verify(runtime, lazy_string.call("Rack::Handler::Bogus") - ).should_not == "Rack::Handler::Bogus" + describe "newContainer" do + it "should create a new Ruby container with the rack environment pre-loaded" do + container = app_factory.newContainer + container.runScriptlet("defined?(::Rack)").should be_true + container.runScriptlet("defined?(::Rack::Handler::Servlet)").should be_true + container.runScriptlet("defined?(Rack::Handler::Bogus)").should_not be_true end it "should initialize the $servlet_context global variable" do - runtime = app_factory.newRuntime - app_factory.verify(runtime, "defined?($servlet_context)").should_not be_empty + container = app_factory.newContainer + container.runScriptlet("defined?($servlet_context)").should_not be_empty end it "should handle jruby.compat.version == '1.9' and start up in 1.9 mode" do @rack_config.stub!(:getCompatVersion).and_return org.jruby.CompatVersion::RUBY1_9 - runtime = app_factory.newRuntime - runtime.instance_config.compat_version.should == org.jruby.CompatVersion::RUBY1_9 + container = app_factory.newContainer + container.compat_version.should == org.jruby.CompatVersion::RUBY1_9 end it "should have environment variables cleared if the configuration ignores the environment" do ENV["HOME"].should_not == "" - runtime = app_factory.newRuntime - app_factory.verify(runtime, 'ENV["HOME"]').should == ENV["HOME"] @rack_config.stub!(:isIgnoreEnvironment).and_return true - runtime = app_factory.newRuntime - app_factory.verify(runtime, 'ENV["HOME"]').should == "" + container = app_factory.newContainer + container.runScriptlet('ENV["HOME"]').should be_nil end end end