package org.stokesdrift.container.jruby;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import org.jruby.Ruby;
import org.jruby.RubyInstanceConfig;
import org.jruby.exceptions.RaiseException;
import org.jruby.javasupport.JavaUtil;
import org.jruby.rack.DefaultRackApplicationFactory;
import org.jruby.rack.DefaultRackConfig;
import org.jruby.rack.RackConfig;
import org.jruby.rack.RackContext;
import org.jruby.rack.RackLogger;
import org.jruby.runtime.builtin.IRubyObject;
public class DriftRackApplicationFactory extends DefaultRackApplicationFactory implements RubyRuntimeManager {
private RackContext rackServletContext;
private RubyInstanceConfig runtimeConfig;
private static final Logger logger = Logger.getLogger(DriftRackApplicationFactory.class.getName());
/**
* Creates a new application instance (without initializing it).
* NOTE: exception handling is left to the outer factory.
*
* @return new application instance
*/
@Override
public org.jruby.rack.RackApplication newApplication() {
return new JrubyRackApplicationImpl(new ApplicationObjectFactory() {
public IRubyObject create(Ruby runtime) {
return createApplicationObject(runtime);
}
}, this);
}
public RubyInstanceConfig getRuntimeConfig() {
return runtimeConfig;
}
/**
* Sets all the jar files as load paths
*
* @param config
*/
protected void setLoadPaths(RubyInstanceConfig config) {
String libDir = getEnvVariable("STOKESDRIFT_LIB_DIR");
List<String> loadPaths = new ArrayList<String>();
if (libDir != null) {
File dir = new File(libDir);
String[] fileList = dir.list();
for (String fileName : fileList) {
StringBuilder sb = new StringBuilder(libDir).append(File.separatorChar).append(fileName);
loadPaths.add(sb.toString());
}
}
// Add root gems eg: /opt/jruby/lib/ruby/gems/shared/gems/
String jrubyRootDir = getEnvVariable("JRUBY_HOME");
if (jrubyRootDir != null) {
StringBuilder gemPath = new StringBuilder();
gemPath.append(jrubyRootDir);
String[] paths = new String[] { "lib", "jruby", "gems", "shared", "gems" };
for (String path : paths) {
gemPath.append(File.separator).append(path);
}
File gemPathDir = new File(gemPath.toString());
if (gemPathDir.exists()) {
File[] gemPaths = gemPathDir.listFiles();
for (File path : gemPaths) {
loadPaths.add(path.getAbsolutePath());
}
}
}
String gemPathEnv = getEnvVariable("GEM_PATH");
if (gemPathEnv != null) {
loadPaths.add(gemPathEnv);
}
config.setLoadPaths(loadPaths);
config.setRunRubyInProcess(true);
config.setDebug(true);
config.setLoader(ClassLoader.getSystemClassLoader());
}
protected RubyInstanceConfig initRuntimeConfig(final RubyInstanceConfig config) {
final RackConfig rackConfig = rackServletContext.getConfig();
config.setLoader(Thread.currentThread().getContextClassLoader());
final Map<String, String> newEnv = rackConfig.getRuntimeEnvironment();
if (newEnv != null) {
config.setEnvironment(newEnv);
}
config.processArguments(rackConfig.getRuntimeArguments());
if (rackConfig.getCompatVersion() != null) {
config.setCompatVersion(rackConfig.getCompatVersion());
}
String home = getEnvVariable("JRUBY_HOME");
logger.info("stokesdrift.env JRUBY_HOME=" + home);
try {
if (home != null) {
config.setJRubyHome(home);
}
} catch (Exception e) {
rackServletContext.log(RackLogger.DEBUG, "won't set-up jruby.home from jar", e);
}
return config;
}
/**
* Returns either a system property or an environment variable
*
*/
public String getEnvVariable(String name) {
String envValue = System.getProperty(name);
if (envValue == null) {
envValue = System.getenv(name);
}
return envValue;
}
/**
* Initialize this factory using the given context.
* NOTE: exception handling is left to the outer factory.
*
* @param rackContext
*/
@Override
public void init(final RackContext rackContext) {
this.rackServletContext = rackContext;
super.init(rackContext);
this.runtimeConfig = createRuntimeConfig();
}
// TODO contribute back on integration points for the Jruby Rack project
@Override
public Ruby newRuntime() throws RaiseException {
setLoadPaths(runtimeConfig);
Ruby runtime = Ruby.getThreadLocalRuntime();
if (runtime == null) {
runtime = Ruby.newInstance(runtimeConfig);
}
initDriftRuntime(runtime);
return runtime;
}
/**
* TODO contribute back hooks for integration purposes Initializes the
* runtime (exports the context, boots the Rack handler).
*
* NOTE: (package) visible due specs
*
* @param runtime
*/
protected void initDriftRuntime(final Ruby runtime) {
// set $servlet_context :
runtime.getGlobalVariables().set("$servlet_context", JavaUtil.convertJavaToRuby(runtime, rackServletContext));
// load our (servlet) Rack handler :
runtime.evalScriptlet("require 'rack/handler/servlet'");
// NOTE: this is experimental stuff and might change in the future :
String env = rackServletContext.getConfig().getProperty("jruby.rack.handler.env");
// currently supported "env" values are 'default' and 'servlet'
if (env != null) {
runtime.evalScriptlet("Rack::Handler::Servlet.env = '" + env + "'");
}
String response = rackServletContext.getConfig().getProperty("jruby.rack.handler.response");
if (response == null) {
response = rackServletContext.getConfig().getProperty("jruby.rack.response");
}
if (response != null) { // JRuby::Rack::JettyResponse ->
// 'jruby/rack/jetty_response'
runtime.evalScriptlet("Rack::Handler::Servlet.response = '" + response + "'");
}
// configure (Ruby) bits and pieces :
String dechunk = rackServletContext.getConfig().getProperty("jruby.rack.response.dechunk");
Boolean dechunkFlag = (Boolean) DefaultRackConfig.toStrictBoolean(dechunk, null);
if (dechunkFlag != null) {
runtime.evalScriptlet("JRuby::Rack::Response.dechunk = " + dechunkFlag + "");
} else { // dechunk null (default) or not a true/false value ... we're
// patch :
runtime.evalScriptlet("JRuby::Rack::Booter.on_boot { require 'jruby/rack/chunked' }");
// `require 'jruby/rack/chunked'` that happens after Rack is loaded
}
String swallowAbort = rackServletContext.getConfig().getProperty("jruby.rack.response.swallow_client_abort");
Boolean swallowAbortFlag = (Boolean) DefaultRackConfig.toStrictBoolean(swallowAbort, null);
if (swallowAbortFlag != null) {
runtime.evalScriptlet("JRuby::Rack::Response.swallow_client_abort = " + swallowAbortFlag + "");
}
}
public RackContext getContext() {
return rackServletContext;
}
}