Index: src/main/java/com/sun/grizzly/jruby/RailsAdapter.java =================================================================== --- src/main/java/com/sun/grizzly/jruby/RailsAdapter.java (revision 1075) +++ src/main/java/com/sun/grizzly/jruby/RailsAdapter.java (working copy) @@ -56,6 +56,7 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; +import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import java.util.WeakHashMap; @@ -84,7 +85,7 @@ private ConcurrentHashMap cache = new ConcurrentHashMap(); - private final boolean debugMode; + private final DispatchFilter debugDispatchFilter; public RailsAdapter(String railsRoot, String jrubyLib, int numRt, int minRt, int maxRt, boolean asyncExecution, RubyRuntimeAsyncFilter asyncFilter) { this("/", railsRoot, jrubyLib, numRt, minRt, maxRt, asyncExecution); @@ -104,7 +105,7 @@ JRubyVersion jrubyVersion = new JRubyVersion(); logger.log(Level.INFO, Messages.format(Messages.JRUBY_VERSION, jrubyVersion)); - this.debugMode = (jrubyVersion.compare("1.1.4") < 0); + debugDispatchFilter = isDebugMode() ? this.getDebugDispatchFilter(jrubyVersion) : null; //JRuby 1.1.4 and below has a bug where multiple runtimes cannot start at the same time due to a static HashSet. // It is fixed and will be available in 1.1.5. Until then, we will not attempt to start multiple runtimes at the same time @@ -114,6 +115,12 @@ numThreads = Math.min(Runtime.getRuntime().availableProcessors(), numRt); } + // FIXME can this information be retrieved from elsewhere? (e.g. RubyAdapter) + private static boolean isDebugMode() { + String rdebug = System.getProperty("glassfish.rdebug"); + return rdebug != null && rdebug.length() > 0; + } + /** * JRuby version, assumes the version to be of format major.minor.suffix */ @@ -283,10 +290,9 @@ throw new IllegalStateException(Messages.format(Messages.JRUBY_RUNTIME_NOTAVAILABLE)); } // Leave all processing to the runtime - if (debugMode) { - //This is only so that NB 6.5 beta works with jruby < 1.1.3. - //FIX ME: remove after - dispatchRailsRequestDebugMode(runtime, req, res); + if (debugDispatchFilter != null) { + // Works around varying JRuby thread handling behavior across versions. + debugDispatchFilter.dispatchRailsRequest(runtime, req, res); } else { dispatchRailsRequest(runtime, req, res); } @@ -326,17 +332,135 @@ return contextThread; } - private void dispatchRailsRequestDebugMode(Ruby runtime, GrizzlyRequest req, GrizzlyResponse res) throws IOException { - RubyThread oldContext = runtime.getThreadService().getMainThread(); - try { + private DispatchFilter getDebugDispatchFilter(JRubyVersion version) { + DispatchFilter filter = null; + if(version.compare("1.3.0") == 0) { + filter = new DebugJRuby130(); + } if(version.compare("1.2.0") == 0) { + filter = new DebugJRuby120(); + } else if(version.compare("1.1.4") < 0) { + filter = new DebugJRuby113(); + } + return filter; + } + + abstract class DispatchFilter { + + private volatile boolean initialized = false; + + protected abstract void init(Ruby runtime); + + void dispatchRailsRequest( + Ruby runtime, GrizzlyRequest req, GrizzlyResponse res) throws IOException { + if(!initialized) { + synchronized(this) { + if(!initialized) { + init(runtime); + initialized = true; + } + } + } + dispatchRailsRequestImpl(runtime, req, res); + } + + protected abstract void dispatchRailsRequestImpl( + Ruby runtime, GrizzlyRequest req, GrizzlyResponse res) throws IOException; + + protected Method getMethod(Class clazz, String methodName, Class... args) { + Method result = null; + try { + result = clazz.getDeclaredMethod(methodName, args); + } catch(Exception ex) { + String message = clazz.getSimpleName() + ".getMethod( " + methodName + " )"; + getLogger().log(Level.WARNING, message, ex); + throw new RuntimeException(message, ex); + } + return result; + } + + protected Object invoke(Method method, Object instance, Object... args) { + Object result = null; + try { + result = method.invoke(instance, args); + } catch (Exception ex) { + getLogger().log(Level.WARNING, method.getName() + ".invoke( ... )", ex); + } + return result; + } + + } + + private class DebugJRuby130 extends DispatchFilter { + + private Method tsAssociateThread = null; + private Method tsDissociateThread = null; + + protected void init(Ruby runtime) { + Class tsClass = runtime.getThreadService().getClass(); + tsAssociateThread = getMethod(tsClass, "associateThread", Object.class, RubyThread.class); + tsDissociateThread = getMethod(tsClass, "dissociateThread", Object.class); + } + + protected void dispatchRailsRequestImpl( + Ruby runtime, GrizzlyRequest req, GrizzlyResponse res) throws IOException { RubyThread context = getContextThread(runtime); - runtime.getThreadService().setMainThread(context); - dispatchRailsRequest(runtime, req, res); - } finally { - runtime.getThreadService().setMainThread(oldContext); + try { + invoke(tsAssociateThread, runtime.getThreadService(), Thread.currentThread(), context); + RailsAdapter.this.dispatchRailsRequest(runtime, req, res); + } finally { + invoke(tsDissociateThread, runtime.getThreadService(), Thread.currentThread()); + } } + } + private class DebugJRuby120 extends DispatchFilter { + + private Method tsAssociateThread = null; + private Method tsUnregisterThread = null; + + protected void init(Ruby runtime) { + Class tsClass = runtime.getThreadService().getClass(); + tsAssociateThread = getMethod(tsClass, "associateThread", Object.class, RubyThread.class); + tsUnregisterThread = getMethod(tsClass, "unregisterThread", RubyThread.class); + } + + protected void dispatchRailsRequestImpl( + Ruby runtime, GrizzlyRequest req, GrizzlyResponse res) throws IOException { + RubyThread context = getContextThread(runtime); + try { + invoke(tsAssociateThread, runtime.getThreadService(), Thread.currentThread(), context); + RailsAdapter.this.dispatchRailsRequest(runtime, req, res); + } finally { + invoke(tsUnregisterThread, runtime.getThreadService(), context); + } + } + + } + + private class DebugJRuby113 extends DispatchFilter { + + private Method tsSetMainThread = null; + + protected void init(Ruby runtime) { + Class tsClass = runtime.getThreadService().getClass(); + tsSetMainThread = getMethod(tsClass, "setMainThread", RubyThread.class); + } + + protected void dispatchRailsRequestImpl( + Ruby runtime, GrizzlyRequest req, GrizzlyResponse res) throws IOException { + RubyThread oldContext = runtime.getThreadService().getMainThread(); + try { + RubyThread context = getContextThread(runtime); + invoke(tsSetMainThread, runtime.getThreadService(), context); + RailsAdapter.this.dispatchRailsRequest(runtime, req, res); + } finally { + invoke(tsSetMainThread, runtime.getThreadService(), oldContext); + } + } + + } + private void dispatchRailsRequest(Ruby runtime, GrizzlyRequest req, GrizzlyResponse res) throws IOException { try { OutputStream os = new RailsOutputStream(res);