// Path: src/main/java/ru/vyarus/guice/ext/ExtAnnotationsModule.java
// @SuppressWarnings("checkstyle:ClassDataAbstractionCoupling")
// public class ExtAnnotationsModule extends AbstractModule {
// 
//     private Matcher<Object> typeMatcher;
// 
//     /**
//      * Default module constructor to check annotations on all beans.
//      */
//     public ExtAnnotationsModule() {
//         this(Matchers.any());
//     }
// 
//     /**
//      * Constructs annotation module with annotation scan limited to provided package.
//      * (used mainly for startup performance optimization)
//      *
//      * @param pkg package to limit beans, where annotations processed
//      */
//     public ExtAnnotationsModule(final String pkg) {
//         this(new ObjectPackageMatcher<Object>(pkg));
//     }
// 
//     /**
//      * Constructs annotation module with custom bean matcher for annotations processing.
//      *
//      * @param typeMatcher matcher to select beans for annotations processing
//      */
//     public ExtAnnotationsModule(final Matcher<Object> typeMatcher) {
//         this.typeMatcher = typeMatcher;
//     }
// 
//     @Override
//     protected void configure() {
//         final DestroyableManager manager = configureManager(new DestroyableManager());
// 
//         bindListener(typeMatcher, new GeneralTypeListener<Destroyable>(
//                 Destroyable.class, new DestroyableTypeProcessor(manager)));
// 
//         bindListener(typeMatcher, new AnnotatedMethodTypeListener<PostConstruct>(
//                 PostConstruct.class, new PostConstructAnnotationProcessor()));
// 
//         bindListener(typeMatcher, new AnnotatedMethodTypeListener<PreDestroy>(
//                 PreDestroy.class, new PreDestroyAnnotationProcessor(manager)));
// 
//         bindListener(typeMatcher, new AnnotatedFieldTypeListener<Log>(
//                 Log.class, new Slf4jLogAnnotationProcessor()));
//     }
// 
// 
//     /**
//      * Registers destroyable manager in injector and adds shutdown hook to process destroy on jvm shutdown.
//      *
//      * @param manager destroyable manager instance
//      * @return manager instance
//      */
//     protected DestroyableManager configureManager(final DestroyableManager manager) {
//         bind(DestroyableManager.class).toInstance(manager);
//         // if logic will not call destroy at least it will be called before jvm shutdown
//         Runtime.getRuntime().addShutdownHook(new Thread(manager));
//         return manager;
//     }
// }
// 
// Path: src/main/java/ru/vyarus/guice/ext/managed/destroyable/Destroyable.java
// public interface Destroyable {
// 
//     /**
//      * Called on context shutdown (by default on jvm shutdown), but may be called manually through destroy manager
//      * {@code ru.vyarus.guice.ext.managed.destroyable.DestroyableManager#destroy()}.
//      * Will be called one time no matter how many time destroy will be asked by manager.
//      * It is safe to avoid explicit exception handling (except special cases required by logic).
//      *
//      * @throws Exception  on any unrecoverable error
//      */
//     void preDestroy() throws Exception;
// }
// 
// Path: src/main/java/ru/vyarus/guice/ext/managed/destroyable/DestroyableManager.java
// public class DestroyableManager implements Runnable {
//     private final Logger logger = LoggerFactory.getLogger(DestroyableManager.class);
// 
//     private final List<Destroyable> destroyListeners = new ArrayList<>();
// 
//     /**
//      * Register destroyable instance to be called on context shutdown.
//      * Not thread safe (assuming single thread injector initialization)
//      *
//      * @param destroyable destroyable instance
//      * @see ru.vyarus.guice.ext.managed.PostConstructAnnotationProcessor regsters annotated methods
//      * @see ru.vyarus.guice.ext.managed.DestroyableTypeProcessor registers beans anootated with {@code Destroyable}
//      */
//     public void register(final Destroyable destroyable) {
//         // assuming single thread injector creation
//         destroyListeners.add(destroyable);
//     }
// 
//     /**
//      * Called on context shutdown to call all registered destroyable instances.
//      * By default called on jvm shutdown, but may be called manually to synchronise with
//      * some other container shutdown (e.g. web container)
//      * Safe to call many times, but all destroy instances will be processed only on first call.
//      * Thread safe.
//      */
//     public void destroy() {
//         // just for the case
//         synchronized (this) {
//             for (Destroyable destroyable : destroyListeners) {
//                 try {
//                     destroyable.preDestroy();
//                 } catch (Exception ex) {
//                     logger.error("Failed to properly destroy bean", ex);
//                 }
//             }
//             destroyListeners.clear();
//         }
//     }
// 
//     @Override
//     public void run() {
//         destroy();
//     }
// }


import com.google.inject.Guice;
import com.google.inject.Injector;
import org.junit.Before;
import org.junit.Test;
import ru.vyarus.guice.ext.ExtAnnotationsModule;
import ru.vyarus.guice.ext.managed.destroyable.Destroyable;
import ru.vyarus.guice.ext.managed.destroyable.DestroyableManager;
import static org.junit.Assert.assertEquals;
package ru.vyarus.guice.ext.predestroy;



/**
 * @author Vyacheslav Rusakov
 * @since 30.06.2014
 */
public class DestroyableTest {
    Injector injector;
    DestroyableManager manager;

    @Before
    public void setUp() throws Exception {
        injector = Guice.createInjector(new ExtAnnotationsModule());
        manager = injector.getInstance(DestroyableManager.class);
    }

    @Test
    public void testCall() throws Exception {
        Bean bean = injector.getInstance(Bean.class);
        manager.destroy();
        assertEquals(1, bean.counter);

    }

    public static class Bean implements Destroyable {
        int counter;

// Path: src/main/java/ru/vyarus/guice/ext/ExtAnnotationsModule.java
// @SuppressWarnings("checkstyle:ClassDataAbstractionCoupling")
// public class ExtAnnotationsModule extends AbstractModule {
// 
//     private Matcher<Object> typeMatcher;
// 
//     /**
//      * Default module constructor to check annotations on all beans.
//      */
//     public ExtAnnotationsModule() {
//         this(Matchers.any());
//     }
// 
//     /**
//      * Constructs annotation module with annotation scan limited to provided package.
//      * (used mainly for startup performance optimization)
//      *
//      * @param pkg package to limit beans, where annotations processed
//      */
//     public ExtAnnotationsModule(final String pkg) {
//         this(new ObjectPackageMatcher<Object>(pkg));
//     }
// 
//     /**
//      * Constructs annotation module with custom bean matcher for annotations processing.
//      *
//      * @param typeMatcher matcher to select beans for annotations processing
//      */
//     public ExtAnnotationsModule(final Matcher<Object> typeMatcher) {
//         this.typeMatcher = typeMatcher;
//     }
// 
//     @Override
//     protected void configure() {
//         final DestroyableManager manager = configureManager(new DestroyableManager());
// 
//         bindListener(typeMatcher, new GeneralTypeListener<Destroyable>(
//                 Destroyable.class, new DestroyableTypeProcessor(manager)));
// 
//         bindListener(typeMatcher, new AnnotatedMethodTypeListener<PostConstruct>(
//                 PostConstruct.class, new PostConstructAnnotationProcessor()));
// 
//         bindListener(typeMatcher, new AnnotatedMethodTypeListener<PreDestroy>(
//                 PreDestroy.class, new PreDestroyAnnotationProcessor(manager)));
// 
//         bindListener(typeMatcher, new AnnotatedFieldTypeListener<Log>(
//                 Log.class, new Slf4jLogAnnotationProcessor()));
//     }
// 
// 
//     /**
//      * Registers destroyable manager in injector and adds shutdown hook to process destroy on jvm shutdown.
//      *
//      * @param manager destroyable manager instance
//      * @return manager instance
//      */
//     protected DestroyableManager configureManager(final DestroyableManager manager) {
//         bind(DestroyableManager.class).toInstance(manager);
//         // if logic will not call destroy at least it will be called before jvm shutdown
//         Runtime.getRuntime().addShutdownHook(new Thread(manager));
//         return manager;
//     }
// }
// 
// Path: src/main/java/ru/vyarus/guice/ext/managed/destroyable/Destroyable.java
// public interface Destroyable {
// 
//     /**
//      * Called on context shutdown (by default on jvm shutdown), but may be called manually through destroy manager
//      * {@code ru.vyarus.guice.ext.managed.destroyable.DestroyableManager#destroy()}.
//      * Will be called one time no matter how many time destroy will be asked by manager.
//      * It is safe to avoid explicit exception handling (except special cases required by logic).
//      *
//      * @throws Exception  on any unrecoverable error
//      */
//     void preDestroy() throws Exception;
// }
// 
// Path: src/main/java/ru/vyarus/guice/ext/managed/destroyable/DestroyableManager.java
// public class DestroyableManager implements Runnable {
//     private final Logger logger = LoggerFactory.getLogger(DestroyableManager.class);
// 
//     private final List<Destroyable> destroyListeners = new ArrayList<>();
// 
//     /**
//      * Register destroyable instance to be called on context shutdown.
//      * Not thread safe (assuming single thread injector initialization)
//      *
//      * @param destroyable destroyable instance
//      * @see ru.vyarus.guice.ext.managed.PostConstructAnnotationProcessor regsters annotated methods
//      * @see ru.vyarus.guice.ext.managed.DestroyableTypeProcessor registers beans anootated with {@code Destroyable}
//      */
//     public void register(final Destroyable destroyable) {
//         // assuming single thread injector creation
//         destroyListeners.add(destroyable);
//     }
// 
//     /**
//      * Called on context shutdown to call all registered destroyable instances.
//      * By default called on jvm shutdown, but may be called manually to synchronise with
//      * some other container shutdown (e.g. web container)
//      * Safe to call many times, but all destroy instances will be processed only on first call.
//      * Thread safe.
//      */
//     public void destroy() {
//         // just for the case
//         synchronized (this) {
//             for (Destroyable destroyable : destroyListeners) {
//                 try {
//                     destroyable.preDestroy();
//                 } catch (Exception ex) {
//                     logger.error("Failed to properly destroy bean", ex);
//                 }
//             }
//             destroyListeners.clear();
//         }
//     }
// 
//     @Override
//     public void run() {
//         destroy();
//     }
// }


// Path: src/test/java/ru/vyarus/guice/ext/predestroy/DestroyableTest.java
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.junit.Before;
import org.junit.Test;
import ru.vyarus.guice.ext.ExtAnnotationsModule;
import ru.vyarus.guice.ext.managed.destroyable.Destroyable;
import ru.vyarus.guice.ext.managed.destroyable.DestroyableManager;
import static org.junit.Assert.assertEquals;

package ru.vyarus.guice.ext.predestroy;



/**
 * @author Vyacheslav Rusakov
 * @since 30.06.2014
 */
public class DestroyableTest {
    Injector injector;
    DestroyableManager manager;

    @Before
    public void setUp() throws Exception {
        injector = Guice.createInjector(new ExtAnnotationsModule());
        manager = injector.getInstance(DestroyableManager.class);
    }

    @Test
    public void testCall() throws Exception {
        Bean bean = injector.getInstance(Bean.class);
        manager.destroy();
        assertEquals(1, bean.counter);

    }

    public static class Bean implements Destroyable {
        int counter;

        @Override