PageRenderTime 12ms CodeModel.GetById 2ms app.highlight 7ms RepoModel.GetById 1ms app.codeStats 0ms

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

http://ambienttalk.googlecode.com/
Java | 229 lines | 100 code | 22 blank | 107 comment | 29 complexity | 3041f7ab351ed07135bf7f2598a193ce 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.BlockingFuture;
 31import edu.vub.at.actors.eventloops.EventLoop;
 32import edu.vub.at.actors.eventloops.EventLoop.EventProcessor;
 33import edu.vub.at.actors.natives.ELActor;
 34import edu.vub.at.exceptions.XIllegalOperation;
 35import edu.vub.at.exceptions.XTypeMismatch;
 36import edu.vub.at.objects.ATObject;
 37import edu.vub.at.objects.mirrors.Reflection;
 38import edu.vub.at.objects.symbiosis.Symbiosis;
 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 * TODO: we should implement a mapping uniquely identifiying a Coercer per
 64 * ATObject, Interface pair, i.e. we must implement a Map:
 65 *  (ATObject x Class) -> Coercer
 66 * 
 67 * @author tvcutsem
 68 */
 69public final class Coercer implements InvocationHandler, Serializable {
 70	
 71	private final ATObject principal_;
 72	
 73	// we have to remember which thread owned the principal
 74	private transient Thread wrappingThread_;
 75	
 76	private Coercer(ATObject principal, Thread owningThread) {
 77		principal_ = principal;
 78		wrappingThread_ = owningThread;
 79	}
 80	
 81	public String toString() {
 82		return "<coercer on: "+principal_+">";
 83	}
 84	
 85	/**
 86	 * Try to coerce the given AmbientTalk object into the given Java type. This variant implicitly assumes that
 87	 * the coercion is performed by the thread owning the object, which is the case when coercing arguments to a
 88	 * Java method call or when passing an AmbientTalk object as a result.
 89	 * 
 90	 * Note that the returned coercer object is also an instance of {@link ATObject} and
 91	 * in the AmbientTalk world the coercer will behave as its coerced AT object.
 92	 * 
 93	 * @param object the AmbientTalk object to coerce
 94	 * @param type the class object representing the target type
 95	 * @return a Java object <tt>o</tt> for which it holds that <tt>type.isInstance(o)</tt>
 96	 * @throws XTypeMismatch if the coercion fails
 97	 */
 98	public static final Object coerce(ATObject object, Class type) throws XTypeMismatch {
 99		return coerce(object, type, Thread.currentThread());
100	}
101
102	/**
103	 * Try to coerce the given AmbientTalk object into the given Java type, while explicitly providing the thread
104	 * which is the owning actor for the object. 
105	 * <p>
106	 * This variant of coerce is provided explicitly to allow coercion to occur from a Java thread which is not the 
107	 * owning actor of the object. This occurs when the coercion is performed explicitly on the return value of an 
108	 * evaluation, after the latter has been finalized. 
109	 *  
110	 * @param object the AmbientTalk object to coerce
111	 * @param type the class object representing the target type
112	 * @param owningThread the owning Actor
113	 * @return a Java object <tt>o</tt> for which it holds that <tt>type.isInstance(o)</tt>
114	 * @throws XTypeMismatch if the coercion fails
115	 */
116	public static final Object coerce(ATObject object, Class type, Thread owningThread) throws XTypeMismatch {
117		if (type.isInstance(object)) { // object instanceof type
118			return object; // no need to coerce
119		} else if (type.isInterface()) {
120			
121			// see which class loader is required to load the interface
122			
123			// first try this thread's context class loader
124			ClassLoader loader = Thread.currentThread().getContextClassLoader();
125			try {
126				Class.forName(type.getName(), false, loader);
127			} catch(ClassNotFoundException e) {
128				// if that fails, try the class loader that created the interface type
129				loader = type.getClassLoader();
130			}
131			
132			// note that the proxy implements both the required type
133			// and the Symbiotic object marker interface to identify it as a wrapper
134			return Proxy.newProxyInstance(loader,
135                    new Class[] { type, ATObject.class },
136                    new Coercer(object, owningThread));	
137		} else {
138			throw new XTypeMismatch(type, object);
139		}
140	}
141	
142	public Object invoke(Object receiver, final Method method, Object[] arguments) throws Throwable {
143		Class methodImplementor = method.getDeclaringClass();
144		// handle toString, hashCode and equals in a dedicated fashion
145		// similarly, handle any native AT methods by simply forwarding them to the native AT object
146		if (methodImplementor == Object.class || methodImplementor == ATObject.class) {
147			// invoke these methods on the principal rather than on the proxy
148			if (Thread.currentThread() != wrappingThread_) {
149				if (Thread.currentThread() instanceof EventProcessor) {
150					// another event loop has direct access to this object, this means
151					// an AT object has been shared between actors via Java, signal an error
152					throw new XIllegalOperation("Detected illegal invocation of "+method.getName()+": sharing via Java level of object " + principal_);
153				}
154				
155				ELActor owningActor = (ELActor) EventLoop.toEventLoop(wrappingThread_);
156				
157				// synchronous symbiotic invocation
158				BlockingFuture future = owningActor.sync_event_symbioticForwardInvocation(principal_, method, arguments);
159				return future.get();
160			} else {
161				// immediate symbiotic invocation
162				try {
163					return method.invoke(principal_, arguments);
164				} catch (InvocationTargetException e) {
165					throw e.getTargetException();
166				}	
167			}
168		} else {
169			
170			final ATObject[] symbioticArgs;
171            // support for variable-arity invocations from within AmbientTalk
172			if ((arguments != null) && (arguments.length == 1) && (arguments[0] instanceof ATObject[])) {
173				// no need to convert arguments
174				symbioticArgs = (ATObject[]) arguments[0];
175			} else {
176				symbioticArgs = new ATObject[(arguments == null) ? 0 : arguments.length];
177				for (int i = 0; i < symbioticArgs.length; i++) {
178					symbioticArgs[i] = Symbiosis.javaToAmbientTalk(arguments[i]);
179				}
180			}
181			
182			// if the current thread is not an actor thread, treat the Java invocation
183			// as a message send instead and enqueue it in my actor's thread
184			
185			if (Thread.currentThread() != wrappingThread_) {
186				if (Thread.currentThread() instanceof EventProcessor) {
187					// another event loop has direct access to this object, this means
188					// an AT object has been shared between actors via Java, signal an error
189					throw new XIllegalOperation("Detected illegal invocation of "+method.getName()+": sharing via Java level of object " + principal_);
190				}
191				
192				ELActor owningActor = (ELActor) EventLoop.toEventLoop(wrappingThread_);
193				
194				// if the invoked method is part of an EventListener interface, treat the
195				// invocation as a pure asynchronous message send, if the returntype is void
196				if (Symbiosis.isEvent(method)) {
197					// asynchronous symbiotic invocation
198					owningActor.event_symbioticInvocation(principal_, method, symbioticArgs);
199					return null; // void return type
200				} else {
201					// because a message send is asynchronous and Java threads work synchronously,
202					// we'll have to make the Java thread wait for the result
203					BlockingFuture future = owningActor.sync_event_symbioticInvocation(principal_, method, symbioticArgs);
204					if (method.getReturnType().equals(BlockingFuture.class)) {
205						// future-type symbiotic invocation
206						return future;
207					} else {
208						// synchronous symbiotic invocation
209						return future.get();
210					}
211				}
212			} else {
213				// perform an immediate symbiotic invocation
214				ATObject result = Reflection.downInvocation(principal_, method, symbioticArgs);
215				// properly 'cast' the returned object into the appropriate interface
216				return Symbiosis.ambientTalkToJava(result, method.getReturnType());		
217			}
218		}
219	}
220	
221	/**
222	 * Upon deserialization, re-assign the thread to the actor deserializing this coercer
223	 */
224	private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
225		in.defaultReadObject();
226		wrappingThread_ = Thread.currentThread();
227	}
228		
229}