001: /*
002: * Copyright 2005 Joe Walker
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.directwebremoting.util;
017:
018: import java.lang.reflect.InvocationTargetException;
019: import java.lang.reflect.Method;
020:
021: import javax.servlet.http.HttpServletRequest;
022:
023: import org.apache.commons.logging.LogFactory;
024: import org.apache.commons.logging.Log;
025:
026: /**
027: * A wrapper around Jetty Ajax Continuations
028: * @author Joe Walker [joe at getahead dot ltd dot uk]
029: */
030: public class Continuation {
031: /**
032: * Fish the Jetty continuation out of the request if it exists
033: * @param request The http request
034: */
035: public Continuation(HttpServletRequest request) {
036: proxy = request.getAttribute(ATTRIBUTE_JETTY_CONTINUATION);
037: if (proxy == null && isGrizzly()) {
038: try {
039: Class<?> gContinuation = LocalUtil
040: .classForName("com.sun.grizzly.Continuation");
041: Method gMethod = gContinuation
042: .getMethod("getContinuation");
043: proxy = gMethod
044: .invoke((Object[]) null, (Object[]) null);
045: } catch (Throwable ex) {
046: }
047: }
048: }
049:
050: /**
051: * Are continuations working?
052: * If this method returns false then all the other methods will fail.
053: * @return true if Jetty continuations are working
054: */
055: public boolean isAvailable() {
056: return proxy != null;
057: }
058:
059: /**
060: * Suspend the thread for a maximum of sleepTime milliseconds
061: * @param sleepTime The maximum time to wait
062: * @throws Exception If reflection breaks
063: */
064: public void suspend(long sleepTime) throws Exception {
065: try {
066: suspendMethod.invoke(proxy, sleepTime);
067: } catch (InvocationTargetException ex) {
068: rethrowWithoutWrapper(ex);
069: }
070: }
071:
072: /**
073: * Resume an continuation.
074: * For Jetty: does not work like a real continuation because it restarts
075: * the http request.
076: * @throws Exception If reflection breaks
077: */
078: public void resume() throws Exception {
079: try {
080: resumeMethod.invoke(proxy);
081: } catch (InvocationTargetException ex) {
082: rethrowWithoutWrapper(ex);
083: }
084: }
085:
086: /**
087: * Accessor for the object associated with this continuation
088: * @return the object associated with this continuation
089: * @throws Exception If reflection breaks
090: */
091: public Object getObject() throws Exception {
092: try {
093: return getObject.invoke(proxy);
094: } catch (InvocationTargetException ex) {
095: return rethrowWithoutWrapper(ex);
096: }
097: }
098:
099: /**
100: * Accessor for the object associated with this continuation
101: * @param object the object associated with this continuation
102: * @throws Exception If reflection breaks
103: */
104: public void setObject(Object object) throws Exception {
105: try {
106: setObject.invoke(proxy, object);
107: } catch (InvocationTargetException ex) {
108: rethrowWithoutWrapper(ex);
109: }
110: }
111:
112: /**
113: * We shouldn't be catching Jetty RetryRequests so we rethrow them.
114: * @param th The exception to test for continuation-ness
115: */
116: public static void rethrowIfContinuation(Throwable th) {
117: Throwable ex = th;
118:
119: if (ex instanceof InvocationTargetException) {
120: ex = ((InvocationTargetException) ex).getTargetException();
121: }
122:
123: // Allow Jetty RequestRetry exception to propogate to container!
124: if ("org.mortbay.jetty.RetryRequest".equals(ex.getClass()
125: .getName())) {
126: throw (RuntimeException) ex;
127: }
128: }
129:
130: /**
131: * Unwrap an InvocationTargetException
132: * @param ex The exception to unwrap
133: * @return Nothing. This method will not complete normally
134: * @throws Exception If reflection breaks
135: */
136: private static Object rethrowWithoutWrapper(
137: InvocationTargetException ex) throws Exception {
138: Throwable target = ex.getTargetException();
139: if (target instanceof Exception) {
140: throw (Exception) target;
141: }
142:
143: if (target instanceof Error) {
144: throw (Error) target;
145: }
146:
147: throw ex;
148: }
149:
150: /**
151: * The real continuation object
152: */
153: private Object proxy;
154:
155: /**
156: * The log stream
157: */
158: private static final Log log = LogFactory
159: .getLog(Continuation.class);
160:
161: /**
162: * The attribute under which Jetty stores it's Contuniations.
163: * TODO: This feels like a mighty hack. I hope Greg doesn't change it without telling us!
164: */
165: private static final String ATTRIBUTE_JETTY_CONTINUATION = "org.mortbay.jetty.ajax.Continuation";
166:
167: /**
168: * Jetty code used by reflection to allow it to run outside of Jetty
169: */
170: protected static Class<?> continuationClass = null;
171:
172: /**
173: * How we suspend the continuation
174: */
175: protected static Method suspendMethod = null;
176:
177: /**
178: * How we resume the continuation
179: */
180: protected static Method resumeMethod = null;
181:
182: /**
183: * How we get the associated continuation object
184: */
185: protected static Method getObject = null;
186:
187: /**
188: * How we set the associated continuation object
189: */
190: protected static Method setObject = null;
191:
192: /**
193: * Are we using Jetty at all?
194: */
195: protected static boolean isJetty = false;
196:
197: /**
198: * Are we using Grizzly at all?
199: */
200: protected static boolean isGrizzly = false;
201:
202: /**
203: * Can we use Jetty?
204: */
205: static {
206: try {
207: try {
208: continuationClass = LocalUtil
209: .classForName("org.mortbay.util.ajax.Continuation");
210: isJetty = true;
211: } catch (Exception ex) {
212: Class<?> gContinuation = LocalUtil
213: .classForName("com.sun.grizzly.Continuation");
214: Method gMethod = gContinuation
215: .getMethod("getContinuation");
216: continuationClass = gMethod.invoke(gMethod).getClass();
217: isGrizzly = true;
218: }
219:
220: suspendMethod = continuationClass.getMethod("suspend",
221: Long.TYPE);
222: resumeMethod = continuationClass.getMethod("resume");
223: getObject = continuationClass.getMethod("getObject");
224: setObject = continuationClass.getMethod("setObject",
225: Object.class);
226: } catch (Exception ex) {
227: isJetty = false;
228: log
229: .debug("No Jetty or Grizzly Continuation class, using standard Servlet API");
230: }
231: }
232:
233: /**
234: * @return True if we have detected Jetty classes
235: */
236: public static boolean isJetty() {
237: return isJetty;
238: }
239:
240: /**
241: * @return True if we have detected Grizzly classes
242: */
243: public static boolean isGrizzly() {
244: return isGrizzly;
245: }
246: }
|