PageRenderTime 21ms CodeModel.GetById 2ms app.highlight 13ms RepoModel.GetById 2ms app.codeStats 0ms

/interpreter/tags/at2-build270707/src/edu/vub/at/objects/coercion/Coercer.java

http://ambienttalk.googlecode.com/
Java | 191 lines | 82 code | 17 blank | 92 comment | 26 complexity | 779aed8c19b5c24d541106e60df21f6a 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;
 38import edu.vub.at.objects.symbiosis.SymbioticATObjectMarker;
 39
 40import java.io.IOException;
 41import java.io.Serializable;
 42import java.lang.reflect.InvocationHandler;
 43import java.lang.reflect.InvocationTargetException;
 44import java.lang.reflect.Method;
 45import java.lang.reflect.Proxy;
 46
 47/**
 48 * A coercer is a dynamic proxy which is used to 'cast' Ambienttalk base-level NATObjects to a certain ATxxx interface.
 49 * The dynamic proxy is responsible for transforming java calls to meta_invoke calls.
 50 * 
 51 * For example, a method from an AT interface
 52 * 
 53 * ATExpression base_expression() throws NATException
 54 * 
 55 * receives the following implementation:
 56 * 
 57 * ATExpression expression() throws NATException {
 58 *	return principal.meta_invoke(principal, Reflection.downSelector("getExpression"), NATTable.EMPTY).asExpression();
 59 * }
 60 * 
 61 * where principal is the original object 'coerced into' the given interface
 62 * 
 63 * @author tvcutsem
 64 */
 65public final class Coercer implements InvocationHandler, Serializable {
 66	
 67	private final ATObject principal_;
 68	
 69	// we have to remember which thread owned the principal
 70	private transient Thread wrappingThread_;
 71	
 72	private Coercer(ATObject principal, Thread owningThread) {
 73		principal_ = principal;
 74		wrappingThread_ = owningThread;
 75	}
 76	
 77	public String toString() {
 78		return "<coercer on: "+principal_+">";
 79	}
 80	
 81	/**
 82	 * Try to coerce the given AmbientTalk object into the given Java type. This variant implicitly assumes that
 83	 * the coercion is performed by the thread owning the object, which is the case when coercing arguments to a
 84	 * Java method call or when passing an AmbientTalk object as a result.
 85	 * 
 86	 * @param object the AmbientTalk object to coerce
 87	 * @param type the class object representing the target type
 88	 * @return a Java object <tt>o</tt> for which it holds that <tt>type.isInstance(o)</tt>
 89	 * @throws XTypeMismatch if the coercion fails
 90	 */
 91	public static final Object coerce(ATObject object, Class type) throws XTypeMismatch {
 92		return coerce(object, type, Thread.currentThread());
 93	}
 94
 95	/**
 96	 * Try to coerce the given AmbientTalk object into the given Java type, while explicitly providing the thread
 97	 * which is the owning actor for the object. 
 98	 * <p>
 99	 * This variant of coerce is provided explicitly to allow coercion to occur from a Java thread which is not the 
100	 * owning actor of the object. This occurs when the coercion is performed explicitly on the return value of an 
101	 * evaluation, after the latter has been finalized. 
102	 *  
103	 * @param object the AmbientTalk object to coerce
104	 * @param type the class object representing the target type
105	 * @param owningThread the owning Actor
106	 * @return a Java object <tt>o</tt> for which it holds that <tt>type.isInstance(o)</tt>
107	 * @throws XTypeMismatch if the coercion fails
108	 */
109	public static final Object coerce(ATObject object, Class type, Thread owningThread) throws XTypeMismatch {
110		if (type.isInstance(object)) { // object instanceof type
111			return object; // no need to coerce
112		} else if (type.isInterface()) {
113			// note that the proxy implements both the required type
114			// and the Symbiotic object marker interface to identify it as a wrapper
115			return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
116					                      new Class[] { type, SymbioticATObjectMarker.class },
117					                      new Coercer(object, owningThread));
118		} else {
119			throw new XTypeMismatch(type, object);
120		}
121	}
122	
123	public Object invoke(Object receiver, final Method method, Object[] arguments) throws Throwable {
124		Class methodImplementor = method.getDeclaringClass();
125		// handle toString, hashCode and equals in a dedicated fashion
126		// similarly, handle AT conversion methods by simply forwarding them to the native AT object
127		if (methodImplementor == Object.class || methodImplementor == ATConversions.class) {
128			// invoke these methods on the principal rather than on the proxy
129			try {
130				return method.invoke(principal_, arguments);
131			} catch (InvocationTargetException e) {
132				throw e.getTargetException();
133			}
134		// intercept access to the wrapped object for Java->AT value conversion
135		// or for serialization purposes
136		} else if (method.getDeclaringClass() == SymbioticATObjectMarker.class) {
137			return principal_;
138		} else {
139			
140			final ATObject[] symbioticArgs;
141            // support for variable-arity invocations from within AmbientTalk
142			if ((arguments != null) && (arguments.length == 1) && (arguments[0] instanceof ATObject[])) {
143				// no need to convert arguments
144				symbioticArgs = (ATObject[]) arguments[0];
145			} else {
146				symbioticArgs = new ATObject[(arguments == null) ? 0 : arguments.length];
147				for (int i = 0; i < symbioticArgs.length; i++) {
148					symbioticArgs[i] = Symbiosis.javaToAmbientTalk(arguments[i]);
149				}
150			}
151			
152			// if the current thread is not an actor thread, treat the Java invocation
153			// as a message send instead and enqueue it in my actor's thread
154			
155			if (Thread.currentThread() != wrappingThread_) {
156				if (Thread.currentThread() instanceof EventProcessor) {
157					// another event loop has direct access to this object, this means
158					// an AT object has been shared between actors via Java, signal an error
159					throw new XIllegalOperation("Detected illegal invocation: sharing via Java level of object " + principal_);
160				}
161				
162				ELActor owningActor = (ELActor) EventLoop.toEventLoop(wrappingThread_);
163				
164				// if the invoked method is part of an EventListener interface, treat the
165				// invocation as a pure asynchronous message send, if the returntype is void
166				if (Symbiosis.isEvent(method)) {
167					owningActor.event_symbioticInvocation(principal_, method, symbioticArgs);
168					return null; // void return type
169				} else {
170					// because a message send is asynchronous and Java threads work synchronously,
171					// we'll have to make the Java thread wait for the result
172					return owningActor.sync_event_symbioticInvocation(principal_, method, symbioticArgs);
173				}
174			} else {
175				// perform a synchronous invocation
176				ATObject result = Reflection.downInvocation(principal_, method, symbioticArgs);
177				// properly 'cast' the returned object into the appropriate interface
178				return Symbiosis.ambientTalkToJava(result, method.getReturnType());		
179			}
180		}
181	}
182	
183	/**
184	 * Upon deserialization, re-assign the thread to the actor deserializing this coercer
185	 */
186	private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
187		in.defaultReadObject();
188		wrappingThread_ = Thread.currentThread();
189	}
190		
191}