All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jgroups.blocks.MessageDispatcher Maven / Gradle / Ivy

There is a newer version: 9.1.7.Final
Show newest version

package org.jgroups.blocks;

import org.jgroups.*;
import org.jgroups.blocks.mux.Muxer;
import org.jgroups.logging.Log;
import org.jgroups.logging.LogFactory;
import org.jgroups.protocols.TP;
import org.jgroups.protocols.relay.SiteAddress;
import org.jgroups.stack.DiagnosticsHandler;
import org.jgroups.stack.Protocol;
import org.jgroups.stack.StateTransferInfo;
import org.jgroups.util.*;

import java.io.*;
import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;


/**
 * Provides synchronous and asynchronous message sending with request-response
 * correlation; i.e., matching responses with the original request.
 * It also offers push-style message reception (by internally using the PullPushAdapter).
 * 

* Channels are simple patterns to asynchronously send a receive messages. * However, a significant number of communication patterns in group communication * require synchronous communication. For example, a sender would like to send a * message to the group and wait for all responses. Or another application would * like to send a message to the group and wait only until the majority of the * receivers have sent a response, or until a timeout occurred. MessageDispatcher * offers a combination of the above pattern with other patterns. *

* Used on top of channel to implement group requests. Client's {@code handle()} * method is called when request is received. Is the equivalent of RpcProtocol on * the application instead of protocol level. * * @author Bela Ban */ public class MessageDispatcher implements AsyncRequestHandler, ChannelListener, Closeable { protected Channel channel; protected RequestCorrelator corr; protected MessageListener msg_listener; protected MembershipListener membership_listener; protected RequestHandler req_handler; protected boolean async_dispatching; protected boolean wrap_exceptions=true; protected ProtocolAdapter prot_adapter; protected volatile Collection

members=new HashSet<>(); protected Address local_addr; protected final Log log=LogFactory.getLog(MessageDispatcher.class); protected boolean hardware_multicast_supported=false; protected final AtomicInteger sync_unicasts=new AtomicInteger(0); protected final AtomicInteger async_unicasts=new AtomicInteger(0); protected final AtomicInteger sync_multicasts=new AtomicInteger(0); protected final AtomicInteger async_multicasts=new AtomicInteger(0); protected final AtomicInteger sync_anycasts=new AtomicInteger(0); protected final AtomicInteger async_anycasts=new AtomicInteger(0); protected final Set channel_listeners=new CopyOnWriteArraySet<>(); protected final DiagnosticsHandler.ProbeHandler probe_handler=new MyProbeHandler(); public MessageDispatcher() { } public MessageDispatcher(Channel channel, MessageListener l, MembershipListener l2) { this.channel=channel; prot_adapter=new ProtocolAdapter(); if(channel != null) { local_addr=channel.getAddress(); channel.addChannelListener(this); } setMessageListener(l); setMembershipListener(l2); if(channel != null) installUpHandler(prot_adapter, true); start(); } public MessageDispatcher(Channel channel, RequestHandler req_handler) { this(channel, null, null, req_handler); } public MessageDispatcher(Channel channel, MessageListener l, MembershipListener l2, RequestHandler req_handler) { this(channel, l, l2); setRequestHandler(req_handler); } public boolean asyncDispatching() {return async_dispatching;} public MessageDispatcher asyncDispatching(boolean flag) { async_dispatching=flag; if(corr != null) corr.asyncDispatching(flag); return this; } public boolean wrapExceptions() {return wrap_exceptions;} public MessageDispatcher wrapExceptions(boolean flag) { wrap_exceptions=flag; if(corr != null) corr.wrapExceptions(flag); return this;} public UpHandler getProtocolAdapter() { return prot_adapter; } /** * If this dispatcher is using a user-provided PullPushAdapter, then need to set the members from the adapter * initially since viewChange has most likely already been called in PullPushAdapter. */ protected void setMembers(List
new_mbrs) { if(new_mbrs != null) members=new HashSet<>(new_mbrs); // volatile write - seen by a subsequent read } /** * Adds a new channel listener to be notified on the channel's state change. */ public void addChannelListener(ChannelListener l) { if(l != null) channel_listeners.add(l); } public void removeChannelListener(ChannelListener l) { if(l != null) channel_listeners.remove(l); } public void start() { if(corr == null) corr=createRequestCorrelator(prot_adapter, this, local_addr) .asyncDispatching(async_dispatching).wrapExceptions(this.wrap_exceptions); correlatorStarted(); corr.start(); if(channel != null) { List
tmp_mbrs=channel.getView() != null ? channel.getView().getMembers() : null; setMembers(tmp_mbrs); if(channel instanceof JChannel) { TP transport=channel.getProtocolStack().getTransport(); corr.registerProbeHandler(transport); } TP transport=channel.getProtocolStack().getTransport(); hardware_multicast_supported=transport.supportsMulticasting(); transport.registerProbeHandler(probe_handler); } } protected RequestCorrelator createRequestCorrelator(Protocol transport, RequestHandler handler, Address local_addr) { return new RequestCorrelator(transport, handler, local_addr); } protected void correlatorStarted() { ; } @Override public void close() throws IOException {stop();} public void stop() { if(corr != null) corr.stop(); if(channel instanceof JChannel) { TP transport=channel.getProtocolStack().getTransport(); transport.unregisterProbeHandler(probe_handler); corr.unregisterProbeHandler(transport); } } public final void setMessageListener(MessageListener l) { msg_listener=l; } public MessageListener getMessageListener() { return msg_listener; } public final void setMembershipListener(MembershipListener l) { membership_listener=l; } public final void setRequestHandler(RequestHandler rh) { req_handler=rh; } public Channel getChannel() { return channel; } public void setChannel(Channel ch) { if(ch == null) return; this.channel=ch; local_addr=channel.getAddress(); if(prot_adapter == null) prot_adapter=new ProtocolAdapter(); // Don't force installing the UpHandler so subclasses can use this // method and still integrate with a MuxUpHandler installUpHandler(prot_adapter, false); } /** * Sets the given UpHandler as the UpHandler for the channel, or, if the * channel already has a Muxer installed as it's UpHandler, sets the given * handler as the Muxer's {@link Muxer#setDefaultHandler(Object) default handler}. * If the relevant handler is already installed, the {@code canReplace} * controls whether this method replaces it (after logging a WARN) or simply * leaves {@code handler} uninstalled. *

* Passing {@code false} as the {@code canReplace} value allows * callers to use this method to install defaults without concern about * inadvertently overriding * * @param handler the UpHandler to install * @param canReplace {@code true} if an existing Channel upHandler or * Muxer default upHandler can be replaced; {@code false} * if this method shouldn't install */ protected void installUpHandler(UpHandler handler, boolean canReplace) { UpHandler existing = channel.getUpHandler(); if (existing == null) { channel.setUpHandler(handler); } else if (existing instanceof Muxer) { @SuppressWarnings("unchecked") Muxer mux = (Muxer) existing; if (mux.getDefaultHandler() == null) { mux.setDefaultHandler(handler); } else if (canReplace) { log.warn("Channel Muxer already has a default up handler installed (%s) but now it is being overridden", mux.getDefaultHandler()); mux.setDefaultHandler(handler); } } else if (canReplace) { log.warn("Channel already has an up handler installed (%s) but now it is being overridden", existing); channel.setUpHandler(handler); } } /** * Sends a message to all members and expects responses from members in dests (if non-null). * @param dests A list of group members from which to expect responses (if the call is blocking). * @param msg The message to be sent * @param options A set of options that govern the call. See {@link org.jgroups.blocks.RequestOptions} for details * @return RspList A list of Rsp elements * @throws Exception If the request cannot be sent * @since 2.9 */ public RspList castMessage(final Collection

dests, Message msg, RequestOptions options) throws Exception { GroupRequest req=cast(dests, msg, options, true); return req != null? req.getResults() : new RspList(); } /** * Sends a message to all members and expects responses from members in dests (if non-null). * @param dests A list of group members from which to expect responses (if the call is blocking). * @param msg The message to be sent * @param options A set of options that govern the call. See {@link org.jgroups.blocks.RequestOptions} for details * @param listener A FutureListener which will be registered (if non null) with the future before the call is invoked * @return NotifyingFuture A future from which the results (RspList) can be retrieved * @throws Exception If the request cannot be sent */ public NotifyingFuture> castMessageWithFuture(final Collection
dests, Message msg, RequestOptions options, FutureListener> listener) throws Exception { GroupRequest req=cast(dests,msg,options,false, listener); return req != null? req : new NullFuture<>(new RspList()); } /** * Sends a message to all members and expects responses from members in dests (if non-null). * @param dests A list of group members from which to expect responses (if the call is blocking). * @param msg The message to be sent * @param options A set of options that govern the call. See {@link org.jgroups.blocks.RequestOptions} for details * @return NotifyingFuture A future from which the results (RspList) can be retrieved * @throws Exception If the request cannot be sent */ public NotifyingFuture> castMessageWithFuture(final Collection
dests, Message msg, RequestOptions options) throws Exception { return castMessageWithFuture(dests, msg, options, null); } protected GroupRequest cast(final Collection
dests, Message msg, RequestOptions options, boolean block_for_results, FutureListener> listener) throws Exception { if(msg.getDest() != null && !(msg.getDest() instanceof AnycastAddress)) throw new IllegalArgumentException("message destination is non-null, cannot send message"); if(options == null) { log.warn("request options were null, using default of sync"); options=RequestOptions.SYNC(); } msg.setFlag(options.getFlags()).setTransientFlag(options.getTransientFlags()); if(options.getScope() > 0) msg.setScope(options.getScope()); List
real_dests; // we need to clone because we don't want to modify the original if(dests != null) { real_dests=new ArrayList<>(dests.size()); for(Address dest: dests) { if(dest instanceof SiteAddress || this.members.contains(dest)) { if(!real_dests.contains(dest)) real_dests.add(dest); } } } else real_dests=new ArrayList<>(members); // if local delivery is off, then we should not wait for the message from the local member. // therefore remove it from the membership Channel tmp=channel; if((tmp != null && tmp.getDiscardOwnMessages()) || msg.isTransientFlagSet(Message.TransientFlag.DONT_LOOPBACK)) { if(local_addr == null) local_addr=tmp != null? tmp.getAddress() : null; if(local_addr != null) real_dests.remove(local_addr); } if(options.hasExclusionList()) { Address[] exclusion_list=options.exclusionList(); for(Address excluding: exclusion_list) real_dests.remove(excluding); } // don't even send the message if the destination list is empty if(log.isTraceEnabled()) log.trace("real_dests=%s", real_dests); if(real_dests.isEmpty()) { if(log.isTraceEnabled()) log.trace("destination list is empty, won't send message"); return null; } boolean async=options.getMode() == ResponseMode.GET_NONE; if(options.getAnycasting()) { if(async) async_anycasts.incrementAndGet(); else sync_anycasts.incrementAndGet(); } else { if(async) async_multicasts.incrementAndGet(); else sync_multicasts.incrementAndGet(); } GroupRequest req=new GroupRequest<>(msg, corr, real_dests, options); if(listener != null) req.setListener(listener); req.setResponseFilter(options.getRspFilter()); req.setAnycasting(options.getAnycasting()); req.setBlockForResults(block_for_results); req.execute(); return req; } protected GroupRequest cast(final Collection
dests, Message msg, RequestOptions options, boolean block_for_results) throws Exception { return cast(dests, msg, options, block_for_results, null); } public void done(long req_id) { corr.done(req_id); } /** * Sends a unicast message and - depending on the options - returns a result * @param msg the message to be sent. The destination needs to be non-null * @param opts the options to be used * @return T the result * @throws Exception If there was problem sending the request, processing it at the receiver, or processing * it at the sender. * @throws TimeoutException If the call didn't succeed within the timeout defined in options (if set) */ public T sendMessage(Message msg, RequestOptions opts) throws Exception { Address dest=msg.getDest(); if(dest == null) throw new IllegalArgumentException("message destination is null, cannot send message"); if(opts == null) { log.warn("request options were null, using default of sync"); opts=RequestOptions.SYNC(); } msg.setFlag(opts.getFlags()).setTransientFlag(opts.getTransientFlags()); if(opts.getScope() > 0) msg.setScope(opts.getScope()); if(opts.getMode() == ResponseMode.GET_NONE) async_unicasts.incrementAndGet(); else sync_unicasts.incrementAndGet(); UnicastRequest req=new UnicastRequest<>(msg, corr, dest, opts); req.execute(); if(opts.getMode() == ResponseMode.GET_NONE) return null; Rsp rsp=req.getResult(); if(rsp.wasSuspected()) throw new SuspectedException(dest); Throwable exception=rsp.getException(); if(exception != null) { if(exception instanceof Error) throw (Error)exception; else if(exception instanceof RuntimeException) throw (RuntimeException)exception; else if(exception instanceof Exception) throw (Exception)exception; else throw new RuntimeException(exception); } if(rsp.wasUnreachable()) throw new UnreachableException(dest); if(!rsp.wasReceived() && !req.responseReceived()) throw new TimeoutException("timeout waiting for response from " + dest + ", request: " + req.toString()); return rsp.getValue(); } /** * Sends a unicast message to the target defined by msg.getDest() and returns a future * @param msg The unicast message to be sent. msg.getDest() must not be null * @param options * @param listener A FutureListener which will be registered (if non null) with the future before the call is invoked * @return NotifyingFuture A future from which the result can be fetched * @throws Exception If there was problem sending the request, processing it at the receiver, or processing * it at the sender. {@link java.util.concurrent.Future#get()} will throw this exception * @throws TimeoutException If the call didn't succeed within the timeout defined in options (if set) */ public NotifyingFuture sendMessageWithFuture(Message msg, RequestOptions options, FutureListener listener) throws Exception { Address dest=msg.getDest(); if(dest == null) throw new IllegalArgumentException("message destination is null, cannot send message"); if(options == null) { log.warn("request options were null, using default of sync"); options=RequestOptions.SYNC(); } msg.setFlag(options.getFlags()).setTransientFlag(options.getTransientFlags()); if(options.getScope() > 0) msg.setScope(options.getScope()); if(options.getMode() == ResponseMode.GET_NONE) async_unicasts.incrementAndGet(); else sync_unicasts.incrementAndGet(); UnicastRequest req=new UnicastRequest<>(msg, corr, dest, options); if(listener != null) req.setListener(listener); req.setBlockForResults(false); req.execute(); if(options.getMode() == ResponseMode.GET_NONE) return new NullFuture<>(null); return req; } /** * Sends a unicast message to the target defined by msg.getDest() and returns a future * @param msg The unicast message to be sent. msg.getDest() must not be null * @param options * @return NotifyingFuture A future from which the result can be fetched * @throws Exception If there was problem sending the request, processing it at the receiver, or processing * it at the sender. {@link java.util.concurrent.Future#get()} will throw this exception * @throws TimeoutException If the call didn't succeed within the timeout defined in options (if set) */ public NotifyingFuture sendMessageWithFuture(Message msg, RequestOptions options) throws Exception { return sendMessageWithFuture(msg, options, null); } /* ------------------------ RequestHandler Interface ---------------------- */ @Override public Object handle(Message msg) throws Exception { if(req_handler != null) return req_handler.handle(msg); return null; } /* -------------------- End of RequestHandler Interface ------------------- */ /* -------------------- AsyncRequestHandler Interface --------------------- */ @Override public void handle(Message request, Response response) throws Exception { if(req_handler != null) { if(req_handler instanceof AsyncRequestHandler) ((AsyncRequestHandler)req_handler).handle(request, response); else { Object retval=req_handler.handle(request); if(response != null) response.send(retval, false); } return; } Object retval=handle(request); if(response != null) response.send(retval, false); } /* ------------------ End of AsyncRequestHandler Interface----------------- */ /* --------------------- Interface ChannelListener ---------------------- */ @Override public void channelConnected(Channel channel) { for(ChannelListener l: channel_listeners) { try { l.channelConnected(channel); } catch(Throwable t) { log.warn("notifying channel listener " + l + " failed", t); } } } @Override public void channelDisconnected(Channel channel) { stop(); for(ChannelListener l: channel_listeners) { try { l.channelDisconnected(channel); } catch(Throwable t) { log.warn("notifying channel listener " + l + " failed", t); } } } @Override public void channelClosed(Channel channel) { stop(); for(ChannelListener l: channel_listeners) { try { l.channelClosed(channel); } catch(Throwable t) { log.warn("notifying channel listener " + l + " failed", t); } } } /* ----------------------------------------------------------------------- */ protected Object handleUpEvent(Event evt) throws Exception { switch(evt.getType()) { case Event.MSG: if(msg_listener != null) msg_listener.receive((Message) evt.getArg()); break; case Event.GET_APPLSTATE: // reply with GET_APPLSTATE_OK byte[] tmp_state=null; if(msg_listener != null) { ByteArrayOutputStream output=new ByteArrayOutputStream(1024); msg_listener.getState(output); tmp_state=output.toByteArray(); } return new StateTransferInfo(null, 0L, tmp_state); case Event.GET_STATE_OK: if(msg_listener != null) { StateTransferResult result=(StateTransferResult)evt.getArg(); if(result.hasBuffer()) { ByteArrayInputStream input=new ByteArrayInputStream(result.getBuffer()); msg_listener.setState(input); } } break; case Event.STATE_TRANSFER_OUTPUTSTREAM: OutputStream os=(OutputStream)evt.getArg(); if(msg_listener != null && os != null) { msg_listener.getState(os); } break; case Event.STATE_TRANSFER_INPUTSTREAM: InputStream is=(InputStream)evt.getArg(); if(msg_listener != null && is!=null) msg_listener.setState(is); break; case Event.VIEW_CHANGE: View v=(View) evt.getArg(); List
new_mbrs=v.getMembers(); setMembers(new_mbrs); if(membership_listener != null) membership_listener.viewAccepted(v); break; case Event.SET_LOCAL_ADDRESS: log.trace("setting local_addr (%s) to %s", local_addr, evt.getArg()); local_addr=(Address)evt.getArg(); break; case Event.SUSPECT: if(membership_listener != null) membership_listener.suspect((Address) evt.getArg()); break; case Event.BLOCK: if(membership_listener != null) membership_listener.block(); break; case Event.UNBLOCK: if(membership_listener != null) membership_listener.unblock(); break; } return null; } class MyProbeHandler implements DiagnosticsHandler.ProbeHandler { @Override public Map handleProbe(String... keys) { Map retval=new HashMap<>(); for(String key: keys) { if("rpcs".equals(key)) { String channel_name = channel != null ? channel.getClusterName() : ""; retval.put(channel_name + ": sync unicast RPCs", sync_unicasts.toString()); retval.put(channel_name + ": sync multicast RPCs", sync_multicasts.toString()); retval.put(channel_name + ": async unicast RPCs", async_unicasts.toString()); retval.put(channel_name + ": async multicast RPCs", async_multicasts.toString()); retval.put(channel_name + ": sync anycast RPCs", sync_anycasts.toString()); retval.put(channel_name + ": async anycast RPCs", async_anycasts.toString()); } if("rpcs-reset".equals(key)) { sync_unicasts.set(0); sync_multicasts.set(0); async_unicasts.set(0); async_multicasts.set(0); sync_anycasts.set(0); async_anycasts.set(0); } } return retval; } @Override public String[] supportedKeys() { return new String[]{"rpcs", "rpcs-reset"}; } } class ProtocolAdapter extends Protocol implements UpHandler { /* ------------------------- Protocol Interface --------------------------- */ @Override public String getName() { return "MessageDispatcher"; } /** * Called by channel (we registered before) when event is received. This is the UpHandler interface. */ @Override public Object up(Event evt) { if(corr != null) { if(!corr.receive(evt)) { try { return handleUpEvent(evt); } catch(Throwable t) { throw new RuntimeException(t); } } } return null; } @Override public Object down(Event evt) { if(channel != null) { if(evt.getType() == Event.MSG && !(channel.isConnected() || channel.isConnecting())) throw new IllegalStateException("channel is not connected"); return channel.down(evt); } return null; } /* ----------------------- End of Protocol Interface ------------------------ */ } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy