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

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

The newest version!
// $Id: DistributedQueue.java,v 1.23 2009/05/13 13:06:54 belaban Exp $
package bboss.org.jgroups.blocks;

import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.NoSuchElementException;
import java.util.Vector;

import bboss.org.jgroups.Address;
import bboss.org.jgroups.Channel;
import bboss.org.jgroups.ChannelClosedException;
import bboss.org.jgroups.ChannelException;
import bboss.org.jgroups.ChannelFactory;
import bboss.org.jgroups.ChannelNotConnectedException;
import bboss.org.jgroups.JChannel;
import bboss.org.jgroups.MembershipListener;
import bboss.org.jgroups.Message;
import bboss.org.jgroups.MessageListener;
import bboss.org.jgroups.View;
import bboss.org.jgroups.annotations.Unsupported;
import bboss.org.jgroups.logging.Log;
import bboss.org.jgroups.logging.LogFactory;
import bboss.org.jgroups.util.RspList;
import bboss.org.jgroups.util.Util;


/**
 * Provides the abstraction of a java.util.LinkedList that is replicated at several
 * locations. Any change to the list (reset, add, remove, etc.) will transparently be
 * propagated to all replicas in the group. All read-only methods will always access the
 * local replica.

* Both keys and values added to the list must be serializable, the reason * being that they will be sent across the network to all replicas of the group. * An instance of this class will contact an existing member of the group to fetch its * initial state. * Beware to use a total protocol on initialization or elements would not be in same * order on all replicas. * @author Romuald du Song */ @Unsupported public class DistributedQueue implements MessageListener, MembershipListener, Cloneable { public interface Notification { void entryAdd(Object value); void entryRemoved(Object key); void viewChange(Vector new_mbrs, Vector old_mbrs); void contentsCleared(); void contentsSet(Collection new_entries); } protected Log logger = LogFactory.getLog(getClass()); private long internal_timeout = 10000; // 10 seconds to wait for a response /*lock object for synchronization*/ protected final Object mutex = new Object(); protected boolean stopped = false; // whether to we are stopped ! protected LinkedList internalQueue; protected Channel channel; protected RpcDispatcher disp = null; protected String groupname = null; protected Vector notifs = new Vector(); // to be notified when mbrship changes protected Vector members = new Vector(); // keeps track of all DHTs private Class[] add_signature = null; private Class[] addAtHead_signature = null; private Class[] addAll_signature = null; private Class[] reset_signature = null; private Class[] remove_signature = null; /** * Creates a DistributedQueue * @param groupname The name of the group to join * @param factory The ChannelFactory which will be used to create a channel * @param properties The property string to be used to define the channel * @param state_timeout The time to wait until state is retrieved in milliseconds. A value of 0 means wait forever. */ public DistributedQueue(String groupname, ChannelFactory factory, String properties, long state_timeout) throws ChannelException { if (logger.isDebugEnabled()) { logger.debug("DistributedQueue(" + groupname + ',' + properties + ',' + state_timeout); } this.groupname = groupname; initSignatures(); internalQueue = new LinkedList(); channel = (factory != null) ? factory.createChannel((Object)properties) : new JChannel(properties); disp = new RpcDispatcher(channel, this, this, this); disp.setDeadlockDetection(false); // To ensure strict FIFO MethodCall channel.connect(groupname); start(state_timeout); } public DistributedQueue(JChannel channel) { this.groupname = channel.getClusterName(); this.channel = channel; init(); } /** * Uses a user-provided PullPushAdapter to create the dispatcher rather than a Channel. If id is non-null, it will be * used to register under that id. This is typically used when another building block is already using * PullPushAdapter, and we want to add this building block in addition. The id is the used to discriminate * between messages for the various blocks on top of PullPushAdapter. If null, we will assume we are the * first block created on PullPushAdapter. * The caller needs to call start(), before using the this block. It gives the opportunity for the caller * to register as a lessoner for Notifications events. * @param adapter The PullPushAdapter which to use as underlying transport * @param id A serializable object (e.g. an Integer) used to discriminate (multiplex/demultiplex) between * requests/responses for different building blocks on top of PullPushAdapter. */ public DistributedQueue(PullPushAdapter adapter, Serializable id) { this.channel = (Channel)adapter.getTransport(); this.groupname = this.channel.getClusterName(); initSignatures(); internalQueue = new LinkedList(); disp = new RpcDispatcher(adapter, id, this, this, this); disp.setDeadlockDetection(false); // To ensure strict FIFO MethodCall } protected final void init() { initSignatures(); internalQueue = new LinkedList(); disp = new RpcDispatcher(channel, this, this, this); disp.setDeadlockDetection(false); // To ensure strict FIFO MethodCall } public final void start(long state_timeout) throws ChannelClosedException, ChannelNotConnectedException { boolean rc; logger.debug("DistributedQueue.initState(" + groupname + "): starting state retrieval"); rc = channel.getState(null, state_timeout); if (rc) { logger.info("DistributedQueue.initState(" + groupname + "): state was retrieved successfully"); } else { logger.info("DistributedQueue.initState(" + groupname + "): state could not be retrieved (first member)"); } } public Address getLocalAddress() { return (channel != null) ? channel.getAddress() : null; } public Channel getChannel() { return channel; } public void addNotifier(Notification n) { if (n != null && !notifs.contains(n)) { notifs.addElement(n); } } public void removeNotifier(Notification n) { notifs.removeElement(n); } public void stop() { /*lock the queue from other threads*/ synchronized (mutex) { internalQueue.clear(); if (disp != null) { disp.stop(); disp = null; } if (channel != null) { channel.close(); channel = null; } stopped = true; } } /** * Add the speficied element at the bottom of the queue * @param value */ public void add(Object value) { try { Object retval = null; RspList rsp = disp.callRemoteMethods(null, "_add", new Object[]{value}, add_signature, GroupRequest.GET_ALL, 0); Vector results = rsp.getResults(); if (results.size() > 0) { retval = results.elementAt(0); if (logger.isDebugEnabled()) { checkResult(rsp, retval); } } } catch (Exception e) { logger.error("Unable to add value " + value, e); } } /** * Add the speficied element at the top of the queue * @param value */ public void addAtHead(Object value) { try { disp.callRemoteMethods(null, "_addAtHead", new Object[]{value}, addAtHead_signature, GroupRequest.GET_ALL, 0); } catch (Exception e) { logger.error("Unable to addAtHead value " + value, e); } } /** * Add the speficied collection to the top of the queue. * Elements are added in the order that they are returned by the specified * collection's iterator. * @param values */ public void addAll(Collection values) { try { disp.callRemoteMethods(null, "_addAll", new Object[]{values}, addAll_signature, GroupRequest.GET_ALL, 0); } catch (Exception e) { logger.error("Unable to addAll value: " + values, e); } } public Vector getContents() { Vector result = new Vector(); for (Iterator e = internalQueue.iterator(); e.hasNext();) result.add(e.next()); return result; } public int size() { return internalQueue.size(); } /** * returns the first object on the queue, without removing it. * If the queue is empty this object blocks until the first queue object has * been added * @return the first object on the queue */ public Object peek() { Object retval = null; try { retval = internalQueue.getFirst(); } catch (NoSuchElementException e) { } return retval; } public void reset() { try { disp.callRemoteMethods(null, "_reset", null, reset_signature, GroupRequest.GET_ALL, 0); } catch (Exception e) { logger.error("DistributedQueue.reset(" + groupname + ')', e); } } protected void checkResult(RspList rsp, Object retval) { if (logger.isDebugEnabled()) { logger.debug("Value updated from " + groupname + " :" + retval); } Vector results = rsp.getResults(); for (int i = 0; i < results.size(); i++) { Object data = results.elementAt(i); if (!data.equals(retval)) { logger.error("Reference value differs from returned value " + retval + " != " + data); } } } /** * Try to return the first objet in the queue.It does not wait for an object. * @return the first object in the queue or null if none were found. */ public Object remove() { Object retval = null; RspList rsp = disp.callRemoteMethods(null, "_remove", null, remove_signature, GroupRequest.GET_ALL, internal_timeout); Vector results = rsp.getResults(); if (results.size() > 0) { retval = results.elementAt(0); if (logger.isDebugEnabled()) { checkResult(rsp, retval); } } return retval; } /** * @param timeout The time to wait until an entry is retrieved in milliseconds. A value of 0 means wait forever. * @return the first object in the queue or null if none were found */ public Object remove(long timeout) { Object retval = null; long start = System.currentTimeMillis(); if (timeout <= 0) { while (!stopped && (retval == null)) { RspList rsp = disp.callRemoteMethods(null, "_remove", null, remove_signature, GroupRequest.GET_ALL, internal_timeout); Vector results = rsp.getResults(); if (results.size() > 0) { retval = results.elementAt(0); if (logger.isDebugEnabled()) { checkResult(rsp, retval); } } if (retval == null) { try { synchronized (mutex) { mutex.wait(); } } catch (InterruptedException e) { } } } } else { while (((System.currentTimeMillis() - start) < timeout) && !stopped && (retval == null)) { RspList rsp = disp.callRemoteMethods(null, "_remove", null, remove_signature, GroupRequest.GET_ALL, internal_timeout); Vector results = rsp.getResults(); if (results.size() > 0) { retval = results.elementAt(0); if (logger.isDebugEnabled()) { checkResult(rsp, retval); } } if (retval == null) { try { long delay = timeout - (System.currentTimeMillis() - start); synchronized (mutex) { if (delay > 0) { mutex.wait(delay); } } } catch (InterruptedException e) { } } } } return retval; } public String toString() { return internalQueue.toString(); } /*------------------------ Callbacks -----------------------*/ public void _add(Object value) { if (logger.isDebugEnabled()) { logger.debug(groupname + '@' + getLocalAddress() + " _add(" + value + ')'); } /*lock the queue from other threads*/ synchronized (mutex) { internalQueue.add(value); /*wake up all the threads that are waiting for the lock to be released*/ mutex.notifyAll(); } for (int i = 0; i < notifs.size(); i++) ((Notification)notifs.elementAt(i)).entryAdd(value); } public void _addAtHead(Object value) { /*lock the queue from other threads*/ synchronized (mutex) { internalQueue.addFirst(value); /*wake up all the threads that are waiting for the lock to be released*/ mutex.notifyAll(); } for (int i = 0; i < notifs.size(); i++) ((Notification)notifs.elementAt(i)).entryAdd(value); } public void _reset() { if (logger.isDebugEnabled()) { logger.debug(groupname + '@' + getLocalAddress() + " _reset()"); } _private_reset(); for (int i = 0; i < notifs.size(); i++) ((Notification)notifs.elementAt(i)).contentsCleared(); } protected void _private_reset() { /*lock the queue from other threads*/ synchronized (mutex) { internalQueue.clear(); /*wake up all the threads that are waiting for the lock to be released*/ mutex.notifyAll(); } } public Object _remove() { Object retval = null; try { /*lock the queue from other threads*/ synchronized (mutex) { retval = internalQueue.removeFirst(); /*wake up all the threads that are waiting for the lock to be released*/ mutex.notifyAll(); } if (logger.isDebugEnabled()) { logger.debug(groupname + '@' + getLocalAddress() + "_remove(" + retval + ')'); } for (int i = 0; i < notifs.size(); i++) ((Notification)notifs.elementAt(i)).entryRemoved(retval); } catch (NoSuchElementException e) { logger.debug(groupname + '@' + getLocalAddress() + "_remove(): nothing to remove"); } return retval; } public void _addAll(Collection c) { if (logger.isDebugEnabled()) { logger.debug(groupname + '@' + getLocalAddress() + " _addAll(" + c + ')'); } /*lock the queue from other threads*/ synchronized (mutex) { internalQueue.addAll(c); /*wake up all the threads that are waiting for the lock to be released*/ mutex.notifyAll(); } for (int i = 0; i < notifs.size(); i++) ((Notification)notifs.elementAt(i)).contentsSet(c); } /*----------------------------------------------------------*/ /*-------------------- State Exchange ----------------------*/ public void receive(Message msg) { } public byte[] getState() { Vector copy = (Vector)getContents().clone(); try { return Util.objectToByteBuffer(copy); } catch (Throwable ex) { logger.error("DistributedQueue.getState(): exception marshalling state.", ex); return null; } } public void setState(byte[] new_state) { Vector new_copy; try { new_copy = (Vector)Util.objectFromByteBuffer(new_state); if (new_copy == null) { return; } } catch (Throwable ex) { logger.error("DistributedQueue.setState(): exception unmarshalling state.", ex); return; } _private_reset(); // remove all elements _addAll(new_copy); } /*------------------- Membership Changes ----------------------*/ public void viewAccepted(View new_view) { Vector new_mbrs = new_view.getMembers(); if (new_mbrs != null) { sendViewChangeNotifications(new_mbrs, members); // notifies observers (joined, left) members.removeAllElements(); for (int i = 0; i < new_mbrs.size(); i++) members.addElement(new_mbrs.elementAt(i)); } } /** Called when a member is suspected */ public void suspect(Address suspected_mbr) { ; } /** Block sending and receiving of messages until ViewAccepted is called */ public void block() { } void sendViewChangeNotifications(Vector new_mbrs, Vector old_mbrs) { Vector joined; Vector left; Object mbr; Notification n; if ((notifs.size() == 0) || (old_mbrs == null) || (new_mbrs == null) || (old_mbrs.size() == 0) || (new_mbrs.size() == 0)) { return; } // 1. Compute set of members that joined: all that are in new_mbrs, but not in old_mbrs joined = new Vector(); for (int i = 0; i < new_mbrs.size(); i++) { mbr = new_mbrs.elementAt(i); if (!old_mbrs.contains(mbr)) { joined.addElement(mbr); } } // 2. Compute set of members that left: all that were in old_mbrs, but not in new_mbrs left = new Vector(); for (int i = 0; i < old_mbrs.size(); i++) { mbr = old_mbrs.elementAt(i); if (!new_mbrs.contains(mbr)) { left.addElement(mbr); } } for (int i = 0; i < notifs.size(); i++) { n = (Notification)notifs.elementAt(i); n.viewChange(joined, left); } } final void initSignatures() { try { if (add_signature == null) { add_signature = new Class[] { Object.class }; } if (addAtHead_signature == null) { addAtHead_signature = new Class[] { Object.class }; } if (addAll_signature == null) { addAll_signature = new Class[] { Collection.class }; } if (reset_signature == null) { reset_signature = new Class[0]; } if (remove_signature == null) { remove_signature = new Class[0]; } } catch (Throwable ex) { logger.error("DistributedQueue.initMethods()", ex); } } public static void main(String[] args) { try { // The setup here is kind of weird: // 1. Create a channel // 2. Create a DistributedQueue (on the channel) // 3. Connect the channel (so the HT gets a VIEW_CHANGE) // 4. Start the HT // // A simpler setup is // DistributedQueue ht = new DistributedQueue("demo", null, // "file://c:/JGroups-2.0/conf/total-token.xml", 5000); JChannel c = new JChannel("file:/c:/JGroups-2.0/conf/conf/total-token.xml"); DistributedQueue ht = new DistributedQueue(c); c.connect("demo"); ht.start(5000); ht.add("name"); ht.add("Michelle Ban"); Object old_key = ht.remove(); System.out.println("old key was " + old_key); old_key = ht.remove(); System.out.println("old value was " + old_key); ht.add("name 'Michelle Ban'"); System.out.println("queue is " + ht); } catch (Throwable t) { t.printStackTrace(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy