/*
* JBoss, Home of Professional Open Source
* Copyright 2010, Red Hat Middleware LLC, and others contributors as indicated
* by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* 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,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package org.jboss.internal.soa.esb.services.registry;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.log4j.Logger;
import org.jboss.soa.esb.Service;
import org.jboss.soa.esb.addressing.EPR;
import org.jboss.soa.esb.services.registry.AbstractRegistryInterceptor;
import org.jboss.soa.esb.services.registry.RegistryException;
import org.jboss.soa.esb.services.registry.ServiceNotFoundException;
/**
* Local registry interceptor.
*
* @author <a href='mailto:Kevin.Conner@jboss.com'>Kevin Conner</a>
*/
public class LocalRegistryInterceptor extends AbstractRegistryInterceptor
{
/**
* The logger for the registry cache
*/
private static final Logger LOGGER = Logger.getLogger(LocalRegistryInterceptor.class) ;
/**
* Mapping of service names to service informatio.
*/
private final HashMap<Service, ServiceInfo> serviceInfoMap = new HashMap<Service, ServiceInfo>() ;
/**
* Find all Services assigned to the Red Hat/JBossESB organization.
* @return Collection of Strings containing the service names.
* @throws RegistryException
*/
public List<String> findAllServices() throws RegistryException
{
return getRegistry().findAllServices() ;
}
/**
* Find all services that belong to the supplied category.
*
* @param serviceCategoryName - name of the category to which the service belongs.
* @return Collection of Strings containing the service names
* @throws RegistryException
*/
public List<String> findServices(final String category)
throws RegistryException
{
return getRegistry().findServices(category) ;
}
/**
* Returns the first EPR in the list that belong to a specific category and service combination.
*
* @param serviceCategoryName - name of the category to which the service belongs.
* @param serviceName - name of the service to which the EPS belongs.
* @return EPR.
* @throws RegistryException
*/
public EPR findEPR(final String category, final String name)
throws RegistryException, ServiceNotFoundException
{
final Service service = new Service(category, name) ;
final ConcurrentMap<EPR, AtomicLong> eprMap = getEPRs(service) ;
if (eprMap != null)
{
final Iterator<EPR> eprIter = eprMap.keySet().iterator() ;
if (eprIter.hasNext())
{
final EPR epr = eprIter.next() ;
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("Returning locally registered EPR for category " + category + ", name " + name) ;
}
return epr ;
}
}
return getRegistry().findEPR(category, name);
}
/**
* Finds all the EPRs that belong to a specific category and service combination.
*
* @param serviceCategoryName - name of the category to which the service belongs.
* @param serviceName - name of the service to which the EPS belongs.
* @return Collection of EPRs.
* @throws RegistryException
*/
public List<EPR> findEPRs(final String category, final String name)
throws RegistryException, ServiceNotFoundException
{
final Service service = new Service(category, name) ;
final ConcurrentMap<EPR, AtomicLong> eprMap = getEPRs(service) ;
if (eprMap != null)
{
final Set<EPR> eprSet = eprMap.keySet() ;
final List<EPR> eprs = Arrays.asList(eprSet.toArray(new EPR[0])) ;
if (!eprs.isEmpty())
{
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("Returning locally registered EPRs for category " + category + ", name " + name) ;
}
return eprs ;
}
}
return getRegistry().findEPRs(category, name) ;
}
/**
* Registers an EPR under the specified category and service. If the specified service does
* not exist, it will be created at the same time.
*
* @param serviceCategoryName - name of the category to which the service belongs.
* @param serviceName - name of the service to which the EPS belongs.
* @param serviceDescription - human readable description of the service,
* only used when it the service does not yet exist.
* @param epr - the EndPointReference (EPR) that needs to be registered.
* @param eprDescription - human readable description of the EPR
* @throws RegistryException
*/
public void registerEPR(final String category, final String name,
final String serviceDescription, final EPR epr, final String eprDescription)
throws RegistryException
{
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("Registering EPR " + epr + " for category " + category + ", name " + name) ;
}
final Service service = new Service(category, name) ;
final ServiceInfo serviceInfo = createWriteLockServiceInfo(service) ;
try
{
getRegistry().registerEPR(category, name, serviceDescription, epr, eprDescription) ;
final ConcurrentMap<EPR, AtomicLong> eprs = serviceInfo.getEPRs() ;
addEPR(eprs, epr) ;
}
finally
{
serviceInfo.getWriteLock().unlock() ;
}
}
/**
* Removes an EPR from the Registry.
* @param serviceCategoryName - name of the category to which the service belongs.
* @param serviceName - name of the service to which the EPS belongs.
* @param epr - the EndPointReference (EPR) that needs to be unregistered.
* @throws RegistryException
*/
public void unRegisterEPR(final String category, final String name,
final EPR epr) throws RegistryException, ServiceNotFoundException
{
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("Unregistering EPR " + epr + " for category " + category + ", name " + name) ;
}
final Service service = new Service(category, name) ;
final ServiceInfo serviceInfo = getWriteLockServiceInfo(service) ;
if (serviceInfo != null)
{
try
{
final ConcurrentMap<EPR, AtomicLong> eprs = serviceInfo.getEPRs() ;
final AtomicLong count = eprs.get(epr) ;
if (count != null)
{
if (count.decrementAndGet() == 0)
{
eprs.remove(epr) ;
if (eprs.isEmpty())
{
removeServiceInfo(service, serviceInfo) ;
}
}
}
getRegistry().unRegisterEPR(category, name, epr) ;
}
finally
{
serviceInfo.getWriteLock().unlock() ;
}
}
else
{
getRegistry().unRegisterEPR(category, name, epr) ;
}
}
/**
* Removes a service from the Registry along with all the ServiceBindings underneath it.
*
* @param category - name of the service category, for example 'transformation'.
* @param serviceName - name of the service, for example 'smooks'.
* @throws RegistryException
*/
public void unRegisterService(final String category, final String name)
throws RegistryException, ServiceNotFoundException
{
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("Unregistering service category " + category + ", name " + name) ;
}
final Service service = new Service(category, name) ;
final ServiceInfo serviceInfo = getWriteLockServiceInfo(service) ;
if (serviceInfo != null)
{
try
{
removeServiceInfo(service, serviceInfo) ;
getRegistry().unRegisterService(category, name) ;
}
finally
{
serviceInfo.getWriteLock().unlock() ;
}
}
else
{
getRegistry().unRegisterService(category, name) ;
}
}
/**
* Add an EPR entry into the map.
* @param eprs The current map.
* @param epr The epr to add.
*/
private static void addEPR(final ConcurrentMap<EPR, AtomicLong> eprs, final EPR epr)
{
final AtomicLong newCount = new AtomicLong(1) ;
final AtomicLong count = eprs.putIfAbsent(epr, newCount) ;
if (count != null)
{
count.incrementAndGet() ;
}
}
/**
* Remove the service info from the current map.
* @param service The service to remove.
* @param serviceInfo The service information being removed.
* @param ifEmpty Only remove the service information if it is empty.
*/
private synchronized void removeServiceInfo(final Service service, final ServiceInfo serviceInfo)
{
final ServiceInfo currentServiceInfo = serviceInfoMap.remove(service) ;
if (serviceInfo != currentServiceInfo)
{
serviceInfoMap.put(service, currentServiceInfo) ;
}
}
/**
* Acquire a read lock for the current service info.
* @param service The service to lock.
* @return The current service info in the map.
*/
private ConcurrentMap<EPR, AtomicLong> getEPRs(final Service service)
{
final boolean traceEnabled = LOGGER.isTraceEnabled() ;
while(true)
{
final ServiceInfo serviceInfo ;
final Lock readLock ;
synchronized(this)
{
serviceInfo = serviceInfoMap.get(service) ;
if (traceEnabled)
{
LOGGER.trace("Retrieved service information for service " + service + ", serviceInfo: " + serviceInfo) ;
}
if (serviceInfo == null)
{
return null ;
}
readLock = serviceInfo.getReadLock() ;
if (readLock.tryLock())
{
if (traceEnabled)
{
LOGGER.trace("Obtained read lock, returning EPRs") ;
}
try
{
return serviceInfo.getEPRs() ;
}
finally
{
readLock.unlock() ;
}
}
}
LOGGER.trace("Waiting for read lock") ;
readLock.lock() ;
try
{
synchronized(this)
{
if (serviceInfo == serviceInfoMap.get(service))
{
if (traceEnabled)
{
LOGGER.trace("Returning current EPRs") ;
}
return serviceInfo.getEPRs() ;
}
}
}
finally
{
readLock.unlock() ;
}
}
}
/**
* Acquire a write lock for the current service info, caller <b>must</b> release the lock.
* @param service The service to lock.
* @return The current service info in the map.
*/
private ServiceInfo getWriteLockServiceInfo(final Service service)
{
final boolean traceEnabled = LOGGER.isTraceEnabled() ;
while(true)
{
final ServiceInfo serviceInfo ;
final Lock writeLock ;
synchronized(this)
{
serviceInfo = serviceInfoMap.get(service) ;
if (traceEnabled)
{
LOGGER.trace("Retrieved service information for service " + service + ", serviceInfo: " + serviceInfo) ;
}
if (serviceInfo == null)
{
return null ;
}
writeLock = serviceInfo.getWriteLock() ;
if (writeLock.tryLock())
{
if (traceEnabled)
{
LOGGER.trace("Obtained write lock, returning locked serviceInfo") ;
}
return serviceInfo ;
}
}
LOGGER.trace("Waiting for write lock") ;
writeLock.lock() ;
synchronized(this)
{
if (serviceInfo == serviceInfoMap.get(service))
{
if (traceEnabled)
{
LOGGER.trace("ServiceInfo stilll current, returning locked serviceInfo") ;
}
return serviceInfo ;
}
else
{
writeLock.unlock() ;
}
}
}
}
/**
* Acquire a write lock for the current service info, caller <b>must</b> release the lock.
* ServiceInfo will be created if not present.
* @param service The service to lock.
* @return The current service info in the map.
*/
private ServiceInfo createWriteLockServiceInfo(final Service service)
{
final boolean traceEnabled = LOGGER.isTraceEnabled() ;
while(true)
{
final ServiceInfo serviceInfo ;
final Lock writeLock ;
synchronized(this)
{
serviceInfo = serviceInfoMap.get(service) ;
if (serviceInfo == null)
{
if (traceEnabled)
{
LOGGER.trace("Creating service information for service " + service) ;
}
final ServiceInfo newServiceInfo = new ServiceInfo() ;
serviceInfoMap.put(service, newServiceInfo) ;
newServiceInfo.getWriteLock().lock() ;
return newServiceInfo ;
}
if (traceEnabled)
{
LOGGER.trace("Retrieved service information for service " + service + ", serviceInfo: " + serviceInfo) ;
}
writeLock = serviceInfo.getWriteLock() ;
if (writeLock.tryLock())
{
if (traceEnabled)
{
LOGGER.trace("Obtained write lock, returning locked serviceInfo") ;
}
return serviceInfo ;
}
}
writeLock.lock() ;
synchronized(this)
{
if (serviceInfo == serviceInfoMap.get(service))
{
if (traceEnabled)
{
LOGGER.trace("ServiceInfo stilll current, returning locked serviceInfo") ;
}
return serviceInfo ;
}
else
{
writeLock.unlock() ;
}
}
}
}
/**
* Class representing the service information
* @author kevin
*/
private static class ServiceInfo
{
private ConcurrentMap<EPR, AtomicLong> eprs = new ConcurrentHashMap<EPR, AtomicLong>() ;
private ReadWriteLock lock = new ReentrantReadWriteLock() ;
ConcurrentMap<EPR, AtomicLong> getEPRs()
{
return eprs ;
}
Lock getWriteLock()
{
return lock.writeLock() ;
}
Lock getReadLock()
{
return lock.readLock() ;
}
}
}