Skip to content

javaf/extra-event

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

java-spine

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).



Concept

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.


Usage

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.


Examples

Default Reflex

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!}

Implement Reflexive

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

Method Reference

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

Anonymous Class

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

Lambda Expression

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

Reflex Method

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

Reflex Class

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

Event Loop

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


Reference

Spine

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);

Reflex

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)

(Reflexive)

Interface for response to stimulus.
Classes implementing this interface can to respond to stimuli.

(Reflexive)
on
(stimulus, args)
Respond to a stimulus.

(Eventable)

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.

@Speed

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).

About

Stimulus-reflex (event) library for java

Resources

License

Stars

Watchers

Forks

Languages