/*
* Copyright (c) 2009, Paul Merlin. All Rights Reserved.
*
* Licensed 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.swing.on.steroids.messagebus;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Base MessageBus implementation.
*
* Only the publish method is abstract, other ones are final.
*
* Safe but could be too slow or too fat for your usage. New implementations will be done/accepted
* with a convincing test case to keep MessageBus small.
*/
public abstract class BaseMessageBus
implements MessageBus
{
private final ConcurrentHashMap<MessageType<?>, CopyOnWriteArrayList<?>> subscribers = new ConcurrentHashMap<MessageType<?>, CopyOnWriteArrayList<?>>();
private final ConcurrentHashMap<MessageType<?>, CopyOnWriteArrayList<?>> vetos = new ConcurrentHashMap<MessageType<?>, CopyOnWriteArrayList<?>>();
@Override
public abstract <S extends Subscriber> void publish( Message<S> msg );
@Override
public <S extends Subscriber> Subscribtion subscribe( MessageType<S> type, S subscriber )
{
subscribers( type ).add( subscriber );
return new Subscribtion( this, type, subscriber );
}
@Override
public <S extends Subscriber> void unsubscribe( MessageType<S> type, S subscriber )
{
CopyOnWriteArrayList<S> l = subscribers( type );
boolean result = l.remove( subscriber );
if ( l.isEmpty() ) {
subscribers.remove( type );
}
assert result : "Tried to remove unknown subscriber: " + subscriber + " for " + type;
}
@Override
public <S extends Subscriber> VetoRegistration registerVeto( MessageType<S> type, Veto veto )
{
vetos( type ).add( veto );
return new VetoRegistration( this, type, veto );
}
@Override
public <S extends Subscriber> void unregisterVeto( MessageType<S> type, Veto veto )
{
CopyOnWriteArrayList<Veto> l = vetos( type );
boolean result = l.remove( veto );
if ( l.isEmpty() ) {
vetos.remove( type );
}
assert result : "Tried to remove unknown veto: " + veto + " for " + type;
}
@Override
public <S extends Subscriber> S getSubscriber( MessageType<S> type, int index )
{
return subscribers( type ).get( index );
}
@Override
public <S extends Subscriber> boolean hasSubscribers( MessageType<S> type )
{
return subscribers.containsKey( type );
}
@Override
public <S extends Subscriber> int countSubscribers( MessageType<S> type )
{
CopyOnWriteArrayList<?> l = subscribers.get( type );
return l == null ? 0 : l.size();
}
/**
* @param <S> Subscriber mark type, for type safety
* @param message Message
* @return True if the Message is vetoed, false otherwise
*/
protected final <S extends Subscriber> boolean vetoed( Message<S> message )
{
CopyOnWriteArrayList<Veto> msgVetos = vetos( message.getMessageType() );
for ( Veto eachVeto : msgVetos ) {
if ( eachVeto.veto( message ) ) {
return true;
}
}
return false;
}
/**
* @param <S> Subscriber mark type, for type safety
* @param type MessageType
* @return The Subscriber collection for a MessageType
*/
@SuppressWarnings( "unchecked" )
protected final <S extends Subscriber> CopyOnWriteArrayList<S> subscribers( MessageType<S> type )
{
subscribers.putIfAbsent( type, new CopyOnWriteArrayList<S>() );
// This cast is safe because we control the puts.
return ( CopyOnWriteArrayList<S> ) subscribers.get( type );
}
/**
* @param <S> Subscriber mark type, for type safety
* @param type MessageType
* @return The Veto collection for a MessageType
*/
@SuppressWarnings( "unchecked" )
protected final <S extends Subscriber> CopyOnWriteArrayList<Veto> vetos( MessageType<S> type )
{
vetos.putIfAbsent( type, new CopyOnWriteArrayList<Veto>() );
// This cast is safe because we control the puts.
return ( CopyOnWriteArrayList<Veto> ) vetos.get( type );
}
}