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

gnu.cajo.utils.extra.Queue Maven / Gradle / Ivy

There is a newer version: 1.0.1
Show newest version
package gnu.cajo.utils.extra;

import gnu.cajo.invoke.Invoke;
import gnu.cajo.invoke.Remote;
import gnu.cajo.utils.ItemServer;
import gnu.cajo.utils.MonitorItem;
import gnu.cajo.utils.Multicast;

import java.rmi.RemoteException;
import java.util.LinkedList;

/*
 * cajo asynchronous object method invocation queue
 * Copyright (C) 2006 John Catherino
 * The cajo project: https://cajo.dev.java.net
 *
 * For issues or suggestions mailto:[email protected]
 *
 * This file Queue.java is part of the cajo library.
 *
 * The cajo library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public Licence as published
 * by the Free Software Foundation, at version 3 of the licence, or (at your
 * option) any later version.
 *
 * The cajo library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public Licence for more details.
 *
 * You should have received a copy of the GNU Lesser General Public Licence
 * along with this library. If not, see http://www.gnu.org/licenses/lgpl.html
 */

/**
 * This class is a cajo-based implementation of the message communication
 * paradigm. One or more producer objects can invoke methods on this
 * object, and the invocations will be asynchronously performed on one 
 * (point to point), or more (publish/subscribe), consumer
 * objects.  A producer, by virtue of having its method invocation return
 * immediately and successfully, can be certain its invocation has been
 * enqueued, and will be invoked on the member consumers (if any). The
 * consumers can be either local or remote objects.

* A producer enqueues its invocations for the member consumers, by invoking the * corresponding method on its reference to an instance of this object. Its * argument(s), if any, will be invoked on the matching method of each of the * consumer objects, in a separate thread. This creates a dynamic buffer of * invocations.

* Due to the asynchronous disconnection between the producer and consumer(s), * no results can be returned from the method invocations. If a producer * wishes to receive data from each of the consumers, it could provide a * callback reference as one of the arguments, for example.

* Interactions between producers and consumers can be as simple as a single * method taking no arguments - notification, to a method taking a * single * argument - messaging, to multiple methods taking multiple arguments - * remote procedure calls.

* Finally, this class is serialisable, to allow passing of Queue objects * between machines, and replication of Queue objects, the possibilities this * creates require a bit of thought indeed... * * @version 1.0, 25-Jun-06 * @author John Catherino */ public class Queue implements Invoke { private static final long serialVersionUID = 1L; /** * Some manner of commonly agreed upon descriptor for the subject matter * about which the producers and consumers are interested. It must be * serialisable. */ protected final Object topic; /** * The list of all pending producer method invocations. */ protected LinkedList invocations = new LinkedList(); /** * The list of consumers, remote and local, to receive producer invocations. */ protected LinkedList consumers = new LinkedList(); /** * This is the thread performing the asynchronous invocation operation, * invoking the corresponding method on consumer objects. It is * instantiated dynamically, upon the first producer invocation. */ protected transient Thread thread; /** * The constructor simply assigns the topic for the object and returns, as * the object is entirely event driven. Note: the descriptor * object must be serialisable. * @param topic A descriptor object, mutually agreed upon by all participants */ public Queue(Object topic) { this.topic = topic; } /** * This method is used to request the topic of the producer/consumer * community. Note: the object returned may be unknown to * caller, thus resulting in a NoClassDefFoundError, in that case, it is * probably not a good idea to join the community as either a producer or * consumer. * @return The community descriptor */ public Object topic() { return topic; } /** * This method is used to add an object to list of consumers awaiting * producer invocation. * @param consumer The object wishing to subscribe */ public synchronized void enqueue(Object consumer) { consumers.add(consumer); } /** * This method is used to remove a consumer from the list. * @param consumer The object wishing to unsubscribe */ public synchronized void dequeue(Object consumer) { consumers.remove(consumer); } /** * This method is called to suspend method invocation dispatching. Producer * invocations will continue to queue. This method is iddmpotent. It can * only be invoked locally. */ public synchronized void pause() { if (thread != null && !thread.isInterrupted()) thread.interrupt(); } /** * This method is called to resume method invocation dispatching. This * method is idempotent. It can only be invoked locally. */ public synchronized void resume() { if (thread != null && thread.isInterrupted()) thread = null; } /** * This is the method a producer, local or remote, would invoke, to be * performed in a message-based fashion, asynchronously, on all registered * consumers. This method returns immediately, implicitly guaranteeing the * invocation has been successfully enqueued.

* Note: normally invocation of the methods topic, enque, and * deque will not be passed along to consumers, as they are performed * on the Queue instance. However, invoking topic with any arguments, and * enqueue or dequeue with no arguments, these will be passed on to * consumers, as they do not apply to the Queue instance. * @param method The public method name on the consumer objects to be * invoked * @param args The argument(s) to invoke on the consumer object's method, * there can be none, to simply perform notification, there can be a single * argument to provide data, or there can be a collection to engage a * behaviour, presumably, the subscribed object has a matching public method * signature. * @return null Since consumer invocation is performed asynchronously, there * can be no synchronous return data, a callback object reference can be * provided as an argument, if result data is required * @throws java.rmi.RemoteException If the invocation failed to enqueue, * due to a network related error */ public synchronized Object invoke(String method, Object args) { if (method.equals("enqueue") && args != null) { enqueue(args); return null; } else if (method.equals("dequeue") && args != null) { dequeue(args); return null; } else if (method.equals("topic") && args == null) return topic(); if (thread == null) { thread = new Thread(new Runnable() { public void run() { try { do { String method; Object args, consumers[]; synchronized(Queue.this) { while (invocations.size() == 0) Queue.this.wait(); method = (String)invocations.removeFirst(); args = invocations.removeFirst(); if (Queue.this.consumers.isEmpty()) continue; consumers = Queue.this.consumers.toArray(); } for (int i = 0; i < consumers.length; i++) try { Remote.invoke(consumers[i], method, args); } catch(RemoteException x) { dequeue(consumers[i]); } catch(Exception x) {} catch(Throwable t) {} } while(!thread.isInterrupted()); } catch(InterruptedException x) {} } }); thread.start(); } invocations.add(method); invocations.add(args); notify(); return null; } /** * This method will start up a remotely accessible Queue object, in its own * JVM. For illustrative purposes, the queue object will be wrapped in a * MonitorItem, which outputs to the console. It will bind locally under * its topic name, and announce itself over multicast, using the cajo IANA * UDP address, on port 1198. * @param args The first string, if defined, will be the topic string, * undefined it will be "void", the second, if defined, will be the TCP port * number on which the queue will accept invocations, undefined it will be * 1198, it can be zero, to use an anonymous port * @throws Exception For startup or machine/network configuration issues */ public static void main(String args[]) throws Exception { String topic = args.length > 0 ? args[0] : "void"; int port = args.length > 1 ? Integer.parseInt(args[1]) : 1198; Remote.config(null, port, null, port); Object queue = new Remote(new MonitorItem(new Queue(topic))); System.out.println("Queue started\n"); ItemServer.bind(queue, topic); new Multicast("224.0.23.162", 1198).announce(queue, 32); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy