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

bboss.org.jgroups.blocks.PullPushAdapter Maven / Gradle / Ivy

The newest version!
// $Id: PullPushAdapter.java,v 1.31 2010/06/15 06:44:39 belaban Exp $

package bboss.org.jgroups.blocks;


import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import bboss.org.jgroups.Address;
import bboss.org.jgroups.BlockEvent;
import bboss.org.jgroups.Channel;
import bboss.org.jgroups.ChannelClosedException;
import bboss.org.jgroups.ChannelListenerAdapter;
import bboss.org.jgroups.ChannelNotConnectedException;
import bboss.org.jgroups.ExtendedMembershipListener;
import bboss.org.jgroups.ExtendedMessageListener;
import bboss.org.jgroups.GetStateEvent;
import bboss.org.jgroups.Header;
import bboss.org.jgroups.JChannel;
import bboss.org.jgroups.MembershipListener;
import bboss.org.jgroups.Message;
import bboss.org.jgroups.MessageListener;
import bboss.org.jgroups.SetStateEvent;
import bboss.org.jgroups.StreamingGetStateEvent;
import bboss.org.jgroups.StreamingSetStateEvent;
import bboss.org.jgroups.SuspectEvent;
import bboss.org.jgroups.Transport;
import bboss.org.jgroups.UnblockEvent;
import bboss.org.jgroups.View;
import bboss.org.jgroups.conf.ClassConfigurator;
import bboss.org.jgroups.logging.Log;
import bboss.org.jgroups.logging.LogFactory;
import bboss.org.jgroups.util.Util;


/**
 * Allows a client of {@link bboss.org.jgroups.Channel} to be notified when messages have been received
 * instead of having to actively poll the channel for new messages. Typically used in the
 * client role (receive()). As this class does not implement interface
 * {@link bboss.org.jgroups.Transport}, but uses it for receiving messages, an underlying object
 * has to be used to send messages (e.g. the channel on which an object of this class relies).

* Multiple MembershipListeners can register with the PullPushAdapter; when a view is received, they * will all be notified. There is one main message listener which sends and receives message. In addition, * MessageListeners can register with a certain tag (identifier), and then send messages tagged with this * identifier. When a message with such an identifier is received, the corresponding MessageListener will be * looked up and the message dispatched to it. If no tag is found (default), the main MessageListener will * receive the message. * @author Bela Ban * @version $Revision * @deprecated Use {@link bboss.org.jgroups.Receiver} instead, this class will be removed in JGroups 3.0 */ public class PullPushAdapter extends ChannelListenerAdapter implements Runnable { protected Transport transport=null; protected MessageListener listener=null; // main message receiver protected final List membership_listeners=new ArrayList(); protected Thread receiver_thread=null; protected final HashMap listeners=new HashMap(); // keys=identifier (Serializable), values=MessageListeners protected final Log log=LogFactory.getLog(getClass()); static final short PULL_HEADER_ID=ClassConfigurator.getProtocolId(PullPushAdapter.class); public PullPushAdapter(Transport transport) { this.transport=transport; start(); } public PullPushAdapter(Transport transport, MessageListener l) { this.transport=transport; setListener(l); start(); } public PullPushAdapter(Transport transport, MembershipListener ml) { this.transport=transport; addMembershipListener(ml); start(); } public PullPushAdapter(Transport transport, MessageListener l, MembershipListener ml) { this.transport=transport; setListener(l); addMembershipListener(ml); start(); } public PullPushAdapter(Transport transport, MessageListener l, MembershipListener ml, boolean start) { this.transport=transport; setListener(l); addMembershipListener(ml); if(start) start(); } public Transport getTransport() { return transport; } public final void start() { if(receiver_thread == null || !receiver_thread.isAlive()) { receiver_thread=new Thread(this, "PullPushAdapterThread"); receiver_thread.setDaemon(true); receiver_thread.start(); } if(transport instanceof JChannel) ((JChannel)transport).addChannelListener(this); } public void stop() { Thread tmp=null; if(receiver_thread != null && receiver_thread.isAlive()) { tmp=receiver_thread; receiver_thread=null; tmp.interrupt(); try { tmp.join(1000); } catch(Exception ex) { } } receiver_thread=null; } /** * Sends a message to the group - listeners to this identifier will receive the messages. * @param identifier the key that the proper listeners are listenting on * @param msg the Message to be sent * @see #registerListener */ public void send(Serializable identifier, Message msg) throws Exception { if(msg == null) { if(log.isErrorEnabled()) log.error("msg is null"); return; } if(identifier == null) transport.send(msg); else { msg.putHeader(PULL_HEADER_ID, new PullHeader(identifier)); transport.send(msg); } } /** * Sends a message with no identifier; listener member will get this message on the other group members. * @param msg the Message to be sent * @throws Exception */ public void send(Message msg) throws Exception { send(null, msg); } public final void setListener(MessageListener l) { listener=l; } /** * Sets a listener to messages with a given identifier. * Messages sent with this identifier in their headers will be routed to this listener. * Note: there can be only one listener for one identifier; * if you want to register a different listener to an already registered identifier, then unregister first. * @param identifier - messages sent on the group with this object will be received by this listener * @param l - the listener that will get the message */ public void registerListener(Serializable identifier, MessageListener l) { if(l == null || identifier == null) { if(log.isErrorEnabled()) log.error("message listener or identifier is null"); return; } if(listeners.containsKey(identifier)) { if(log.isErrorEnabled()) log.error("listener with identifier=" + identifier + " already exists, choose a different identifier or unregister current listener"); // we do not want to overwrite the listener return; } listeners.put(identifier, l); } /** * Removes a message listener to a given identifier from the message listeners map. * @param identifier - the key to whom we do not want to listen any more */ public void unregisterListener(Serializable identifier) { listeners.remove(identifier); } /** @deprecated Use {@link #addMembershipListener} */ public void setMembershipListener(MembershipListener ml) { addMembershipListener(ml); } public final void addMembershipListener(MembershipListener l) { if(l != null && !membership_listeners.contains(l)) membership_listeners.add(l); } public void removeMembershipListener(MembershipListener l) { if(l != null && membership_listeners.contains(l)) membership_listeners.remove(l); } /** * Reentrant run(): message reception is serialized, then the listener is notified of the * message reception */ public void run() { Object obj; while(receiver_thread != null && Thread.currentThread().equals(receiver_thread)) { try { obj=transport.receive(0); if(obj == null) continue; if(obj instanceof Message) { handleMessage((Message)obj); } else if(obj instanceof GetStateEvent) { byte[] retval=null; GetStateEvent evt=(GetStateEvent)obj; String state_id=evt.getStateId(); if(listener != null) { try { if(listener instanceof ExtendedMessageListener && state_id!=null) { retval=((ExtendedMessageListener)listener).getState(state_id); } else { retval=listener.getState(); } } catch(Throwable t) { log.error("getState() from application failed, will return empty state", t); } } else { log.warn("no listener registered, returning empty state"); } if(transport instanceof Channel) { ((Channel)transport).returnState(retval, state_id); } else { if(log.isErrorEnabled()) log.error("underlying transport is not a Channel, but a " + transport.getClass().getName() + ": cannot return state using returnState()"); } } else if(obj instanceof SetStateEvent) { SetStateEvent evt=(SetStateEvent)obj; String state_id=evt.getStateId(); if(listener != null) { try { if(listener instanceof ExtendedMessageListener && state_id!=null) { ((ExtendedMessageListener)listener).setState(state_id, evt.getArg()); } else { listener.setState(evt.getArg()); } } catch(ClassCastException cast_ex) { if(log.isErrorEnabled()) log.error("received SetStateEvent, but argument " + ((SetStateEvent)obj).getArg() + " is not serializable ! Discarding message."); } } } else if(obj instanceof StreamingGetStateEvent) { StreamingGetStateEvent evt=(StreamingGetStateEvent)obj; if(listener instanceof ExtendedMessageListener) { if(evt.getStateId()==null) { ((ExtendedMessageListener)listener).getState(evt.getArg()); } else { ((ExtendedMessageListener)listener).getState(evt.getStateId(),evt.getArg()); } } } else if(obj instanceof StreamingSetStateEvent) { StreamingSetStateEvent evt=(StreamingSetStateEvent)obj; if(listener instanceof ExtendedMessageListener) { if(evt.getStateId()==null) { ((ExtendedMessageListener)listener).setState(evt.getArg()); } else { ((ExtendedMessageListener)listener).setState(evt.getStateId(),evt.getArg()); } } } else if(obj instanceof View) { notifyViewChange((View)obj); } else if(obj instanceof SuspectEvent) { notifySuspect((Address)((SuspectEvent)obj).getMember()); } else if(obj instanceof BlockEvent) { notifyBlock(); if(transport instanceof Channel) { ((Channel)transport).blockOk(); } } else if(obj instanceof UnblockEvent) { notifyUnblock(); } } catch(ChannelNotConnectedException conn) { Address local_addr=((Channel)transport).getAddress(); if(log.isTraceEnabled()) log.trace('[' + (local_addr == null ? "" : local_addr.toString()) + "] channel not connected, exception is " + conn); Util.sleep(1000); receiver_thread=null; break; } catch(ChannelClosedException closed_ex) { Address local_addr=((Channel)transport).getAddress(); if(log.isTraceEnabled()) log.trace('[' + (local_addr == null ? "" : local_addr.toString()) + "] channel closed, exception is " + closed_ex); // Util.sleep(1000); receiver_thread=null; break; } catch(Throwable e) { } } } /** * Check whether the message has an identifier. If yes, lookup the MessageListener associated with the * given identifier in the hashtable and dispatch to it. Otherwise just use the main (default) message * listener */ protected void handleMessage(Message msg) { PullHeader hdr=(PullHeader)msg.getHeader(PULL_HEADER_ID); Serializable identifier; MessageListener l; if(hdr != null && (identifier=hdr.getIdentifier()) != null) { l=(MessageListener)listeners.get(identifier); if(l == null) { if(log.isErrorEnabled()) log.error("received a messages tagged with identifier=" + identifier + ", but there is no registration for that identifier. Will drop message"); } else l.receive(msg); } else { if(listener != null) listener.receive(msg); } } protected void notifyViewChange(View v) { MembershipListener l; if(v == null) return; for(Iterator it=membership_listeners.iterator(); it.hasNext();) { l=(MembershipListener)it.next(); try { l.viewAccepted(v); } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("exception notifying " + l + " of view(" + v + ")", ex); } } } protected void notifySuspect(Address suspected_mbr) { MembershipListener l; if(suspected_mbr == null) return; for(Iterator it=membership_listeners.iterator(); it.hasNext();) { l=(MembershipListener)it.next(); try { l.suspect(suspected_mbr); } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("exception notifying " + l + " of suspect(" + suspected_mbr + ")", ex); } } } protected void notifyBlock() { MembershipListener l; for(Iterator it=membership_listeners.iterator(); it.hasNext();) { l=(MembershipListener)it.next(); try { l.block(); } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("exception notifying " + l + " of block()", ex); } } } protected void notifyUnblock() { MembershipListener l; for(Iterator it=membership_listeners.iterator(); it.hasNext();) { l=(MembershipListener)it.next(); if(l instanceof ExtendedMembershipListener){ try { ((ExtendedMembershipListener)l).unblock(); } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("exception notifying " + l + " of unblock()", ex); } } } } public void channelConnected(Channel channel) { if(log.isTraceEnabled()) log.trace("channel is connected"); } public void channelDisconnected(Channel channel) { if(log.isTraceEnabled()) log.trace("channel is disconnected"); } public void channelClosed(Channel channel) { } public static final class PullHeader extends Header { Serializable identifier=null; public PullHeader() { } public PullHeader(Serializable identifier) { this.identifier=identifier; } public Serializable getIdentifier() { return identifier; } public int size() { if(identifier == null) return 12; else return 64; } public String toString() { return "PullHeader"; } public void writeTo(DataOutputStream out) throws IOException { try { Util.objectToStream(identifier, out); } catch(Exception e) { IOException ee = new IOException(e.getMessage()); ee.initCause(e); throw ee; } } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { try { identifier=(Serializable)Util.objectFromStream(in); } catch(Exception e) { IOException ee = new IOException(e.getMessage()); ee.initCause(e); throw ee; } } } /** * @return Returns the listener. */ public MessageListener getListener() { return listener; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy