001: package sisc.interpreter;
002:
003: import java.io.IOException;
004: import java.util.*;
005: import sisc.env.DynamicEnvironment;
006: import sisc.util.Util;
007:
008: /**
009: * Context is a utility class which facilitates Java to Scheme
010: * calls in SISC.
011: *
012: * Typically, this involves obtaining an Interpreter for a given
013: * initialized {@link sisc.interpreter.AppContext}, using the Interpreter for one or more
014: * evaluations, then exiting the context, as follows:
015: * <pre>
016: * AppContext ctx = ...
017: * Interpreter r=Context.enter(ctx);
018: * r.eval(someProcedure, new Value[] { ... some arguments ... });
019: * Context.exit();
020: * </pre>
021: * Preferrably, one should use the SchemeCaller supported visitor pattern
022: * to allow Context to handle the management of the Interpreter Context,
023: * as follows:
024: * <pre>
025: * Object returnValue=Context.execute(ctx, mySchemeCaller);
026: * </pre>
027: * The provided SchemeCaller instance's execute method is invoked with
028: * an Interpreter which is automatically obtained and then returned when
029: * the call completes. The return value of the SchemeCaller's execute
030: * method is returned from the Context method.
031: *
032: * @see SchemeCaller
033: * @see AppContext
034: * @see Interpreter
035: */
036: public abstract class Context extends Util {
037:
038: //"appname" -> AppContext
039: private static Map apps = Collections
040: .synchronizedMap(new HashMap());
041:
042: //Thread -> Context
043: private static ThreadLocal currentThreadContext = new ThreadLocal() {
044: protected Object initialValue() {
045: return new ThreadContext();
046: }
047: };
048:
049: private static volatile AppContext defaultAppContext;
050:
051: /*********** application table maintenance ***********/
052:
053: /**
054: * @deprecated
055: */
056: public static void register(String appName, AppContext ctx) {
057: apps.put(appName, ctx);
058: }
059:
060: /**
061: * @deprecated
062: */
063: public static void unregister(String appName) {
064: apps.remove(appName);
065: }
066:
067: /**
068: * @deprecated
069: */
070: public static AppContext lookup(String appName) {
071: return (AppContext) apps.get(appName);
072: }
073:
074: /*********** thread context lookup ***********/
075:
076: public static ThreadContext lookupThreadContext() {
077: return (ThreadContext) currentThreadContext.get();
078: }
079:
080: /*********** main interface ***********/
081:
082: /**
083: * Fetches the current Interpreter, if this is an internal call (that is,
084: * a call from Java to Scheme in the contex of a Scheme->Java call).
085: *
086: * @return the current Interpreter, or null if this is not an
087: * internal call.
088: */
089: public static Interpreter currentInterpreter() {
090: ThreadContext tctx = lookupThreadContext();
091: return tctx.currentInterpreter();
092: }
093:
094: /**
095: * Fetches the nearest enclosing Interpreter that operates on a
096: * given AppContext, if this is an internal call (that is, a call
097: * from Java to Scheme in the contex of a Scheme->Java call).
098: *
099: * @param ctx the AppContext
100: * @return the nearest enclosing Interpreter operating on the
101: * given ctx, or null if no such Interpreter exists.
102: */
103: public static Interpreter currentInterpreter(AppContext ctx) {
104: ThreadContext tctx = lookupThreadContext();
105: return tctx.currentInterpreter(ctx);
106: }
107:
108: /**
109: * Sets the <i>default AppContext</i>, which is used sparingly whenever
110: * a call originates from uncontrolled Java source that involves
111: * the Scheme environment. For example, Java serialization initiated
112: * by a web application server.
113: *
114: * @param ctx the AppContext to make the default.
115: */
116: public synchronized static void setDefaultAppContext(AppContext ctx) {
117: defaultAppContext = ctx;
118: }
119:
120: /**
121: * Returns the currently set default AppContext, or creates
122: * a new AppContext with default values if non was already set,
123: * and attempts to initialize it with the default heap.
124: *
125: */
126: public synchronized static AppContext getDefaultAppContext() {
127: if (defaultAppContext == null) {
128: setDefaultAppContext(new AppContext());
129: try {
130: defaultAppContext.addDefaultHeap();
131: } catch (IOException e) {
132: throw new RuntimeException(Util.liMessage(Util.SISCB,
133: "errorloadingheap"));
134: }
135: }
136:
137: return defaultAppContext;
138: }
139:
140: /**
141: * Returns the AppContext of any current interpreter in an
142: * internal call, or the default AppContext if no current
143: * interpreter is present.
144: */
145: public static AppContext currentAppContext() {
146: Interpreter r = currentInterpreter();
147: return (r == null ? getDefaultAppContext() : r.getCtx());
148: }
149:
150: /**
151: * Returns an Interpreter that shares the AppContext and
152: * DynamicEnvironment with the current Interpreter. If there is no
153: * current Interpreter present, an Interpreter bound to the
154: * default AppContext is returned instead.
155: *
156: * This method provides the usual mechanism for obtaining an
157: * Interpreter for calling from Java to Scheme.
158: *
159: * @see Interpreter
160: */
161: public static Interpreter enter() {
162: Interpreter r = currentInterpreter();
163: return enter(r == null ? new DynamicEnvironment(
164: getDefaultAppContext()) : r.dynenv);
165: }
166:
167: /**
168: * Returns an Interpreter bound to the given AppContext with same
169: * DynamicEnvironment as the nearest enclosing Interpreter in the
170: * same thread that is bound to the same AppContext. If no such
171: * Interpreter exists then a new DynamicEnvironment is created,
172: * bound to the AppContext.
173: *
174: * @param ctx The AppContext
175: * @return The newly created Interpreter
176: */
177: public static Interpreter enter(AppContext ctx) {
178: Interpreter r = currentInterpreter(ctx);
179: return enter(r == null ? new DynamicEnvironment(ctx) : r.dynenv);
180: }
181:
182: /**
183: * Returns an Interpreter bound to the given DynamicEnvironment.
184: *
185: * @param dynenv The DynamicEnvironment
186: * @return The newly created Interpreter
187: */
188: public static Interpreter enter(DynamicEnvironment dynenv) {
189:
190: dynenv.bind();
191:
192: //set thread's context class loader
193: ClassLoader currentClassLoader = Util.currentClassLoader();
194: ClassLoader newClassLoader = determineClassLoader(
195: currentClassLoader, dynenv.getClassLoader());
196: Thread currentThread = Thread.currentThread();
197: try {
198: currentThread.setContextClassLoader(newClassLoader);
199: } catch (java.security.AccessControlException e) {
200: }
201:
202: ThreadContext tctx = lookupThreadContext();
203: tctx.setHostThread(dynenv, currentThread);
204: Interpreter res = createInterpreter(tctx, dynenv);
205: tctx
206: .pushState(new ThreadContext.State(res,
207: currentClassLoader));
208: return res;
209: }
210:
211: private static ClassLoader determineClassLoader(
212: ClassLoader currentCL, ClassLoader newCL) {
213: try {
214: for (ClassLoader cl = newCL; cl != null; cl = cl
215: .getParent()) {
216: if (currentCL == cl)
217: return newCL;
218: }
219: } catch (java.security.AccessControlException e) {
220: }
221: return currentCL;
222: }
223:
224: /**
225: *
226: * @deprecated use {@link #enter(AppContext)} instead
227: */
228: public static Interpreter enter(String appName) {
229: return enter(lookup(appName));
230: }
231:
232: /**
233: * Exits the current context, releasing the current Interpreter.
234: */
235: public static void exit() {
236: ThreadContext tctx = lookupThreadContext();
237: ThreadContext.State s = tctx.popState();
238: Interpreter r = s.interpreter;
239: returnInterpreter(r);
240:
241: //restore thread's context class loader
242: try {
243: Thread.currentThread().setContextClassLoader(s.classLoader);
244: } catch (java.security.AccessControlException e) {
245: }
246: }
247:
248: /**
249: * Calls caller with an Interpreter that shares the AppContext and
250: * DynamicEnvironment with the current Interpreter. If there is no
251: * current Interpreter present, an Interpreter bound to the
252: * default AppContext is created instead.
253: *
254: * This method provides the usual mechanism for managed calls from
255: * Java to Scheme.
256: *
257: * @param caller The SchemeCaller to invoke
258: * @return the result of invoking the SchemeCaller
259: */
260: public static Object execute(SchemeCaller caller)
261: throws SchemeException {
262: Interpreter r = currentInterpreter();
263: return execute((r == null ? new DynamicEnvironment(
264: getDefaultAppContext()) : r.dynenv), caller);
265: }
266:
267: /**
268: * Calls caller with an Interpreter bound to the given AppContext
269: * with same DynamicEnvironment as the nearest enclosing
270: * Interpreter in the same thread that is bound to the same
271: * AppContext. If no such Interpreter exists then a new
272: * DynamicEnvironment is returned, bound to the AppContext.
273: *
274: * @param ctx The AppContext
275: * @param caller The SchemeCaller to invoke.
276: * @return the result of invoking the SchemeCaller
277: */
278: public static Object execute(AppContext ctx, SchemeCaller caller)
279: throws SchemeException {
280: Interpreter r = currentInterpreter(ctx);
281: return execute((r == null ? new DynamicEnvironment(ctx)
282: : r.dynenv), caller);
283: }
284:
285: /**
286: * Obtains an Interpreter bound to the given DynamicEnvironment
287: * and invokes caller.execute(Interpreter) with that Interper.
288: * Once execute returns, the Interpreter is freed, and the return
289: * value of caller.execute() is returned from this method.
290: *
291: * NB: It is critical that the Interpreter reference provided
292: * during the call is used only in the thread which calls this
293: * method. New threads should obtain a different Interpreter via
294: * this or enter() calls.
295: *
296: * @param dynenv The DynamicEnvironment.
297: * @param caller The SchemeCaller to invoke.
298: * @return the result of invoking the SchemeCaller
299: */
300: public static Object execute(DynamicEnvironment dynenv,
301: SchemeCaller caller) throws SchemeException {
302: Interpreter r = Context.enter(dynenv);
303: //Hold this reference. Necessary because ThreadContext
304: //references hostThread only weakly, which is in turn
305: //necessary so that when threads terminate their associated
306: //SISC resources are garbage collected. In this case,
307: //however, we want to guarantee that during the lifetime of
308: //the call, the SchemeThread wrapper object remains available
309: Object t = r.tctx.hostThread.get();
310:
311: try {
312: return caller.execute(r);
313: } finally {
314: if (r != null)
315: Context.exit();
316: }
317: }
318:
319: /**
320: * @deprecated use {@link #execute(AppContext, SchemeCaller)} instead
321: */
322: public static Object execute(String appName, SchemeCaller caller) {
323: //Since this is deprecated, we'll catch the exception to preserve
324: //backwards compatibility through one release.
325: try {
326: return execute(lookup(appName), caller);
327: } catch (SchemeException se) {
328: System.err
329: .println(warn("SchemeException caught from execute:"
330: + se.getMessage()));
331: return null;
332: }
333: }
334:
335: /*********** resource maintenance ***********/
336:
337: /**
338: * We could use a pool of interpreters. However,
339: * a) interpreter creation is quite cheap, and
340: * b) pool maintenance would require thread synchronization
341: */
342: private static Interpreter createInterpreter(ThreadContext tctx,
343: DynamicEnvironment dynenv) {
344: return new Interpreter(tctx, dynenv);
345: }
346:
347: private static void returnInterpreter(Interpreter r) {
348: }
349:
350: }
351:
352: /*
353: * The contents of this file are subject to the Mozilla Public
354: * License Version 1.1 (the "License"); you may not use this file
355: * except in compliance with the License. You may obtain a copy of
356: * the License at http://www.mozilla.org/MPL/
357: *
358: * Software distributed under the License is distributed on an "AS
359: * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
360: * implied. See the License for the specific language governing
361: * rights and limitations under the License.
362: *
363: * The Original Code is the Second Interpreter of Scheme Code (SISC).
364: *
365: * The Initial Developer of the Original Code is Scott G. Miller.
366: * Portions created by Scott G. Miller are Copyright (C) 2000-2007
367: * Scott G. Miller. All Rights Reserved.
368: *
369: * Contributor(s):
370: * Matthias Radestock
371: *
372: * Alternatively, the contents of this file may be used under the
373: * terms of the GNU General Public License Version 2 or later (the
374: * "GPL"), in which case the provisions of the GPL are applicable
375: * instead of those above. If you wish to allow use of your
376: * version of this file only under the terms of the GPL and not to
377: * allow others to use your version of this file under the MPL,
378: * indicate your decision by deleting the provisions above and
379: * replace them with the notice and other provisions required by
380: * the GPL. If you do not delete the provisions above, a recipient
381: * may use your version of this file under either the MPL or the
382: * GPL.
383: */
|