/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package org.apache.directory.studio.connection.core.event;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.directory.studio.connection.core.Connection;
import org.apache.directory.studio.connection.core.ConnectionCoreConstants;
import org.apache.directory.studio.connection.core.ConnectionCorePlugin;
import org.apache.directory.studio.connection.core.ConnectionFolder;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
/**
* The ConnectionEventRegistry is a central point to register for connection specific
* events and to fire events to registered listeners.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public class ConnectionEventRegistry
{
/** The list of threads with suspended event firing. */
private static List<Long> suspendedEventFiringThreads = new ArrayList<Long>();
/** The lock used to synchronize event firings */
protected static Object lock = new Object();
/** The list with time stamps of recent event firings */
private static List<Long> fireTimeStamps = new ArrayList<Long>();
/** A counter for fired events */
private static long fireCount = 0L;
/**
* Checks if event firing is suspended in the current thread.
*
* @return true, if event firing is suspended in the current thread
*/
protected static boolean isEventFiringSuspendedInCurrentThread()
{
boolean suspended = suspendedEventFiringThreads.contains( Thread.currentThread().getId() );
// count the number of fired event in the last second
// if more then five per second: print a warning
if ( !suspended )
{
fireCount++;
synchronized ( fireTimeStamps )
{
long now = System.currentTimeMillis();
// remove all time stamps older than one second
for ( Iterator<Long> it = fireTimeStamps.iterator(); it.hasNext(); )
{
Long ts = it.next();
if ( ts + 1000 < now )
{
it.remove();
}
else
{
break;
}
}
fireTimeStamps.add( now );
if ( fireTimeStamps.size() > 5 )
{
String message = "Warning: More then " + fireTimeStamps.size() + " events were fired per second!"; //$NON-NLS-1$ //$NON-NLS-2$
ConnectionCorePlugin.getDefault().getLog().log(
new Status( IStatus.WARNING, ConnectionCoreConstants.PLUGIN_ID, message,
new Exception( message ) ) );
}
}
}
return suspended;
}
/**
* Gets the number of fired events.
*
* @return the number of fired events
*/
public static long getFireCount()
{
return fireCount;
}
/**
* Resumes event firing in the current thread.
*/
public static void resumeEventFiringInCurrentThread()
{
synchronized ( suspendedEventFiringThreads )
{
suspendedEventFiringThreads.remove( Thread.currentThread().getId() );
}
}
/**
* Suspends event firing in the current thread.
*/
public static void suspendEventFiringInCurrentThread()
{
synchronized ( suspendedEventFiringThreads )
{
suspendedEventFiringThreads.add( Thread.currentThread().getId() );
}
}
private static final EventManager<ConnectionUpdateListener, EventRunner> connectionUpdateEventManager = new EventManager<ConnectionUpdateListener, EventRunner>();
/**
* Adds the connection update listener.
*
* @param listener the listener
* @param runner the runner
*/
public static void addConnectionUpdateListener( ConnectionUpdateListener listener, EventRunner runner )
{
connectionUpdateEventManager.addListener( listener, runner );
}
/**
* Removes the connection update listener.
*
* @param listener the listener
*/
public static void removeConnectionUpdateListener( ConnectionUpdateListener listener )
{
connectionUpdateEventManager.removeListener( listener );
}
/**
* Notifies each {@link ConnectionUpdateListener} about the opened connection.
* Uses the {@link EventRunner}s.
*
* @param connection the opened connection
* @param source the source
*/
public static void fireConnectionOpened( final Connection connection, final Object source )
{
EventRunnableFactory<ConnectionUpdateListener> factory = new EventRunnableFactory<ConnectionUpdateListener>()
{
public EventRunnable createEventRunnable( final ConnectionUpdateListener listener )
{
return new EventRunnable()
{
public void run()
{
listener.connectionOpened( connection );
}
};
}
};
connectionUpdateEventManager.fire( factory );
}
/**
* Notifies each {@link ConnectionUpdateListener} about the closed connection.
* Uses the {@link EventRunner}s.
*
* @param connection the closed connection
* @param source the source
*/
public static void fireConnectionClosed( final Connection connection, final Object source )
{
EventRunnableFactory<ConnectionUpdateListener> factory = new EventRunnableFactory<ConnectionUpdateListener>()
{
public EventRunnable createEventRunnable( final ConnectionUpdateListener listener )
{
return new EventRunnable()
{
public void run()
{
listener.connectionClosed( connection );
}
};
}
};
connectionUpdateEventManager.fire( factory );
}
/**
* Notifies each {@link ConnectionUpdateListener} about the updated connection.
* Uses the {@link EventRunner}s.
*
* @param connection the updated connection
* @param source the source
*/
public static void fireConnectionUpdated( final Connection connection, final Object source )
{
EventRunnableFactory<ConnectionUpdateListener> factory = new EventRunnableFactory<ConnectionUpdateListener>()
{
public EventRunnable createEventRunnable( final ConnectionUpdateListener listener )
{
return new EventRunnable()
{
public void run()
{
listener.connectionUpdated( connection );
}
};
}
};
connectionUpdateEventManager.fire( factory );
}
/**
* Notifies each {@link ConnectionUpdateListener} about the added connection.
* Uses the {@link EventRunner}s.
*
* @param connection the added connection
* @param source the source
*/
public static void fireConnectionAdded( final Connection connection, final Object source )
{
EventRunnableFactory<ConnectionUpdateListener> factory = new EventRunnableFactory<ConnectionUpdateListener>()
{
public EventRunnable createEventRunnable( final ConnectionUpdateListener listener )
{
return new EventRunnable()
{
public void run()
{
listener.connectionAdded( connection );
}
};
}
};
connectionUpdateEventManager.fire( factory );
}
/**
* Notifies each {@link ConnectionUpdateListener} about the removed connection.
* Uses the {@link EventRunner}s.
*
* @param connection the removed connection
* @param source the source
*/
public static void fireConnectionRemoved( final Connection connection, final Object source )
{
EventRunnableFactory<ConnectionUpdateListener> factory = new EventRunnableFactory<ConnectionUpdateListener>()
{
public EventRunnable createEventRunnable( final ConnectionUpdateListener listener )
{
return new EventRunnable()
{
public void run()
{
listener.connectionRemoved( connection );
}
};
}
};
connectionUpdateEventManager.fire( factory );
}
/**
* Notifies each {@link ConnectionUpdateListener} about the modified connection folder.
* Uses the {@link EventRunner}s.
*
* @param connectionFolder the modified connection folder
* @param source the source
*/
public static void fireConnectonFolderModified( final ConnectionFolder connectionFolder, final Object source )
{
EventRunnableFactory<ConnectionUpdateListener> factory = new EventRunnableFactory<ConnectionUpdateListener>()
{
public EventRunnable createEventRunnable( final ConnectionUpdateListener listener )
{
return new EventRunnable()
{
public void run()
{
listener.connectionFolderModified( connectionFolder );
}
};
}
};
connectionUpdateEventManager.fire( factory );
}
/**
* Notifies each {@link ConnectionUpdateListener} about the added connection folder.
* Uses the {@link EventRunner}s.
*
* @param connectionFolder the added connection folder
* @param source the source
*/
public static void fireConnectonFolderAdded( final ConnectionFolder connectionFolder, final Object source )
{
EventRunnableFactory<ConnectionUpdateListener> factory = new EventRunnableFactory<ConnectionUpdateListener>()
{
public EventRunnable createEventRunnable( final ConnectionUpdateListener listener )
{
return new EventRunnable()
{
public void run()
{
listener.connectionFolderAdded( connectionFolder );
}
};
}
};
connectionUpdateEventManager.fire( factory );
}
/**
* Notifies each {@link ConnectionUpdateListener} about the removed connection folder.
* Uses the {@link EventRunner}s.
*
* @param connectionFolder the removed connection folder
* @param source the source
*/
public static void fireConnectonFolderRemoved( final ConnectionFolder connectionFolder, final Object source )
{
EventRunnableFactory<ConnectionUpdateListener> factory = new EventRunnableFactory<ConnectionUpdateListener>()
{
public EventRunnable createEventRunnable( final ConnectionUpdateListener listener )
{
return new EventRunnable()
{
public void run()
{
listener.connectionFolderRemoved( connectionFolder );
}
};
}
};
connectionUpdateEventManager.fire( factory );
}
public static class EventManager<L, R extends EventRunner>
{
private Map<L, EventRunner> listeners = new HashMap<L, EventRunner>();
/**
* Adds the listener.
*
* @param listener the listener
* @param runner the runner
*/
public void addListener( L listener, R runner )
{
assert listener != null;
assert runner != null;
synchronized ( listeners )
{
if ( !listeners.containsKey( listener ) )
{
listeners.put( listener, runner );
}
}
}
/**
* Removes the listener.
*
* @param listener the listener
*/
public void removeListener( L listener )
{
synchronized ( listeners )
{
if ( listeners.containsKey( listener ) )
{
listeners.remove( listener );
}
}
}
/**
* Notifies each {@link ConnectionUpdateListener} about the removed connection.
* Uses the {@link EventRunner}s.
*
* @param connection the removed connection
* @param source the source
*/
public void fire( EventRunnableFactory<L> factory )
{
if ( isEventFiringSuspendedInCurrentThread() )
{
return;
}
Map<L, EventRunner> clone = new HashMap<L, EventRunner>( listeners );
for ( final L listener : clone.keySet() )
{
EventRunner runner = clone.get( listener );
synchronized ( lock )
{
EventRunnable runnable = factory.createEventRunnable( listener );
runner.execute( runnable );
}
}
}
}
}