0
+package com.zumisoft.rack;
0
+import java.io.IOException;
0
+import java.io.PrintWriter;
0
+import java.util.ArrayList;
0
+import java.util.Iterator;
0
+import javax.servlet.FilterConfig;
0
+import javax.servlet.http.HttpServletRequest;
0
+import javax.servlet.http.HttpServletResponse;
0
+import org.apache.commons.logging.Log;
0
+import org.apache.commons.logging.LogFactory;
0
+import org.jruby.RubyArray;
0
+import org.jruby.RubyHash;
0
+import org.jruby.javasupport.JavaEmbedUtils;
0
+import org.jruby.runtime.builtin.IRubyObject;
0
+import static org.jruby.util.KCode.UTF8;
0
+ * Contains an embedded ruby interpreter with a ruby web framework loaded.
0
+ * Requests are translated into Rack compatible hashes and are passes to the framework
0
+@SuppressWarnings("unchecked")
0
+public class RackRequestHandler {
0
+ private static Log log = LogFactory.getLog(RackRequestHandler.class);
0
+ private static final String RACK_METHOD = "call";
0
+ private static final String PS = File.separator;
0
+ private static final String RUBY_VERSION = "1.8";
0
+ private Ruby runtime = null;
0
+ private IRubyObject rackAdapter = null;
0
+ private final String APP_HOME;
0
+ private final String JRUBY_HOME;
0
+ private String[] ignorePaths = new String[0];
0
+ * Create a new request handler
0
+ * <p>Responds to the mandatory filter init-params:</p>
0
+ * <li>framework: The name of the ruby framework to use (only support Merb at the moment, though ashould be able to add others I hope)/li>
0
+ * <p>Responds to the optional filter init-params:</p>
0
+ * <li>APP_HOME: The home directory of the reby web app</li>
0
+ * <li>JRUBY_HOME: Home directory of JRuby installtion</li>
0
+ * <li>ignorePaths: Paths from which serve as static files or to pass processing to other servlets. Defaults to /stylesheets, /images, /favicon.ico, and /javascript</li>
0
+ public RackRequestHandler(FilterConfig config) {
0
+ log.info("Creating new RubyRequestHandler instance");
0
+ // Configure ignore paths
0
+ String pathCfg = config.getInitParameter("ignorePaths");
0
+ if (pathCfg != null) {
0
+ this.ignorePaths = pathCfg.split("\\s+");
0
+ ignorePaths = new String[]{"/images", "/stylesheets", "/javascript", "/favicon.ico"};
0
+ if (log.isInfoEnabled())
0
+ for (int x=0; x<ignorePaths.length; x++)
0
+ log.info(ignorePaths[x] + " is configured as an ignore path.");
0
+ // If jruby home is not sepcified for us, try to find it in the environment
0
+ if (config.getInitParameter("JRUBY_HOME") != null)
0
+ JRUBY_HOME = config.getInitParameter("JRUBY_HOME");
0
+ JRUBY_HOME = System.getenv("JRUBY_HOME");
0
+ if (JRUBY_HOME == null) {
0
+ final String message = "JRUBY_HOME is not set. This must be " +
0
+ "provided by the envoironment or set as as " +
0
+ "init-parameter in web.inf";
0
+ throw new RuntimeException(message);
0
+ log.info("Using JRUBY_HOME = "+JRUBY_HOME);
0
+ // Try to find the root directory of the ruby web app. If not specified, assume that it
0
+ // is the parent directory of the public root
0
+ if (config.getInitParameter("APP_HOME") != null)
0
+ APP_HOME = config.getInitParameter("APP_HOME");
0
+ File publicRoot = new File(config.getServletContext().getRealPath(PS));
0
+ APP_HOME = publicRoot.getParent();
0
+ log.info("Using APP_HOME = "+APP_HOME);
0
+ List<String> loadPath = new ArrayList<String>();
0
+ loadPath.add(JRUBY_HOME+PS+"lib"+PS+"ruby"+PS+"site_ruby"+PS+RUBY_VERSION);
0
+ loadPath.add(JRUBY_HOME+PS+"lib"+PS+"ruby"+PS+"site_ruby");
0
+ loadPath.add(JRUBY_HOME+PS+"lib"+PS+"ruby"+PS+RUBY_VERSION);
0
+ loadPath.add(JRUBY_HOME+PS+"lib"+PS+"ruby"+PS+RUBY_VERSION+PS+"java");
0
+ // Set up Ruby Runtime
0
+ this.runtime = JavaEmbedUtils.initialize(loadPath);
0
+ this.runtime.setKCode(UTF8);
0
+ this.runtime.setJRubyHome(JRUBY_HOME);
0
+ this.runtime.setCurrentDirectory(APP_HOME);
0
+ // Instantiate rack adapter
0
+ final String framework = config.getInitParameter("framework");
0
+ rackAdapter = RackAdapterBuilder.getAdaptorForFramework(runtime, framework);
0
+ log.debug("Loaded "+framework+" rack adapter: " + rackAdapter.inspect());
0
+ String message = "Can't load rack adapter.";
0
+ log.error(message, e);
0
+ throw new RuntimeException(message, e);
0
+ * @throws java.io.IOException
0
+ * @return boolean false if url is matched by an ignore path
0
+ public boolean call(HttpServletRequest req, HttpServletResponse resp) throws IOException {
0
+ // see if this request is to a static path
0
+ String url = req.getServletPath();
0
+ log.debug("URL = "+url);
0
+ for (int x=0; x<ignorePaths.length; x++) {
0
+ if (url.startsWith(ignorePaths[x])) {
0
+ log.debug(url + " matches ignore path " + ignorePaths[x]);
0
+ // Create Rack env parameter
0
+ RubyHash env = RackEnvironmentBuilder.buildEnvironment(runtime, req);
0
+ RubyArray responseArray = null;
0
+ responseArray = (RubyArray)JavaEmbedUtils.invokeMethod(
0
+ runtime, rackAdapter, RACK_METHOD, new IRubyObject[]{env}, Object.class);
0
+ log.debug("Response from adapter: " + responseArray.inspect());
0
+ String message = "There was an error calling the rack adapter.";
0
+ log.error(message, e);
0
+ throw new RuntimeException(message, e);
0
+ Integer responseStatus = Integer.valueOf(responseArray.get(0).toString());
0
+ RubyHash responseHeaders = (RubyHash) responseArray.get(1);
0
+ Object responseBody = (Object) responseArray.get(2);
0
+ resp.setStatus(responseStatus);
0
+ Iterator<String> i = responseHeaders.keys().iterator();
0
+ String header = i.next();
0
+ String value = (String) responseHeaders.get(header);
0
+ resp.setHeader(header, value);
0
+ PrintWriter bodyWriter = resp.getWriter();
0
+ log.debug("Response is of class "+responseBody.getClass().getCanonicalName());
0
+ // Right now this plays well with strings and not much else. I've seen some <File> objects come along.
0
+ // They will likely need to be dealt with
0
+ if (responseBody instanceof Object[]) {
0
+ Object[] arr = (Object[])responseBody;
0
+ for (int x = 0; x < arr.length; x++)
0
+ bodyWriter.write(arr[x].toString());
0
+ bodyWriter.write(responseBody.toString());
0
+ * Call this before disposing of the handler to properly shutdown the ruby runtime
0
+ public void shutdown() {
0
+ log.info("Shutting down request handler.");
Comments
No one has commented yet.