/* * Copyright 2005 Joe Walker * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.directwebremoting.util; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import javax.servlet.http.HttpServletRequest; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * A wrapper around Jetty Ajax Continuations * @author Joe Walker [joe at getahead dot ltd dot uk] */ public class Continuation { /** * Fish the Jetty continuation out of the request if it exists * @param request The http request */ public Continuation(HttpServletRequest request) { // The attribute under which Jetty stores it's Continuations. Object temp = request.getAttribute("org.mortbay.jetty.ajax.Continuation"); if (temp == null && isGrizzly()) { try { // The attribute under which Grizzly stores it's Continuations. Class<?> gContinuation = LocalUtil.classForName("com.sun.grizzly.Continuation"); Method gMethod = gContinuation.getMethod("getContinuation"); temp = gMethod.invoke(null, (Object[]) null); } catch (Throwable ignored) { } } proxy = temp; } /** * Are continuations working? * If this method returns false then all the other methods will fail. * @return true if Jetty continuations are working */ public boolean isAvailable() { return proxy != null; } /** * Suspend the thread for a maximum of sleepTime milliseconds * @param sleepTime The maximum time to wait * @throws Exception If reflection breaks */ public void suspend(long sleepTime) throws Exception { try { suspendMethod.invoke(proxy, sleepTime); } catch (InvocationTargetException ex) { rethrowWithoutWrapper(ex); } } /** * Resume an continuation. * For Jetty: does not work like a real continuation because it restarts * the http request. * @throws Exception If reflection breaks */ public void resume() throws Exception { try { resumeMethod.invoke(proxy); } catch (InvocationTargetException ex) { rethrowWithoutWrapper(ex); } } /** * Accessor for the object associated with this continuation * @return the object associated with this continuation * @throws Exception If reflection breaks */ public Object getObject() throws Exception { try { return getObject.invoke(proxy); } catch (InvocationTargetException ex) { return rethrowWithoutWrapper(ex); } } /** * Accessor for the object associated with this continuation * @param object the object associated with this continuation * @throws Exception If reflection breaks */ public void setObject(Object object) throws Exception { try { setObject.invoke(proxy, object); } catch (InvocationTargetException ex) { rethrowWithoutWrapper(ex); } } /** * We shouldn't be catching Jetty RetryRequests so we re-throw them. * @param th The exception to test for continuation-ness */ public static void rethrowIfContinuation(Throwable th) { Throwable ex = th; if (ex instanceof InvocationTargetException) { ex = ((InvocationTargetException) ex).getTargetException(); } // Allow Jetty RequestRetry exception to propagate to container! if ("org.mortbay.jetty.RetryRequest".equals(ex.getClass().getName())) { throw (RuntimeException) ex; } } /** * Unwrap an InvocationTargetException * @param ex The exception to unwrap * @return Nothing. This method will not complete normally * @throws Exception If reflection breaks */ private static Object rethrowWithoutWrapper(InvocationTargetException ex) throws Exception { Throwable target = ex.getTargetException(); if (target instanceof Exception) { throw (Exception) target; } if (target instanceof Error) { throw (Error) target; } throw ex; } /** * The real continuation object */ private final Object proxy; /** * The log stream */ private static final Log log = LogFactory.getLog(Continuation.class); /** * Jetty code used by reflection to allow it to run outside of Jetty */ protected static final Class<?> continuationClass; /** * How we suspend the continuation */ protected static final Method suspendMethod; /** * How we resume the continuation */ protected static final Method resumeMethod; /** * How we get the associated continuation object */ protected static final Method getObject; /** * How we set the associated continuation object */ protected static final Method setObject; /** * Are we using Jetty at all? */ protected static boolean isJetty = false; /** * Are we using Grizzly at all? */ protected static boolean isGrizzly = false; /** * Can we use Jetty? */ static { Class<?> tempContinuationClass = null; try { try { tempContinuationClass = LocalUtil.classForName("org.mortbay.util.ajax.Continuation"); isJetty = true; } catch (Exception ex) { Class<?> gContinuation = LocalUtil.classForName("com.sun.grizzly.Continuation"); Method gMethod = gContinuation.getMethod("getContinuation"); tempContinuationClass = gMethod.invoke(gMethod).getClass(); isGrizzly = true; } } catch (Exception ex) { isJetty = false; log.debug("No Jetty or Grizzly Continuation class, using standard Servlet API"); } continuationClass = tempContinuationClass; suspendMethod = getMethod("suspend", Long.TYPE); resumeMethod = getMethod("resume"); getObject = getMethod("getObject"); setObject = getMethod("setObject", Object.class); } /** * */ private static Method getMethod(String name, Class<?>... args) { if (continuationClass == null) { return null; } try { return continuationClass.getMethod(name, args); } catch (SecurityException ex) { return null; } catch (NoSuchMethodException ex) { return null; } } /** * @return True if we have detected Jetty classes */ public static boolean isJetty() { return isJetty; } /** * @return True if we have detected Grizzly classes */ public static boolean isGrizzly() { return isGrizzly; } }