package org.rzo.netty.ahessian.rpc.client; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.jboss.netty.util.Timeout; import org.rzo.netty.ahessian.Constants; import org.rzo.netty.ahessian.rpc.callback.Callback; import org.rzo.netty.ahessian.rpc.callback.CallbackReplyMessage; import org.rzo.netty.ahessian.rpc.callback.ClientCallback; import org.rzo.netty.ahessian.rpc.message.HessianRPCReplyMessage; /** * Future object returned when executing a remote method invocation. * <br> * Note: within a continuation scenario, get() will return the last result received. * Results sent by the server will override the result available on the client. */ public class HessianProxyFuture implements Future<Object>, Constants { /** indicates if the invocation has been completed */ private boolean _done = false; /** TODO indicates if the invocation has been canceled or timed out. */ private boolean _canceled = false; /** result of the invocation. */ private HessianRPCReplyMessage _result = null; private Lock _lock = new ReentrantLock(); private Condition _resultReceived = _lock.newCondition(); private Collection<Runnable> _listeners = Collections.synchronizedCollection(new ArrayList<Runnable>()); private volatile Map<Long, ClientCallback> _callbacks = Collections.synchronizedMap(new HashMap()); private volatile Timeout _timeout = null; /* (non-Javadoc) * @see java.util.concurrent.Future#cancel(boolean) */ public boolean cancel(boolean mayInterruptIfRunning) { _canceled = true; return true; } /* (non-Javadoc) * @see java.util.concurrent.Future#get() */ public Object get() throws InterruptedException, ExecutionException { Object result = null; _lock.lock(); try { while (_result == null) { _resultReceived.await(); } if (_result.getFault() != null) throw new ExecutionException(_result.getFault()); else return _result.getValue(); } finally { _lock.unlock(); } } /* (non-Javadoc) * @see java.util.concurrent.Future#get(long, java.util.concurrent.TimeUnit) */ public Object get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { Object result = null; _lock.lock(); try { if (_result == null) _resultReceived.await(timeout, unit); if (_result == null) throw new TimeoutException(); if (_result.getFault() != null) throw new ExecutionException(_result.getFault()); else return _result.getValue(); } finally { _lock.unlock(); } } /* (non-Javadoc) * @see java.util.concurrent.Future#isCancelled() */ public boolean isCancelled() { return _canceled; } /* (non-Javadoc) * @see java.util.concurrent.Future#isDone() */ public boolean isDone() { return _done; } protected synchronized void set(HessianRPCReplyMessage message) { _lock.lock(); if (_timeout != null) _timeout.cancel(); try { if (message instanceof CallbackReplyMessage) { handleCallbackReply((CallbackReplyMessage)message); } else { _done = true; _result = message; _resultReceived.signal(); callListners(); } } finally { _lock.unlock(); } } private void handleCallbackReply(CallbackReplyMessage message) { Long callbackId = message.getCallbackId(); if (callbackId == null) return; ClientCallback callback = _callbacks.get(callbackId); if (callback == null) { System.out.println("no callback found for "+callbackId); return; } callback.invoke(message); if (message.isDone()) { _callbacks.remove(callbackId); //System.out.println("removed callback "+callbackId); } } private void callListners() { synchronized(_listeners) { for (Runnable listener : _listeners) listener.run(); } } /** * Adds the listener. * * @param listener the listener */ public void addListener(Runnable listener) { _lock.lock(); if (isDone()) listener.run(); else _listeners.add(listener); _lock.unlock(); } /** * Removes the listener. * * @param listener the listener */ public void removeListener(Runnable listener) { _listeners.remove(listener); } public void handleCallbacks(Object[] args) { if (args == null) return; for (int i=0; i<args.length; i++) { if (args[i] instanceof Callback) { ClientCallback c = new ClientCallback((Callback)args[i]); _callbacks.put(c.getId(), c); args[i] = c; } } } public boolean hasCallbacks() { return _callbacks.size() != 0; } public void setTimeout(Timeout timeout) { _timeout = timeout; } public void timedOut() { _lock.lock(); _timeout = null; try { this.set(new HessianRPCReplyMessage(null, new TimeoutException(),null)); } finally { _lock.unlock(); } } }