/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.ha.framework.server;
import java.io.Serializable;
import java.util.EventObject;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import org.jboss.ha.framework.interfaces.DistributedReplicantManager;
import org.jboss.ha.framework.interfaces.EventListener;
import org.jboss.ha.framework.interfaces.GroupRpcDispatcher;
import org.jboss.ha.framework.interfaces.HAPartition;
import org.jboss.ha.framework.interfaces.HAService;
import org.jboss.logging.Logger;
/**
* Base class for clustered services.
* Provides clustered event facility.
*
* @param <E> type of event generated by this service
* @author Paul Ferraro
*/
public class HAServiceImpl<E extends EventObject> implements HAService<E>, EventFacility<E>, DistributedReplicantManager.ReplicantListener
{
private static final Class<?>[] HANDLE_EVENT_TYPES = new Class[] { EventObject.class };
private static final String REPLICANT_TOKEN = "";
protected final Logger log = Logger.getLogger(this.getClass());
private final HAServiceRpcHandler<E> rpcHandler = new RpcHandler();
private final EventFacility<E> eventFacility;
private final EventFactory<E> eventFactory;
private final List<EventListener<E>> eventListeners = new CopyOnWriteArrayList<EventListener<E>>();
private volatile HAPartition partition;
private volatile String name;
private volatile boolean registerClassLoader = false;
public HAServiceImpl(EventFactory<E> eventFactory, EventFacility<E> eventFacility)
{
this.eventFactory = eventFactory;
this.eventFacility = eventFacility;
}
public HAServiceImpl(EventFactory<E> eventFactory)
{
this.eventFactory = eventFactory;
this.eventFacility = this;
}
public void addEventListener(EventListener<E> listener)
{
this.eventListeners.add(listener);
}
public void removeEventListener(EventListener<E> listener)
{
this.eventListeners.remove(listener);
}
/**
* @see org.jboss.ha.framework.server.EventFacility#notifyListeners(java.util.EventObject)
*/
@Override
public void notifyListeners(E event)
{
for (EventListener<E> listener: this.eventListeners)
{
try
{
listener.handleEvent(event);
}
catch (Exception e)
{
this.log.warn(e.getMessage(), e);
}
}
}
/**
* @see org.jboss.ha.framework.interfaces.HAService#getHAPartition()
*/
@Override
public HAPartition getHAPartition()
{
return this.partition;
}
/**
* @see org.jboss.ha.framework.interfaces.HAService#setHAPartition(org.jboss.ha.framework.interfaces.HAPartition)
*/
@Override
public void setHAPartition(HAPartition partition)
{
this.partition = partition;
}
/**
* @see org.jboss.ha.framework.interfaces.HAService#isRegisterThreadContextClassLoader()
*/
@Override
public boolean isRegisterThreadContextClassLoader()
{
return this.registerClassLoader;
}
/**
* @see org.jboss.ha.framework.interfaces.HAService#setRegisterThreadContextClassLoader(boolean)
*/
@Override
public void setRegisterThreadContextClassLoader(boolean registerClassLoader)
{
this.registerClassLoader = registerClassLoader;
}
@Override
public String getServiceHAName()
{
return this.name;
}
/**
* @see org.jboss.ha.framework.interfaces.HAService#setServiceHAName(java.lang.String)
*/
@Override
public void setServiceHAName(String name)
{
this.name = name;
}
/**
* @see org.jboss.ha.framework.interfaces.EventListener#handleEvent(java.util.EventObject)
*/
@Override
public void handleEvent(E event) throws Exception
{
this.callAsyncMethodOnPartition("handleEvent", new Object[] { event }, HANDLE_EVENT_TYPES);
}
protected HAServiceRpcHandler<E> getRpcHandler()
{
return this.rpcHandler;
}
protected EventFactory<E> getEventFactory()
{
return this.eventFactory;
}
protected EventFacility<E> getEventFacility()
{
return this.eventFacility;
}
/**
* @see org.jboss.ha.framework.interfaces.HAService#create()
*/
@Override
public void create() throws Exception
{
}
/**
* @see org.jboss.ha.framework.interfaces.HAService#start()
*/
@Override
public void start() throws Exception
{
this.registerRPCHandler();
this.registerDRMListener();
}
/**
* @see org.jboss.ha.framework.interfaces.HAService#stop()
*/
@Override
public void stop()
{
try
{
this.unregisterDRMListener();
this.unregisterRPCHandler();
}
catch (Exception e)
{
this.log.error("Failed to stop service.", e);
}
}
/**
* @see org.jboss.ha.framework.interfaces.HAService#destroy()
*/
@Override
public void destroy()
{
}
@SuppressWarnings("unchecked")
protected <T> List<T> callMethodOnPartition(String methodName, Object[] args, Class<?>[] types) throws Exception
{
GroupRpcDispatcher dispatcher = this.partition;
return (List<T>) dispatcher.callMethodOnCluster(this.name, methodName, args, types, true);
}
protected void callAsyncMethodOnPartition(String methodName, Object[] args, Class<?>[] types) throws Exception
{
this.partition.callAsynchMethodOnCluster(this.name, methodName, args, types, true);
}
protected void registerRPCHandler()
{
String key = this.getHAServiceKey();
if (this.isRegisterThreadContextClassLoader())
{
this.partition.registerRPCHandler(key, this.getRpcHandler(), Thread.currentThread().getContextClassLoader());
}
else
{
this.partition.registerRPCHandler(key, this.getRpcHandler());
}
}
protected void unregisterRPCHandler()
{
this.partition.unregisterRPCHandler(this.getHAServiceKey(), this.getRpcHandler());
}
protected void registerDRMListener() throws Exception
{
if (log.isDebugEnabled())
{
log.debug("HAServiceImpl.registerDRMListener for service=" + getHAServiceKey());
}
DistributedReplicantManager drm = this.partition.getDistributedReplicantManager();
String key = this.getHAServiceKey();
drm.registerListener(key, this);
// this ensures that the DRM knows that this node has the MBean deployed
drm.add(key, this.getReplicant());
}
protected void unregisterDRMListener() throws Exception
{
DistributedReplicantManager drm = this.partition.getDistributedReplicantManager();
String key = this.getHAServiceKey();
// remove replicant node
drm.remove(key);
// unregister
drm.unregisterListener(key, this);
}
/**
* {@inheritDoc}
* @return the key used by the DRM and partition rpc handler mapping.
*/
@Override
public String getHAServiceKey()
{
return this.name;
}
/**
* @return the object to be registered with the {@link org.jboss.ha.framework.interfaces.DistributedReplicantManager}.
*/
protected Serializable getReplicant()
{
return REPLICANT_TOKEN;
}
/**
* @see org.jboss.ha.framework.interfaces.DistributedReplicantManager.ReplicantListener#replicantsChanged(java.lang.String, java.util.List, int, boolean)
*/
@Override
public void replicantsChanged(String key, List<?> newReplicants, int newReplicantsViewId, boolean merge)
{
if (key.equals(this.getHAServiceKey()))
{
// This synchronized block was added when the internal behavior of
// DistributedReplicantManagerImpl was changed so that concurrent
// replicantsChanged notifications are possible. Synchronization
// ensures that this change won't break non-thread-safe
// subclasses of HAServiceMBeanSupport.
synchronized (this)
{
// change in the topology callback
this.partitionTopologyChanged(newReplicants, newReplicantsViewId, merge);
}
}
}
protected void partitionTopologyChanged(List<?> newReplicants, int newReplicantsViewId, boolean merge)
{
this.log.debug("partitionTopologyChanged(). cluster view id: " + newReplicantsViewId);
}
protected class RpcHandler implements HAServiceRpcHandler<E>
{
/**
* @see org.jboss.ha.framework.interfaces.HAService.RpcHandler#handleNotification(java.lang.Object)
*/
@Override
public void handleEvent(E event) throws Exception
{
HAServiceImpl.this.getEventFacility().notifyListeners(event);
}
}
}