org.zeromq.ZStar Maven / Gradle / Ivy
Show all versions of jeromq Show documentation
package org.zeromq;
import java.io.IOException;
import java.nio.channels.Selector;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;
import org.zeromq.ZMQ.Socket;
import org.zeromq.ZThread.IAttachedRunnable;
/**
* First implementation for the base of a remotely controlled background service for 0MQ.
*
*
* A side or endpoint designates the same thing: the thread where lives one of the two parts of the system.
*
* A {@link ZStar Star} has 2 sides (with a trial to use theater terms for fun (: and hopefully clarity :)
*
* - the Corbeille side, or control side
*
* This is where one can {@link ZAgent#send(ZMsg) send} and {@link ZAgent#recv() receive} control messages to the underneath star via the ZStar agent.
*
* The ZStar lives on this side and is a way to safely communicate with the distant provided star.
*
* Note: Corbeille is a french word designing the most privileged places in the theater where the King was standing,
* 1st floor, just above the orchestra (Wikipedia...).
*
*
* - the Plateau side, or background side where all the performances are made by the provided star.
*
* The provided star is living on the Plateau.
*
* The Plateau is made of the Stage where effective acting takes place and of the Wings
* where the provided star can perform control decisions to stop or continue the acting.
*
* From this side, the work is done via callbacks using the template-method pattern, applied with interfaces (?)
*
* Communication between the 2 sides is ensured by {@link #pipe() pipes} that SHALL NEVER be mixed between background and control sides.
*
* Instead, each side use its own pipe to communicate with the other one, transmitting only 0MQ messages.
* The main purpose of this class is to ease this separation and to provide contracts, responsibilities and action levers for each side.
*
* For example, instead of using pipe() which is useful to have more control, fast users would want to call {@link #agent()} to get the agent
* or directly call {@link #send(ZMsg)} or {@link #recv()} from the ZStar as the ZStar is itself an agent!
*
* The ZStar takes care of the establishment of the background processing, calling the provided star
* at appropriate times. It can also manage the {@link ZAgent#sign() exited} state on the control side,
* if providing a non-null "Mot de la Fin".
*
* It also takes care of the closing of sockets and context if it had to create one.
*
* A {@link Star star} is basically a contract interface that anyone who uses this ZStar to create a background service SHALL comply to.
*
* PS: Je sais qu'il y a une différence entre acteur et comédien :)
* PPS: I know nothing about theater!
*
* For an example of code, please refer to the {@link ZActor} source code.
*
*/
// remote controlled background message processing API for 0MQ.
public class ZStar implements ZAgent
{
/**
* Contract interface when acting in plain sight.
*/
// contract interface for acting with the spot lights on
public interface Star
{
/**
* Called when the star is in the wings.
* Hint: Can be used to initialize the service, or ...
* Key point: no loop has started already.
*/
void prepare();
/**
* Called when the star in on stage, just before acting.
* Hint: Can be used to poll events or get input/events from other sources, or ...
* Key point: a loop just started.
*
* @return the number of events to process
*/
int breathe();
/**
* Where acting takes place ...
* Hint: Can be used to process the events or input acquired from the previous step, or ...
* Key point: in the middle of a loop.
* Decision: to act on the next loop or not
*
* @param events the number of events to process
* @return true to continue till the end of the act, false to stop loopS here.
*/
boolean act(int events);
/**
* Called as an interval between each act.
* Hint: Can be used to perform decisions to continue next loop or not, or to send computed data to outputs, or ...
* Key point: at the end of a loop.
* Decision: to act on the next loop or not
*
* @return true to continue acting, false to stop loopS here.
*/
boolean entract();
/**
* Does the star want to renew for a new performance ?
* Hint: Can be used to perform decisions to continue looping or not, or to send computed data to outputs, or ...
* Key point: the inner looping mechanism just ended
* Decision: to exit or not
*
* @return true to restart acting, false to leave here
*/
boolean renews();
}
/**
* Utility class with callback for when the Star has finished its performances.
*/
public interface TimeTaker
{
/**
* Called when the show is finished but no cleaning is still done.
* Useful to make the background thread wait a little bit, for example.
*
* @param ctx the shadow context
*/
void party(ZContext ctx);
}
// party time easily done. Wait for the specified amount of time
public static void party(long time, TimeUnit unit)
{
LockSupport.parkNanos(TimeUnit.NANOSECONDS.convert(time, unit));
}
// contract for a creator of stars
public interface Fortune extends TimeTaker
{
/**
* This is the grand premiere!
* Called when the star enters the plateau.
* The show is about to begin. Inform the public about that.
* Called before the creation of the first star and its sockets
*
* @param mic the pipe to the Corbeille side
* @param args the arguments passed as parameters of the star constructor
*
* @return the name of the upcoming performance.
*/
String premiere(Socket mic, Object... args);
/**
* Creates a star.
*
* @param ctx the context used for the creation of the sockets
* @param mic the pipe to the Corbeille side
* @param sel the selector used for polling
* @param count the number of times a star was created.
* @param previous the previous star if any (null at the first creation)
* @param args the arguments passed as parameters of the star constructor
*
* @return a new star is born!
*/
Star create(ZContext ctx, Socket mic, Selector sel, int count, Star previous, Object... args);
/**
* The show is over.
* Called when the show is over.
*
* @param mic the pipe to the Corbeille side
* @return true to allow to spread the word and close all future communications
*/
boolean interview(Socket mic);
}
/**
* Control for the end of the remote operations.
*/
public interface Exit
{
/**
* Causes the current thread to wait in blocking mode until the end of the remote operations.
*/
void awaitSilent();
/**
* Causes the current thread to wait in blocking mode until the end of the remote operations,
* unless the thread is interrupted.
*
*
If the current thread:
*
* - has its interrupted status set on entry to this method; or
*
- is {@linkplain Thread#interrupt interrupted} while waiting,
*
* then {@link InterruptedException} is thrown and the current thread's
* interrupted status is cleared.
*
* @throws InterruptedException if the current thread is interrupted
* while waiting
*/
void await() throws InterruptedException;
/**
* Causes the current thread to wait in blocking mode until the end of the remote operations,
* unless the thread is interrupted, or the specified waiting time elapses.
*
* If the current thread:
*
* - has its interrupted status set on entry to this method; or
*
- is {@linkplain Thread#interrupt interrupted} while waiting,
*
* then {@link InterruptedException} is thrown and the current thread's
* interrupted status is cleared.
*
* If the specified waiting time elapses then the value {@code false}
* is returned. If the time is less than or equal to zero, the method
* will not wait at all.
*
* @param timeout the maximum time to wait
* @param unit the time unit of the {@code timeout} argument
* @return {@code true} if the remote operations ended and {@code false}
* if the waiting time elapsed before the remote operations ended
* @throws InterruptedException if the current thread is interrupted
* while waiting
*/
boolean await(long timeout, TimeUnit unit) throws InterruptedException;
/**
* Checks in non-blocking mode, if the remote operations have ended.
* @return true if the runnable where the remote operations occurred if finished, otherwise false.
*/
boolean isExited();
}
/**
* Returns the Corbeille endpoint.
* Can be used to send or receive control messages to the distant star via Backstage.
*
* @return the agent/mailbox used to send and receive control messages to and from the star.
*/
public ZAgent agent()
{
return agent;
}
/**
* Returns the control of the proper exit of the remote operations.
* @return a structure used for checking the end of the remote operations.
*/
public Exit exit()
{
return plateau;
}
/**
* Creates a new ZStar.
*
* @param actor the creator of stars on the Plateau
* @param lock the final word used to mark the end of the star. Null to disable this mechanism.
* @param args the optional arguments that will be passed to the distant star
*/
public ZStar(Fortune actor, String lock, Object... args)
{
this(new VerySimpleSelectorCreator(), actor, lock, args);
}
/**
* Creates a new ZStar.
*
* @param selector the creator of the selector used on the Plateau.
* @param fortune the creator of stars on the Plateau
* @param motdelafin the final word used to mark the end of the star. Null to disable this mechanism.
* @param args the optional arguments that will be passed to the distant star
*/
public ZStar(SelectorCreator selector, Fortune fortune, String motdelafin, Object... args)
{
this(null, selector, fortune, motdelafin, args);
}
/**
* Creates a new ZStar.
*
* @param context
* the main context used. If null, a new context will be created
* and closed at the stop of the operation.
* If not null, it is the responsibility of the caller to close it.
*
* @param selector the creator of the selector used on the Plateau.
* @param fortune the creator of stars on the Plateau
* @param motdelafin the final word used to mark the end of the star. Null to disable this mechanism.
* @param bags the optional arguments that will be passed to the distant star
*/
public ZStar(final ZContext context, final SelectorCreator selector, final Fortune fortune, String motdelafin,
final Object... bags)
{
super();
assert (fortune != null);
// entering platform to load trucks
// the story writer
SelectorCreator feather = selector;
if (selector == null) {
// initialize selector
feather = new VerySimpleSelectorCreator();
}
// initialize the context
ZContext chef = context;
ZContext producer = null;
if (chef == null) {
// no context provided, create one
chef = new ZContext();
// it will be destroyed, so this is the main context
producer = chef;
}
this.context = chef;
assert (this.context != null);
// retrieve the last optional set and entourage given in input
Set set = null;
Entourage entourage = null;
for (Object bag : bags) {
if (bag instanceof Set) {
set = (Set) bag;
}
if (bag instanceof Entourage) {
entourage = (Entourage) bag;
}
}
if (set == null) {
set = new SimpleSet();
}
final List