PageRenderTime 34ms CodeModel.GetById 14ms app.highlight 14ms RepoModel.GetById 1ms app.codeStats 0ms

/interpreter/tags/at2dist130208/src/edu/vub/at/objects/coercion/Coercer.java

http://ambienttalk.googlecode.com/
Java | 201 lines | 85 code | 20 blank | 96 comment | 23 complexity | ed99002b47c623eb17650e9d3b37f4db MD5 | raw file
  1/**
  2 * AmbientTalk/2 Project
  3 * Coercer.java created on 3-okt-2006 at 16:12:05
  4 * (c) Programming Technology Lab, 2006 - 2007
  5 * Authors: Tom Van Cutsem & Stijn Mostinckx
  6 * 
  7 * Permission is hereby granted, free of charge, to any person
  8 * obtaining a copy of this software and associated documentation
  9 * files (the "Software"), to deal in the Software without
 10 * restriction, including without limitation the rights to use,
 11 * copy, modify, merge, publish, distribute, sublicense, and/or
 12 * sell copies of the Software, and to permit persons to whom the
 13 * Software is furnished to do so, subject to the following
 14 * conditions:
 15 *
 16 * The above copyright notice and this permission notice shall be
 17 * included in all copies or substantial portions of the Software.
 18 *
 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 21 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 22 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 23 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 24 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 26 * OTHER DEALINGS IN THE SOFTWARE.
 27 */
 28package edu.vub.at.objects.coercion;
 29
 30import edu.vub.at.actors.eventloops.EventLoop;
 31import edu.vub.at.actors.eventloops.EventLoop.EventProcessor;
 32import edu.vub.at.actors.natives.ELActor;
 33import edu.vub.at.exceptions.XIllegalOperation;
 34import edu.vub.at.exceptions.XTypeMismatch;
 35import edu.vub.at.objects.ATObject;
 36import edu.vub.at.objects.mirrors.Reflection;
 37import edu.vub.at.objects.symbiosis.Symbiosis;
 38
 39import java.io.IOException;
 40import java.io.Serializable;
 41import java.lang.reflect.InvocationHandler;
 42import java.lang.reflect.InvocationTargetException;
 43import java.lang.reflect.Method;
 44import java.lang.reflect.Proxy;
 45
 46/**
 47 * A coercer is a dynamic proxy which is used to 'cast' Ambienttalk base-level NATObjects to a certain ATxxx interface.
 48 * The dynamic proxy is responsible for transforming java calls to meta_invoke calls.
 49 * 
 50 * For example, a method from an AT interface
 51 * 
 52 * ATExpression base_expression() throws NATException
 53 * 
 54 * receives the following implementation:
 55 * 
 56 * ATExpression expression() throws NATException {
 57 *	return principal.meta_invoke(principal, Reflection.downSelector("getExpression"), NATTable.EMPTY).asExpression();
 58 * }
 59 * 
 60 * where principal is the original object 'coerced into' the given interface
 61 * 
 62 * @author tvcutsem
 63 */
 64public final class Coercer implements InvocationHandler, Serializable {
 65	
 66	private final ATObject principal_;
 67	
 68	// we have to remember which thread owned the principal
 69	private transient Thread wrappingThread_;
 70	
 71	private Coercer(ATObject principal, Thread owningThread) {
 72		principal_ = principal;
 73		wrappingThread_ = owningThread;
 74	}
 75	
 76	public String toString() {
 77		return "<coercer on: "+principal_+">";
 78	}
 79	
 80	/**
 81	 * Try to coerce the given AmbientTalk object into the given Java type. This variant implicitly assumes that
 82	 * the coercion is performed by the thread owning the object, which is the case when coercing arguments to a
 83	 * Java method call or when passing an AmbientTalk object as a result.
 84	 * 
 85	 * Note that the returned coercer object is also an instance of {@link ATObject} and
 86	 * in the AmbientTalk world the coercer will behave as its coerced AT object.
 87	 * 
 88	 * @param object the AmbientTalk object to coerce
 89	 * @param type the class object representing the target type
 90	 * @return a Java object <tt>o</tt> for which it holds that <tt>type.isInstance(o)</tt>
 91	 * @throws XTypeMismatch if the coercion fails
 92	 */
 93	public static final Object coerce(ATObject object, Class type) throws XTypeMismatch {
 94		return coerce(object, type, Thread.currentThread());
 95	}
 96
 97	/**
 98	 * Try to coerce the given AmbientTalk object into the given Java type, while explicitly providing the thread
 99	 * which is the owning actor for the object. 
100	 * <p>
101	 * This variant of coerce is provided explicitly to allow coercion to occur from a Java thread which is not the 
102	 * owning actor of the object. This occurs when the coercion is performed explicitly on the return value of an 
103	 * evaluation, after the latter has been finalized. 
104	 *  
105	 * @param object the AmbientTalk object to coerce
106	 * @param type the class object representing the target type
107	 * @param owningThread the owning Actor
108	 * @return a Java object <tt>o</tt> for which it holds that <tt>type.isInstance(o)</tt>
109	 * @throws XTypeMismatch if the coercion fails
110	 */
111	public static final Object coerce(ATObject object, Class type, Thread owningThread) throws XTypeMismatch {
112		if (type.isInstance(object)) { // object instanceof type
113			return object; // no need to coerce
114		} else if (type.isInterface()) {
115			
116			// see which class loader is required to load the interface
117			
118			// first try this thread's context class loader
119			ClassLoader loader = Thread.currentThread().getContextClassLoader();
120			try {
121				Class.forName(type.getName(), false, loader);
122			} catch(ClassNotFoundException e) {
123				// if that fails, try the class loader that created the interface type
124				loader = type.getClassLoader();
125			}
126			
127			// note that the proxy implements both the required type
128			// and the Symbiotic object marker interface to identify it as a wrapper
129			return Proxy.newProxyInstance(loader,
130                    new Class[] { type, ATObject.class },
131                    new Coercer(object, owningThread));	
132		} else {
133			throw new XTypeMismatch(type, object);
134		}
135	}
136	
137	public Object invoke(Object receiver, final Method method, Object[] arguments) throws Throwable {
138		Class methodImplementor = method.getDeclaringClass();
139		// handle toString, hashCode and equals in a dedicated fashion
140		// similarly, handle any native AT methods by simply forwarding them to the native AT object
141		if (methodImplementor == Object.class || methodImplementor == ATObject.class) {
142			// invoke these methods on the principal rather than on the proxy
143			try {
144				return method.invoke(principal_, arguments);
145			} catch (InvocationTargetException e) {
146				throw e.getTargetException();
147			}
148		} else {
149			
150			final ATObject[] symbioticArgs;
151            // support for variable-arity invocations from within AmbientTalk
152			if ((arguments != null) && (arguments.length == 1) && (arguments[0] instanceof ATObject[])) {
153				// no need to convert arguments
154				symbioticArgs = (ATObject[]) arguments[0];
155			} else {
156				symbioticArgs = new ATObject[(arguments == null) ? 0 : arguments.length];
157				for (int i = 0; i < symbioticArgs.length; i++) {
158					symbioticArgs[i] = Symbiosis.javaToAmbientTalk(arguments[i]);
159				}
160			}
161			
162			// if the current thread is not an actor thread, treat the Java invocation
163			// as a message send instead and enqueue it in my actor's thread
164			
165			if (Thread.currentThread() != wrappingThread_) {
166				if (Thread.currentThread() instanceof EventProcessor) {
167					// another event loop has direct access to this object, this means
168					// an AT object has been shared between actors via Java, signal an error
169					throw new XIllegalOperation("Detected illegal invocation of "+method.getName()+": sharing via Java level of object " + principal_);
170				}
171				
172				ELActor owningActor = (ELActor) EventLoop.toEventLoop(wrappingThread_);
173				
174				// if the invoked method is part of an EventListener interface, treat the
175				// invocation as a pure asynchronous message send, if the returntype is void
176				if (Symbiosis.isEvent(method)) {
177					owningActor.event_symbioticInvocation(principal_, method, symbioticArgs);
178					return null; // void return type
179				} else {
180					// because a message send is asynchronous and Java threads work synchronously,
181					// we'll have to make the Java thread wait for the result
182					return owningActor.sync_event_symbioticInvocation(principal_, method, symbioticArgs);
183				}
184			} else {
185				// perform a synchronous invocation
186				ATObject result = Reflection.downInvocation(principal_, method, symbioticArgs);
187				// properly 'cast' the returned object into the appropriate interface
188				return Symbiosis.ambientTalkToJava(result, method.getReturnType());		
189			}
190		}
191	}
192	
193	/**
194	 * Upon deserialization, re-assign the thread to the actor deserializing this coercer
195	 */
196	private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
197		in.defaultReadObject();
198		wrappingThread_ = Thread.currentThread();
199	}
200		
201}