001: /*
002: * Copyright 2001-2007 Geert Bevin <gbevin[remove] at uwyn dot com>
003: * Distributed under the terms of either:
004: * - the common development and distribution license (CDDL), v1.0; or
005: * - the GNU Lesser General Public License, v2.1 or later
006: * $Id: ContinuationContext.java 3813 2007-06-25 18:22:03Z gbevin $
007: */
008: package com.uwyn.rife.continuations;
009:
010: import com.uwyn.rife.continuations.exceptions.ContinuableLocalVariableUncloneableException;
011: import com.uwyn.rife.tools.ExceptionUtils;
012: import com.uwyn.rife.tools.UniqueIDGenerator;
013: import java.lang.ref.WeakReference;
014: import java.util.ArrayList;
015: import java.util.List;
016: import java.util.logging.Logger;
017:
018: /**
019: * Contains all contextual data of one particular continuation.
020: * <p>It also provides some static retrieval methods to be able to access
021: * active continuations.
022: * <p>Active continuations are managed in a {@link ContinuationManager} so that
023: * they can be easily retrieved.
024: *
025: * @author Geert Bevin (gbevin[remove] at uwyn dot com)
026: * @version $Revision: 3813 $
027: * @see ContinuationManager
028: * @since 1.6
029: */
030: public class ContinuationContext<T extends ContinuableObject>
031: implements Cloneable {
032: private static transient ThreadLocal<ContinuationContext> sActiveContext = new ThreadLocal<ContinuationContext>();
033:
034: private static transient ThreadLocal<WeakReference<ContinuationContext>> sLastContext = new ThreadLocal<WeakReference<ContinuationContext>>();
035:
036: private transient ContinuationManager mManager = null;
037:
038: private T mContinuable = null;
039: private CallState mCreatedCallState = null;
040: private CallState mActiveCallState = null;
041: private Object mCallAnswer = null;
042: private String mId = null;
043: private String mParentId = null;
044: private List<String> mRelatedIds = null;
045: private long mStart = -1;
046:
047: private int mLabel = -1;
048: private boolean mPaused = false;
049:
050: private ContinuationStack mLocalVars = null;
051: private ContinuationStack mLocalStack = null;
052:
053: /**
054: * [PRIVATE AND UNSUPPORTED] Creates a new continuation context or resets
055: * its expiration time.
056: * <p>This method is used by the instrumented bytecode that provides
057: * continuations support, it's not intended for general use.
058: *
059: * @return a new {@code ContinuationContext}, or the active one with
060: * its expiration time being reset
061: * @since 1.6
062: */
063: public static ContinuationContext createOrResetContext(
064: Object executingInstance) {
065: ContinuationContext context = getActiveContext();
066: if (null == context) {
067: ContinuationConfigRuntime config = ContinuationConfigRuntime
068: .getActiveConfigRuntime();
069: ContinuableObject continuable = config
070: .getAssociatedContinuableObject(executingInstance);
071: context = new ContinuationContext(config
072: .getContinuationManager(continuable), continuable);
073:
074: // check if the last continuation created a call continuation, in that case
075: // pass the call state on to this new continuation
076: ContinuationContext last_context = getLastContext();
077: if (last_context != null) {
078: CallState call_state = null;
079: if (last_context.getCreatedCallState() != null) {
080: call_state = last_context.getCreatedCallState();
081: } else {
082: call_state = last_context.getActiveCallState();
083: }
084:
085: if (call_state != null) {
086: context.setActiveCallState(call_state);
087: }
088: }
089: } else {
090: context.resetStart();
091: }
092:
093: setActiveContext(context);
094:
095: // preserve a reference to the last executed continuation, so that it's
096: // possible to detect call continuations
097: sLastContext
098: .set(new WeakReference<ContinuationContext>(context));
099:
100: return context;
101: }
102:
103: /**
104: * Clears the active currently continuation context for the executing thread.
105: *
106: * @since 1.6
107: */
108: public static void clearActiveContext() {
109: sActiveContext.remove();
110: }
111:
112: /**
113: * Retrieves the identifier of the currently active continuation for the
114: * current thread.
115: *
116: * @return the identifier of the currently active continuation as a unique
117: * string; or
118: * <p>{@code null} if no continuation is currently active
119: * @see #getActiveContext
120: * @since 1.6
121: */
122: public static String getActiveContextId() {
123: ContinuationContext context = sActiveContext.get();
124: if (null == context) {
125: return null;
126: }
127: return context.getId();
128: }
129:
130: /**
131: * Retrieves the currently active continuation for the executing thread.
132: *
133: * @return the currently active continuation; or
134: * <p>{@code null} if no continuation is currently active
135: * @see #getActiveContextId
136: * @since 1.6
137: */
138: public static ContinuationContext getActiveContext() {
139: return sActiveContext.get();
140: }
141:
142: /**
143: * Replaces the active continuation context for the executing thread.
144: *
145: * @param context the new {@code ContinuationContext} that will be active; or
146: * {@code null} if no continuation context should be active
147: * @see #setActiveContext
148: * @since 1.6
149: */
150: public static void setActiveContext(ContinuationContext context) {
151: sActiveContext.set(context);
152: }
153:
154: /**
155: * Retrieves the last active continuation for the executing thread.
156: *
157: * @return the last active continuation; or
158: * <p>{@code null} if no continuation was active
159: * @since 1.6
160: */
161: public static ContinuationContext getLastContext() {
162: WeakReference<ContinuationContext> reference = sLastContext
163: .get();
164: if (reference != null) {
165: return reference.get();
166: }
167: return null;
168: }
169:
170: private ContinuationContext(ContinuationManager manager,
171: T continuable) {
172: mManager = manager;
173: mContinuable = continuable;
174:
175: resetId();
176: resetStart();
177:
178: mLabel = -1;
179:
180: mLocalVars = new ContinuationStack().initialize();
181: mLocalStack = new ContinuationStack().initialize();
182: }
183:
184: /**
185: * Retrieves the manager of this {@code ContinuationContext}.
186: *
187: * @return this continuation's manager instance
188: * @since 1.6
189: */
190: public ContinuationManager getManager() {
191: return mManager;
192: }
193:
194: synchronized void setManager(ContinuationManager manager) {
195: mManager = manager;
196: }
197:
198: /**
199: * Registers this continuation in its manager, so that it can be retrieved later.
200: * @since 1.6
201: */
202: public void registerContext() {
203: synchronized (mManager) {
204: mManager.addContext(this );
205: }
206: }
207:
208: /**
209: * Makes sure that this {@code ContinuationContext} is not the active
210: * one.
211: *
212: * @since 1.6
213: */
214: public void deactivate() {
215: if (this == getActiveContext()) {
216: clearActiveContext();
217: }
218: }
219:
220: /**
221: * Removes this {@code ContinuationContext} instance from its {@link
222: * ContinuationManager}.
223: *
224: * @since 1.6
225: */
226: public synchronized void remove() {
227: mManager.removeContext(mId);
228: deactivate();
229: }
230:
231: /**
232: * Removes the entire continuation tree that this
233: * {@code ContinuationContext} instance belongs to from its {@link
234: * ContinuationManager}.
235: *
236: * @since 1.6
237: */
238: public void removeContextTree() {
239: synchronized (mManager) {
240: mManager.removeContext(mId);
241:
242: if (mRelatedIds != null) {
243:
244: ContinuationContext child;
245: for (String id : mRelatedIds) {
246: child = mManager.getContext(id);
247: if (child != null) {
248: child.removeContextTree();
249: }
250: }
251: }
252:
253: ContinuationContext parent = getParentContext();
254: if (parent != null) {
255: parent.removeContextTree();
256: }
257:
258: deactivate();
259: }
260: }
261:
262: /**
263: * Retrieves the unique identifier of the parent continuation of this
264: * {@code ContinuationContext} instance.
265: *
266: * @return the parent's identifier as a unique string; or
267: * <p>{@code null} if this {@code ContinuationContext} has no
268: * parent
269: * @see #getParentContext
270: * @since 1.6
271: */
272: public String getParentContextId() {
273: return mParentId;
274: }
275:
276: /**
277: * Retrieves the parent {@code ContinuationContext} of this
278: * {@code ContinuationContext} instance.
279: *
280: * @return the parent {@code ContinuationContext}; or
281: * <p>{@code null} if this {@code ContinuationContext} has no
282: * parent
283: * @see #getParentContextId
284: * @since 1.6
285: */
286: public ContinuationContext getParentContext() {
287: return mManager.getContext(getParentContextId());
288: }
289:
290: /**
291: * Retrieves the answer that the call continuation stored in this context.
292: *
293: * @return the call continuation's answer; or
294: * <p>{@code null} if no answer was provided or the corresponding
295: * continuation wasn't a call continuation
296: * @since 1.6
297: */
298: public Object getCallAnswer() {
299: return mCallAnswer;
300: }
301:
302: /**
303: * [PRIVATE AND UNSUPPORTED] Sets whether the continuation if paused.
304: * <p>This method is used by the internals that provide continuations
305: * support, it's not intended for general use.
306: *
307: * @param paused {@code true} if the continuation is paused; or
308: * <p>{@code false} otherwise
309: * @see #isPaused()
310: * @since 1.6
311: */
312: public synchronized void setPaused(boolean paused) {
313: mPaused = paused;
314: }
315:
316: /**
317: * Indicates whether this continuation is actually paused and can be resumed.
318: *
319: * @return {@code true} if the continuation is paused; or
320: * <p>{@code false} otherwise
321: * @since 1.6
322: */
323: public boolean isPaused() {
324: return mPaused;
325: }
326:
327: /**
328: * [PRIVATE AND UNSUPPORTED] Set the number of the bytecode label where
329: * the continuation has to resume execution from.
330: * <p>This method is used by the instrumented bytecode that provides
331: * continuations support, it's not intended for general use.
332: *
333: * @param label the number of the resumed bytecode label
334: * @since 1.6
335: */
336: public synchronized void setLabel(int label) {
337: mLabel = label;
338: }
339:
340: /**
341: * [PRIVATE AND UNSUPPORTED] Retrieves the number of the bytecode label
342: * where the continuation has to resume execution from.
343: * <p>This method is used by the instrumented bytecode that provides
344: * continuations support, it's not intended for general use.
345: *
346: * @return the number of the resumed bytecode label; or
347: * <p>{@code -1} if no label number has been set
348: * @since 1.6
349: */
350: public int getLabel() {
351: return mLabel;
352: }
353:
354: /**
355: * [PRIVATE AND UNSUPPORTED] Retrieves the local variable stack of this
356: * continuation.
357: * <p>This method is used by the instrumented bytecode that provides
358: * continuations support, it's not intended for general use.
359: *
360: * @return this continuation's local variable stack
361: * @since 1.6
362: */
363: public ContinuationStack getLocalVars() {
364: return mLocalVars;
365: }
366:
367: /**
368: * [PRIVATE AND UNSUPPORTED] Retrieves the local operand stack of this
369: * continuation.
370: * <p>This method is used by the instrumented bytecode that provides
371: * continuations support, it's not intended for general use.
372: *
373: * @return this continuation's local operand stack
374: * @since 1.6
375: */
376: public ContinuationStack getLocalStack() {
377: return mLocalStack;
378: }
379:
380: private synchronized void resetStart() {
381: mStart = System.currentTimeMillis();
382: }
383:
384: synchronized void resetId() {
385: mId = UniqueIDGenerator.generate().toString();
386: }
387:
388: /**
389: * Retrieves the continuation ID.
390: * <p>Note that this ID is not necessarily present in the manager and that
391: * trying to retrieve a continuation afterwards from its ID is never
392: * guaranteed to give a result.
393: *
394: * @return the unique ID of this continuation.
395: * @since 1.6
396: */
397: public String getId() {
398: return mId;
399: }
400:
401: /**
402: * Retrieves the ID of this continuation's parent.
403: *
404: * @return the ID of this continuation's parent continuation; or
405: * <p>{@code null} if this continuation has no parent.
406: * @since 1.6
407: */
408: public String getParentId() {
409: return mParentId;
410: }
411:
412: /**
413: * [PRIVATE AND UNSUPPORTED] Associates the ID of another continuation to
414: * this continuation.
415: * <p>This method is used by the instrumented bytecode that provides
416: * continuations support, it's not intended for general use.
417: *
418: * @param id the ID of another continuation that's related to this
419: * continuation
420: * @since 1.6
421: */
422: public synchronized void addRelatedId(String id) {
423: if (null == mRelatedIds) {
424: mRelatedIds = new ArrayList<String>();
425: }
426: mRelatedIds.add(id);
427: }
428:
429: /**
430: * [PRIVATE AND UNSUPPORTED] Set the ID of this continuation's parent.
431: * <p>This method is used by the instrumented bytecode that provides
432: * continuations support, it's not intended for general use.
433: *
434: * @param id the ID of this continuation's parent
435: * @see #getParentId()
436: * @since 1.6
437: */
438: public synchronized void setParentId(String id) {
439: mParentId = id;
440: }
441:
442: /**
443: * Returns the object instance in which this continuation was executing.
444: *
445: * @return this continuation's active object
446: * @since 1.6
447: */
448: public T getContinuable() {
449: return mContinuable;
450: }
451:
452: /**
453: * Sets the call continuation's state when a new call continuation is
454: * created.
455: * <p>This state initiates a call continuation and should be set when
456: * a new call happens, after that it should never be changed.
457: *
458: * @param createdCallState this call continuation's creation state
459: * @see #getCreatedCallState()
460: * @since 1.6
461: */
462: public synchronized void setCreatedCallState(
463: CallState createdCallState) {
464: mCreatedCallState = createdCallState;
465: }
466:
467: /**
468: * Retrieves this continuation's call continuation creation state.
469: * <p>If this returns a non-null value, you can detect from it that this
470: * was a call continuation.
471: *
472: * @return this continuation
473: * @see #setCreatedCallState(CallState)
474: * @since 1.6
475: */
476: public CallState getCreatedCallState() {
477: return mCreatedCallState;
478: }
479:
480: /**
481: * Sets the active call state for this continuation.
482: * <p>This mainly passes on the call state that was created during a call
483: * continuation. It allows quick retrieval of the active call state when
484: * an answer occurs.
485: *
486: * @param callState the active call state
487: * @see #setCreatedCallState(CallState)
488: * @since 1.6
489: */
490: public synchronized void setActiveCallState(CallState callState) {
491: mActiveCallState = callState;
492: }
493:
494: /**
495: * Retrieves the call state that is active during this continuation.
496: *
497: * @return the active {@code CallState}; or
498: * <p>{@code null} if no call state was active for this continuation
499: */
500: public CallState getActiveCallState() {
501: return mActiveCallState;
502: }
503:
504: long getStart() {
505: return mStart;
506: }
507:
508: /**
509: * Set the answer to a call continuation.
510: *
511: * @param answer the object that will be the call continuation's answer; or
512: * {@code null} if there was no answer
513: * @since 1.6
514: */
515: public synchronized void setCallAnswer(Object answer) {
516: mCallAnswer = answer;
517: }
518:
519: /**
520: * [PRIVATE AND UNSUPPORTED] Creates a cloned instance of this
521: * continuation context, this clone is not a perfect copy but is intended
522: * to be a child continuation and all context data is setup for that.
523: * <p>This method is used by the instrumented bytecode that provides
524: * continuations support, it's not intended for general use.
525: *
526: * @return a clone of this continuation for use as a child continuation
527: * @since 1.6
528: */
529: public ContinuationContext clone()
530: throws CloneNotSupportedException {
531: ContinuationContext new_continuationcontext = null;
532: try {
533: new_continuationcontext = (ContinuationContext) super
534: .clone();
535: }
536: ///CLOVER:OFF
537: catch (CloneNotSupportedException e) {
538: // this should never happen
539: Logger.getLogger("com.uwyn.rife.continuations").severe(
540: ExceptionUtils.getExceptionStackTrace(e));
541: }
542: ///CLOVER:ON
543:
544: new_continuationcontext.mContinuable = (ContinuableObject) mContinuable
545: .clone();
546: new_continuationcontext.mCallAnswer = null;
547:
548: new_continuationcontext.mId = UniqueIDGenerator.generate()
549: .toString();
550: new_continuationcontext.mParentId = mId;
551: new_continuationcontext.mPaused = false;
552: addRelatedId(new_continuationcontext.mId);
553:
554: try {
555: new_continuationcontext.mLocalVars = mLocalVars
556: .clone(new_continuationcontext.mContinuable);
557: new_continuationcontext.mLocalStack = mLocalStack
558: .clone(new_continuationcontext.mContinuable);
559: } catch (CloneNotSupportedException e) {
560: throw new ContinuableLocalVariableUncloneableException(
561: mContinuable.getClass(), e.getMessage(), e);
562: }
563:
564: return new_continuationcontext;
565: }
566: }
|