001/*****************************************************************************
002 * Copyright (C) PicoContainer Organization. All rights reserved.            *
003 * ------------------------------------------------------------------------- *
004 * The software in this package is published under the terms of the BSD      *
005 * style license a copy of which has been included with this distribution in *
006 * the LICENSE.txt file.                                                     *
007 *****************************************************************************/
008package org.picocontainer.lifecycle;
009
010import org.picocontainer.ComponentMonitor;
011import org.picocontainer.PicoLifecycleException;
012
013import javax.annotation.PostConstruct;
014import javax.annotation.PreDestroy;
015import java.lang.annotation.Annotation;
016import java.lang.reflect.InvocationTargetException;
017import java.lang.reflect.Method;
018import java.util.HashSet;
019import java.util.Set;
020
021/**
022 * Java EE 5 has some annotations PreDestroy and PostConstruct that map to start() and dispose() in our world
023 *
024 * @author Paul Hammant
025 */
026@SuppressWarnings("serial")
027public final class JavaEE5LifecycleStrategy extends AbstractMonitoringLifecycleStrategy {
028
029    /**
030     * Construct a JavaEE5LifecycleStrategy.
031     *
032     * @param monitor the monitor to use
033     * @throws NullPointerException if the monitor is <code>null</code>
034     */
035    public JavaEE5LifecycleStrategy(final ComponentMonitor monitor) {
036        super(monitor);
037    }
038
039    /** {@inheritDoc} **/
040    public void start(final Object component) {
041        doLifecycleMethod(component, PostConstruct.class, true);
042    }
043
044        /** {@inheritDoc} **/
045    public void stop(final Object component) {
046    }
047
048    /** {@inheritDoc} **/
049    public void dispose(final Object component) {
050        doLifecycleMethod(component, PreDestroy.class, false);
051    }
052
053    private void doLifecycleMethod(final Object component, Class<? extends Annotation> annotation, boolean superFirst) {
054        doLifecycleMethod(component, annotation, component.getClass(), superFirst, new HashSet<String>());
055    }
056
057    private void doLifecycleMethod(Object component, Class<? extends Annotation> annotation, Class<? extends Object> clazz, boolean superFirst, Set<String> doneAlready) {
058        Class<?> parent = clazz.getSuperclass();
059        if (superFirst && parent != Object.class) {
060            doLifecycleMethod(component, annotation, parent, superFirst, doneAlready);
061        }
062        Method[] methods = clazz.getDeclaredMethods();
063        for (Method method : methods) {
064            String signature = signature(method);
065            if (method.isAnnotationPresent(annotation) && !doneAlready.contains(signature)) {
066                try {
067                    long str = System.currentTimeMillis();
068                    currentMonitor().invoking(null, null, method, component, new Object[0]);
069                    method.invoke(component);
070                    doneAlready.add(signature);
071                    currentMonitor().invoked(null, null, method, component, System.currentTimeMillis() - str, new Object[0], null);
072                } catch (IllegalAccessException e) {
073                    throw new PicoLifecycleException(method, component, e);
074                } catch (InvocationTargetException e) {
075                    throw new PicoLifecycleException(method, component, e);
076                }
077            }
078        }
079        if (!superFirst && parent != Object.class) {
080            doLifecycleMethod(component, annotation, parent, superFirst, doneAlready);
081        }
082    }
083
084    private static String signature(Method method) {
085        StringBuilder sb = new StringBuilder(method.getName());
086        Class<?>[] pt = method.getParameterTypes();
087        for (Class<?> objectClass : pt) {
088            sb.append(objectClass.getName());
089        }
090        return sb.toString();
091    }
092
093
094    /**
095     * {@inheritDoc} The component has a lifecycle PreDestroy or PostConstruct are on a method
096     */
097    public boolean hasLifecycle(final Class<?> type) {
098        Method[] methods = type.getDeclaredMethods();
099        for (int i = 0; i < methods.length; i++) {
100            Method method = methods[i];
101            if (method.isAnnotationPresent(PreDestroy.class) || method.isAnnotationPresent(PostConstruct.class)) {
102                return true;
103            }
104        }
105        return false;
106    }
107
108}