package org.togglz.spring.web;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.togglz.core.Feature;
import org.togglz.core.context.FeatureContext;
import java.lang.annotation.Annotation;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* This interceptor checks if a controller or controller method is annotated with the
* {@link FeaturesAreActive} annotation to determine if a controller should be
* activated or not.
* <p>
* Set the togglz.web.register-feature-interceptor to {@code true} to activate this interceptor.
*/
public class FeatureInterceptor extends HandlerInterceptorAdapter {
/**
* Used to store annotations in a ConcurrentHashMap which does not allow storing {@code null} values.
*
* @param <A> annotation type
*/
private static final class AnnotationHolder<A extends Annotation> {
private final A annotation;
public AnnotationHolder(A annotation) {
this.annotation = annotation;
}
public A getAnnotation() {
return annotation;
}
public boolean hasAnnotation() {
return annotation != null;
}
}
/**
* Caches the annotations on the {@link HandlerMethod}s to avoid expensive reflection calls for every request.
*/
private ConcurrentHashMap<HandlerMethod, AnnotationHolder<FeaturesAreActive>> annotations = new ConcurrentHashMap<>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
AnnotationHolder<FeaturesAreActive> annotationHolder = annotations.get(handlerMethod);
if (annotationHolder == null) {
FeaturesAreActive foundAnnotation = handlerAnnotation(handlerMethod, FeaturesAreActive.class);
annotations.putIfAbsent(handlerMethod, new AnnotationHolder<FeaturesAreActive>(foundAnnotation));
annotationHolder = annotations.get(handlerMethod);
}
if (annotationHolder.hasAnnotation()) {
FeaturesAreActive featuresAreActiveAnnotation = annotationHolder.getAnnotation();
if (!Enum.class.isAssignableFrom(featuresAreActiveAnnotation.featureClass())) {
throw new IllegalArgumentException("The featureClass of the " + FeaturesAreActive.class.getSimpleName() + " annotation must be an enum");
}
for (String f : featuresAreActiveAnnotation.features()) {
@SuppressWarnings("unchecked")
Feature feature = (Feature) enumFrom(f, (Class<? extends Enum<?>>) featuresAreActiveAnnotation.featureClass());
if (feature != null && !FeatureContext.getFeatureManager().isActive(feature)) {
response.sendError(featuresAreActiveAnnotation.responseStatus());
return false;
}
}
}
}
return super.preHandle(request, response, handler);
}
protected static <A extends Annotation> A handlerAnnotation(HandlerMethod handlerMethod, Class<A> annotationClass) {
A annotation = handlerMethod.getMethodAnnotation(annotationClass);
if (annotation == null) {
annotation = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), annotationClass);
}
return annotation;
}
protected static <T extends Enum<?>> T enumFrom(String name, Class<T> enumType) {
if (name != null) {
for (T item : enumType.getEnumConstants()) {
if (name.equals(item.name())) {
return item;
}
}
}
return null;
}
}