Просмотр исходного кода

eventlock - javadoc
EventRouter.dispose - wysyła event oraz usuwa tablicę listenerów. dzięki temu można realizować odrejestrowanie kaskadowe

Ranides Atterwim 13 лет назад
Родитель
Сommit
73cf5fa435

+ 1 - 1
pom.xml

@@ -4,7 +4,7 @@
 
     <groupId>net.ranides</groupId>
     <artifactId>assira</artifactId>
-    <version>0.58.2</version>
+    <version>0.58.3</version>
     <packaging>jar</packaging>
 
     <name>assira</name>

+ 29 - 29
src/main/java/net/ranides/assira/events/EventLock.java

@@ -12,25 +12,25 @@ import net.ranides.assira.events.EventLockQue.OwnableLockQue;
 import net.ranides.assira.events.EventLockQue.SingleLock;
 
 /**
- * Klasa pozwalająca na synchroniczną obsługę zdarzeń. Pozwala wstrzymać wykonanie 
- * bieżącego wątku i oczekiwać, aż do wskazanego {@link EventRouter}'a dotrze 
+ * Klasa pozwalająca na synchroniczną obsługę zdarzeń. Pozwala wstrzymać wykonanie
+ * bieżącego wątku i oczekiwać, aż do wskazanego {@link EventRouter}'a dotrze
  * zdarzenie określonego typu.
  * <p>
  * Zależnie od rodzaju implementacji, klasa może oferować usługi o różnym stopniu
  * dokładności i bezpieczeństwa.
  * </p>
- * <p>Najsilniejsze gwarancje, to możliwość obsługi wszystkich zdarzeń określonego 
+ * <p>Najsilniejsze gwarancje, to możliwość obsługi wszystkich zdarzeń określonego
  * rodzaju bez ryzyka pominięcia żadnego. Nawet jeśli jedno lub więcej zdarzeń
  * nastąpiło przed (lub pomiędzy) wywołaniem metody {@link #waitForEvent}, to zostanie
  * ono dostarczone. Gwarantowana jest również prawidłowa kolejność dostarczenia
  * komunikatów przez kolejne wywołania metod blokujących. Implementacje "silne"
- * mają duże wymagania pamięciowe, w ekstremalnym przypadku mogą spowodować 
+ * mają duże wymagania pamięciowe, w ekstremalnym przypadku mogą spowodować
  * przepełnienie sterty, jeśli program zostanie zalany komunikatami.
  * </p>
  * <p>
  * "Słaby {@code EventLock}" daje gwarancje, że metody {@link #waitForEvent} nie
  * będą blokować, jeśli pomiędzy ich wywołaniami (albo przed wywołaniem) wystąpiło
- * obserwowane zdarzenie. Nie dają jednak gwarancji dostarczenia wszystkich 
+ * obserwowane zdarzenie. Nie dają jednak gwarancji dostarczenia wszystkich
  * komunikatów - część z nich może być utracona/pominięta, w przypadku wysokiego
  * obciążenia. Słaby {@code EventLock} udostępnia informację o ilości zgubionych
  * komunikatów. Jego zaletą jest znacznie mniejsze zużycie pamięci oraz brak podatności
@@ -38,7 +38,7 @@ import net.ranides.assira.events.EventLockQue.SingleLock;
  * </p>
  * <p>
  * "Niebezpieczny {@code EventLock}" nie oferuje żadnych gwarancji. Co więcej:
- * metody {@link #waitForEvent} zawsze blokują, a jako wynik zwracają tylko i 
+ * metody {@link #waitForEvent} zawsze blokują, a jako wynik zwracają tylko i
  * wyłącznie wydarzenie, które nastąpiło w czasie blokady. Z tego powodu jest
  * silnie podatny na <i>race condition</i>. Jego zaletą jest niemal całkowity
  * brak obciążenia pamięci oraz procesora. Z tego powodu może być z powodzeniem
@@ -61,7 +61,7 @@ public abstract class EventLock {
      * którymi {@code EventLock} był zainteresowany zostanie utracona.
      */
     public abstract void reset();
-    
+
     /**
      * Metoda, której obsługa przez konkretne implementacje jest obowiązkowa.
      * Obiekt zwrócony przez metodę jest używany do łączenia z routerem, na który
@@ -71,7 +71,7 @@ public abstract class EventLock {
     protected abstract EventListener<? super Event> listener();
 
     /**
-     * Metoda blokująca: wstrzymuje wykonanie bieżącego wątku do momentu, aż 
+     * Metoda blokująca: wstrzymuje wykonanie bieżącego wątku do momentu, aż
      * do obserwowanego {@code EventRouter}'a dotrze dowolne zdarzenie oczekiwane
      * przez {@code EventLock} lub wystąpi wyjątek {@code InterruptedException}.
      * Zobacz opis klasy {@link EventLock} aby poznać szczegóły.
@@ -79,14 +79,14 @@ public abstract class EventLock {
      * @throws InterruptedException
      */
     public abstract Event waitForEvent() throws InterruptedException;
-    
+
     /**
-     * Metoda blokująca: wstrzymuje wykonanie bieżącego wątku do momentu, aż 
+     * Metoda blokująca: wstrzymuje wykonanie bieżącego wątku do momentu, aż
      * do obserwowanego {@code EventRouter}'a dotrze dowolne zdarzenie oczekiwane
      * przez {@code EventLock}, wystąpi wyjątek {@code InterruptedException}, lub
      * minie podany czas.
      * Zobacz opis klasy {@link EventLock} aby poznać szczegóły.
-     * @param timeout 
+     * @param timeout
      * @return oczekiwane zdarzenie
      * @throws InterruptedException
      */
@@ -95,7 +95,7 @@ public abstract class EventLock {
     /**
      * Wersja oczekująca na zdarzenie konkretnego rodzaju. Zachowuje się identycznie
      * jak {@link #waitForEvent()}, z tą różnicą, że reaguje na mniejszy zakres
-     * zdarzeń - zawężony tylko do podanej klasy oraz jej pochodnych. 
+     * zdarzeń - zawężony tylko do podanej klasy oraz jej pochodnych.
      * <p>
      * Metoda przydatna szczególnie wtedy, gdy utworzony obiekt {@code EventLock}
      * reaguje na bardzo ogólną klasę zdarzeń, a użytkownik w danym momencie chce
@@ -107,11 +107,11 @@ public abstract class EventLock {
      * @throws InterruptedException
      */
     public abstract <T extends Event> T waitForEvent(Class<T> event) throws InterruptedException;
-    
+
     /**
      * Wersja oczekująca na zdarzenie konkretnego rodzaju. Zachowuje się identycznie
      * jak {@link #waitForEvent(long timeout)}, z tą różnicą, że reaguje na mniejszy zakres
-     * zdarzeń - zawężony tylko do podanej klasy oraz jej pochodnych. 
+     * zdarzeń - zawężony tylko do podanej klasy oraz jej pochodnych.
      * <p>
      * Metoda przydatna szczególnie wtedy, gdy utworzony obiekt {@code EventLock}
      * reaguje na bardzo ogólną klasę zdarzeń, a użytkownik w danym momencie chce
@@ -119,16 +119,16 @@ public abstract class EventLock {
      * </p>
      * @param <T>
      * @param event
-     * @param timeout 
+     * @param timeout
      * @return oczekiwane zdarzenie lub {@code null}, jeśli minął {@code timeout}
      * @throws InterruptedException
      */
     public abstract <T extends Event> T waitForEvent(Class<T> event, long timeout) throws InterruptedException;
-    
-    
-    
+
+
+
     /**
-     * Metoda tworzy silny {@code EventLock} - szczegóły "silnego kontraktu" 
+     * Metoda tworzy silny {@code EventLock} - szczegóły "silnego kontraktu"
      * są w opisie klasy {@link EventLock}).
      * <p>
      * Jeśli obiekt nie jest już potrzebny, musi zostać zwolniony za pomocą metody {@link #release()}
@@ -140,9 +140,9 @@ public abstract class EventLock {
     public static EventLockQue lock(Class<? extends Event> event, EventRouter router) {
         return bind(event, router, new OwnableLockQue(event, router));
     }
-    
+
     /**
-     * Metoda tworzy silny {@code EventLock} - szczegóły "słabego kontraktu" 
+     * Metoda tworzy słaby {@code EventLock} - szczegóły "słabego kontraktu"
      * są w opisie klasy {@link EventLock}).
      * <p>
      * Jeśli obiekt nie jest już potrzebny, musi zostać zwolniony za pomocą metody {@link #release()}
@@ -155,10 +155,10 @@ public abstract class EventLock {
     public static EventLockQue weakLock(Class<? extends Event> event, EventRouter router, int capacity) {
         return bind(event, router, new OwnableLockQue(event, router, capacity));
     }
-    
+
     /**
-     * Metoda tworzy silny {@code EventLock}, który może zostać wykorzystany tylko raz 
-     * - szczegóły "słabego kontraktu" są w opisie klasy {@link EventLock}). 
+     * Metoda tworzy słaby {@code EventLock}, który może zostać wykorzystany tylko raz
+     * - szczegóły "słabego kontraktu" są w opisie klasy {@link EventLock}).
      * <p>
      * Po wywołaniu zakończeniu metody {@code waitForEvent} obiekt sam automatycznie
      * zwalnia wszystkie zasoby i przestaje być funcjonalny.
@@ -170,9 +170,9 @@ public abstract class EventLock {
     public static EventLock singleLock(Class<? extends Event> event, EventRouter router) {
         return bind(event, router, new SingleLock(event, router));
     }
-    
+
     /**
-     * Metoda tworzy niebezpieczny {@code EventLock} - szczegóły "niebezpiecznego kontraktu" 
+     * Metoda tworzy niebezpieczny {@code EventLock} - szczegóły "niebezpiecznego kontraktu"
      * są w opisie klasy {@link EventLock}).
      * <p>
      * Jeśli obiekt nie jest już potrzebny, musi zostać zwolniony za pomocą metody {@link #release()}
@@ -185,11 +185,11 @@ public abstract class EventLock {
     public static EventLock unsafeLock(Class<? extends Event> event, EventRouter router) {
         return bind(event, router, new EventLockUnsafe(event, router) );
     }
-    
-    
+
+
     private static <T extends EventLock> T bind(Class<? extends Event> event, EventRouter router, T lock) {
         router.addEventListener(event, lock.listener());
         return lock;
     }
-    
+
 }

+ 12 - 0
src/main/java/net/ranides/assira/events/EventRouter.java

@@ -7,6 +7,7 @@
 
 package net.ranides.assira.events;
 
+import java.io.Serializable;
 import java.util.Collection;
 
 /**
@@ -66,4 +67,15 @@ public interface EventRouter extends EventListener<Event> {
      * <table style="margin: 10px 0 0 0; background: #c0F0c0; border: solid 1px #006000;" cellpadding="0"><tr><td>thread-safe method</td></tr></table>
      */
     void dispose();
+
+    class DisposeEvent implements Event {
+        private final EventRouter source;
+
+        public DisposeEvent(EventRouter router) {
+            this.source = router;
+        }
+        public EventRouter source() {
+            return source;
+        }
+    }
 }

+ 3 - 2
src/main/java/net/ranides/assira/events/EventRouterDispatcher.java

@@ -159,8 +159,9 @@ public class EventRouterDispatcher implements EventRouter {
     }
 
     @Override
-    public void dispose() {
-        /* do nothing */
+    public synchronized void dispose() {
+        signalEvent(new DisposeEvent(this));
+        this.listeners = null;
     }
 
 

+ 21 - 20
src/main/java/net/ranides/assira/events/EventRouterThread.java

@@ -12,21 +12,21 @@ import java.util.concurrent.TimeUnit;
 import net.ranides.assira.generic.ValueUtils;
 
 /**
- * EventRouter kolejkujący zdarzenia. Wywołuje procedury obsługi w bliżej 
+ * EventRouter kolejkujący zdarzenia. Wywołuje procedury obsługi w bliżej
  * nieokreślonym czasie po zasygnalizowaniu zdarzenia.
  * <p>
- * Procedury obsługi zdarzenia zarejestrowanych obserwatorów są wywoływane 
- * w oddzielnym wątku, zarządzanym przez {@code EventRouter}. Należy mieć to na 
- * uwadze, ponieważ jest to zupełnie inny wątek, niż ten, który zarejestrował obserwatora, 
+ * Procedury obsługi zdarzenia zarejestrowanych obserwatorów są wywoływane
+ * w oddzielnym wątku, zarządzanym przez {@code EventRouter}. Należy mieć to na
+ * uwadze, ponieważ jest to zupełnie inny wątek, niż ten, który zarejestrował obserwatora,
  * albo który zasygnalizował event.
  * </p>
- * 
+ *
  * <table style="margin: 10px 0 0 0; background: #c0F0c0; border: solid 1px #006000;" cellpadding="0"><tr><td>thread-safe class</td></tr></table>
  * @author ranides
  */
 @edu.umd.cs.findbugs.annotations.SuppressWarnings({
     "DLS_DEAD_LOCAL_STORE" // w klasie mamy kilka świadomie ignorowanych wartości catch(InterruptedException _ )
-}) 
+})
 public class EventRouterThread extends EventRouterDispatcher {
 
     private final ArrayBlockingQueue<Event> events;
@@ -52,7 +52,7 @@ public class EventRouterThread extends EventRouterDispatcher {
     public static EventRouterThread newInstance(String name, int size, long maxtime, EventJoiner joiner) {
         return new EventRouterThread(name, size, maxtime, joiner).start();
     }
-    
+
     /**
      * Tworzy nowy obiekt {@code EventRouter}. Utworzony obiekt należy zniszczyć po użyciu za pomocą
      * {@link #dispose() }.
@@ -71,7 +71,7 @@ public class EventRouterThread extends EventRouterDispatcher {
      * @param name nazwa wątku
      * @param maxtime
      * @param size
-     * @param joiner  
+     * @param joiner
      */
     protected EventRouterThread(final String name, int size, long maxtime, final EventJoiner joiner) {
         this.maxtime = maxtime;
@@ -79,7 +79,7 @@ public class EventRouterThread extends EventRouterDispatcher {
         this.name = name;
         this.exit = new ExitEvent(name);
         this.distributor = new Thread(name) {
-            
+
             @Override
             public void run() {
                 boolean interrupted = true;
@@ -123,13 +123,13 @@ public class EventRouterThread extends EventRouterDispatcher {
         signalEvent(exit);
         return join();
     }
-    
+
     /**
      * Metoda blokująca - czeka, aż event router zakończy swoje działanie.
      * <p>
      * Uwaga! Żeby metoda kiedykolwiek się zakończyła, w aplikacji musi istnieć
      * co najmniej jeden dodatkowy wątek, który wyśle do routera {@link ExitEvent}
-     * lub wywoła metodę {@link #stop}. 
+     * lub wywoła metodę {@link #stop}.
      * </p>
      * <table style="margin: 10px 0 0 0; background: #c0F0c0; border: solid 1px #006000;" cellpadding="0"><tr><td>thread-safe method</td></tr></table>
      * @return
@@ -156,7 +156,7 @@ public class EventRouterThread extends EventRouterDispatcher {
      * konstrukcji routera. Jeśli w kolejce nadal nie ma miejsca - to kończy działanie
      * zwracając {@code false}.
      * </p><p>
-     * Metoda nie czeka na obsłużenie komunikatu przez zarejestrowanych obserwatorów. 
+     * Metoda nie czeka na obsłużenie komunikatu przez zarejestrowanych obserwatorów.
      * Obsługa komunikatu jest uruchamiana w oddzielnym wątku zarządzanym przez router.
      * </p>
      * <table style="margin: 10px 0 0 0; background: #c0F0c0; border: solid 1px #006000;" cellpadding="0"><tr><td>thread-safe method</td></tr></table>
@@ -177,22 +177,23 @@ public class EventRouterThread extends EventRouterDispatcher {
 
     /**
      * Zleca wyłączenie i zwalnionenie zasobów routerowi. Nie czeka na zakończenie
-     * jego pracy. 
+     * jego pracy.
      * <table style="margin: 10px 0 0 0; background: #c0F0c0; border: solid 1px #006000;" cellpadding="0"><tr><td>thread-safe method</td></tr></table>
      */
     @Override
-    public void dispose() {
+    public synchronized void dispose() {
         signalEvent(exit);
+        super.dispose();
     }
-    
-    
+
+
 
 /* ************************************************************************** */
 
     /**
      * Zdarzenie wysyłane do wszystkich zainteresowanych obserwatorów routera.
      * Informuje, że router zakończył swoją pracę. Jeśli router zakończył pracę
-     * z powodu przerwania (interrupt), to komunikat podanej klasy również jest 
+     * z powodu przerwania (interrupt), to komunikat podanej klasy również jest
      * rozsyłany (dokładniej: jego klasa pochodna).
      * @see InterruptEvent
      */
@@ -226,8 +227,8 @@ public class EventRouterThread extends EventRouterDispatcher {
 
     /**
      * Zdarzenie, które może zostać wysłane do routera, w celu zakończenia jego pracy.
-     * Router rozsyła to zdarzenie do obserwatorów, tak samo jak każde inne. 
-     * Router kończy działanie tylko jeśli nazwa w komunikacie jest taka sama, 
+     * Router rozsyła to zdarzenie do obserwatorów, tak samo jak każde inne.
+     * Router kończy działanie tylko jeśli nazwa w komunikacie jest taka sama,
      * jak nazwa wątku zarządzanego przez router.
      */
     public static class ExitEvent implements Event {
@@ -243,7 +244,7 @@ public class EventRouterThread extends EventRouterDispatcher {
         }
 
         /**
-         * Nazwa wątku. Każdy router zakończy pracę, jeśli otrzyma ten komunikat, 
+         * Nazwa wątku. Każdy router zakończy pracę, jeśli otrzyma ten komunikat,
          * i zarządza wątkiem o tej nazwie.
          * @return
          */