blob: b61547bab46532d6058187e9402cdcd6d7ed823b [file] [log] [blame]
/*
* Copyright (c) 2010-2017 BSI Business Systems Integration AG.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* BSI Business Systems Integration AG - initial API and implementation
*/
/*
* Copyright (c) 2010-2015 BSI Business Systems Integration AG.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* BSI Business Systems Integration AG - initial API and implementation
******************************************************************************/
package org.eclipse.scout.rt.platform;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.eclipse.scout.rt.platform.index.AbstractMultiValuesIndex;
import org.eclipse.scout.rt.platform.index.IMultiValueIndex;
import org.eclipse.scout.rt.platform.index.IndexedStore;
import org.eclipse.scout.rt.platform.util.IRegistrationHandle;
import org.eclipse.scout.rt.platform.util.TypeCastUtility;
/**
* A registry for beans implementing an interface with one generic type parameter, and which allows to lookup beans by
* its generic parameter type.
*/
public class TypeParameterBeanRegistry<BEAN> {
protected final ReadWriteLock m_lock = new ReentrantReadWriteLock();
protected final Class<BEAN> m_beanType;
protected final IndexedStore<BeanRegistration<BEAN>> m_inventory = new IndexedStore<>();
protected final IMultiValueIndex<Class<?>, BeanRegistration<BEAN>> m_genericTypeIndex;
protected final Set<Class<?>> m_computedLookupTypes = Collections.synchronizedSet(new HashSet<>());
public TypeParameterBeanRegistry(final Class<BEAN> beanType) {
m_beanType = beanType;
// Register the index to lookup beans by their generic parameter type, or by a super type of that generic parameter type.
m_genericTypeIndex = m_inventory.registerIndex(new AbstractMultiValuesIndex<Class<?>, BeanRegistration<BEAN>>() {
@Override
protected Set<Class<?>> calculateIndexesFor(final BeanRegistration<BEAN> beanRegistration) {
final Set<Class<?>> indexValues = new HashSet<>();
indexValues.add(beanRegistration.getGenericTypeParameter());
indexValues.addAll(beanRegistration.getLazyLookupSubTypes());
return indexValues;
}
});
}
/**
* Registers the given beans.
*
* @return A token representing the registration of the given beans. This token can later be used to unregister the
* beans.
*/
public IRegistrationHandle registerBeans(final List<BEAN> beans) {
final List<IRegistrationHandle> registrations = new ArrayList<>();
for (final BEAN bean : beans) {
registrations.add(registerBean(bean));
}
return () -> {
for (final IRegistrationHandle registration : registrations) {
registration.dispose();
}
};
}
/**
* Registers the given bean by using its (first) generic type parameter as key.
*
* @return A token representing the registration of the given bean. This token can later be used to unregister the
* bean.
*/
public IRegistrationHandle registerBean(final BEAN bean) {
final Class genericTypeParameter = TypeCastUtility.getGenericsParameterClass(bean.getClass(), m_beanType);
return registerBean(bean, genericTypeParameter);
}
/**
* Registers the given bean using the given {@code genericTypeParameter} as key.
*
* @return A token representing the registration of the given bean. This token can later be used to unregister the
* bean.
*/
public IRegistrationHandle registerBean(final BEAN bean, final Class<?> genericTypeParameter) {
m_computedLookupTypes.clear();
m_lock.writeLock().lock();
try {
final BeanRegistration<BEAN> beanRegistration = new BeanRegistration<>(bean, genericTypeParameter);
// Add the bean to the inventory. Thereby, the indexes are computed for that element.
m_inventory.add(beanRegistration);
return () -> m_inventory.remove(beanRegistration);
}
finally {
m_lock.writeLock().unlock();
}
}
/**
* Returns all beans with the given generic type parameter, or which are declared with a super type of that parameter.
* <p>
* The beans are returned in the original order (see {@link #registerBeans(Class, List)}).
* </p>
*
* @param lookupType
* generic parameter type, not <code>null</code>.
*/
public List<BEAN> getBeans(final Class<?> lookupType) {
updateLookupTypeHierarchyIndex(lookupType);
m_lock.readLock().lock();
try {
final List<BEAN> beans = new ArrayList<>();
for (final BeanRegistration<BEAN> registration : m_genericTypeIndex.get(lookupType)) {
beans.add(registration.getBean());
}
return beans;
}
finally {
m_lock.readLock().unlock();
}
}
/**
* Registers the given lookup type with beans which have a generic type parameter that is a super type of the given
* lookup type.
* <p>
* This is done only the first time a lookup for that lookup type is done.
*/
protected void updateLookupTypeHierarchyIndex(final Class<?> lookupType) {
if (m_computedLookupTypes.contains(lookupType)) {
return;
}
m_lock.writeLock().lock();
try {
if (m_computedLookupTypes.contains(lookupType)) {
return; // double checked locking
}
for (final BeanRegistration<BEAN> element : m_inventory.values()) {
if (element.getGenericTypeParameter().isAssignableFrom(lookupType)) {
element.registerLazyLookupSubType(lookupType);
// Replace the element in the inventory to update the index.
m_inventory.remove(element);
m_inventory.add(element);
}
}
m_computedLookupTypes.add(lookupType);
}
finally {
m_lock.writeLock().unlock();
}
}
protected static class BeanRegistration<T> {
private final T m_bean;
private final Class<?> m_genericTypeParameter;
private final Set<Class<?>> m_lazyLookupSubTypes;
public BeanRegistration(final T bean, final Class<?> genericTypeParameter) {
m_bean = bean;
m_genericTypeParameter = genericTypeParameter;
m_lazyLookupSubTypes = new HashSet<>();
}
public void registerLazyLookupSubType(final Class<?> lookupType) {
m_lazyLookupSubTypes.add(lookupType);
}
public T getBean() {
return m_bean;
}
/**
* Returns the generic bean type.
*/
public Class<?> getGenericTypeParameter() {
return m_genericTypeParameter;
}
/**
* Returns the sub-types of the bean's generic type parameter. Those sub types are only populated upon a bean lookup
* for such a sub type, and which is remembered to speed up subsequent lookups.
*/
public Set<Class<?>> getLazyLookupSubTypes() {
return m_lazyLookupSubTypes;
}
}
}