package org.dcache.cells; import java.io.Serializable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import diskCacheV111.util.CacheException; import diskCacheV111.vehicles.Message; import dmg.cells.nucleus.CellEndpoint; import dmg.cells.nucleus.CellMessage; import dmg.cells.nucleus.Reply; import org.dcache.util.CacheExceptionFactory; /** * Encapsulates a Message reply. * * Similar to dmg.cells.nucleus.DelayedReply, except that MessageReply * knows about the dCache Message base class, and that the reply call * is non-blocking. The latter means one can safely send the reply * from the message delivery thread. */ public class MessageReply<T extends Message> implements Reply, Future<T> { private CellEndpoint _endpoint; private CellMessage _envelope; private T _msg; @Override public synchronized void deliver(CellEndpoint endpoint, CellMessage envelope) { if (endpoint == null || envelope == null) { throw new NullPointerException("Arguments must not be null"); } _endpoint = endpoint; _envelope = envelope; if (_msg != null) { send(); } } public boolean isValidIn(long delay) { return (_envelope == null || delay <= _envelope.getTtl() - _envelope.getLocalAge()); } public void fail(T msg, Throwable e) { if (e instanceof CacheException) { CacheException ce = (CacheException) e; fail(msg, ce.getRc(), ce.getMessage()); } else if (e instanceof IllegalArgumentException) { fail(msg, CacheException.INVALID_ARGS, e.getMessage()); } else { fail(msg, CacheException.UNEXPECTED_SYSTEM_EXCEPTION, e); } } public void fail(T msg, int rc, Serializable e) { msg.setFailed(rc, e); reply(msg); } public synchronized void reply(T msg) { _msg = msg; _msg.setReply(); if (_envelope != null) { send(); } notifyAll(); } protected synchronized void send() { _envelope.revertDirection(); _envelope.setMessageObject(_msg); _endpoint.sendMessage(_envelope); } @Override public boolean cancel(boolean mayInterruptIfRunning) { return false; } private synchronized T get(T msg) throws ExecutionException { if (msg.getReturnCode() != 0) { Exception e; Object o = msg.getErrorObject(); if (o instanceof Exception) { e = (Exception) o; } else { e = CacheExceptionFactory.exceptionOf(msg.getReturnCode(), String.valueOf(o)); } throw new ExecutionException(e.getMessage(), e); } return msg; } @Override public synchronized T get() throws InterruptedException, ExecutionException { while (_msg == null) { wait(); } return get(_msg); } @Override public synchronized T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { long expirationTime = System.currentTimeMillis() + unit.toMillis(timeout); while (_msg == null) { long timeLeft = expirationTime - System.currentTimeMillis(); if (timeLeft <= 0) { throw new TimeoutException(); } unit.timedWait(this, timeLeft); } return get(_msg); } @Override public boolean isCancelled() { return false; } @Override public synchronized boolean isDone() { return (_msg != null); } }