001/*
002 * GWTEventService
003 * Copyright (c) 2011 and beyond, strawbill UG (haftungsbeschr?nkt)
004 *
005 * This is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU Lesser General Public License as
007 * published by the Free Software Foundation; either version 3 of
008 * the License, or (at your option) any later version.
009 * Other licensing for GWTEventService may also be possible on request.
010 * Please view the license.txt of the project for more information.
011 *
012 * This software is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * You should have received a copy of the GNU Lesser General Public
018 * License along with this software; if not, write to the Free
019 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021 */
022package de.novanic.eventservice.client.event;
023
024import de.novanic.eventservice.client.connection.callback.AsyncCallbackWrapper;
025import de.novanic.eventservice.client.event.listener.EventNotification;
026import de.novanic.eventservice.client.connection.strategy.connector.RemoteEventConnector;
027import de.novanic.eventservice.client.event.listener.RemoteEventListener;
028import de.novanic.eventservice.client.event.filter.EventFilter;
029import de.novanic.eventservice.client.event.domain.Domain;
030import de.novanic.eventservice.client.event.domain.DomainFactory;
031import de.novanic.eventservice.client.event.command.*;
032import de.novanic.eventservice.client.event.listener.unlisten.UnlistenEventListener;
033import de.novanic.eventservice.client.event.listener.unlisten.UnlistenEvent;
034
035import java.util.*;
036
037import com.google.gwt.user.client.rpc.AsyncCallback;
038
039/**
040 * The RemoteEventService supports listening to the server via RemoteEventListeners ({@link de.novanic.eventservice.client.event.listener.RemoteEventListener}).
041 * It keeps a connection to the server. When an event occurred at the server, the RemoteEventService informs the RemoteEventListeners
042 * about the event and starts listening at the server again. When no RemoteEventListeners registered anymore, the
043 * RemoteEventService stops listening till new RemoteEventListeners are registered.
044 * The listening works with a domain/context scope. See the documentation/manual to get more information about the
045 * listening concept.
046 *
047 * @author sstrohschein
048 * <br>Date: 06.06.2008
049 * <br>Time: 18:56:46
050 */
051public class DefaultRemoteEventService extends RemoteEventServiceAccessor implements RemoteEventService
052{
053    private Map<Domain, List<RemoteEventListener>> myDomainListenerMapping;
054
055    /**
056     * Creates a new RemoteEventService.
057     * @param aRemoteEventConnector {@link de.novanic.eventservice.client.connection.strategy.connector.RemoteEventConnector} for the connection
058     * between client side and server side
059     */
060    protected DefaultRemoteEventService(RemoteEventConnector aRemoteEventConnector) {
061        super(aRemoteEventConnector);
062        myDomainListenerMapping = new HashMap<Domain, List<RemoteEventListener>>();
063    }
064
065    /**
066     * Adds a listener for a domain.
067     * It activates the RemoteEventService if it was inactive.
068     * @param aDomain domain
069     * @param aRemoteListener new listener
070     */
071    public void addListener(Domain aDomain, RemoteEventListener aRemoteListener) {
072        addListener(aDomain, aRemoteListener, (AsyncCallback<Void>)null);
073    }
074
075    /**
076     * Adds a listener for a domain.
077     * It activates the RemoteEventService if it was inactive.
078     * @param aDomain domain
079     * @param aRemoteListener new listener
080     * @param aCallback callback (only called when no listener is already registered for the domain)
081     */
082    public void addListener(Domain aDomain, RemoteEventListener aRemoteListener, AsyncCallback<Void> aCallback) {
083        if(addListenerLocal(aDomain, aRemoteListener)) {
084            activate(aDomain, aCallback);
085        }
086    }
087
088    /**
089     * Adds a listener for a domain. The EventFilter is applied to the domain to filter events before the
090     * RemoteEventListener recognizes the event.
091     * It activates the RemoteEventService if it was inactive.
092     * @param aDomain domain
093     * @param aRemoteListener new listener
094     * @param anEventFilter EventFilter to filter the events before RemoteEventListener
095     */
096    public void addListener(Domain aDomain, RemoteEventListener aRemoteListener, EventFilter anEventFilter) {
097        addListener(aDomain, aRemoteListener, anEventFilter, null);
098    }
099
100    /**
101     * Adds a listener for a domain. The EventFilter is applied to the domain to filter events before the
102     * RemoteEventListener recognizes the event.
103     * It activates the RemoteEventService if it was inactive.
104     * @param aDomain domain
105     * @param aRemoteListener new listener
106     * @param anEventFilter EventFilter to filter the events before RemoteEventListener
107     * @param aCallback callback (only called when no listener is registered for the domain)
108     */
109    public void addListener(Domain aDomain, RemoteEventListener aRemoteListener, EventFilter anEventFilter, AsyncCallback<Void> aCallback) {
110        if(addListenerLocal(aDomain, aRemoteListener, anEventFilter)) {
111            activate(aDomain, anEventFilter, aCallback);
112        }
113    }
114
115    /**
116     * Adds a listener for a domain.
117     * @param aDomain domain
118     * @param aRemoteListener new listener
119     * @return true, when it is a new / unregistered domain for the client, otherwise false
120     */
121    private boolean addListenerLocal(Domain aDomain, RemoteEventListener aRemoteListener) {
122        return addListenerLocal(aDomain, aRemoteListener, null);
123    }
124
125    /**
126     * Adds a listener for a domain. The EventFilter is applied to the domain to filter events before the
127     * RemoteEventListener recognizes the event.
128     * @param aDomain domain
129     * @param aRemoteListener new listener
130     * @param anEventFilter EventFilter to filter the events before RemoteEventListener
131     * @return true, when it is a new / unregistered domain for the client, otherwise false
132     */
133    private boolean addListenerLocal(Domain aDomain, RemoteEventListener aRemoteListener, EventFilter anEventFilter) {
134        List<RemoteEventListener> theListeners = myDomainListenerMapping.get(aDomain);
135        final boolean isNewDomain = theListeners == null;
136        if(isNewDomain) {
137            theListeners = new ArrayList<RemoteEventListener>();
138        } else if(anEventFilter != null) {
139            registerEventFilter(aDomain, anEventFilter);
140        }
141        theListeners = addOnCopy(theListeners, aRemoteListener);
142        myDomainListenerMapping.put(aDomain, theListeners);
143        return isNewDomain;
144    }
145
146    /**
147     * Registers an {@link de.novanic.eventservice.client.event.listener.unlisten.UnlistenEventListener} to listen for all
148     * user/client domain deregistrations and timeouts. The scope for unlisten events to receive is set to
149     * {@link de.novanic.eventservice.client.event.listener.unlisten.UnlistenEventListener.Scope#UNLISTEN} by default.
150     * To use other scopes see
151     * {@link de.novanic.eventservice.client.event.RemoteEventService#addUnlistenListener(de.novanic.eventservice.client.event.listener.unlisten.UnlistenEventListener.Scope, de.novanic.eventservice.client.event.listener.unlisten.UnlistenEventListener, com.google.gwt.user.client.rpc.AsyncCallback)}.
152     * @param anUnlistenEventListener {@link de.novanic.eventservice.client.event.listener.unlisten.UnlistenEventListener}
153     * to listen for all user/client domain deregistrations and timeouts.
154     * @param aCallback callback
155     */
156    public void addUnlistenListener(UnlistenEventListener anUnlistenEventListener, AsyncCallback<Void> aCallback) {
157        addUnlistenListener(UnlistenEventListener.Scope.UNLISTEN, anUnlistenEventListener, aCallback);
158    }
159
160    /**
161     * Registers an {@link de.novanic.eventservice.client.event.listener.unlisten.UnlistenEventListener} to listen for all
162     * user/client domain deregistrations and timeouts.
163     * @param anUnlistenEventListener {@link de.novanic.eventservice.client.event.listener.unlisten.UnlistenEventListener}
164     * to listen for all user/client domain deregistrations and timeouts.
165     * @param anUnlistenScope scope of the unlisten events to receive
166     * @param aCallback callback
167     */
168    public void addUnlistenListener(UnlistenEventListener.Scope anUnlistenScope, UnlistenEventListener anUnlistenEventListener, AsyncCallback<Void> aCallback) {
169        addUnlistenListener(anUnlistenScope, anUnlistenEventListener, null, aCallback);
170    }
171
172    /**
173     * Registers an {@link de.novanic.eventservice.client.event.listener.unlisten.UnlistenEventListener} to listen for all
174     * user/client domain deregistrations and timeouts. The custom {@link de.novanic.eventservice.client.event.listener.unlisten.UnlistenEvent}
175     * will be registered at the server side and transferred to all users/clients which have an {@link de.novanic.eventservice.client.event.listener.unlisten.UnlistenEventListener}
176     * registered. That {@link de.novanic.eventservice.client.event.listener.unlisten.UnlistenEvent} can for example contain user information
177     * of your specific user-system to recover the user in your user-system on a timeout. The scope for unlisten events to receive is set to
178     * {@link de.novanic.eventservice.client.event.listener.unlisten.UnlistenEventListener.Scope#UNLISTEN} by default.
179     * To use other scopes see
180     * {@link de.novanic.eventservice.client.event.RemoteEventService#addUnlistenListener(de.novanic.eventservice.client.event.listener.unlisten.UnlistenEventListener.Scope, de.novanic.eventservice.client.event.listener.unlisten.UnlistenEventListener, de.novanic.eventservice.client.event.listener.unlisten.UnlistenEvent, com.google.gwt.user.client.rpc.AsyncCallback)}.
181     * @param anUnlistenEventListener {@link de.novanic.eventservice.client.event.listener.unlisten.UnlistenEventListener}
182     * to listen for all user/client domain deregistrations and timeouts.
183     * @param anUnlistenEvent {@link de.novanic.eventservice.client.event.listener.unlisten.UnlistenEvent} which can contain custom data
184     * @param aCallback callback
185     */
186    public void addUnlistenListener(final UnlistenEventListener anUnlistenEventListener, UnlistenEvent anUnlistenEvent, AsyncCallback<Void> aCallback) {
187        addUnlistenListener(UnlistenEventListener.Scope.UNLISTEN, anUnlistenEventListener, anUnlistenEvent, aCallback);
188    }
189
190    /**
191     * Registers an {@link de.novanic.eventservice.client.event.listener.unlisten.UnlistenEventListener} to listen for all
192     * user/client domain deregistrations and timeouts. The custom {@link de.novanic.eventservice.client.event.listener.unlisten.UnlistenEvent}
193     * will be registered at the server side and transferred to all users/clients which have an {@link de.novanic.eventservice.client.event.listener.unlisten.UnlistenEventListener}
194     * registered. That {@link de.novanic.eventservice.client.event.listener.unlisten.UnlistenEvent} can for example contain user information
195     * of your specific user-system to recover the user in your user-system on a timeout.
196     * @param anUnlistenScope scope of the unlisten events to receive
197     * @param anUnlistenEventListener {@link de.novanic.eventservice.client.event.listener.unlisten.UnlistenEventListener}
198     * to listen for all user/client domain deregistrations and timeouts.
199     * @param anUnlistenEvent {@link de.novanic.eventservice.client.event.listener.unlisten.UnlistenEvent} which can contain custom data
200     * @param aCallback callback
201     */
202    public void addUnlistenListener(final UnlistenEventListener.Scope anUnlistenScope, UnlistenEventListener anUnlistenEventListener, final UnlistenEvent anUnlistenEvent, final AsyncCallback<Void> aCallback) {
203        if(UnlistenEventListener.Scope.LOCAL == anUnlistenScope) {
204            addListenerLocal(DomainFactory.UNLISTEN_DOMAIN, anUnlistenEventListener);
205            schedule(new RegistrationUnlistenEventCommand(anUnlistenScope, getRemoteEventConnector(), anUnlistenEvent, aCallback));
206        } else {
207            addListener(DomainFactory.UNLISTEN_DOMAIN, anUnlistenEventListener, new AsyncCallbackWrapper<Void>(aCallback) {
208                public void onSuccess(Void aResult) {
209                    schedule(new RegistrationUnlistenEventCommand(anUnlistenScope, getRemoteEventConnector(), anUnlistenEvent, getCallback()));
210                }
211            });
212        }
213    }
214
215    /**
216     * Removes a listener for a domain.
217     * The RemoteEventService will get inactive, when no other listeners are registered.
218     * @param aDomain domain
219     * @param aRemoteListener listener to remove
220     */
221    public void removeListener(Domain aDomain, RemoteEventListener aRemoteListener) {
222        removeListener(aDomain, aRemoteListener, new VoidAsyncCallback());
223    }
224
225    /**
226     * Removes a listener for a domain.
227     * The RemoteEventService will get inactive, when no other listeners are registered.
228     * @param aDomain domain to remove the listener from (the domain will be removed when no other listeners are registered to the domain)
229     * @param aRemoteListener listener to remove
230     * @param aCallback callback
231     */
232    public void removeListener(Domain aDomain, RemoteEventListener aRemoteListener, AsyncCallback<Void> aCallback) {
233        if(aRemoteListener != null && myDomainListenerMapping.containsKey(aDomain)) {
234            //remove the listener
235            List<RemoteEventListener> theListeners = myDomainListenerMapping.get(aDomain);
236            if(theListeners != null) {
237                theListeners = removeOnCopy(theListeners, aRemoteListener);
238                myDomainListenerMapping.put(aDomain, theListeners);
239            }
240            //When it was the last listener, the domain will be deregistered for listening, because there aren't any listeners registered for the domain.
241            if(theListeners == null || theListeners.isEmpty()) {
242                removeDomain(aDomain, aCallback);
243            }
244        }
245    }
246
247    /**
248     * Registers the domain for listening and activates the RemoteEventService (starts listening) if it is inactive.
249     * @param aDomain domain to register/activate
250     * @param aCallback callback
251     */
252    private void activate(Domain aDomain, AsyncCallback<Void> aCallback) {
253        activate(aDomain, null, aCallback);
254    }
255
256    /**
257     * Registers the domain with the EventFilter for listening and activates the RemoteEventService (starts listening)
258     * if it is inactive.
259     * @param aDomain domain to register/activate
260     * @param anEventFilter EventFilter to filter the events before RemoteEventListener
261     * @param aCallback callback
262     */
263    private void activate(Domain aDomain, EventFilter anEventFilter, AsyncCallback<Void> aCallback) {
264        schedule(new ActivationCommand(getRemoteEventConnector(), aDomain, anEventFilter, new ListenerEventNotification(), aCallback));
265    }
266
267    /**
268     * Registers an EventFilter for a domain. This can be used when a listener is already added and an EventFilter
269     * needed later or isn't available when the listener is added.
270     * @param aDomain domain
271     * @param anEventFilter EventFilter to filter the events before RemoteEventListener
272     */
273    public void registerEventFilter(Domain aDomain, EventFilter anEventFilter) {
274        registerEventFilter(aDomain, anEventFilter, new VoidAsyncCallback());
275    }
276
277    /**
278     * Registers an EventFilter for a domain. This can be used when a listener is already added and an EventFilter
279     * needed later or isn't available when the listener is added.
280     * @param aDomain domain
281     * @param anEventFilter EventFilter to filter the events before RemoteEventListener
282     * @param aCallback callback
283     */
284    public void registerEventFilter(Domain aDomain, EventFilter anEventFilter, AsyncCallback<Void> aCallback) {
285        schedule(new RegistrationEventFilterCommand(getRemoteEventConnector(), aDomain, anEventFilter, aCallback));
286    }
287
288    /**
289     * Deregisters the EventFilter for a domain.
290     * @param aDomain domain to remove the EventFilter from
291     */
292    public void deregisterEventFilter(Domain aDomain) {
293        deregisterEventFilter(aDomain, new VoidAsyncCallback());
294    }
295
296    /**
297     * Deregisters the EventFilter for a domain.
298     * @param aDomain domain to remove the EventFilter from
299     * @param aCallback callback
300     */
301    public void deregisterEventFilter(Domain aDomain, AsyncCallback<Void> aCallback) {
302        schedule(new DeregistrationEventFilterCommand(getRemoteEventConnector(), aDomain, aCallback));
303    }
304
305    /**
306     * Checks if the RemoteEventService is active (listening).
307     * @return true when active/listening, otherwise false
308     */
309    public boolean isActive() {
310        return isListenActive();
311    }
312
313    /**
314     * Returns all active domains (all domains where the client has listeners registered).
315     * @return all active domains
316     */
317    public Set<Domain> getActiveDomains() {
318        return myDomainListenerMapping.keySet();
319    }
320
321    /**
322     * Returns all registered listeners of a domain.
323     * @param aDomain domain
324     * @return all registered listeners of the domain
325     */
326    public List<RemoteEventListener> getRegisteredListeners(Domain aDomain) {
327        return myDomainListenerMapping.get(aDomain);
328    }
329
330    /**
331     * Removes all RemoteEventListeners and deactivates the RemoteEventService (stop listening).
332     */
333    public void removeListeners() {
334        removeListeners(new VoidAsyncCallback());
335    }
336
337    /**
338     * Removes all RemoteEventListeners and deactivates the RemoteEventService (stop listening).
339     * @param aCallback callback (only called when a listener is registered for the domain)
340     */
341    public void removeListeners(AsyncCallback<Void> aCallback) {
342        removeListeners(myDomainListenerMapping.keySet(), aCallback);
343    }
344
345    /**
346     * Calls unlisten for a set of domains (stop listening for these domains). The RemoteEventListeners for these
347     * domains will also be removed.
348     * {@link DefaultRemoteEventService#removeListeners()} can be used to call unlisten for all domains.
349     * @param aDomains domains to unlisten
350     */
351    public void removeListeners(Set<Domain> aDomains) {
352        removeListeners(aDomains, new VoidAsyncCallback());
353    }
354
355    /**
356     * Calls unlisten for a set of domains (stop listening for these domains). The RemoteEventListeners for these
357     * domains will also be removed.
358     * {@link DefaultRemoteEventService#removeListeners()} can be used to call unlisten for all domains.
359     * @param aDomains domains to unlisten
360     * @param aCallback callback (only called when a listener is registered for the domain)
361     */
362    public void removeListeners(Set<Domain> aDomains, AsyncCallback<Void> aCallback) {
363        removeDomains(aDomains, aCallback);
364    }
365
366    /**
367     * Stops listening for the corresponding domain. The RemoteEventFilters for the domain will also be removed.
368     * {@link DefaultRemoteEventService#removeListeners()} can be used to call unlisten for all domains.
369     * @param aDomain domain to unlisten
370     */
371    public void removeListeners(Domain aDomain) {
372        removeListeners(aDomain, new VoidAsyncCallback());
373    }
374
375    /**
376     * Stops listening for the corresponding domain. The RemoteEventFilters for the domain will also be removed.
377     * {@link DefaultRemoteEventService#removeListeners()} can be used to call unlisten for all domains.
378     * @param aDomain domain to unlisten
379     * @param aCallback callback (only called when a listener is registered for the domain)
380     */
381    public void removeListeners(Domain aDomain, AsyncCallback<Void> aCallback) {
382        unlisten(aDomain, aCallback);
383    }
384
385    /**
386     * Removes an {@link de.novanic.eventservice.client.event.listener.unlisten.UnlistenEventListener}.
387     * The RemoteEventService will get inactive, when no other listeners are registered.
388     * @param anUnlistenEventListener {@link de.novanic.eventservice.client.event.listener.unlisten.UnlistenEventListener} to remove
389     * @param aCallback callback
390     */
391    public void removeUnlistenListener(UnlistenEventListener anUnlistenEventListener, AsyncCallback<Void> aCallback) {
392        removeListener(DomainFactory.UNLISTEN_DOMAIN, anUnlistenEventListener, aCallback);
393    }
394
395    /**
396     * Stops listening for {@link de.novanic.eventservice.client.event.listener.unlisten.UnlistenEvent} instances.
397     * @param aCallback callback (only called when an {@link de.novanic.eventservice.client.event.listener.unlisten.UnlistenEventListener} is registered)
398     */
399    public void removeUnlistenListeners(AsyncCallback<Void> aCallback) {
400        unlisten(DomainFactory.UNLISTEN_DOMAIN, aCallback);
401    }
402
403    /**
404     * Adds / sends an event to a domain. The event will be received from all clients which are registered to that domain.
405     * User-specific events can be added with the usage of this domain: {@link de.novanic.eventservice.client.event.domain.DomainFactory#USER_SPECIFIC_DOMAIN}.
406     * @param aDomain domain
407     * @param anEvent event
408     */
409    public void addEvent(Domain aDomain, Event anEvent) {
410        addEvent(aDomain, anEvent, new VoidAsyncCallback());
411    }
412
413    /**
414     * Adds / sends an event to a domain. The event will be received from all clients which are registered to that domain.
415     * User-specific events can be added with the usage of this domain: {@link de.novanic.eventservice.client.event.domain.DomainFactory#USER_SPECIFIC_DOMAIN}.
416     * @param aDomain domain
417     * @param anEvent event
418     * @param aCallback callback
419     */
420    public void addEvent(Domain aDomain, Event anEvent, AsyncCallback<Void> aCallback) {
421        schedule(new EventExecutionCommand(getRemoteEventConnector(), aDomain, anEvent, aCallback));
422    }
423
424    /**
425     * Stops listening for the corresponding domain. The RemoteEventFilters for the domain will also be removed.
426     * {@link DefaultRemoteEventService#removeListeners()} can be used to call unlisten for all domains.
427     * @param aDomain domain to unlisten
428     * @param aCallback callback (if it is NULL, no call is executed to the server)
429     * @return true when listeners registered (remote call needed), otherwise false
430     */
431    private boolean unlisten(Domain aDomain, AsyncCallback<Void> aCallback) {
432        return removeDomain(aDomain, aCallback);
433    }
434
435    /**
436     * Removes the domain with all listener registrations to the domain.
437     * @param aDomain domain to remove
438     * @param aCallback callback (only called when the domain isn't already removed)
439     * @return true when the domain was removed, otherwise false (false when the domain was already removed)
440     */
441    private boolean removeDomain(Domain aDomain, AsyncCallback<Void> aCallback) {
442        //remove the domain (all domain registrations)
443        boolean isRemoved = (myDomainListenerMapping.remove(aDomain) != null);
444        if(isRemoved) {
445            schedule(new DeactivationCommand(getRemoteEventConnector(), aDomain, aCallback));
446            if(myDomainListenerMapping.isEmpty()) {
447                reset();
448            }
449        }
450        return isRemoved;
451    }
452
453    /**
454     * Removes the domains with all listener registrations to the domains.
455     * @param aDomains domains to remove
456     * @param aCallback callback (only called when at least one of the domains isn't already removed)
457     * @return true when at least one domain was removed, otherwise false (false when all domains were already removed)
458     */
459    private boolean removeDomains(Set<Domain> aDomains, AsyncCallback<Void> aCallback) {
460        //remove the domains (all domain registrations)
461        Set<Domain> theRemovableDomains = new HashSet<Domain>(aDomains);
462        Iterator<Domain> theDomainIterator = theRemovableDomains.iterator();
463        while(theDomainIterator.hasNext()) {
464            Domain theDomain = theDomainIterator.next();
465            if(myDomainListenerMapping.remove(theDomain) == null) {
466                theDomainIterator.remove();
467            }
468        }
469        boolean isRemoved = !theRemovableDomains.isEmpty();
470        if(isRemoved) {
471            schedule(new DeactivationCommand(getRemoteEventConnector(), theRemovableDomains, aCallback));
472            if(myDomainListenerMapping.isEmpty()) {
473                reset();
474            }
475        }
476        return isRemoved;
477    }
478
479    /**
480     * Adds an entry to a list and avoids {@link ConcurrentModificationException} when an entry is added while iterating.
481     * @param aList list to add an entry to
482     * @param anEntry entry to add
483     * @param <CT> type of the contained objects
484     * @return new list instance with the added entry
485     */
486    private static <CT> List<CT> addOnCopy(List<CT> aList, CT anEntry) {
487        List<CT> theCollectionCopy = new ArrayList<CT>(aList);
488        if(theCollectionCopy.add(anEntry)) {
489            return theCollectionCopy;
490        }
491        return aList;
492    }
493
494    /**
495     * Removes an entry from a list and avoids {@link ConcurrentModificationException} when an entry is removed while iterating.
496     * @param aList list to remove an entry from
497     * @param anEntry entry to remove
498     * @param <CT> type of the contained objects
499     * @return new list instance without the removed entry
500     */
501    private static <CT> List<CT> removeOnCopy(List<CT> aList, CT anEntry) {
502        List<CT> theCollectionCopy = new ArrayList<CT>(aList);
503        if(theCollectionCopy.remove(anEntry)) {
504            return theCollectionCopy;
505        }
506        return aList;
507    }
508
509    /**
510     * The ListenEventCallback is used to produce the listen cycle. It is attached as callback for the listen server call.
511     */
512    private final class ListenerEventNotification implements EventNotification
513    {
514        /**
515        * That method will be called when a new event is arriving.
516        * @param aDomainEvent incoming event
517        */
518        public void onNotify(DomainEvent aDomainEvent) {
519            //all listeners for the domain of the event will be executed
520            List<RemoteEventListener> theListeners = myDomainListenerMapping.get(aDomainEvent.getDomain());
521            if(theListeners != null) {
522                final Event theEvent = aDomainEvent.getEvent();
523                for(RemoteEventListener theListener: theListeners) {
524                    theListener.apply(theEvent);
525                }
526            }
527        }
528
529        /**
530        * That method will be called when the listening for events is aborted (unexpected).
531        */
532        public void onAbort() {
533            //if the remote doesn't know the client, all listeners will be removed and the connection gets inactive
534            removeListeners();
535        }
536    }
537}