View Javadoc

1   /*
2    * Grapht, an open source dependency injector.
3    * Copyright 2014-2015 various contributors (see CONTRIBUTORS.txt)
4    * Copyright 2010-2014 Regents of the University of Minnesota
5    *
6    * This program is free software; you can redistribute it and/or modify
7    * it under the terms of the GNU Lesser General Public License as
8    * published by the Free Software Foundation; either version 2.1 of the
9    * License, or (at your option) any later version.
10   *
11   * This program is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13   * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14   * details.
15   *
16   * You should have received a copy of the GNU General Public License along with
17   * this program; if not, write to the Free Software Foundation, Inc., 51
18   * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19   */
20  package org.grouplens.grapht;
21  
22  import com.google.common.base.Throwables;
23  import com.google.common.util.concurrent.UncheckedExecutionException;
24  import org.apache.commons.lang3.reflect.MethodUtils;
25  import org.slf4j.Logger;
26  import org.slf4j.LoggerFactory;
27  
28  import javax.annotation.PreDestroy;
29  import java.lang.reflect.InvocationTargetException;
30  import java.lang.reflect.Method;
31  import java.util.Deque;
32  import java.util.LinkedList;
33  
34  public class LifecycleManager implements AutoCloseable {
35      private static final Logger logger = LoggerFactory.getLogger(LifecycleManager.class);
36      private Deque<TeardownAction> actions = new LinkedList<TeardownAction>();
37  
38      /**
39       * Register a component with the lifecycle manager.  The component will be torn down when the lifecycle manager
40       * is closed, using whatever teardown the lifecycle manager institutes.
41       *
42       * @param instance The component to register.
43       */
44      public void registerComponent(Object instance) {
45          if (instance == null) {
46              return;
47          }
48  
49          if (instance instanceof AutoCloseable) {
50              actions.add(new CloseAction((AutoCloseable) instance));
51          }
52          for (Method m: MethodUtils.getMethodsListWithAnnotation(instance.getClass(), PreDestroy.class)) {
53              actions.add(new PreDestroyAction(instance, m));
54          }
55      }
56  
57      /**
58       * Close the lifecycle manager, shutting down all components it manages.
59       */
60      @Override
61      public void close() {
62          Throwable error = null;
63          while (!actions.isEmpty()) {
64              TeardownAction action = actions.removeFirst();
65              try {
66                  action.destroy();
67              } catch (Throwable th) {
68                  if (error == null) {
69                      error = th;
70                  } else {
71                      error.addSuppressed(th);
72                  }
73              }
74          }
75          if (error != null) {
76              throw Throwables.propagate(error);
77          }
78      }
79  
80      /**
81       * Interface for actions that tear down components.
82       */
83      interface TeardownAction {
84          void destroy();
85      }
86  
87      static class PreDestroyAction implements TeardownAction {
88          private final Object instance;
89          private final Method method;
90  
91          public PreDestroyAction(Object inst, Method m) {
92              instance = inst;
93              method = m;
94          }
95  
96          @Override
97          public void destroy() {
98              try {
99                  logger.debug("invoking pre-destroy method {} on {}", method, instance);
100                 method.invoke(instance);
101             } catch (IllegalAccessException e) {
102                 throw new RuntimeException("cannot access " + method, e);
103             } catch (InvocationTargetException e) {
104                 throw new UncheckedExecutionException("error invoking " + method, e);
105             }
106         }
107     }
108 
109     static class CloseAction implements TeardownAction {
110         private final AutoCloseable instance;
111 
112         public CloseAction(AutoCloseable inst) {
113             instance = inst;
114         }
115 
116         @Override
117         public void destroy() {
118             try {
119                 logger.debug("closing {}", instance);
120                 instance.close();
121             } catch (Exception e) {
122                 throw new UncheckedExecutionException("Error destroying " + instance, e);
123             }
124         }
125     }
126 }