java-spine
is a minimal stimulus-reflex (event) library for Java. It is based on
publish-subscribe pattern,
and can be used in event-loop construct as well, see
examples. Here, everthing related to events in computer science is named in
terms of their biological counterparts to make it easier to feel and memorize.
It is simple and tiny,
helping you reduce your design complexity, development time and software bugs. java-spine
is available for Java 6+, and can be used for Desktop, Web, or Mobile (Android).
Spinal Cord or Spine is the connected set of
thinking bones in our body. We know it as the one who pulls our hand back from a hot object,
or leg from a sharp stone. It autonomously and almost instantly provides a reflex
to a stimulus and also informs our brain to enable it to do something more on a given
stimulus. java-spine
follows this concept.
java-spine
consists of class Spine which is used to associate reflexes to
stimuli as spine.on(stimuli, reflexes)
. Appropriate reflexes are triggered when a
stimulus is indicated as spine.is(stimulus, args)
. Any reflex to a stimulus can be removed
as spine.off(stimuli, reflexes)
. If a stimulus has no associated reflex, the fallback
reflex is invoked, whch simply prints out the details of the stimulus. This behaviour can
be changed with Spine.fallback(reflex)
to your own fallback reflex.
A reflex can be made as a class implementing interface Reflexive,
using method reference, as anonymous class, as
lambda expression, or as methods encapsulated by class Reflex.
Reflexes are categorized as slow and fast. A fast reflex is invoked by Spine
synchronously (because
it is fast), while a slow reflex asynchronously (on a seperate thread from threadpool). This speed can
be set by using annotation @Speed as @Speed("slow")
for slow reflex (@Speed("fast")
is
optional). It can also be set by reflex.speed(speed)
(not suggested).
While indicating a stimulus as spine.is("mouse-click", "x", x, "y", y)
, additional arguments
or information can be passed to the reflex. This information can be obtained at the reflex as
args.get("x")
to get x
value passed above. It is possible to create an class full of reflexes
which respond to specific stimulus by creating static methods with names as on<stimulus>
and
creating a Spine
object as new Spine(ClassName.class)
. A similar way is possible for instance
methods. This technique can be used to create an Event Loop.
Get started by going through the examples. To use this library, goto
releases, and download the JAR files,
java-spine.jar
, java-spine-sources.jar
, java-spine-javadoc.jar
of the appropriate
JDK version and add them to your project. Use reference or
javadoc while developing
an application.
Main.java
package main;
// required modules
import org.event.*;
public class Main {
public static void main(String[] args) {
Spine spine = new Spine();
// trigger default reflex
spine.is("hot-object", "msg", "Ouch!");
}
}
Output : @Coding Ground
[hot-object] : {msg=Ouch!}
HelloReflex.java
package main;
// required modules
import java.util.*;
import org.event.*;
public class HelloReflex implements Reflexive {
@Override
public void on(String stimulus, Map args) {
System.out.println("Lets get to work");
}
}
ByeReflex.java
package main;
// required modules
import java.util.*;
import org.event.*;
public class ByeReflex implements Reflexive {
@Override
@Speed("slow")
public void on(String stimulus, Map args) {
System.out.print("Name: ");
Scanner in = new Scanner(System.in);
String name = in.next();
System.out.println("Nice to meet you "+name);
}
}
Main.java
package main;
// required modules
import org.event.*;
public class Main {
public static void main(String[] args) {
HelloReflex hello = new HelloReflex();
// annotations only work when encapsulated by Reflex
Reflexive bye = new Reflex(new ByeReflex());
Spine spine = new Spine();
// chaining method calls is supported
spine.on("hello", hello).on("bye", bye);
spine.is("hello").is("bye");
}
}
Output : @Coding Ground
Lets get to work
Name: anonymous
Nice to meet you anonymous
Main.java
package main;
// required modules
import java.util.*;
import org.event.*;
public class Main {
public static void helloReflex(String stimulus, Map args) {
System.out.println("Lets get to work");
}
public void byeReflex(String stimulus, Map args) {
System.out.print("Name: ");
Scanner in = new Scanner(System.in);
String name = in.next();
System.out.println("Nice to meet you "+name);
}
public static void main(String[] args) {
Main main = new Main();
Spine spine = new Spine();
// static reflex method reference
spine.on("hello", Main::helloReflex);
// instance reflex method reference
// speed can be indicated manually as well
spine.on("bye", new Reflex(main::byeReflex).speed("slow"));
spine.is("hello");
// slow reflexes trigger asynchronously
spine.is("bye");
System.out.println("ok?");
}
}
Output : @Coding Ground
Lets get to work
Name: anonymous
Nice to meet you anonymous
Main.java
package main;
// required modules
import java.util.*;
import org.event.*;
public class Main {
public static void main(String[] args) {
// annotations allowed in anonymous class
Reflexive bye = new Reflex(new Reflexive() {
@Override
@Speed("slow")
public void on(String stimulus, Map args) {
String name = "anonymous";
System.out.println("Nice to meet you "+name);
}
});
Spine spine = new Spine();
spine.on("bye", bye);
// default reflex for hello
spine.is("hello").is("bye");
}
}
Output : @Coding Ground
[hello] : {}
Name: anonymous
Nice to meet you anonymous
Main.java
package main;
// required modules
import java.util.*;
import org.event.*;
public class Main {
public static void main(String[] args) {
Spine spine = new Spine();
// lambda expressions are much simpler to use
// but they dont support annotations
// hence speed must be set manually
spine.on("bye", (stimulus, eargs) -> {
String name = "anonymous";
System.out.println("Nice to meet you "+name);
});
// default reflex for hello
spine.is("hello").is("bye");
}
}
Output : @Coding Ground
[hello] : {}
Name: anonymous
Nice to meet you anonymous
Main.java
package main;
// required modules
import java.util.*;
import org.event.*;
public class Main {
public static void helloReflex(String stimulus, Map args) {
System.out.println("Lets get to work");
}
public void byeReflex(String stimulus, Map args) {
System.out.print("Name: ");
Scanner in = new Scanner(System.in);
String name = in.next();
System.out.println("Nice to meet you "+name);
}
public static void main(String[] args) {
Main main = new Main();
Spine spine = new Spine();
// static reflex method
spine.on("hello", new Reflex(Main.class, "helloReflex"));
// instance reflex method
// speed can be indicated manually as well
spine.on("bye", new Reflex(main, "byeReflex").speed("slow"));
spine.is("hello");
// slow reflexes trigger asynchronously
spine.is("bye");
System.out.println("ok?");
}
}
Output : @Coding Ground
Lets get to work
ok?
Name: anonymous
Nice to meet you anonymous
Introducer.java
package main;
// required modules
import java.util.*;
public class Introducer {
public static void onHello(String stimulus, Map args) {
System.out.println("Lets get to work");
}
public void onBye(String stimulus, Map args) {
System.out.print("Name: ");
Scanner in = new Scanner(System.in);
String name = in.next();
System.out.println("Nice to meet you "+name);
}
}
Main.java
package main;
// required modules
import org.event.*;
public class Main {
public static void main(String[] args) {
Introducer introducer = new Introducer();
// static reflex methods are triggered
Spine spine1 = new Spine(Introducer.class);
spine1.is("hello").is("bye");
System.out.println();
// instance methods are triggered
Spine spine2 = new Spine(introducer);
spine2.is("hello").is("bye");
System.out.println();
// import spine1 to spine2 (or any Map<String, Reactable>)
spine2.on(spine1);
spine2.is("hello").is("bye");
}
}
Output : @Coding Ground
Lets get to work
[bye] : {}
[hello] : {}
Name: anonymous
Nice to meet you anonymous
Lets get to work
Name: anonymous
Nice to meet you anonymous
Introducer.java
package main;
// required modules
import java.util.concurrent.*;
import java.util.*;
import org.event.*;
public class Introducer extends Thread implements Reflexive {
public Spine spine;
BlockingQueue<Object[]> events;
public Introducer() {
spine = new Spine(this);
events = new LinkedBlockingQueue<Object[]>();
}
public void onHello(String stimulus, Map args) {
System.out.println("Lets get to work");
}
public void onBye(String stimulus, Map args) {
System.out.print("Name: ");
Scanner in = new Scanner(System.in);
String name = in.next();
System.out.println("Nice to meet you "+name);
}
@Override
public void run() {
try {
while(true) {
Object[] o = events.take();
spine.is((String)o[0], (Map)o[1]);
}
}
catch(InterruptedException e) {}
}
@Override
public void on(String stimulus, Map args) {
try { events.put(new Object[]{stimulus, args}); }
catch(InterruptedException e) {}
}
}
Main.java
package main;
// required modules
import org.event.*;
public class Main {
public static void main(String[] args) {
Introducer introducer = new Introducer();
introducer.start();
Spine spine = new Spine();
spine.on(introducer.spine.keySet(), introducer);
// onHello & onBye run on introducer thread
spine.is("hello").is("bye");
// introducer thread still running
}
}
Output : @Coding Ground
Lets get to work
Name: anonymous
Nice to meet you anonymous
Represents stimuli with associated reflexes.
When a stimulus occurs, appropriate reflexes are triggered.
Spine | Map<String, Set<Reflexive>> |
---|---|
Spine () , (cls) , (obj) |
Create spine : empty, with reflexes from class, or object Spine spine = new Spine(); Spine stimuli = new Spine(NetHandler.class); Spine stimuli = new Spine(netHandlerObj); |
on (stimulus, reflex) , (stimulus, reflexes) , (stimuli, reflex) , (assoc) |
Set reflexes to trigger on stimuli spine.on("write-done", writeDoneReflex); spine.on(multipleDone, doneReflex); spine.on("done", doneReflexes); spine.on(anotherSpine); |
off (stimulus, reflex) , (stimulus, reflexes) , (stimuli, reflex) , (assoc) |
Turn off reflexes for stimuli spine.off("write-done", writeDoneReflex); spine.off(multipleDone, doneReflex); spine.off("done", doneReflexes); spine.off(anotherSpine); |
is (stimulus, args) (stimulus, args...) |
indicate a stimulus, causing reflexes to trigger spine.is("write", argsMap); spine.is("write", "err", e, "data", data); |
fallback () , (fallback) |
get / set fallback reflex, for stimulus with no reflexes Reactable fallback = spine.fallback(); spine.fallback(myFallbackReaction); |
Response to a stimulus.
Encapsulates a fast or slow response.
Slow response is run on a separate thread from threadpool.
Reflex | (Reflexive) |
---|---|
Reflex (reflexive) , (cls, mthd) , (obj, mthd) |
Create a reflex that can be invoked on a stimulus Reflex done = new Reflex(reflexive); Reflex done = new Reflex(MthdCls.class, "mthd"); Reflex done = new Reflex(MthdObj, "mthd"); |
speed () , (speed) |
Get / set speed of reflex String speed = reflex.speed(); reflex.speed("slow"); |
on (stimulus, args) |
Invoke the response. (internal) |
Interface for response to stimulus.
Classes implementing this interface can to respond to stimuli.
(Reflexive) | |
---|---|
on (stimulus, args) |
Respond to a stimulus. |
Classes implementing this interface can accept reflexes to stimuli.
In other words, it can be used to notify events to listeners.
(Eventable) | |
---|---|
event () |
Returns the spine object for events associated with this object. |
Annotation to specify speed of a method.
Use @Speed("fast")
for fast method (optional)
Use @Speed("slow")
for slow method (required)
@Speed | |
---|---|
value () , (speed) |
Speed of method (fast or slow). |