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}