/*******************************************************************************
* Copyright (c) 2008, 2014 Stuart McCulloch
* 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:
* Stuart McCulloch - initial API and implementation
*******************************************************************************/
package org.eclipse.sisu.peaberry.cache;
import static org.eclipse.sisu.peaberry.util.Filters.ldap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.eclipse.sisu.peaberry.AttributeFilter;
import org.eclipse.sisu.peaberry.Import;
import org.eclipse.sisu.peaberry.ServiceRegistry;
import org.eclipse.sisu.peaberry.ServiceWatcher;
/**
* Partial {@link ServiceRegistry} implementation with generation based caching.
*
* @author mcculls@gmail.com (Stuart McCulloch)
*/
public abstract class AbstractServiceRegistry
implements CachingServiceRegistry {
private final boolean useNativeFilter;
// per-class/filter map of service listeners (much faster than polling)
private final ConcurrentMap<String, AbstractServiceListener<?>> listenerMap =
new ConcurrentHashMap<String, AbstractServiceListener<?>>(16, 0.75f, 2);
protected AbstractServiceRegistry(final boolean useNativeFilter) {
this.useNativeFilter = useNativeFilter;
}
@SuppressWarnings("unchecked")
public final <T> Iterable<Import<T>> lookup(final Class<T> clazz, final AttributeFilter filter) {
// might combine class filter and user filter as one LDAP string
final AttributeFilter[] filterRef = new AttributeFilter[]{filter};
final String ldapFilter = getLdapFilter(clazz, filterRef);
return new FilteredIterableService(registerListener(ldapFilter), filterRef[0]);
}
@SuppressWarnings("unchecked")
public final <T> void watch(final Class<T> clazz, final AttributeFilter filter,
final ServiceWatcher<? super T> watcher) {
// might combine class filter and user filter as one LDAP string
final AttributeFilter[] filterRef = new AttributeFilter[]{filter};
final String ldapFilter = getLdapFilter(clazz, filterRef);
if (null == filterRef[0]) {
registerListener(ldapFilter).addWatcher(watcher);
} else {
registerListener(ldapFilter).addWatcher(new FilteredServiceWatcher(filter, watcher));
}
}
public final void flush(final int targetGeneration) {
// look for unused cached service instances to flush...
for (final AbstractServiceListener<?> i : listenerMap.values()) {
i.flush(targetGeneration);
}
}
private AbstractServiceListener<?> registerListener(final String ldapFilter) {
final String key = null == ldapFilter ? "" : ldapFilter;
AbstractServiceListener<?> listener;
listener = listenerMap.get(key);
if (null == listener) {
final AbstractServiceListener<?> newListener = createListener(ldapFilter);
listener = listenerMap.putIfAbsent(key, newListener);
if (null == listener) {
newListener.start();
return newListener;
}
}
return listener;
}
private String getLdapFilter(final Class<?> clazz, final AttributeFilter[] filterRef) {
final String clazzFilter;
if (null != clazz && Object.class != clazz) { // NOPMD
clazzFilter = "(objectClass=" + clazz.getName() + ')';
} else {
clazzFilter = null;
}
if (useNativeFilter && null != filterRef[0]) {
try {
// can the user filter object be normalized to an LDAP string?
final String ldapFilter = ldap(filterRef[0].toString()).toString();
filterRef[0] = null; // yes, so we don't need object anymore
return null == clazzFilter ? ldapFilter : "(&" + clazzFilter + ldapFilter + ')';
} catch (final IllegalArgumentException e) {/* not native LDAP */} // NOPMD
}
return clazzFilter;
}
protected abstract <T> AbstractServiceListener<T> createListener(String ldapFilter);
}