/*
* JBoss, a division of Red Hat
* Copyright 2012, Red Hat Middleware, LLC, and individual
* contributors as indicated by the @authors tag. See the
* copyright.txt 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.gatein.registration.impl;
import org.gatein.common.util.ParameterValidation;
import org.gatein.registration.Consumer;
import org.gatein.registration.ConsumerGroup;
import org.gatein.registration.InvalidConsumerDataException;
import org.gatein.registration.NoSuchRegistrationException;
import org.gatein.registration.Registration;
import org.gatein.registration.RegistrationDestructionListener;
import org.gatein.registration.RegistrationException;
import org.gatein.registration.RegistrationManager;
import org.gatein.registration.RegistrationPersistenceManager;
import org.gatein.registration.RegistrationPolicy;
import org.gatein.registration.RegistrationStatus;
import org.gatein.registration.spi.ConsumerSPI;
import org.gatein.registration.spi.RegistrationSPI;
import org.gatein.wsrp.registration.PropertyDescription;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
/**
* @author <a href="mailto:chris.laprun@jboss.com">Chris Laprun</a>
* @version $Revision: 11604 $
* @since 2.6
*/
public class RegistrationManagerImpl implements RegistrationManager
{
private static final Logger log = LoggerFactory.getLogger(RegistrationManager.class);
private RegistrationPolicy policy;
private RegistrationPersistenceManager persistenceManager;
private AtomicReference<CopyOnWriteArrayList<RegistrationDestructionListener>> listeners = new AtomicReference<CopyOnWriteArrayList<RegistrationDestructionListener>>();
public static final String NON_REGISTERED_CONSUMER = "NONREGISTERED";
public RegistrationManagerImpl()
{
}
public RegistrationPolicy getPolicy()
{
return policy;
}
public void setPolicy(RegistrationPolicy policy)
{
this.policy = policy;
}
public RegistrationPersistenceManager getPersistenceManager()
{
return persistenceManager;
}
public void setPersistenceManager(RegistrationPersistenceManager persistenceManager)
{
this.persistenceManager = persistenceManager;
}
public void addRegistrationDestructionListener(RegistrationDestructionListener listener)
{
ParameterValidation.throwIllegalArgExceptionIfNull(listener, "RegistrationDestructionListener");
listeners.compareAndSet(null, new CopyOnWriteArrayList<RegistrationDestructionListener>());
listeners.get().add(listener);
}
public void removeRegistrationDestructionListener(RegistrationDestructionListener listener)
{
ParameterValidation.throwIllegalArgExceptionIfNull(listener, "RegistrationDestructionListener");
if (listeners.get() == null)
{
return;
}
listeners.get().remove(listener);
}
public Registration addRegistrationTo(String consumerName, Map<QName, Object> registrationProperties, final Map<QName, ? extends PropertyDescription> expectations, boolean createConsumerIfNeeded)
throws RegistrationException
{
// the policy determines the identity of the consumer based on the given information (note that this might be obsoleted by using WS-Security)
String identity = policy.getConsumerIdFrom(consumerName, registrationProperties);
// validate the registration information
policy.validateRegistrationDataFor(registrationProperties, identity, expectations, this);
// todo: GTNWSRP-251, GTNWSRP-253: implemented but requires changing the consumer names to work properly
/*try
{
policy.validateRegistrationDataFor(registrationProperties, identity, expectations, this);
}
catch (DuplicateRegistrationException e)
{
log.info("Consumer '" + consumerName + "' was already registered with the same set of registration properties.");
// check if the policy set the existing registration to be returned
final Registration existingRegistration = e.getExistingRegistration();
if (existingRegistration != null)
{
return existingRegistration;
}
else
{
// if we didn't get a registration to return, just throw the exception
throw e;
}
}*/
Consumer consumer = getOrCreateConsumer(identity, createConsumerIfNeeded, consumerName);
// create the actual registration
RegistrationSPI registration = persistenceManager.addRegistrationFor((ConsumerSPI)consumer, registrationProperties);
// let the policy decide what the handle should be
createAndSetRegistrationHandle(registration);
return registration;
}
private void createAndSetRegistrationHandle(RegistrationSPI registration)
{
String handle = policy.createRegistrationHandleFor(registration.getPersistentKey());
registration.setRegistrationHandle(handle);
}
public Consumer createConsumer(String name) throws RegistrationException, InvalidConsumerDataException
{
// check with policy if we allow the consumer
policy.validateConsumerName(name, this);
String identity = policy.getConsumerIdFrom(name, Collections.<QName, Object>emptyMap());
Consumer consumer = persistenceManager.createConsumer(identity, name);
// deal with group if needed
// let the policy decide if there should be a group associated with the Consumer and if yes, with which id
String groupName = policy.getAutomaticGroupNameFor(name);
if (groupName != null)
{
addConsumerToGroupNamed(name, groupName, true, false);
}
return consumer;
}
public Consumer addConsumerToGroupNamed(String consumerName, String groupName, boolean createGroupIfNeeded, boolean createConsumerIfNeeded) throws RegistrationException
{
// check with the policy if we allow the group name in case we need to create it
if (createGroupIfNeeded)
{
policy.validateConsumerGroupName(groupName, this);
}
// check with policy if we allow the consumer name in case we need to create it
if (createConsumerIfNeeded)
{
policy.validateConsumerName(consumerName, this);
}
ConsumerGroup group = getConsumerGroup(groupName);
boolean justCreatedGroup = false;
if (group == null)
{
if (createGroupIfNeeded)
{
createConsumerGroup(groupName);
justCreatedGroup = true;
}
else
{
throw new NoSuchRegistrationException("There is no existing ConsumerGroup named '" + groupName + "'.");
}
}
String identity = policy.getConsumerIdFrom(consumerName, Collections.EMPTY_MAP);
try
{
getOrCreateConsumer(identity, createConsumerIfNeeded, consumerName);
}
catch (NoSuchRegistrationException e)
{
if (justCreatedGroup)
{
removeConsumerGroup(groupName);
}
}
return persistenceManager.addConsumerToGroupNamed(identity, groupName);
}
public ConsumerGroup createConsumerGroup(String groupName) throws RegistrationException
{
// check with the policy if we allow the group
policy.validateConsumerGroupName(groupName, this);
return persistenceManager.createConsumerGroup(groupName);
}
public void removeConsumer(String identity) throws RegistrationException, NoSuchRegistrationException
{
Consumer consumer = getOrCreateConsumer(identity, false, null);
ConsumerGroup group = consumer.getGroup();
if (group != null)
{
group.removeConsumer(consumer);
}
// cascade delete the registrations
ArrayList<Registration> registrations = new ArrayList<Registration>(consumer.getRegistrations());
for (Registration reg : registrations)
{
removeRegistration(reg);
}
// let the registry do the actual deletion
persistenceManager.removeConsumer(identity);
}
public void removeConsumer(Consumer consumer) throws RegistrationException, NoSuchRegistrationException
{
ParameterValidation.throwIllegalArgExceptionIfNull(consumer, "Consumer");
removeConsumer(consumer.getId());
}
public Consumer getConsumerByIdentity(String identity) throws RegistrationException
{
ParameterValidation.throwIllegalArgExceptionIfNullOrEmpty(identity, "identity", null);
return persistenceManager.getConsumerById(identity);
}
public boolean isConsumerExisting(String consumerId) throws RegistrationException
{
ParameterValidation.throwIllegalArgExceptionIfNullOrEmpty(consumerId, "consumer id", null);
return persistenceManager.isConsumerExisting(consumerId);
}
public Consumer getConsumerFor(String registrationHandle) throws RegistrationException
{
return (Consumer)getConsumerOrRegistration(registrationHandle, true);
}
public Registration getRegistration(String registrationHandle) throws RegistrationException
{
return (Registration)getConsumerOrRegistration(registrationHandle, false);
}
public Registration getNonRegisteredRegistration() throws RegistrationException
{
//TODO: this might be better to place somewhere else and use the RegistrationHandler.register instead of
// doing basically the same thing below.
Consumer unregConsumer = getConsumerByIdentity(NON_REGISTERED_CONSUMER);
if (unregConsumer == null)
{
Registration registration = addRegistrationTo(NON_REGISTERED_CONSUMER, new HashMap<QName, Object>(), null, true);
registration.setStatus(RegistrationStatus.VALID);
getPersistenceManager().saveChangesTo(registration);
return registration;
}
else
{
//The unregistered consumer should only ever have one registration, return that
final Registration registration = unregConsumer.getRegistrations().iterator().next();
// but first check that we don't have an improper persisted state due to GTNWSRP-283
if (registration.getRegistrationHandle() == null || RegistrationStatus.PENDING == registration.getStatus())
{
// if we have improper persisted state, correct it
createAndSetRegistrationHandle((RegistrationSPI)registration);
registration.setStatus(RegistrationStatus.VALID);
getPersistenceManager().saveChangesTo(registration);
}
return registration;
}
}
public void removeRegistration(String registrationHandle) throws RegistrationException, NoSuchRegistrationException
{
Registration registration = getRegistration(registrationHandle);
if (registration == null)
{
throw new NoSuchRegistrationException("There is no Registration with handle '" + registrationHandle + "'");
}
removeRegistration(registration);
}
public void removeRegistration(Registration registration) throws RegistrationException
{
ParameterValidation.throwIllegalArgExceptionIfNull(registration, "Registration");
registration.setStatus(RegistrationStatus.INVALID); // just in case...
AtomicBoolean canRemove = new AtomicBoolean(true);
if (listeners.get() != null)
{
for (RegistrationDestructionListener listener : listeners.get())
{
RegistrationDestructionListener.Vote vote = listener.destructionScheduledFor(registration);
if (canRemove.compareAndSet(false, vote.result))
{
throw new RegistrationException("Could not remove Registration '" + registration.getRegistrationHandle()
+ "' because listener '" + listener + "' vetoed removal. Cause: " + vote.reason);
}
}
}
persistenceManager.removeRegistration(registration.getPersistentKey());
}
public ConsumerGroup getConsumerGroup(String groupName) throws RegistrationException
{
ParameterValidation.throwIllegalArgExceptionIfNullOrEmpty(groupName, "ConsumerGroup name", null);
return persistenceManager.getConsumerGroup(groupName);
}
private Consumer getOrCreateConsumer(String identity, boolean createConsumerIfNeeded, String consumerName)
throws RegistrationException
{
Consumer consumer = getConsumerByIdentity(identity);
if (consumer == null)
{
if (createConsumerIfNeeded)
{
consumer = createConsumer(consumerName);
}
else
{
throw new NoSuchRegistrationException("There is no Consumer named '" + consumerName + "'.");
}
}
return consumer;
}
private Object getConsumerOrRegistration(String registrationHandle, boolean getConsumer) throws RegistrationException
{
ParameterValidation.throwIllegalArgExceptionIfNullOrEmpty(registrationHandle, "registration handle", null);
Registration registration = persistenceManager.getRegistration(registrationHandle);
if (registration == null)
{
return null;
}
else
{
return getConsumer ? registration.getConsumer() : registration;
}
}
public Collection<? extends ConsumerGroup> getConsumerGroups() throws RegistrationException
{
return persistenceManager.getConsumerGroups();
}
public void removeConsumerGroup(ConsumerGroup group) throws RegistrationException
{
ParameterValidation.throwIllegalArgExceptionIfNull(group, "ConsumerGroup");
for (Object consumer : group.getConsumers())
{
removeConsumer((Consumer)consumer);
}
persistenceManager.removeConsumerGroup(group.getName());
}
public void removeConsumerGroup(String name) throws RegistrationException
{
ParameterValidation.throwIllegalArgExceptionIfNull(name, "ConsumerGroup name");
removeConsumerGroup(getConsumerGroup(name));
}
public Collection<? extends Consumer> getConsumers() throws RegistrationException
{
return persistenceManager.getConsumers();
}
public void clear() throws RegistrationException
{
Collection<Consumer> consumers = new ArrayList<Consumer>(getConsumers());
for (Consumer consumer : consumers)
{
removeConsumer(consumer);
}
Collection<ConsumerGroup> groups = new ArrayList<ConsumerGroup>(getConsumerGroups());
for (ConsumerGroup group : groups)
{
removeConsumerGroup(group);
}
}
/**
* We listen to registration property changes on the producer configuration so that we can invalidate the current
* registrations. Consumers will need to call modifyRegistration since properties have changed... which requires
* throwing an OperationFailedFault... not an InvalidRegistrationFault!
*
* @param registrationProperties
*/
public void propertiesHaveChanged(Map<QName, ? extends PropertyDescription> registrationProperties)
{
if (log.isDebugEnabled())
{
log.debug("Registration properties have changed, existing registrations will be invalidated...");
}
try
{
Collection registrations = persistenceManager.getRegistrations();
for (Object registration : registrations)
{
Registration reg = (Registration)registration;
// pending instead of invalid as technically, the registration is not yet invalid
reg.setStatus(RegistrationStatus.PENDING);
// make changes persistent
Consumer consumer = reg.getConsumer();
try
{
persistenceManager.saveChangesTo(consumer);
}
catch (RegistrationException e)
{
if (log.isDebugEnabled())
{
log.debug("Couldn't persist changes to Consumer '" + consumer.getId() + "'", e);
}
}
}
}
catch (RegistrationException e)
{
if (log.isDebugEnabled())
{
log.debug("Couldn't retrieve registrations...", e);
}
}
}
/**
* We listen for RegistrationPolicy changes so that we can provide the proper behavior at all time if the policy has
* been changed by users since this RegistrationManager was initialized...
*
* @param policy
*/
public void policyUpdatedTo(RegistrationPolicy policy)
{
setPolicy(policy);
}
}