Modified: trunk/rails-integration/src/main/java/org/jruby/webapp/RailsServlet.java (648 => 649)
--- trunk/rails-integration/src/main/java/org/jruby/webapp/RailsServlet.java 2007-06-22 08:31:05 UTC (rev 648)
+++ trunk/rails-integration/src/main/java/org/jruby/webapp/RailsServlet.java 2007-06-22 13:02:59 UTC (rev 649)
@@ -1,227 +1,231 @@
-package org.jruby.webapp;
-import org.jruby.Ruby;
-import org.jruby.RubyException;
-import org.jruby.RubyIO;
-import org.jruby.RubyClass;
-import org.jruby.RubyHash;
-import org.jruby.RubyModule;
-import org.jruby.RubySymbol;
-import org.jruby.webapp.session.SessionHolder;
-import org.jruby.javasupport.JavaEmbedUtils;
-import org.jruby.runtime.builtin.IRubyObject;
-import org.jruby.runtime.Block;
-import org.jruby.exceptions.RaiseException;
-import org.apache.commons.pool.ObjectPool;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.util.NoSuchElementException;
-import java.util.Enumeration;
-
-/**
- * This servlet dispatches to a Ruby on Rails application, using JRuby as the interpreter.
- *
- * The dispatcher implementation is defined in subclasses.
- *
- * @author Robert Egglestone
- */
-public class RailsServlet extends HttpServlet {
-
- /** How long in seconds to suggest the client waits between requests if we're overloaded */
- private int retryAfterOverloading = 60;
-
- /**
- * Process the HTTP request.
- */
- protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
- try {
- serviceRequest(request, response);
- } catch (ServletException e) {
- throw e;
- } catch (IOException e) {
- throw e;
- } catch (Exception e) {
- throw new ServletException(e);
- }
- }
-
- protected void serviceRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
- ObjectPool runtimePool = getRuntimePool();
- Ruby runtime = null;
- // long startTime = System.currentTimeMillis();
- try {
- runtime = (Ruby)runtimePool.borrowObject();
- dispatchRequest(runtime, request, response);
- } catch (RaiseException e) {
- String action = "" == null) ? "initialise runtime" : "invoke rails";
- log("Failed to " + action + ": " + e.getMessage() + "\n" + getBacktrace(e.getException()));
- throw new ServletException("Failed to " + action + ", please see the log for more details");
- } catch (NoSuchElementException e) {
- // all objects in the pool have been used up
- log("Warning: All JRuby processes in the pool are in use");
- response.setHeader("Retry-After", Integer.toString(retryAfterOverloading));
- response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "The server is currently overloaded, please try again later");
- } finally {
- if (runtime != null) runtimePool.returnObject(runtime);
- }
- // log("Invocation time: " + (System.currentTimeMillis() - startTime) + "ms");
- }
-
- protected void dispatchRequest(Ruby runtime, HttpServletRequest request, HttpServletResponse response) throws Exception {
- RubyIO stdin = new RubyIO(runtime, request.getInputStream());
- RubyIO stdout = new RubyIO(runtime, new HttpOutput(response));
-
- runtime.defineReadonlyVariable("$stdin", stdin);
- runtime.defineReadonlyVariable("$stdout", stdout);
-
- // setup the environment
- setupEnvironment(runtime, request);
-
- // setup the default session
- String value = getServletContext().getInitParameter("jruby.session_store");
- if(value == null || value.intern() != "db") {
- setJavaSessionStoreAsDefault(runtime, request);
- }
- setJavaRequestSymbol(runtime, request);
-
- // create a cgi instance to store the data
- IRubyObject cgi = createCgi(runtime);
-
- // get the rails dispatcher
- RubyClass dispatcher = (RubyClass)runtime.getClass("Dispatcher");
- if (dispatcher == null) throw new ServletException("The rails dispatcher could not be found");
-
- // dispatch the request
- Object[] args = { cgi };
- JavaEmbedUtils.invokeMethod(runtime, dispatcher, "dispatch", args, void.class);
- }
-
- protected IRubyObject createCgi(Ruby runtime) {
- RubyClass cgiClass = runtime.getClass("CGI");
- IRubyObject[] cgiArgs = { JavaEmbedUtils.javaToRuby(runtime, "query") };
- return cgiClass.newInstance(cgiArgs, Block.NULL_BLOCK);
- }
-
- protected void setupEnvironment(Ruby runtime, HttpServletRequest request) {
- RubyHash env = (RubyHash)runtime.getObject().getConstant("ENV");
-
- String requestUri = chomp(request.getRequestURI(), "/");
- if (requestUri.length() == 0) requestUri = "/";
-
- // RFC3875 The Common Gateway Interface (CGI) Version 1.1
- setEnv(env, "AUTH_TYPE", request.getAuthType());
- if (request.getContentLength() != -1) {
- setEnv(env, "CONTENT_LENGTH", String.valueOf(request.getContentLength()));
- }
- setEnv(env, "CONTENT_TYPE", request.getContentType());
- setEnv(env, "GATEWAY_INTERFACE", "CGI/1.1");
- // see this for path info http://blogs.msdn.com/david.wang/archive/2005/08/04/What-is-PATH-TRANSLATED.aspx
- // setEnv(env, "PATH_INFO", ?);
- setEnv(env, "PATH_TRANSLATED", requestUri);
- setEnv(env, "QUERY_STRING", request.getQueryString(), "");
- setEnv(env, "REMOTE_ADDR", request.getRemoteAddr());
- setEnv(env, "REMOTE_HOST", request.getRemoteHost());
- setEnv(env, "REMOTE_USER", request.getRemoteUser());
- setEnv(env, "REQUEST_METHOD", request.getMethod());
- // setEnv(env, "'SCRIPT_NAME'", ?);
- setEnv(env, "SERVER_ADDR", request.getServerName());
- setEnv(env, "SERVER_NAME", request.getServerName() + ":" + request.getServerPort());
- setEnv(env, "SERVER_PORT", String.valueOf(request.getServerPort()));
- setEnv(env, "SERVER_PROTOCOL", request.getProtocol());
- setEnv(env, "SERVER_SOFTWARE", getServletContext().getServerInfo());
-
- // Extra values that Rails can use use of
- setEnv(env, "REQUEST_URI", requestUri);
- setEnv(env, "RAILS_RELATIVE_URL_ROOT", chomp(request.getContextPath(), "/"));
- if (request.getScheme().equals("https")) {
- setEnv(env, "HTTPS", "on");
- }
-
- // add all the HTTP headers
- Enumeration headerNames = request.getHeaderNames();
- while (headerNames.hasMoreElements()) {
- String name = (String)headerNames.nextElement();
- String envName = "HTTP_" + name.toUpperCase().replace('-', '_');
- setEnv(env, envName, request.getHeader(name));
- }
- }
-
- protected RubyHash getDefaultSessionOptions(Ruby runtime) {
- RubyModule cgiRequestClass = runtime.getClassFromPath("ActionController::CgiRequest");
- if (cgiRequestClass == null) {
- log("JavaServletStore disabled because ActionController::CgiRequest could not be found");
- return null;
- }
- return (RubyHash)cgiRequestClass.getConstant("DEFAULT_SESSION_OPTIONS");
- }
-
- protected void setJavaSessionStoreAsDefault(Ruby runtime, HttpServletRequest request) throws ServletException {
- RubyHash defaultSessionOptions = getDefaultSessionOptions(runtime);
- if (defaultSessionOptions == null) return;
-
- runtime.getLoadService().require("builtin/rails-integration/session/java_servlet_store");
- RubyModule javaServletStore = runtime.getClassFromPath("CGI::Session::JavaServletStore");
- if (javaServletStore == null) {
- log("JavaServletStore disabled because CGI::Session::JavaServletStore could not be found");
- return;
- }
-
- // store the default database manager
- RubySymbol databaseManagerSymbol = RubySymbol.newSymbol(runtime, "database_manager");
- defaultSessionOptions.aset(databaseManagerSymbol, javaServletStore);
- }
-
- protected void setJavaRequestSymbol(Ruby runtime, HttpServletRequest request) throws ServletException {
- RubyHash defaultSessionOptions = getDefaultSessionOptions(runtime);
- if (defaultSessionOptions == null) return;
-
- // store the java request
- RubySymbol javaRequestSymbol = RubySymbol.newSymbol(runtime, "java_request");
- IRubyObject javaRequest = JavaEmbedUtils.javaToRuby(runtime, new SessionHolder(request));
-
- defaultSessionOptions.aset(javaRequestSymbol, javaRequest);
- }
-
- protected ObjectPool getRuntimePool() throws ServletException {
- ObjectPool runtimePool = (ObjectPool)getServletContext().getAttribute(RailsContextListener.RUNTIME_POOL_ATTRIBUTE);
- if (runtimePool == null) {
- throw new ServletException("No runtime pool is available, please check RailsContextListener");
- }
- return runtimePool;
- }
-
- private String getBacktrace(RubyException exception) {
- ByteArrayOutputStream buffer = new ByteArrayOutputStream();
- exception.printBacktrace(new PrintStream(buffer));
- return buffer.toString();
- }
-
- protected void setEnv(RubyHash env, String name, String value, String defaultValue) {
- if (value == null) {
- value = defaultValue;
- }
- setEnv(env, name, value);
- }
-
- protected void setEnv(RubyHash env, String name, String value) {
- Ruby runtime = env.getRuntime();
- if (value != null) {
- IRubyObject rubyName = JavaEmbedUtils.javaToRuby(runtime, name);
- IRubyObject rubyValue = JavaEmbedUtils.javaToRuby(runtime, value);
- env.aset(rubyName, rubyValue);
- }
- }
-
- private String chomp(String message, String suffix) {
- if (message.endsWith(suffix)) {
- return message.substring(0, message.length() - suffix.length());
- } else {
- return message;
- }
- }
-
-}
+package org.jruby.webapp;
+import org.jruby.Ruby;
+import org.jruby.RubyException;
+import org.jruby.RubyIO;
+import org.jruby.RubyClass;
+import org.jruby.RubyHash;
+import org.jruby.RubyModule;
+import org.jruby.RubySymbol;
+import org.jruby.webapp.session.SessionHolder;
+import org.jruby.javasupport.JavaEmbedUtils;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.jruby.runtime.Block;
+import org.jruby.exceptions.RaiseException;
+import org.apache.commons.pool.ObjectPool;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.NoSuchElementException;
+import java.util.Enumeration;
+
+/**
+ * This servlet dispatches to a Ruby on Rails application, using JRuby as the interpreter.
+ *
+ * The dispatcher implementation is defined in subclasses.
+ *
+ * @author Robert Egglestone
+ */
+public class RailsServlet extends HttpServlet {
+
+ /** How long in seconds to suggest the client waits between requests if we're overloaded */
+ private int retryAfterOverloading = 60;
+
+ /**
+ * Process the HTTP request.
+ */
+ protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ try {
+ serviceRequest(request, response);
+ } catch (ServletException e) {
+ throw e;
+ } catch (IOException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new ServletException(e);
+ }
+ }
+
+ protected void serviceRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ ObjectPool runtimePool = getRuntimePool();
+ Ruby runtime = null;
+ // long startTime = System.currentTimeMillis();
+ try {
+ runtime = (Ruby)runtimePool.borrowObject();
+ dispatchRequest(runtime, request, response);
+ } catch (RaiseException e) {
+ String action = "" == null) ? "initialise runtime" : "invoke rails";
+ log("Failed to " + action + ": " + e.getMessage() + "\n" + getBacktrace(e.getException()));
+ throw new ServletException("Failed to " + action + ", please see the log for more details");
+ } catch (NoSuchElementException e) {
+ // all objects in the pool have been used up
+ log("Warning: All JRuby processes in the pool are in use");
+ response.setHeader("Retry-After", Integer.toString(retryAfterOverloading));
+ response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "The server is currently overloaded, please try again later");
+ } finally {
+ if (runtime != null) runtimePool.returnObject(runtime);
+ }
+ // log("Invocation time: " + (System.currentTimeMillis() - startTime) + "ms");
+ }
+
+ protected void dispatchRequest(Ruby runtime, HttpServletRequest request, HttpServletResponse response) throws Exception {
+ RubyIO stdin = new RubyIO(runtime, request.getInputStream());
+ RubyIO stdout = new RubyIO(runtime, new HttpOutput(response));
+
+ runtime.defineVariable(new org.jruby.runtime.GlobalVariable(runtime, "$stdin", stdin));
+ runtime.defineVariable(new org.jruby.runtime.GlobalVariable(runtime, "$stdout", stdout));
+ runtime.defineVariable(new org.jruby.runtime.GlobalVariable(runtime, "$>", stdout));
+ runtime.defineVariable(new org.jruby.runtime.GlobalVariable(runtime, "$defout", stdout));
+ runtime.defineGlobalConstant("STDIN", stdin);
+ runtime.defineGlobalConstant("STDOUT", stdout);
+
+ // setup the environment
+ setupEnvironment(runtime, request);
+
+ // setup the default session
+ String value = getServletContext().getInitParameter("jruby.session_store");
+ if(value == null || value.intern() != "db") {
+ setJavaSessionStoreAsDefault(runtime, request);
+ }
+ setJavaRequestSymbol(runtime, request);
+
+ // create a cgi instance to store the data
+ IRubyObject cgi = createCgi(runtime);
+
+ // get the rails dispatcher
+ RubyClass dispatcher = (RubyClass)runtime.getClass("Dispatcher");
+ if (dispatcher == null) throw new ServletException("The rails dispatcher could not be found");
+
+ // dispatch the request
+ Object[] args = { cgi };
+ JavaEmbedUtils.invokeMethod(runtime, dispatcher, "dispatch", args, void.class);
+ }
+
+ protected IRubyObject createCgi(Ruby runtime) {
+ RubyClass cgiClass = runtime.getClass("CGI");
+ IRubyObject[] cgiArgs = { JavaEmbedUtils.javaToRuby(runtime, "query") };
+ return cgiClass.newInstance(cgiArgs, Block.NULL_BLOCK);
+ }
+
+ protected void setupEnvironment(Ruby runtime, HttpServletRequest request) {
+ RubyHash env = (RubyHash)runtime.getObject().getConstant("ENV");
+
+ String requestUri = chomp(request.getRequestURI(), "/");
+ if (requestUri.length() == 0) requestUri = "/";
+
+ // RFC3875 The Common Gateway Interface (CGI) Version 1.1
+ setEnv(env, "AUTH_TYPE", request.getAuthType());
+ if (request.getContentLength() != -1) {
+ setEnv(env, "CONTENT_LENGTH", String.valueOf(request.getContentLength()));
+ }
+ setEnv(env, "CONTENT_TYPE", request.getContentType());
+ setEnv(env, "GATEWAY_INTERFACE", "CGI/1.1");
+ // see this for path info http://blogs.msdn.com/david.wang/archive/2005/08/04/What-is-PATH-TRANSLATED.aspx
+ // setEnv(env, "PATH_INFO", ?);
+ setEnv(env, "PATH_TRANSLATED", requestUri);
+ setEnv(env, "QUERY_STRING", request.getQueryString(), "");
+ setEnv(env, "REMOTE_ADDR", request.getRemoteAddr());
+ setEnv(env, "REMOTE_HOST", request.getRemoteHost());
+ setEnv(env, "REMOTE_USER", request.getRemoteUser());
+ setEnv(env, "REQUEST_METHOD", request.getMethod());
+ // setEnv(env, "'SCRIPT_NAME'", ?);
+ setEnv(env, "SERVER_ADDR", request.getServerName());
+ setEnv(env, "SERVER_NAME", request.getServerName() + ":" + request.getServerPort());
+ setEnv(env, "SERVER_PORT", String.valueOf(request.getServerPort()));
+ setEnv(env, "SERVER_PROTOCOL", request.getProtocol());
+ setEnv(env, "SERVER_SOFTWARE", getServletContext().getServerInfo());
+
+ // Extra values that Rails can use use of
+ setEnv(env, "REQUEST_URI", requestUri);
+ setEnv(env, "RAILS_RELATIVE_URL_ROOT", chomp(request.getContextPath(), "/"));
+ if (request.getScheme().equals("https")) {
+ setEnv(env, "HTTPS", "on");
+ }
+
+ // add all the HTTP headers
+ Enumeration headerNames = request.getHeaderNames();
+ while (headerNames.hasMoreElements()) {
+ String name = (String)headerNames.nextElement();
+ String envName = "HTTP_" + name.toUpperCase().replace('-', '_');
+ setEnv(env, envName, request.getHeader(name));
+ }
+ }
+
+ protected RubyHash getDefaultSessionOptions(Ruby runtime) {
+ RubyModule cgiRequestClass = runtime.getClassFromPath("ActionController::CgiRequest");
+ if (cgiRequestClass == null) {
+ log("JavaServletStore disabled because ActionController::CgiRequest could not be found");
+ return null;
+ }
+ return (RubyHash)cgiRequestClass.getConstant("DEFAULT_SESSION_OPTIONS");
+ }
+
+ protected void setJavaSessionStoreAsDefault(Ruby runtime, HttpServletRequest request) throws ServletException {
+ RubyHash defaultSessionOptions = getDefaultSessionOptions(runtime);
+ if (defaultSessionOptions == null) return;
+
+ runtime.getLoadService().require("builtin/rails-integration/session/java_servlet_store");
+ RubyModule javaServletStore = runtime.getClassFromPath("CGI::Session::JavaServletStore");
+ if (javaServletStore == null) {
+ log("JavaServletStore disabled because CGI::Session::JavaServletStore could not be found");
+ return;
+ }
+
+ // store the default database manager
+ RubySymbol databaseManagerSymbol = RubySymbol.newSymbol(runtime, "database_manager");
+ defaultSessionOptions.aset(databaseManagerSymbol, javaServletStore);
+ }
+
+ protected void setJavaRequestSymbol(Ruby runtime, HttpServletRequest request) throws ServletException {
+ RubyHash defaultSessionOptions = getDefaultSessionOptions(runtime);
+ if (defaultSessionOptions == null) return;
+
+ // store the java request
+ RubySymbol javaRequestSymbol = RubySymbol.newSymbol(runtime, "java_request");
+ IRubyObject javaRequest = JavaEmbedUtils.javaToRuby(runtime, new SessionHolder(request));
+
+ defaultSessionOptions.aset(javaRequestSymbol, javaRequest);
+ }
+
+ protected ObjectPool getRuntimePool() throws ServletException {
+ ObjectPool runtimePool = (ObjectPool)getServletContext().getAttribute(RailsContextListener.RUNTIME_POOL_ATTRIBUTE);
+ if (runtimePool == null) {
+ throw new ServletException("No runtime pool is available, please check RailsContextListener");
+ }
+ return runtimePool;
+ }
+
+ private String getBacktrace(RubyException exception) {
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ exception.printBacktrace(new PrintStream(buffer));
+ return buffer.toString();
+ }
+
+ protected void setEnv(RubyHash env, String name, String value, String defaultValue) {
+ if (value == null) {
+ value = defaultValue;
+ }
+ setEnv(env, name, value);
+ }
+
+ protected void setEnv(RubyHash env, String name, String value) {
+ Ruby runtime = env.getRuntime();
+ if (value != null) {
+ IRubyObject rubyName = JavaEmbedUtils.javaToRuby(runtime, name);
+ IRubyObject rubyValue = JavaEmbedUtils.javaToRuby(runtime, value);
+ env.aset(rubyName, rubyValue);
+ }
+ }
+
+ private String chomp(String message, String suffix) {
+ if (message.endsWith(suffix)) {
+ return message.substring(0, message.length() - suffix.length());
+ } else {
+ return message;
+ }
+ }
+
+}