Skip to content
/ evt Public

A custom java event implementation with priorities

License

Notifications You must be signed in to change notification settings

HanSolo/evt

Repository files navigation

EVT - Custom Java Events with priority

This projects simply offers custom Java events that supports priorities if you like.

One can create a custom event as shown below

public class MyEvt extends Evt {

    public static final EvtType<MyEvt> ANY = new EvtType<>(Evt.ANY, "MY_EVENT");

    public MyEvt(final EvtType<? extends MyEvt> evtType) {
        super(evtType);
    }
    public MyEvt(final Object source, final EvtType<? extends MyEvt> evtType) {
        super(source, evtType);
    }
    public MyEvt(final Object source, final EvtType<? extends MyEvt> evtType, final EvtPriority priority) {
        super(source, evtType, priority);
    }


    public EvtType<? extends MyEvt> getEvtType() {
        return (EvtType<? extends MyEvt>) super.getEvtType();
    }
}

Now with your event in place you can create specific events extending MyEvt as follows:

public class MyValueEvt extends MyEvt {
    public  static final EvtType<MyValueEvt> ANY          = new EvtType<>(MyEvt.ANY, "VALUE");
    public  static final EvtType<MyValueEvt> VALUE_TYPE_1 = new EvtType<>(MyValueEvt.ANY, "VALUE_TYPE_1");
    public  static final EvtType<MyValueEvt> VALUE_TYPE_2 = new EvtType<>(MyValueEvt.ANY, "VALUE_TYPE_2");
    private              double              value;


    // ******************** Constructors **************************************
    public MyValueEvt(final Object source, final EvtType<? extends MyValueEvt> evtType, final double value) {
        this(source, evtType, value, EvtPriority.NORMAL);
    }
    public MyValueEvt(final Object source, final EvtType<? extends MyValueEvt> evtType, final double value, final EvtPriority priority) {
        super(source, evtType, priority);
        this.value = value;
    }


    // ******************** Methods *******************************************
    public EvtType<? extends MyValueEvt> getEvtType() {
        return (EvtType<? extends MyValueEvt>) super.getEvtType();
    }

    public double getValue() { return value; }
} 

As an example we use a control with a value property as follows:

public interface MyControl {
    void fireEvt(final Evt evt);

    void dispose();
}

public class MyClass implements MyControl {
    private final Map<String, List<EvtObserver>> observers = new ConcurrentHashMap<>();
    private double                               value;
    private String string;


    // ******************** Constructors **************************************
    public MyClass() {
        this.value  = 0;
        this.string = "";
    }


    // ******************** Methods *******************************************
    public double getValue() { return value; }
    public void setValue(final double value) {
        this.value = value;
        fireEvt(new MyValueEvt(MyClass.this, MyValueEvt.VALUE_TYPE_1, value));
    }

    public String getString() { return string; }
    public void setString(final String string) {
        this.string = string;
        fireEvt(new MyStringEvt(MyClass.this, MyStringEvt.STRING_TYPE_1, string));
    }


    // ******************** EventHandling *************************************
    public void setOnEvt(final EvtType<? extends Evt> type, final EvtObserver observer) {
        if (!observers.keySet().contains(type.getName())) { observers.put(type.getName(), new CopyOnWriteArrayList<>()); }
        if (!observers.get(type.getName()).contains(observer)) { observers.get(type.getName()).add(observer); }
    }
    public void removeOnEvt(final EvtType<? extends Evt> type, final EvtObserver observer) {
        if (!observers.keySet().contains(type.getName())) { return; }
        if (observers.get(type.getName()).contains(observer)) { observers.get(type.getName()).remove(observer); }
    }
    public void removeAllObservers() { observers.entrySet().forEach(entry -> entry.getValue().clear()); }

    @Override public void fireEvt(final Evt evt) {
        final EvtType<? extends Evt> type = evt.getEvtType();
        if (observers.containsKey(type.getName())) {
            observers.get(type.getName()).forEach(observer -> observer.handle(evt));
        }
    }

    @Override public void dispose() {

    }
}

Now if we would like to listen to those events we can create an instance of it and set a value as follows:

public class Main {
    private MyClass                      myClass;
    private MyOtherClass             myOtherClass;
    private EvtObserver<MyValueEvt>  valueEventEvtObserver;
    private EvtObserver<MyStringEvt> stringEventEvtObserver;


    public Main() {
        myClass      = new MyClass();        

        valueEventEvtObserver = e -> System.out.println("New Value Event with priority " + e.getPriority().name() + " and value: " + e.getValue() + " from class " + e.getSource().getClass());
        myClass.setOnEvt(MyValueEvt.VALUE_TYPE_1, valueEventEvtObserver);        

        stringEventEvtObserver = e -> System.out.println("New String Event with priority " + e.getPriority().name() + " and string: " + e.getString() + " from class " + e.getSource().getClass());
        myClass.setOnEvt(MyStringEvt.STRING_TYPE_1, stringEventEvtObserver);        


        myClass.setValue(312);
        myClass.setString("Gerrit");
    }
    
    public static void main(String[] args) {
        new Main();
    }
}

In the example above we simply set the value and fire a MyValueEvt with priority NORMAL.

In case we would like to make use of the priorities we can create another control as follows:

public class MyOtherClass implements MyControl {
    private final Map<String, List<EvtObserver>> observers       = new ConcurrentHashMap<>();
    private final BlockingQueue<Evt>             evtQueue        = new PriorityBlockingQueue<>();
    private final Callable<Boolean>              consumerTask    = () -> {
        while (true) {
            final Evt evt;
            try {
                evt = evtQueue.take();
                final EvtType<? extends Evt> type = evt.getEvtType();
                if (observers.containsKey(type.getName())) {
                    observers.get(type.getName()).forEach(observer -> observer.handle(evt));
                }
            } catch (InterruptedException ex) {
                return false;
            }
        }
    };
    private ScheduledExecutorService             consumerService = new ScheduledThreadPoolExecutor(1, runnable -> new Thread(runnable, "EventConsumerThread"));
    private double                               value;
    private String                               string;


    // ******************** Constructors **************************************
    public MyOtherClass() {
        this.value  = 0;
        this.string = "";

        consumerService.schedule(consumerTask, 0, TimeUnit.MILLISECONDS);
    }


    // ******************** Methods *******************************************
    public double getValue() { return value; }
    public void setValue(final double value) {
        this.value = value;
        // Fires events with Priority LOW which will be served last
        fireEvt(new MyValueEvt(MyOtherClass.this, MyValueEvt.VALUE_TYPE_2, value, EvtPriority.LOW));
    }

    public String getString() { return string; }
    public void setString(final String string) {
        this.string = string;
        // Fires events with Priority HIGH which will be served first
        fireEvt(new MyStringEvt(MyOtherClass.this, MyStringEvt.STRING_TYPE_2, string, EvtPriority.HIGH));
    }


    // ******************** EventHandling *************************************
    public void setOnEvent(final EvtType<? extends Evt> type, final EvtObserver observer) {
        if (!observers.keySet().contains(type.getName())) { observers.put(type.getName(), new CopyOnWriteArrayList<>()); }
        if (!observers.get(type.getName()).contains(observer)) { observers.get(type.getName()).add(observer); }
    }
    public void removeOnEvent(final EvtType<? extends Evt> type, final EvtObserver observer) {
        if (!observers.keySet().contains(type.getName())) { return; }
        if (observers.get(type.getName()).contains(observer)) { observers.get(type.getName()).remove(observer); }
    }
    public void removeAllObservers() { observers.entrySet().forEach(entry -> entry.getValue().clear()); }

    @Override public void fireEvt(final Evt evt) {
        evtQueue.add(evt);
    }

    @Override public void dispose() {
        consumerService.shutdown();
        try {
            if (!consumerService.awaitTermination(500, TimeUnit.MILLISECONDS)) {
                consumerService.shutdownNow();
            }
        } catch (InterruptedException e) {
            consumerService.shutdownNow();
        }
    }
}

In this class we put the events on a concurrent priority queue so that they will be ordered by priority. Here we fire an HIGH priority event in case the string property was changed and a LOW priority event in case the value changed.

The Main class to observe the priority based events will look as follows:

public class Main {
    private MyClass                      myClass;
    private MyOtherClass             myOtherClass;
    private EvtObserver<MyValueEvt>  valueEventEvtObserver;
    private EvtObserver<MyStringEvt> stringEventEvtObserver;


    public Main() {
        myClass      = new MyClass();
        myOtherClass = new MyOtherClass();

        valueEventEvtObserver = e -> System.out.println("New Value Event with priority " + e.getPriority().name() + " and value: " + e.getValue() + " from class " + e.getSource().getClass());
        myClass.setOnEvt(MyValueEvt.VALUE_TYPE_1, valueEventEvtObserver);
        myOtherClass.setOnEvent(MyValueEvt.VALUE_TYPE_2, valueEventEvtObserver);

        stringEventEvtObserver = e -> System.out.println("New String Event with priority " + e.getPriority().name() + " and string: " + e.getString() + " from class " + e.getSource().getClass());
        myClass.setOnEvt(MyStringEvt.STRING_TYPE_1, stringEventEvtObserver);
        myOtherClass.setOnEvent(MyStringEvt.STRING_TYPE_2, stringEventEvtObserver);


        myClass.setValue(312);
        myClass.setString("Gerrit");

        Random rnd = new Random();
        for (int i = 0 ; i < 100 ; i++) {
            boolean callValue = rnd.nextBoolean();
            if (callValue) {
                myOtherClass.setValue(rnd.nextInt(100));
            } else {
                myOtherClass.setString("Neo " + rnd.nextInt(100));
            }
        }

        stop();
    }

    private void stop() {
        myClass.dispose();
        myOtherClass.dispose();
    }

    public static void main(String[] args) {
        new Main();
    }
}

In the output of the last example you will see that first all HIGH priority events will be handled followed by the LOW priority events.

About

A custom java event implementation with priorities

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages