001package org.intellimate.izou.events; 002 003import org.intellimate.izou.util.IzouModule; 004import org.intellimate.izou.identification.Identification; 005import org.intellimate.izou.identification.IdentificationManager; 006import org.intellimate.izou.identification.IdentificationManagerM; 007import org.intellimate.izou.identification.IllegalIDException; 008import org.intellimate.izou.main.Main; 009 010import java.util.Optional; 011import java.util.concurrent.BlockingQueue; 012import java.util.concurrent.ConcurrentHashMap; 013import java.util.concurrent.LinkedBlockingQueue; 014 015/** 016 * This class is used to manage local events. 017 */ 018public class LocalEventManager extends IzouModule implements Runnable { 019 //here are all the Instances which fire events stored 020 private final ConcurrentHashMap<Identification, EventCaller> callers = new ConcurrentHashMap<>(); 021 //the queue where all the Events are stored 022 final BlockingQueue<EventModel> events = new LinkedBlockingQueue<>(1); 023 //if false, run() will stop 024 private boolean stop = false; 025 private final EventCallable eventCallable; 026 027 public LocalEventManager(Main main) { 028 super(main); 029 IdentificationManagerM identificationManager = IdentificationManager.getInstance(); 030 identificationManager.registerIdentification(this); 031 Optional<EventCallable> eventCallable = identificationManager.getIdentification(this) 032 .flatMap(id -> { 033 try { 034 return getMain().getEventDistributor().registerEventPublisher(id); 035 } catch (IllegalIDException e) { 036 log.fatal("Illegal ID for LocalEventManager", e); 037 return Optional.empty(); 038 } 039 }); 040 if (!eventCallable.isPresent()) { 041 log.fatal("Unable to obtain EventCallable for " + getID()); 042 System.exit(1); 043 this.eventCallable = null; 044 } else { 045 this.eventCallable = eventCallable.get(); 046 } 047 } 048 049 /** 050 * Registers with the EventManager to fire an event. 051 * 052 * Note: the same Event can be fired from multiple sources. 053 * Method is thread-safe. 054 * 055 * @param identification the Identification of the the instance 056 * @return an Optional, empty if already registered 057 * @throws IllegalIDException not yet implemented 058 */ 059 @SuppressWarnings({"SynchronizationOnLocalVariableOrMethodParameter"}) 060 public Optional<EventCallable> registerCaller(Identification identification) throws IllegalIDException { 061 if(identification == null || 062 callers.containsKey(identification)) return Optional.empty(); 063 EventCaller eventCaller = new EventCaller(events); 064 callers.put(identification, eventCaller); 065 return Optional.of(eventCaller); 066 } 067 068 /** 069 * Unregister with the EventManager. 070 * 071 * Method is thread-safe. 072 * 073 * @param identification the Identification of the the instance 074 */ 075 @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter") 076 public void unregisterCaller(Identification identification) { 077 if(!callers.containsKey(identification)) return; 078 callers.get(identification).localEvents = null; 079 callers.remove(identification); 080 } 081 082 /** 083 * This method fires an Event 084 * 085 * @param event the fired Event 086 * @throws IllegalIDException not yet implemented 087 * @throws org.intellimate.izou.events.MultipleEventsException if there is currently another event getting processed 088 */ 089 public void fireEvent(EventModel event) throws IllegalIDException, org.intellimate.izou.events.MultipleEventsException { 090 if(events == null) return; 091 if(events.isEmpty()) { 092 events.add(event); 093 } else { 094 throw new org.intellimate.izou.events.MultipleEventsException(); 095 } 096 } 097 098 @Override 099 public void run() { 100 stop = false; 101 while (!stop) { 102 EventModel event; 103 try { 104 event = events.take(); 105 if (!event.getSource().isCreatedFromInstance()) { 106 error("event: " + event + "has invalid source"); 107 continue; 108 } 109 try { 110 eventCallable.fire(event); 111 } catch (org.intellimate.izou.events.MultipleEventsException e) { 112 log.error("unable to fire Event", e); 113 } 114 } catch (InterruptedException e) { 115 log.warn(e); 116 } 117 } 118 } 119 120 /** 121 * Should stop the EventManager. 122 * 123 * The method run() is a while-loop that repeats itself as long as a variable isn't true. This sets the variable true 124 * but does NOT interrupt the execution! If its not processing, it is waiting for an event, so this Thread still may 125 * not stop without interruption. 126 */ 127 @SuppressWarnings("UnusedDeclaration") 128 public void stop() { 129 stop = true; 130 } 131 132 /** 133 * Class used to fire events. 134 * 135 * To fire events a class must register with registerCaller, then this class will be returned. 136 * Use fire() to fire the event; 137 */ 138 @SuppressWarnings("SameParameterValue") 139 public final class EventCaller implements EventCallable { 140 private BlockingQueue<EventModel> localEvents; 141 //private, so that this class can only constructed by EventManager 142 private EventCaller(BlockingQueue<EventModel> events) { 143 this.localEvents = events; 144 } 145 146 /** 147 * This method is used to fire the event. 148 * 149 * @throws org.intellimate.izou.events.MultipleEventsException an Exception will be thrown if there are currently other events fired 150 */ 151 public void fire(EventModel event) throws org.intellimate.izou.events.MultipleEventsException { 152 if(events.isEmpty()) { 153 localEvents.add(event); 154 } else { 155 throw new org.intellimate.izou.events.MultipleEventsException(); 156 } 157 } 158 } 159 160 /** 161 * Exception thrown if there are multiple Events fired at the same time. 162 */ 163 @SuppressWarnings("WeakerAccess") 164 @Deprecated 165 public static class MultipleEventsException extends Exception { 166 public MultipleEventsException() { 167 super("Multiple Events fired at the same time"); 168 } 169 } 170}