/*******************************************************************************
* 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.internal;
import static jsr166y.ConcurrentReferenceHashMap.ReferenceType.SOFT;
import static jsr166y.ConcurrentReferenceHashMap.ReferenceType.WEAK;
import static org.eclipse.sisu.peaberry.internal.ComputedMapFactory.computedMap;
import static org.eclipse.sisu.peaberry.internal.ImportProxyClassLoader.getProxyConstructor;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Iterator;
import java.util.concurrent.ConcurrentMap;
import org.eclipse.sisu.peaberry.Import;
import org.eclipse.sisu.peaberry.ServiceException;
import org.eclipse.sisu.peaberry.builders.ImportDecorator;
import org.eclipse.sisu.peaberry.internal.ComputedMapFactory.Function;
/**
* Factory methods for dynamic service proxies.
*
* @author mcculls@gmail.com (Stuart McCulloch)
*/
final class ServiceProxyFactory {
// instances not allowed
private ServiceProxyFactory() {}
static <T> Iterable<T> serviceProxies(final Class<T> clazz, final Iterable<Import<T>> services,
final ImportDecorator<? super T> decorator) {
// attempt to cache service proxies between iterations
final Constructor<T> ctor = getProxyConstructor(clazz);
final ConcurrentMap<Import<T>, T> PROXY_CACHE =
computedMap(WEAK, SOFT, 8, new Function<Import<T>, T>() {
public T compute(final Import<T> service) {
return buildProxy(ctor, decorator, service);
}
});
return new Iterable<T>() {
public Iterator<T> iterator() {
return new Iterator<T>() {
// original service iterator, provided by the registry
private final Iterator<Import<T>> i = services.iterator();
public boolean hasNext() {
return i.hasNext();
}
public T next() {
return PROXY_CACHE.get(i.next());
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public String toString() {
final StringBuilder buf = new StringBuilder().append('[');
String delim = "";
for (final T t : this) {
buf.append(delim).append(t);
delim = ", ";
}
return buf.append(']').toString();
}
};
}
static <T> T serviceProxy(final Class<T> clazz, final Iterable<Import<T>> services,
final ImportDecorator<? super T> decorator) {
// provide concurrent access to best import and wrap as a decorated proxy
return buildProxy(getProxyConstructor(clazz), decorator, new ConcurrentImport<T>(services));
}
static <T> T buildProxy(final Constructor<T> constructor,
final ImportDecorator<? super T> decorator, final Import<T> service) {
try {
// minimize wrapping of exceptions to help with problem determination
return constructor.newInstance(null == decorator ? service : decorator.decorate(service));
} catch (final InstantiationException e) {
// /CLOVER:OFF
throw new ServiceException(e);
} catch (final IllegalAccessException e) {
throw new ServiceException(e);
} catch (final InvocationTargetException e) {
throw new ServiceException(e);
// /CLOVER:ON
}
}
}