/* Copyright 2012 Tacit Knowledge
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.tacitknowledge.flip.spi;
import java.lang.annotation.Annotation;
import java.util.Collection;
import org.reflections.Reflections;
import com.google.common.base.Function;
import com.google.common.base.Predicates;
import com.google.common.collect.Collections2;
/**
* Class used to lookup classes by annotation. This class is capable to find classes
* in specific packages available by current class loader which are marked by an
* annotation. Additionally this class is able to instantiate the classes found
* using default constructor.
*
* @author Serghei Soloviov <ssoloviov@tacitknowledge.com>
*/
public class ServiceProvider
{
private final Reflections reflections;
/**
* Constructs new ServiceProvider which is able to find classes in declared
* packages. The packages passed should be available from current class loader.
* This class do not loads the classes into class loader. It reads the class
* file and analyzes it to contain the specific annotation.
*
* @param pkgs the packages where to lookup the classes.
* @throws IllegalArgumentException if there are no packages passed to lookup.
*/
public ServiceProvider(final String... pkgs)
{
if (pkgs == null || pkgs.length == 0)
{
throw new IllegalArgumentException("The list of packages to find classes should contain at least one item.");
}
reflections = new Reflections(pkgs);
}
/**
* Finds classes by the annotation. The annotation should
*
* @param annotationClass the annotation class which marks the classes to find.
* @return the collection of classes found. If no classes found returns an empty collection.
*/
protected Collection<Class<?>> findClass(final Class<? extends Annotation> annotationClass)
{
return reflections.getTypesAnnotatedWith(annotationClass);
}
/**
* Finds the classes marked by the annotation and instantiates them.
*
* @param annotatedClass the annotation which marks the classes.
* @return the collections of objects instantiated from classes found. If no
* classes were found returns an empty collection.
*/
public Collection<Object> find(final Class<? extends Annotation> annotatedClass)
{
return find(annotatedClass, Object.class);
}
/**
* Finds classes by the annotation and which extends from the resultingClass
* and instantiates them. If the classes which are marked by the annotation
* and do not extend the resultingClass then they are ignored.
*
* @param <T> the class of objects to be returned.
* @param annotatedClass the annotation which marks the classes to find.
* @param resultingClass the class from which the found classes should extend.
* It could be an interface or a class.
* @return the collection of objects found.
*/
public <T> Collection<T> find(final Class<? extends Annotation> annotatedClass, final Class<T> resultingClass)
{
final Collection<Class<?>> filteredList = Collections2.filter(findClass(annotatedClass),
Predicates.assignableFrom(resultingClass));
return Collections2.transform(filteredList, new ClassToObjectTransformer<T>());
}
private static class ClassToObjectTransformer<T> implements Function<Class<?>, T>
{
@Override
public T apply(final Class<?> input)
{
try
{
return (T) input.newInstance();
}
catch (final InstantiationException ex)
{
throw new RuntimeException(String.format("Cannot create an instance for type %s.", input.getName()), ex);
}
catch (final IllegalAccessException ex)
{
throw new RuntimeException(String.format("Cannot create an instance for type %s.", input.getName()), ex);
}
}
}
}