package com.netflix.governator.internal;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.PreDestroy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Supplier;
import com.netflix.governator.LifecycleAction;
import com.netflix.governator.LifecycleFeature;
import com.netflix.governator.internal.JSR250LifecycleAction.ValidationMode;
import com.netflix.governator.internal.TypeInspector.TypeVisitor;
/**
* Special LifecycleFeature to support @PreDestroy annotation processing and
* java.lang.AutoCloseable detection. Note that this feature is implicit in
* LifecycleModule and therefore does not need to be added using the
* LifecycleFeature multibinding.
*
* @author elandau
*/
public final class PreDestroyLifecycleFeature implements LifecycleFeature {
private static final Logger LOG = LoggerFactory.getLogger(PreDestroyLifecycleFeature.class);
private final ValidationMode validationMode;
public PreDestroyLifecycleFeature(ValidationMode validationMode) {
this.validationMode = validationMode;
}
@Override
public List<LifecycleAction> getActionsForType(final Class<?> type) {
return TypeInspector.accept(type, new PreDestroyVisitor());
}
@Override
public String toString() {
return "PreDestroy";
}
private class PreDestroyVisitor implements TypeVisitor, Supplier<List<LifecycleAction>> {
private Set<String> visitContext = new HashSet<>();
private List<LifecycleAction> typeActions = new ArrayList<>();
@Override
public boolean visit(final Class<?> clazz) {
boolean continueVisit = !clazz.isInterface();
if (continueVisit && AutoCloseable.class.isAssignableFrom(clazz)) {
AutoCloseableLifecycleAction closeableAction = new AutoCloseableLifecycleAction(
clazz.asSubclass(AutoCloseable.class));
LOG.debug("adding action {}", closeableAction);
typeActions.add(closeableAction);
continueVisit = false;
}
return continueVisit;
}
@Override
public boolean visit(final Method method) {
final String methodName = method.getName();
if (method.isAnnotationPresent(PreDestroy.class)) {
if (!visitContext.contains(methodName)) {
try {
LifecycleAction destroyAction = new JSR250LifecycleAction(PreDestroy.class, method, validationMode);
LOG.debug("adding action {}", destroyAction);
typeActions.add(destroyAction);
visitContext.add(methodName);
} catch (IllegalArgumentException e) {
LOG.info("ignoring @PreDestroy method {}.{}() - {}", method.getDeclaringClass().getName(),
methodName, e.getMessage());
}
}
} else if (method.getReturnType() == Void.TYPE && method.getParameterTypes().length == 0 && !Modifier.isFinal(method.getModifiers())) {
// method potentially overrides superclass method and annotations
visitContext.add(methodName);
}
return true;
}
@Override
public boolean visit(Field field) {
return true;
}
@Override
public List<LifecycleAction> get() {
return Collections.unmodifiableList(typeActions);
}
}
private static final class AutoCloseableLifecycleAction implements LifecycleAction {
private final String description;
private AutoCloseableLifecycleAction(Class<? extends AutoCloseable> clazz) {
this.description = new StringBuilder().append("AutoCloseable@").append(System.identityHashCode(this)).append("[").append(clazz.getName()).append(".")
.append("close()").append("]").toString();
}
@Override
public void call(Object obj) throws Exception {
LOG.info("calling action {}", description);
AutoCloseable.class.cast(obj).close();
}
@Override
public String toString() {
return description;
}
}
}