package org.jgroups.blocks; import org.jgroups.*; import org.jgroups.annotations.GuardedBy; import org.jgroups.protocols.relay.SiteAddress; import org.jgroups.util.Rsp; import java.util.Collection; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * Sends a request to a single target destination * * @author Bela Ban */ public class UnicastRequest<T> extends Request { protected final Rsp<T> result; protected final Address target; protected int num_received=0; public UnicastRequest(Message msg, RequestCorrelator corr, Address target, RequestOptions options) { super(msg, corr, options); this.target=target; result=new Rsp<T>(target); } public UnicastRequest(Message msg, Address target, RequestOptions options) { super(msg, null, options); this.target=target; result=new Rsp<T>(target); } protected void sendRequest() throws Exception { try { if(log.isTraceEnabled()) log.trace(new StringBuilder("sending request (id=").append(req_id).append(')')); corr.sendUnicastRequest(req_id, target, request_msg, options.getMode() == ResponseMode.GET_NONE? null : this); } catch(Exception ex) { if(corr != null) corr.done(req_id); throw ex; } } /* ---------------------- Interface RspCollector -------------------------- */ /** * <b>Callback</b> (called by RequestCorrelator or Transport). * Adds a response to the response table. When all responses have been received, * <code>execute()</code> returns. */ public void receiveResponse(Object response_value, Address sender, boolean is_exception) { RspFilter rsp_filter=options.getRspFilter(); lock.lock(); try { if(done) return; if(!result.wasReceived()) { num_received++; if(rsp_filter == null || rsp_filter.isAcceptable(response_value, sender)) { if(is_exception && response_value instanceof Throwable) result.setException((Throwable)response_value); else result.setValue((T)response_value); } } done=responsesComplete() || (rsp_filter != null && !rsp_filter.needMoreResponses()); if(done && corr != null) corr.done(req_id); } finally { completed.signalAll(); // wakes up execute() lock.unlock(); } checkCompletion(this); } public boolean responseReceived() {return num_received >= 1;} /** * <b>Callback</b> (called by RequestCorrelator or Transport). * Report to <code>GroupRequest</code> that a member is reported as faulty (suspected). * This method would probably be called when getting a suspect message from a failure detector * (where available). It is used to exclude faulty members from the response list. */ public void suspect(Address suspected_member) { if(suspected_member == null || !suspected_member.equals(target)) return; lock.lock(); try { if(done) return; if(result != null && !result.wasReceived()) result.setSuspected(); done=true; if(corr != null) corr.done(req_id); completed.signalAll(); } finally { lock.unlock(); } checkCompletion(this); } public void siteUnreachable(short site) { if(!(target instanceof SiteAddress)) return; lock.lock(); try { if(done) return; if(result != null && !result.wasUnreachable()) result.setUnreachable(); done=true; if(corr != null) corr.done(req_id); completed.signalAll(); } finally { lock.unlock(); } checkCompletion(this); } /** * If the target address is not a member of the new view, we'll mark the response as not received and unblock * the caller of execute() */ public void viewChange(View new_view) { Collection<Address> mbrs=new_view != null? new_view.getMembers() : null; if(mbrs == null) return; lock.lock(); try { // SiteAddresses are not checked as they might be in a different cluster if(!(target instanceof SiteAddress) && !mbrs.contains(target)) { result.setSuspected(); done=true; if(corr != null) corr.done(req_id); completed.signalAll(); } } finally { lock.unlock(); } checkCompletion(this); } public void transportClosed() { lock.lock(); try { if(done) return; if(result != null && !result.wasReceived()) result.setException(new IllegalStateException("transport was closed")); done=true; if(corr != null) corr.done(req_id); completed.signalAll(); } finally { lock.unlock(); } checkCompletion(this); } /* -------------------- End of Interface RspCollector ----------------------------------- */ public Rsp<T> getResult() { return result; } public T getValue() throws ExecutionException { if(result.wasSuspected()) throw new ExecutionException(new SuspectedException(target)); if(result.hasException()) throw new ExecutionException(result.getException()); if(result.wasUnreachable()) throw new ExecutionException(new UnreachableException(target)); if(!result.wasReceived()) throw new ExecutionException(new TimeoutException("timeout sending message to " + target)); return result.getValue(); } public T get() throws InterruptedException, ExecutionException { lock.lock(); try { waitForResults(0); return getValue(); } finally { lock.unlock(); } } public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { boolean ok; lock.lock(); try { ok=waitForResults(unit.toMillis(timeout)); } finally { lock.unlock(); } if(!ok) throw new TimeoutException(); return getValue(); } public String toString() { StringBuilder ret=new StringBuilder(128); ret.append(super.toString()); ret.append(", target=" + target); return ret.toString(); } @GuardedBy("lock") protected boolean responsesComplete() { return done || options.getMode() == ResponseMode.GET_NONE || result.wasReceived() || result.wasSuspected() || result.wasUnreachable() || num_received >= 1; } }