/*
* JBoss, Home of Professional Open Source.
* Copyright 2009 Red Hat, Inc. 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.core.framework.server;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.jboss.ha.framework.interfaces.ClusterNode;
import org.jboss.ha.framework.interfaces.DistributedReplicantManager;
import org.jboss.ha.framework.interfaces.GroupMembershipListener;
import org.jboss.ha.framework.interfaces.HAPartition;
import org.jboss.ha.framework.interfaces.SerializableStateTransferResult;
import org.jboss.ha.framework.interfaces.StateTransferProvider;
import org.jboss.ha.framework.server.spi.ManagedDistributedState;
/**
* Extends {@link CoreGroupCommunicationService} to add implemenation of the
* {@link HAPartition} methods that are not part of its superinterfaces.
*
* @author <a href="mailto:sacha.labourey@cogito-info.ch">Sacha Labourey</a>.
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>.
* @author Scott.Stark@jboss.org
* @author Brian Stansberry
* @author Vladimir Blagojevic
* @author <a href="mailto:galder.zamarreno@jboss.com">Galder Zamarreno</a>
* @version $Revision: 104456 $
*/
public class HAPartitionImpl
extends CoreGroupCommunicationService
implements HAPartition
{
// Constants -----------------------------------------------------
public static final Short HAPARTITION_SCOPE_ID = Short.valueOf((short) 111);
// Attributes ----------------------------------------------------
@SuppressWarnings("deprecation")
private final Map<String, HAPartitionStateTransfer> initialStateRecipients = new ConcurrentHashMap<String, HAPartitionStateTransfer>();
/** The cluster replicant manager */
private DistributedReplicantManagerImpl replicantManager;
/** The DistributedState service we manage */
@SuppressWarnings("deprecation")
private org.jboss.ha.framework.interfaces.DistributedState distributedState;
// Static --------------------------------------------------------
// Constructors --------------------------------------------------
public HAPartitionImpl()
{
this.setScopeId(HAPARTITION_SCOPE_ID);
this.logHistory("Partition object created");
}
// HAPartition implementation ----------------------------------------------
public String getPartitionName()
{
return getGroupName();
}
public DistributedReplicantManager getDistributedReplicantManager()
{
return this.replicantManager;
}
@SuppressWarnings("deprecation")
public org.jboss.ha.framework.interfaces.DistributedState getDistributedStateService()
{
return this.distributedState;
}
// *************************
// *************************
// Group Membership listeners
// *************************
// *************************
@SuppressWarnings("deprecation")
public void registerMembershipListener(HAMembershipListener listener)
{
GroupMembershipListener adapter = new GroupMembershipListenerAdapter(listener);
boolean isAsynch = (!getAllowSynchronousMembershipNotifications())
|| (listener instanceof AsynchHAMembershipListener)
|| (listener instanceof AsynchHAMembershipExtendedListener);
registerGroupMembershipListener(adapter, !isAsynch);
}
@SuppressWarnings("deprecation")
public void unregisterMembershipListener(HAMembershipListener listener)
{
GroupMembershipListener adapter = new GroupMembershipListenerAdapter(listener);
boolean isAsynch = (!getAllowSynchronousMembershipNotifications())
|| (listener instanceof AsynchHAMembershipListener)
|| (listener instanceof AsynchHAMembershipExtendedListener);
unregisterGroupMembershipListener(adapter, !isAsynch);
}
// *************************
// *************************
// State transfer management
// *************************
// *************************
@SuppressWarnings("deprecation")
public void subscribeToStateTransferEvents(String serviceName, HAPartitionStateTransfer subscriber)
{
StateTransferProvider provider = new StateTransferProviderAdapter(subscriber);
this.registerStateTransferProvider(serviceName, provider);
this.initialStateRecipients.put(serviceName, subscriber);
}
@SuppressWarnings("deprecation")
public void unsubscribeFromStateTransferEvents(String serviceName, HAPartitionStateTransfer subscriber)
{
this.initialStateRecipients.remove(serviceName);
}
// Public ------------------------------------------------------------------
@SuppressWarnings("deprecation")
public void setDistributedStateImpl(org.jboss.ha.framework.interfaces.DistributedState distributedState)
{
this.distributedState = distributedState;
}
public void setPartitionName(String newName)
{
setGroupName(newName);
}
public DistributedReplicantManagerImpl getDistributedReplicantManagerImpl()
{
return this.replicantManager;
}
// Protected --------------------------------------------------------------
@Override
protected void createService() throws Exception
{
this.log.info("Initializing partition " + this.getPartitionName());
this.logHistory ("Initializing partition " + this.getPartitionName());
if (this.distributedState instanceof ManagedDistributedState)
{
((ManagedDistributedState) this.distributedState).createService();
}
if (getChannelSource() == null && distributedState instanceof ChannelSource)
{
log.debug("Using " + distributedState + " as a " + ChannelSource.class.getSimpleName());
setChannelSource((ChannelSource) distributedState);
}
super.createService();
if (this.replicantManager == null)
{
this.replicantManager = new DistributedReplicantManagerImpl(this);
}
// registerDRM();
this.replicantManager.createService();
this.log.debug("done initializing partition " + this.getPartitionName());
}
@Override
protected void startService() throws Exception
{
this.logHistory ("Starting partition " + this.getPartitionName());
// Start DS first, so it can start its cache which will want to
// do the channel connection stuff itself before we try
if (this.distributedState instanceof ManagedDistributedState)
{
((ManagedDistributedState) this.distributedState).startService();
}
super.startService();
this.fetchInitialState();
this.replicantManager.startService();
}
@Override
protected void stopService() throws Exception
{
this.logHistory ("Stopping partition");
this.log.info("Stopping partition " + this.getPartitionName());
this.replicantManager.stopService();
super.stopService();
if (this.distributedState instanceof ManagedDistributedState)
{
((ManagedDistributedState) this.distributedState).stopService();
}
this.log.info("Partition " + this.getPartitionName() + " stopped.");
}
@Override
protected void destroyService()
{
this.log.debug("Destroying HAPartition: " + this.getPartitionName());
try
{
this.replicantManager.destroyService();
// unregisterDRM();
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
this.log.error("Destroying " + DistributedReplicantManager.class.getSimpleName() + " failed", e);
}
catch (Exception e)
{
this.log.error("Destroying " + DistributedReplicantManager.class.getSimpleName() + " failed", e);
}
super.destroyService();
try
{
if (this.distributedState instanceof ManagedDistributedState)
{
((ManagedDistributedState) this.distributedState).destroyService();
}
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
this.log.error("Destroying " + DistributedReplicantManager.class.getSimpleName() + " failed", e);
}
catch (Exception e)
{
this.log.error("Destroying " + DistributedReplicantManager.class.getSimpleName() + " failed", e);
}
this.log.info("Partition " + this.getPartitionName() + " destroyed.");
}
// Private -------------------------------------------------------
@SuppressWarnings("deprecation")
private void fetchInitialState() throws Exception
{
this.log.info("Fetching initial service state (will wait for " + this.getStateTransferTimeout() +
" milliseconds for each service):");
for (Map.Entry<String, HAPartitionStateTransfer> entry : this.initialStateRecipients.entrySet())
{
log.debug("Fetching state for " + entry.getKey());
Future<SerializableStateTransferResult> future = this.getServiceState(entry.getKey());
SerializableStateTransferResult result = future.get(this.getStateTransferTimeout(), TimeUnit.MILLISECONDS);
if (result.stateReceived())
{
if (result.getStateTransferException() != null)
{
throw result.getStateTransferException();
}
else
{
entry.getValue().setCurrentState(result.getState());
log.debug("Received state for " + entry.getKey());
}
}
else if (result.getStateTransferException() != null)
{
throw result.getStateTransferException();
}
else
{
log.debug("No state available for " + entry.getKey() + " -- we must be sole member of group");
}
}
}
// Inner classes -------------------------------------------------
/**
* Adapts the legacy HAMembershipListener API to the new GroupMemberhshipListener API.
*/
@SuppressWarnings("deprecation")
private static class GroupMembershipListenerAdapter implements GroupMembershipListener
{
private final HAMembershipListener target;
GroupMembershipListenerAdapter(HAMembershipListener target)
{
this.target = target;
}
public void membershipChanged(List<ClusterNode> deadMembers, List<ClusterNode> newMembers,
List<ClusterNode> allMembers)
{
target.membershipChanged(castMembers(deadMembers), castMembers(newMembers), castMembers(allMembers));
}
public void membershipChangedDuringMerge(List<ClusterNode> deadMembers, List<ClusterNode> newMembers,
List<ClusterNode> allMembers, List<List<ClusterNode>> originatingGroups)
{
if (target instanceof HAMembershipExtendedListener)
{
((HAMembershipExtendedListener) target).membershipChangedDuringMerge(castMembers(deadMembers), castMembers(newMembers), castMembers(allMembers), castOriginatingGroups(originatingGroups));
}
else
{
membershipChanged(deadMembers, newMembers, allMembers);
}
}
public boolean equals(Object obj)
{
return obj instanceof GroupMembershipListenerAdapter && ((GroupMembershipListenerAdapter) obj).target.equals(target);
}
public int hashCode()
{
return target.hashCode();
}
private static Vector<ClusterNode> castMembers(List<ClusterNode> members)
{
return members instanceof Vector<?> ? (Vector<ClusterNode>) members : new Vector<ClusterNode>(members);
}
private static Vector<List<ClusterNode>> castOriginatingGroups(List<List<ClusterNode>> groups)
{
return groups instanceof Vector<?> ? (Vector<List<ClusterNode>>) groups : new Vector<List<ClusterNode>>(groups);
}
}
/**
* Allows a legacy HAPartitionStateTransfer to be used as a StateTransferProvider.
*/
private static class StateTransferProviderAdapter implements StateTransferProvider
{
@SuppressWarnings("deprecation")
private final HAPartitionStateTransfer delegate;
@SuppressWarnings("deprecation")
private StateTransferProviderAdapter(HAPartitionStateTransfer delegate)
{
this.delegate = delegate;
}
public Serializable getCurrentState()
{
return delegate.getCurrentState();
}
}
}