001package common.http.interceptor;
002
003import java.lang.annotation.Annotation;
004import java.lang.reflect.Modifier;
005import java.lang.reflect.ParameterizedType;
006import java.util.concurrent.ConcurrentHashMap;
007import java.util.concurrent.ConcurrentMap;
008
009/**
010 * Questa classe rappresenta un <i>factory</i> per la creazione di oggetti che estendono la classe astratta
011 * {@link ServletInterceptor}.<br/>
012 *
013 * I metodi di questa classe consentono la registrazione di classi ammissibili per l'istanziazione
014 * e l'istanziazione di questi ultimi.
015 *
016 */
017public final class  ServletInterceptorFactory {
018    private ServletInterceptorFactory(){}
019
020    private static final
021    ConcurrentMap<Class<? extends Annotation>, Class<? extends ServletInterceptor<?>>> classes =
022            new ConcurrentHashMap<>();
023
024    /**
025     * Restituisce un'istanza di {@link ServletInterceptor} associata all'annotazione passata come parametro.<br/>
026     * La classe concreta dell'oggetto restituito corrisponde a una sottoclasse di {@link ServletInterceptor} registrata
027     * nella classe factory per mezzo di una previa chiamata al metodo {@link ServletInterceptorFactory#register(Class)}.<br/>
028     *
029     * Per poter essere istanziata dal factory, la classe concreta in questione deve possedere un costruttore vuoto pubblico.
030     *
031     *
032     * @param annotation Il tipo di annotazione associato alla classe interceptor da istanziare
033     * @param <T> Tipo parametrizzato corrispondente al tipo dell'annotazione
034     * @return Un'istanza di {@link ServletInterceptor} associata all'annotazione passata come parametro
035     *
036     * @throws RuntimeException Se la creazione dell'interceptor fallisce
037     */
038    public static <T extends Annotation> ServletInterceptor<T> instantiate(T annotation){
039        Class<? extends ServletInterceptor<?>> clazz = ServletInterceptorFactory.classes.get(annotation.annotationType());
040        if(clazz == null)
041            return null;
042
043        ServletInterceptor<T> interceptor;
044        try {
045            //noinspection unchecked
046            interceptor = (ServletInterceptor<T>) clazz.getConstructor().newInstance();
047        } catch (ReflectiveOperationException e) {
048            throw new RuntimeException(e);
049        }
050
051        interceptor.init(annotation);
052        return interceptor;
053    }
054
055    /**
056     * Registra una sottoclasse di {@link ServletInterceptor} per una seguente istanziazione
057     *
058     * @param interceptorClass La classe da registrare
059     * @throws IllegalArgumentException se si tenta di registrare una classe astratta oppure
060     * se si tenta di registrare una classe con lo stesso tipo parametrizzato
061     */
062    public static void register(Class<? extends ServletInterceptor<? extends Annotation>> interceptorClass){
063        if(Modifier.isAbstract(interceptorClass.getModifiers())){
064            throw new IllegalArgumentException("Interceptor class must be concrete");
065        }
066
067        //get ServletInterceptor.class immediate subclass
068        Class<?> clazz = interceptorClass;
069        while(clazz.getSuperclass() != ServletInterceptor.class){
070            clazz = clazz.getSuperclass();
071        }
072        //get ServletInterceptor.class as ParameterizedType object
073        ParameterizedType genericSuperclass = (ParameterizedType) clazz.getGenericSuperclass();
074
075        @SuppressWarnings("unchecked")
076        Class<? extends Annotation> actualTypeArgument =
077                (Class<? extends Annotation>) genericSuperclass.getActualTypeArguments()[0];
078
079        Class<?> oldValue = classes.putIfAbsent(actualTypeArgument, interceptorClass);
080        if(oldValue != null){
081            throw new IllegalArgumentException("There already is a registered Servlet Interceptor with annotation "
082                    + actualTypeArgument.getName());
083        }
084    }
085}