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

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

Go to download

This artifact provides a single jar that contains all classes required to use remote Jakarta Enterprise Beans and Jakarta Messaging, including all dependencies. It is intended for use by those not using maven, maven users should just import the Jakarta Enterprise Beans and Jakarta Messaging BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 35.0.0.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 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 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 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); 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 canReplace * controls whether this method replaces it (after logging a WARN) or simply * leaves handler uninstalled. *

* Passing false as the canReplace value allows * callers to use this method to install defaults without concern about * inadvertently overriding * * @param handler the UpHandler to install * @param canReplace true if an existing Channel upHandler or * Muxer default upHandler can be replaced; 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 (" + mux.getDefaultHandler() + ") but now it is being overridden"); mux.setDefaultHandler(handler); } } else if (canReplace) { log.warn("Channel already has an up handler installed (" + existing + ") but now it is being overridden"); 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) { 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<>(); 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 != null && 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=" + real_dests); if(real_dests.isEmpty()) { if(log.isTraceEnabled()) log.trace("destination list is empty, won't send message"); return null; } if(options != 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); if(options != null) { 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) { 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 != null && 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) { 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 != null && 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: if(log.isTraceEnabled()) log.trace("setting local_addr (" + local_addr + ") to " + 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