ResponseMode mode = supportReplay ? ResponseMode.GET_ALL : this.mode;
RspList<Object> retval = null;
Buffer buf;
if (broadcast || FORCE_MCAST) {
RequestOptions opts = new RequestOptions();
opts.setMode(mode);
opts.setTimeout(timeout);
opts.setRspFilter(filter);
opts.setAnycasting(false);
buf = marshallCall();
retval = castMessage(dests, constructMessage(buf, null), opts);
} else {
Set<Address> targets = new HashSet<Address>(dests); // should sufficiently randomize order.
RequestOptions opts = new RequestOptions();
opts.setMode(mode);
opts.setTimeout(timeout);
targets.remove(channel.getAddress()); // just in case
if (targets.isEmpty()) return new RspList();
buf = marshallCall();
// if at all possible, try not to use JGroups' ANYCAST for now. Multiple (parallel) UNICASTs are much faster.
if (filter != null) {
// This is possibly a remote GET.
// These UNICASTs happen in parallel using sendMessageWithFuture. Each future has a listener attached
// (see FutureCollator) and the first successful response is used.
FutureCollator futureCollator = new FutureCollator(filter, targets.size(), timeout);
for (Address a : targets) {
NotifyingFuture<Object> f = sendMessageWithFuture(constructMessage(buf, a), opts);
futureCollator.watchFuture(f, a);
}
retval = futureCollator.getResponseList();
} else if (mode == ResponseMode.GET_ALL) {
// A SYNC call that needs to go everywhere
Map<Address, Future<Object>> futures = new HashMap<Address, Future<Object>>(targets.size());
for (Address dest : targets) futures.put(dest, sendMessageWithFuture(constructMessage(buf, dest), opts));
retval = new RspList();
// a get() on each future will block till that call completes.
for (Map.Entry<Address, Future<Object>> entry : futures.entrySet()) {
try {
retval.addRsp(entry.getKey(), entry.getValue().get(timeout, MILLISECONDS));
} catch (java.util.concurrent.TimeoutException te) {
throw new TimeoutException(formatString("Timed out after %s waiting for a response from %s",
prettyPrintTime(timeout), entry.getKey()));
}
}
} else if (mode == ResponseMode.GET_NONE) {
// An ASYNC call. We don't care about responses.
for (Address dest : targets) sendMessage(constructMessage(buf, dest), opts);
}
}
// we only bother parsing responses if we are not in ASYNC mode.
if (mode != ResponseMode.GET_NONE) {
if (trace) log.tracef("Responses: %s", retval);
// a null response is 99% likely to be due to a marshalling problem - we throw a NSE, this needs to be changed when
// JGroups supports http://jira.jboss.com/jira/browse/JGRP-193
// the serialization problem could be on the remote end and this is why we cannot catch this above, when marshalling.
if (retval == null)
throw new NotSerializableException("RpcDispatcher returned a null. This is most often caused by args for "
+ command.getClass().getSimpleName() + " not being serializable.");
if (supportReplay) {
boolean replay = false;
List<Address> ignorers = new LinkedList<Address>();
for (Map.Entry<Address, Rsp<Object>> entry : retval.entrySet()) {
Object value = entry.getValue().getValue();
if (value instanceof RequestIgnoredResponse) {
ignorers.add(entry.getKey());
} else if (value instanceof ExtendedResponse) {
ExtendedResponse extended = (ExtendedResponse) value;
replay |= extended.isReplayIgnoredRequests();
entry.getValue().setValue(extended.getResponse());
}
}
if (replay && !ignorers.isEmpty()) {
Message msg = constructMessage(buf, null);
//Since we are making a sync call make sure we don't bundle
//See ISPN-192 for more details
msg.setFlag(Message.DONT_BUNDLE);
if (trace)
log.tracef("Replaying message to ignoring senders: %s", ignorers);
RequestOptions opts = new RequestOptions();
opts.setMode(ResponseMode.GET_ALL);
opts.setTimeout(timeout);
opts.setAnycasting(anycasting);
opts.setRspFilter(filter);
RspList responses = castMessage(ignorers, msg, opts);
if (responses != null)
retval.putAll(responses);
}
}