Skip to content

Commit

Permalink
Put DefaultPicoRegistry in it's own class. Added cache for class lookup.
Browse files Browse the repository at this point in the history
  • Loading branch information
jboesl committed Apr 21, 2017
1 parent d9568bf commit a62a614
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 70 deletions.
86 changes: 86 additions & 0 deletions src/main/java/de/adito/picoservice/DefaultPicoRegistry.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package de.adito.picoservice;

import javax.annotation.Nonnull;
import java.lang.annotation.Annotation;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Stream;

/**
* @author j.boesl, 21.04.17
*/
public class DefaultPicoRegistry implements IPicoRegistry
{

private final Collection<IPicoRegistration> loadedServices;
private final Map<Class<?>, Collection<Class<?>>> searchedTypeToAnnotatedClassesMap;


protected DefaultPicoRegistry()
{
loadedServices = _loadServices();
searchedTypeToAnnotatedClassesMap = new HashMap<>();
}

@Nonnull
@Override
public <C, A extends Annotation> Map<Class<? extends C>, A> find(@Nonnull Class<C> pSearchedType,
@Nonnull Class<A> pAnnotationClass)
{
Map<Class<? extends C>, A> map = new HashMap<>();
for (Class<? extends C> cls : _getSearchedTypes(pSearchedType)) {
A annotation = cls.getAnnotation(pAnnotationClass);
if (annotation != null)
map.put(cls, annotation);
}
return map;
}

@Nonnull
@Override
public <T, C> Stream<T> find(@Nonnull Class<C> pSearchedType,
@Nonnull Function<Class<? extends C>, T> pResolverFunction)
{
Collection<Class<? extends C>> searchedTypes = _getSearchedTypes(pSearchedType);
return searchedTypes.stream()
.map(pResolverFunction)
.filter(Objects::nonNull);
}

@SuppressWarnings("unchecked")
private <T> Collection<Class<? extends T>> _getSearchedTypes(Class<T> pSearchedType)
{
return (Collection) searchedTypeToAnnotatedClassesMap.computeIfAbsent(pSearchedType, searchedType -> {
ArrayList<Class<? extends T>> st = new ArrayList<>();
for (IPicoRegistration registration : loadedServices) {
Class<?> annotatedClass = registration.getAnnotatedClass();
if (searchedType.isAssignableFrom(annotatedClass))
st.add((Class<? extends T>) annotatedClass);
}
if (st.isEmpty())
return Collections.emptySet();
st.trimToSize();
return Collections.unmodifiableCollection(st);
});
}

/**
* We have to load all of our PicoServices into a separate collection, because
* the Java-ServiceLoader throws a ConcurrentModificationException if >1 iterators
* are iterated at the same time.
* Reloading of cached Registrations is currently not supported yet.
*
* @see <a href="https://anydoby.com/jblog/en/java/2128">https://anydoby.com/jblog/en/java/2128</a>
* @see <a href="https://issues.apache.org/jira/browse/SIS-193">https://issues.apache.org/jira/browse/SIS-193</a>
*/
@Nonnull
private Collection<IPicoRegistration> _loadServices()
{
ServiceLoader<IPicoRegistration> serviceLoader = ServiceLoader.load(IPicoRegistration.class);
Set<IPicoRegistration> foundServices = new HashSet<>();
for (IPicoRegistration registration : serviceLoader)
foundServices.add(registration);
return Collections.unmodifiableSet(foundServices);
}

}
71 changes: 1 addition & 70 deletions src/main/java/de/adito/picoservice/InstanceLoader.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
package de.adito.picoservice;

import javax.annotation.Nonnull;
import java.lang.annotation.Annotation;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Stream;

/**
* Default implementation for {@link de.adito.picoservice.IPicoRegistry} available at
Expand All @@ -25,71 +21,6 @@ IPicoRegistry load()
return iterator.next();

// create default
return new IPicoRegistry()
{
private final ServiceLoader<IPicoRegistration> serviceLoader = ServiceLoader.load(IPicoRegistration.class);
private final Collection<IPicoRegistration> loadedServices = _loadServices();

@Nonnull
@Override
public <C, A extends Annotation> Map<Class<? extends C>, A> find(@Nonnull Class<C> pSearchedType,
@Nonnull Class<A> pAnnotationClass)
{
Map<Class<? extends C>, A> map = new HashMap<>();
for (IPicoRegistration registration : loadedServices)
{
Class<?> annotatedClass = registration.getAnnotatedClass();
if (pSearchedType.isAssignableFrom(annotatedClass))
{
Annotation annotation = annotatedClass.getAnnotation(pAnnotationClass);
if (annotation != null)
//noinspection unchecked
map.put((Class<C>) annotatedClass, (A) annotation);
}
}
return map;
}

@Nonnull
@Override
public <T, C> Stream<T> find(@Nonnull Class<C> pSearchedType,
@Nonnull Function<Class<? extends C>, T> pResolverFunction)
{
Stream.Builder<T> streamBuilder = Stream.builder();
for (IPicoRegistration registration : loadedServices)
{
Class<?> annotatedClass = registration.getAnnotatedClass();
if (pSearchedType.isAssignableFrom(annotatedClass))
{
@SuppressWarnings("unchecked")
T result = pResolverFunction.apply((Class<? extends C>) annotatedClass);
if (result != null)
streamBuilder.add(result);
}
}
return streamBuilder.build();
}

/**
* We have to load all of our PicoServices into a separate collection, because
* the Java-ServiceLoader throws a ConcurrentModificationException if >1 iterators
* are iterated at the same time.
* Reloading of cached Registrations is currently not supported yet.
*
* @see <a href="https://anydoby.com/jblog/en/java/2128">https://anydoby.com/jblog/en/java/2128</a>
* @see <a href="https://issues.apache.org/jira/browse/SIS-193">https://issues.apache.org/jira/browse/SIS-193</a>
*/
@Nonnull
private Collection<IPicoRegistration> _loadServices()
{
synchronized (serviceLoader)
{
HashSet<IPicoRegistration> foundServices = new HashSet<>();
for (IPicoRegistration registration : serviceLoader)
foundServices.add(registration);
return Collections.unmodifiableSet(foundServices);
}
}
};
return new DefaultPicoRegistry();
}
}

0 comments on commit a62a614

Please sign in to comment.