package com.maxifier.guice.lifecycle; import com.google.inject.*; import com.google.inject.matcher.AbstractMatcher; import com.google.inject.matcher.Matchers; import com.google.inject.spi.InjectionListener; import com.google.inject.spi.TypeEncounter; import com.google.inject.spi.TypeListener; import javax.annotation.PostConstruct; import javax.inject.Inject; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; /** * Project: Maxifier * Date: 10.09.2009 * Time: 17:20:37 * <p/> * Copyright (c) 1999-2009 Magenta Corporation Ltd. All Rights Reserved. * Magenta Technology proprietary and confidential. * Use is subject to license terms. * * @author Aleksey Didik */ public class LifecycleModule extends AbstractModule { public static void bind(Binder binder) { binder.install(new LifecycleModule()); } @Override protected void configure() { //@PostConstruct bindListener( new AnnotatedMethodMatcher(PostConstruct.class), new PostConstructTypeListener(PostConstruct.class) ); //@ShutdownSafe bindInterceptor(Matchers.any(), Matchers.annotatedWith(ShutdownSafe.class), new SafeShutdownInterceptor()); //Destroy container during JVM shutdown bind(ShutdownHook.class).asEagerSingleton(); } static class AnnotatedMethodMatcher extends AbstractMatcher<TypeLiteral<?>> { private final Class<? extends Annotation>[] annotations; public AnnotatedMethodMatcher(Class<? extends Annotation>... annotations) { this.annotations = annotations; } @Override public boolean matches(TypeLiteral<?> typeLiteral) { final Class<?> type = typeLiteral.getRawType(); return isMatches(type); } private boolean isMatches(Class<?> type) { while (true) { if (type.equals(Object.class)) { return false; } for (Method method : type.getDeclaredMethods()) { for (Class<? extends Annotation> a : annotations) { if (method.isAnnotationPresent(a)) { return true; } } } type = type.getSuperclass(); } } @Override public String toString() { return "AnnotatedMethodMatcher{" + "annotations=" + (annotations == null ? null : Arrays.asList(annotations)) + '}'; } } private static class PostConstructTypeListener implements TypeListener { private final AnnotatedMethodCache cache; public PostConstructTypeListener(Class<? extends Annotation>... annotationClasses) { this.cache = new AnnotatedMethodCache(annotationClasses); } @Override public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) { encounter.register(new InjectionListener<I>() { @Override public void afterInjection(I injectee) { Method[] methods = cache.get(injectee.getClass()); for (Method method : methods) { try { method.invoke(injectee); } catch (InvocationTargetException ie) { String message = ie.getTargetException().getMessage(); throw new ProvisionException("Exception in @PostConstruct: " + message, ie.getTargetException()); } catch (IllegalAccessException e) { throw new ProvisionException("Exception in @PostConstruct: " + e.getMessage(), e); } } } }); } } static class ShutdownHook { @Inject void register(final Injector injector) { Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { Lifecycle.destroy(injector); } }); } } }