001: /*****************************************************************************
002: * *
003: * This file is part of the BeanShell Java Scripting distribution. *
004: * Documentation and updates may be found at http://www.beanshell.org/ *
005: * *
006: * Sun Public License Notice: *
007: * *
008: * The contents of this file are subject to the Sun Public License Version *
009: * 1.0 (the "License"); you may not use this file except in compliance with *
010: * the License. A copy of the License is available at http://www.sun.com *
011: * *
012: * The Original Code is BeanShell. The Initial Developer of the Original *
013: * Code is Pat Niemeyer. Portions created by Pat Niemeyer are Copyright *
014: * (C) 2000. All Rights Reserved. *
015: * *
016: * GNU Public License Notice: *
017: * *
018: * Alternatively, the contents of this file may be used under the terms of *
019: * the GNU Lesser General Public License (the "LGPL"), in which case the *
020: * provisions of LGPL are applicable instead of those above. If you wish to *
021: * allow use of your version of this file only under the terms of the LGPL *
022: * and not to allow others to use your version of this file under the SPL, *
023: * indicate your decision by deleting the provisions above and replace *
024: * them with the notice and other provisions required by the LGPL. If you *
025: * do not delete the provisions above, a recipient may use your version of *
026: * this file under either the SPL or the LGPL. *
027: * *
028: * Patrick Niemeyer (pat@pat.net) *
029: * Author of Learning Java, O'Reilly & Associates *
030: * http://www.pat.net/~pat/ *
031: * *
032: *****************************************************************************/package bsh;
033:
034: import java.lang.reflect.*;
035: import java.lang.reflect.InvocationHandler;
036: import java.io.*;
037: import java.util.Hashtable;
038:
039: /**
040: XThis is a dynamically loaded extension which extends This.java and adds
041: support for the generalized interface proxy mechanism introduced in
042: JDK1.3. XThis allows bsh scripted objects to implement arbitrary
043: interfaces (be arbitrary event listener types).
044:
045: Note: This module relies on new features of JDK1.3 and will not compile
046: with JDK1.2 or lower. For those environments simply do not compile this
047: class.
048:
049: Eventually XThis should become simply This, but for backward compatability
050: we will maintain This without requiring support for the proxy mechanism.
051:
052: XThis stands for "eXtended This" (I had to call it something).
053:
054: @see JThis See also JThis with explicit JFC support for compatability.
055: @see This
056: */
057: public class XThis extends This {
058: /**
059: A cache of proxy interface handlers.
060: Currently just one per interface.
061: */
062: Hashtable interfaces;
063:
064: InvocationHandler invocationHandler = new Handler();
065:
066: public XThis(NameSpace namespace, Interpreter declaringInterp) {
067: super (namespace, declaringInterp);
068: }
069:
070: public String toString() {
071: return "'this' reference (XThis) to Bsh object: " + namespace;
072: }
073:
074: /**
075: Get dynamic proxy for interface, caching those it creates.
076: */
077: public Object getInterface(Class clas) {
078: return getInterface(new Class[] { clas });
079: }
080:
081: /**
082: Get dynamic proxy for interface, caching those it creates.
083: */
084: public Object getInterface(Class[] ca) {
085: if (interfaces == null)
086: interfaces = new Hashtable();
087:
088: // Make a hash of the interface hashcodes in order to cache them
089: int hash = 21;
090: for (int i = 0; i < ca.length; i++)
091: hash *= ca[i].hashCode() + 3;
092: Object hashKey = new Integer(hash);
093:
094: Object interf = interfaces.get(hashKey);
095:
096: if (interf == null) {
097: ClassLoader classLoader = ca[0].getClassLoader(); // ?
098: interf = Proxy.newProxyInstance(classLoader, ca,
099: invocationHandler);
100: interfaces.put(hashKey, interf);
101: }
102:
103: return interf;
104: }
105:
106: /**
107: This is the invocation handler for the dynamic proxy.
108: <p>
109:
110: Notes:
111: Inner class for the invocation handler seems to shield this unavailable
112: interface from JDK1.2 VM...
113:
114: I don't understand this. JThis works just fine even if those
115: classes aren't there (doesn't it?) This class shouldn't be loaded
116: if an XThis isn't instantiated in NameSpace.java, should it?
117: */
118: class Handler implements InvocationHandler, java.io.Serializable {
119: public Object invoke(Object proxy, Method method, Object[] args)
120: throws Throwable {
121: try {
122: return invokeImpl(proxy, method, args);
123: } catch (TargetError te) {
124: // Unwrap target exception. If the interface declares that
125: // it throws the ex it will be delivered. If not it will be
126: // wrapped in an UndeclaredThrowable
127: throw te.getTarget();
128: } catch (EvalError ee) {
129: // Ease debugging...
130: // XThis.this refers to the enclosing class instance
131: if (Interpreter.DEBUG)
132: Interpreter
133: .debug("EvalError in scripted interface: "
134: + XThis.this .toString() + ": " + ee);
135: throw ee;
136: }
137: }
138:
139: public Object invokeImpl(Object proxy, Method method,
140: Object[] args) throws EvalError {
141: String methodName = method.getName();
142: CallStack callstack = new CallStack(namespace);
143:
144: /*
145: If equals() is not explicitly defined we must override the
146: default implemented by the This object protocol for scripted
147: object. To support XThis equals() must test for equality with
148: the generated proxy object, not the scripted bsh This object;
149: otherwise callers from outside in Java will not see a the
150: proxy object as equal to itself.
151: */
152: BshMethod equalsMethod = null;
153: try {
154: equalsMethod = namespace.getMethod("equals",
155: new Class[] { Object.class });
156: } catch (UtilEvalError e) {/*leave null*/
157: }
158: if (methodName.equals("equals") && equalsMethod == null) {
159: Object obj = args[0];
160: return proxy == obj ? Boolean.TRUE : Boolean.FALSE;
161: }
162:
163: /*
164: If toString() is not explicitly defined override the default
165: to show the proxy interfaces.
166: */
167: BshMethod toStringMethod = null;
168: try {
169: toStringMethod = namespace.getMethod("toString",
170: new Class[] {});
171: } catch (UtilEvalError e) {/*leave null*/
172: }
173:
174: if (methodName.equals("toString") && toStringMethod == null) {
175: Class[] ints = proxy.getClass().getInterfaces();
176: // XThis.this refers to the enclosing class instance
177: StringBuffer sb = new StringBuffer(XThis.this
178: .toString()
179: + "\nimplements:");
180: for (int i = 0; i < ints.length; i++)
181: sb.append(" " + ints[i].getName()
182: + ((ints.length > 1) ? "," : ""));
183: return sb.toString();
184: }
185:
186: Class[] paramTypes = method.getParameterTypes();
187: return Primitive.unwrap(invokeMethod(methodName, Primitive
188: .wrap(args, paramTypes)));
189: }
190: };
191: }
|