/*
* Copyright (c) 2002-2009 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.kernel.impl.event;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.logging.Level;
import java.util.logging.Logger;
// TODO: rewrite this one completly, use new concurrent util stuff
/**
* The EventManager is used to register/unregister event listeners and to
* generate events. There are two types of events and event listeners,
* pro-active and re-active. A generated event consists of
* {@link Event event type} and {@link EventData event data} and will be sent to
* the event listeners registered on the event type.
* <p>
* Pro-active events are synchronous, generating a pro-active event will tell
* all the pro-active event listeners registered on that event to negotiate and
* return an answer. This is a very useful way to ask about something but the
* caller don't have to know who needs to be asked. Generate a pro-active event
* and continue depending on what answer you get.
* <p>
* The other type of events, re-active events, are asynchronous. Generating a
* re-active event will just place the event in a queue. The re-active events
* are removed from the queue in the near future and sent to the re-active event
* listeners registered on that event. This is a good way to tell someone that
* something has been done without knowing who you have to tell.
*/
public class EventManager
{
private static Logger log = Logger.getLogger( EventManager.class.getName() );
private Map<Event,List<ProActiveEventListener>> proActiveEventListeners =
new HashMap<Event,List<ProActiveEventListener>>();
private Map<Event,List<ReActiveEventListener>> reActiveEventListeners =
new HashMap<Event,List<ReActiveEventListener>>();
private LinkedList<EventElement> eventElements =
new LinkedList<EventElement>();
private volatile boolean startIsOk = true;
private volatile boolean destroyed = true;
private EventQueue eventQueue;
public EventManager()
{
eventQueue = new EventQueue( this );
}
/**
* Generates a pro-active event to all pro-active event listeners registered
* on <CODE>event</CODE>. The event listeners will negotiate and an
* answer will be returned, synchronously.
* <p>
* The results returned from the registered event listeners are anded and
* returned, if there are no pro-active event listeners registered on
* <CODE>event</CODE>, <CODE>true</CODE> will be returned.
*
* @param event
* the event type
* @param data
* the event data
* @return the negotiated answers from the event listeners
*/
public boolean generateProActiveEvent( Event event, EventData data )
{
assert !destroyed;
// TODO: fix this, use safe ArrayMap instead
List<ProActiveEventListener> listenerList = proActiveEventListeners
.get( event );
if ( listenerList != null )
{
Iterator<ProActiveEventListener> listItr = listenerList.iterator();
boolean result = true;
// no concurrent mod, list is copied in modyfing blocks
while ( listItr.hasNext() && result )
{
ProActiveEventListener listener = listItr.next();
try
{
if ( !listener.proActiveEventReceived( event, data ) )
{
result = false;
}
}
catch ( Throwable t )
{
log.log( Level.SEVERE,
"Exception sending pro-active event to " + listener, t );
result = false;
}
}
return result;
}
return true;
}
/**
* Generates a re-active event to all re-active event listeners registered
* on <CODE>event</CODE>. The event will be sent to the listeners in the
* near future, asynchronous communication.
*
* @param event
* the event type
* @param data
* the event data
*/
public void generateReActiveEvent( Event event, EventData data )
{
assert !destroyed;
markWithOriginatingThread( data );
EventElement evtElement = new EventElement( event, data, false );
synchronized ( eventElements )
{
eventElements.add( evtElement );
}
}
EventElement getNextEventElement()
{
synchronized ( eventElements )
{
if ( !eventElements.isEmpty() )
{
try
{
return eventElements.removeFirst();
}
catch ( NoSuchElementException e )
{
// ok return null
}
}
}
return null;
}
/**
* Registers a pro-active event listener to <CODE>event</CODE>.
*
* @param listener
* the pro-active event listener to register
* @param event
* the event to register <CODE>listener</CODE> to
* @throws EventListenerAlreadyRegisteredException
* if the <CODE>listener</CODE> is already registered
* @throws EventListenerNotRegisteredException
* if <CODE>listener</CODE> or <CODE>event</CODE> is
* <CODE>null</CODE>
*/
public synchronized void registerProActiveEventListener(
ProActiveEventListener listener, Event event )
throws EventListenerAlreadyRegisteredException,
EventListenerNotRegisteredException
{
assert !destroyed;
if ( listener == null || event == null )
{
throw new EventListenerNotRegisteredException(
"Null parameter, listener=" + listener + ", event=" + event );
}
if ( proActiveEventListeners.containsKey( event ) )
{
List<ProActiveEventListener> listenerList = proActiveEventListeners
.get( event );
if ( !listenerList.contains( listener ) )
{
List<ProActiveEventListener> newList =
new ArrayList<ProActiveEventListener>();
newList.addAll( listenerList );
newList.add( listener );
proActiveEventListeners.put( event, newList );
}
else
{
throw new EventListenerAlreadyRegisteredException( " listener="
+ listener + ", event=" + event );
}
}
else
{
List<ProActiveEventListener> listenerList =
new ArrayList<ProActiveEventListener>();
listenerList.add( listener );
proActiveEventListeners.put( event, listenerList );
}
}
/**
* Removes a pro-active event listener from <CODE>event</CODE>.
*
* @param listener
* the pro-active event listener
* @param event
* the event to remove <CODE>listener</CODE> from
* @throws EventListenerNotRegisteredException
* if <CODE>listener</CODE> is no registered
*/
public synchronized void unregisterProActiveEventListener(
ProActiveEventListener listener, Event event )
throws EventListenerNotRegisteredException
{
assert !destroyed;
if ( proActiveEventListeners.containsKey( event ) )
{
List<ProActiveEventListener> listenerList = proActiveEventListeners
.get( event );
if ( listenerList.contains( listener ) )
{
List<ProActiveEventListener> newList =
new ArrayList<ProActiveEventListener>();
newList.addAll( listenerList );
newList.remove( listener );
proActiveEventListeners.put( event, newList );
if ( newList.size() == 0 )
{
proActiveEventListeners.remove( event );
}
}
else
{
throw new EventListenerNotRegisteredException( " listener="
+ listener + ", event=" + event );
}
}
else
{
throw new EventListenerNotRegisteredException( " listener="
+ listener + ", event=" + event );
}
}
/**
* Registers a re-active event listener to <CODE>event</CODE>.
*
* @param listener
* the re-active event listener to register
* @param event
* the event to register <CODE>listener</CODE> to
* @throws EventListenerAlreadyRegisteredException
* if the <CODE>listeners</CODE> is already registered
* @throws EventListenerNotRegisteredException
* if <CODE>listener</CODE> or <CODE>event</CODE> is
* <CODE>null</CODE>
*/
public synchronized void registerReActiveEventListener(
ReActiveEventListener listener, Event event )
throws EventListenerAlreadyRegisteredException,
EventListenerNotRegisteredException
{
assert !destroyed;
if ( listener == null || event == null )
{
throw new EventListenerNotRegisteredException(
"Null parameter, listener=" + listener + ", event=" + event );
}
if ( reActiveEventListeners.containsKey( event ) )
{
List<ReActiveEventListener> listenerList = reActiveEventListeners
.get( event );
if ( !listenerList.contains( listener ) )
{
List<ReActiveEventListener> newList =
new ArrayList<ReActiveEventListener>();
newList.addAll( listenerList );
newList.add( listener );
reActiveEventListeners.put( event, newList );
}
else
{
throw new EventListenerAlreadyRegisteredException( " listener="
+ listener + ", event=" + event );
}
}
else
{
List<ReActiveEventListener> listenerList =
new ArrayList<ReActiveEventListener>();
listenerList.add( listener );
reActiveEventListeners.put( event, listenerList );
}
}
/**
* Removes a re-active event listener from <CODE>event</CODE>.
*
* @param listener
* the re-active event listener
* @param event
* the event to remove <CODE>listener</CODE> from
* @throws EventListenerNotRegisteredException
* if <CODE>listener</CODE> is no registered
*/
public synchronized void unregisterReActiveEventListener(
ReActiveEventListener listener, Event event )
throws EventListenerNotRegisteredException
{
assert !destroyed;
if ( reActiveEventListeners.containsKey( event ) )
{
List<ReActiveEventListener> listenerList = reActiveEventListeners
.get( event );
if ( listenerList.contains( listener ) )
{
List<ReActiveEventListener> newList =
new ArrayList<ReActiveEventListener>();
newList.addAll( listenerList );
newList.remove( listener );
reActiveEventListeners.put( event, newList );
if ( newList.size() == 0 )
{
reActiveEventListeners.remove( event );
}
}
else
{
throw new EventListenerNotRegisteredException( " listener="
+ listener + ", event=" + event );
}
}
else
{
throw new EventListenerNotRegisteredException( " listener="
+ listener + ", event=" + event );
}
}
void sendReActiveEvent( Event event, EventData data )
{
// TODO: fix this, use safe ArrayMap instead
List<ReActiveEventListener> listeners =
reActiveEventListeners.get( event );
if ( listeners != null )
{
Iterator<ReActiveEventListener> listItr = listeners.iterator();
while ( listItr.hasNext() )
{
ReActiveEventListener listener = listItr.next();
try
{
listener.reActiveEventReceived( event, data );
}
catch ( Throwable t )
{
t.printStackTrace();
log.severe( "Exception sending re-active event to "
+ listener );
}
}
}
}
synchronized void start()
{
if ( startIsOk )
{
eventQueue.start();
startIsOk = false;
destroyed = false;
}
else
{
log.warning( "EventModule already started" );
}
}
synchronized void stop()
{
assert !destroyed;
if ( !startIsOk )
{
startIsOk = true;
eventQueue.shutdown();
eventQueue = new EventQueue( this );
}
else
{
log.warning( "EventModule already stopped" );
}
}
synchronized void destroy()
{
assert !destroyed;
if ( startIsOk )
{
removeListeners();
destroyed = true;
proActiveEventListeners =
new HashMap<Event,List<ProActiveEventListener>>();
reActiveEventListeners =
new HashMap<Event,List<ReActiveEventListener>>();
eventElements = new LinkedList<EventElement>();
startIsOk = true;
destroyed = true;
}
else
{
log.severe( "EventModule not in stopped state" );
}
}
void setReActiveEventQueueWaitTime( int time )
{
assert !destroyed;
eventQueue.setWaitTime( time );
}
int getReActiveEventQueueWaitTime()
{
assert !destroyed;
return eventQueue.getWaitTime();
}
void setReActiveEventQueueNotifyOnCount( int count )
{
assert !destroyed;
eventQueue.setNotifyOnCount( count );
}
int getReActiveEventQueueNotifyOnCount()
{
assert !destroyed;
return eventQueue.getNotifyOnCount();
}
private void markWithOriginatingThread( EventData data )
{
if ( data != null )
{
data.setOriginatingThread( Thread.currentThread() );
}
}
private void removeListeners()
{
Iterator<Event> itr = proActiveEventListeners.keySet().iterator();
while ( itr.hasNext() )
{
Event event = itr.next();
List<ProActiveEventListener> listenerList =
proActiveEventListeners.get( event );
StringBuffer stringList = new StringBuffer();
for ( int i = 0; i < listenerList.size(); i++ )
{
stringList.append( listenerList.get( i ) );
stringList.append( "\n" );
}
}
itr = reActiveEventListeners.keySet().iterator();
while ( itr.hasNext() )
{
Event event = itr.next();
List<ReActiveEventListener> listenerList =
reActiveEventListeners.get( event );
StringBuffer stringList = new StringBuffer();
for ( int i = 0; i < listenerList.size(); i++ )
{
stringList.append( listenerList.get( i ) );
stringList.append( "\n" );
}
}
}
}