Java tutorial
/* * 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.server.jetty; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import javax.servlet.http.HttpServletRequest; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.directwebremoting.extend.Sleeper; import org.directwebremoting.impl.ThreadWaitSleeper; import org.directwebremoting.util.Continuation; /** * A Sleeper that works with Jetty Continuations * @author Joe Walker [joe at getahead dot ltd dot uk] * @author Tim Peierls [tim at peierls dot net] */ public class JettyContinuationSleeper implements Sleeper { /** * @param request The request into which we store this as an attribute */ public JettyContinuationSleeper(HttpServletRequest request) { this.continuation = new Continuation(request); this.request = request; } /** * Is this a restarted continuation? * @param request The request on which a Sleeper might be stored * @return true if this request is from a restarted Continuation */ public static boolean isRestart(HttpServletRequest request) { return getSleeper(request) != null; } /** * Act on a restarted continuation by executing the onAwakening action * @param request The request on which the Sleeper is stored */ public static void restart(HttpServletRequest request) { JettyContinuationSleeper sleeper = getSleeper(request); if (sleeper == null) { throw new IllegalStateException("No JettyContinuationSleeper in HttpServletRequest"); } request.removeAttribute(ATTRIBUTE_CONDUIT); sleeper.onAwakening.run(); } /* (non-Javadoc) * @see org.directwebremoting.dwrp.Sleeper#goToSleep(java.lang.Runnable) */ public void goToSleep(Runnable awakening) { if (awakening == null) { throw new NullPointerException("Null value for awakening"); } if (state.compareAndSet(State.INITIAL, State.ABOUT_TO_SLEEP)) { try { continuation.suspend(); } catch (Exception ex) { Continuation.rethrowIfContinuation(ex); // Log unsuccessful attempt to use Jetty continuation. log.warn("Exception", ex); // Revert to thread waiting. proxy = new ThreadWaitSleeper(); state.set(State.BLOCKED); // write volatile // Block until wakeUp call. proxy.goToSleep(awakening); } finally { if (state.compareAndSet(State.BLOCKED, State.FINAL)) { // We have just been awakened from BLOCKED and // awakening task has run. } else // state.get() == ABOUT_TO_SLEEP { // This is a rethrow for a Jetty continuation, // so we store the onAwakening task and tell the // request how to restart on continuation resume. onAwakening = awakening; saveSleeperOnRequest(); state.set(State.SLEEPING); // write volatile } } } else if (state.compareAndSet(State.PRE_AWAKENED, State.FINAL)) { awakening.run(); } else { throw new IllegalStateException("Attempt to goToSleep in state " + state.get()); } } /* (non-Javadoc) * @see org.directwebremoting.extend.Sleeper#wakeUp() */ public void wakeUp() { boolean retry; do { retry = false; switch (state.get()) // read volatile { case INITIAL: // We might have been awakened before goToSleep. state.compareAndSet(State.INITIAL, State.PRE_AWAKENED); retry = true; break; case PRE_AWAKENED: // Do nothing now; goToSleep will eventually run // its onAwakening argument. break; case ABOUT_TO_SLEEP: // Spin until we're either SLEEPING or BLOCKED. // This case is unlikely, but if it does occur, // the spin won't be for very long. try { do { TimeUnit.MILLISECONDS.sleep(1); } while (state.get() == State.ABOUT_TO_SLEEP); retry = true; } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } break; case SLEEPING: if (state.compareAndSet(State.SLEEPING, State.RESUMING)) { try { continuation.resume(); } catch (Exception ex) { log.error("Broken reflection", ex); } } else { // Someone else got in first. The only states // we could be in now are going to ignore the // wakeUp call, but for completeness we retry. retry = true; } break; case BLOCKED: // Cause onAwakening to be called in blocked thread. proxy.wakeUp(); break; case RESUMING: case FINAL: // wakeUp called already, nothing to do. break; } } while (retry); } /** * Internal transition after request is restarted. */ private void resume() { if (!state.compareAndSet(State.RESUMING, State.FINAL)) // read-volatile { throw new IllegalStateException("Attempt to resume from state " + state.get()); } request.removeAttribute(ATTRIBUTE_CONDUIT); onAwakening.run(); } /** * Stash this sleeper on its request. */ private void saveSleeperOnRequest() { request.setAttribute(ATTRIBUTE_CONDUIT, this); } /** * Retrieve a stashed sleeper from a request. */ private static JettyContinuationSleeper getSleeper(HttpServletRequest request) { return (JettyContinuationSleeper) request.getAttribute(ATTRIBUTE_CONDUIT); } enum State { INITIAL, // the state at construction time PRE_AWAKENED, // wakeUp called before goToSleep ABOUT_TO_SLEEP, // trying to sleep SLEEPING, // sleeping using continuation BLOCKED, // sleeping by blocking with ThreadWaitSleeper RESUMING, // resuming from continuation sleep FINAL, // the state after resumption or pre-awakened sleep attempt } /** * Atomic enum to manage state. */ private final AtomicReference<State> state = new AtomicReference<State>(State.INITIAL); /** * If continuations fail, we proxy to a Thread Wait version */ /* @GuardedBy("state") */private ThreadWaitSleeper proxy = null; /** * The continuation object */ private final Continuation continuation; /** * What we do when we are woken up */ /* @GuardedBy("state") */private Runnable onAwakening; /** * The request on which we save this Sleeper for later retrieval. */ private final HttpServletRequest request; /** * We remember the notify conduit so we can reuse it */ protected static final String ATTRIBUTE_CONDUIT = "org.directwebremoting.server.jetty.notifyConduit"; /** * The log stream */ private static final Log log = LogFactory.getLog(JettyContinuationSleeper.class); }