PageRenderTime 55ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/mycila-plugin/trunk/src/main/java/com/mycila/guice/spi/DefaultPluginManager.java

http://mycila.googlecode.com/
Java | 199 lines | 164 code | 17 blank | 18 comment | 30 complexity | 4a35bcab3ce6f249782f8b22f862e05d MD5 | raw file
Possible License(s): Apache-2.0
  1. /**
  2. * Copyright (C) 2010 Mathieu Carbou <mathieu.carbou@gmail.com>
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.mycila.guice.spi;
  17. import com.google.inject.Binder;
  18. import com.google.inject.Guice;
  19. import com.google.inject.Injector;
  20. import com.google.inject.Key;
  21. import com.google.inject.Module;
  22. import com.google.inject.Singleton;
  23. import com.google.inject.Stage;
  24. import com.mycila.guice.CyclicPluginDependencyException;
  25. import com.mycila.guice.InvokeException;
  26. import com.mycila.guice.PluginManager;
  27. import com.mycila.guice.annotation.ActivateAfter;
  28. import com.mycila.guice.annotation.ActivateBefore;
  29. import com.mycila.guice.annotation.OnActivate;
  30. import com.mycila.guice.annotation.OnClose;
  31. import com.mycila.guice.annotation.Plugin;
  32. import org.jgrapht.DirectedGraph;
  33. import org.jgrapht.alg.CycleDetector;
  34. import org.jgrapht.graph.DefaultDirectedWeightedGraph;
  35. import org.jgrapht.graph.DefaultEdge;
  36. import org.jgrapht.traverse.TopologicalOrderIterator;
  37. import java.lang.reflect.InvocationTargetException;
  38. import java.lang.reflect.Method;
  39. import java.util.ArrayList;
  40. import java.util.Arrays;
  41. import java.util.Iterator;
  42. import java.util.LinkedList;
  43. import java.util.List;
  44. import java.util.Set;
  45. import java.util.TreeSet;
  46. /**
  47. * @author Mathieu Carbou (mathieu.carbou@gmail.com)
  48. */
  49. public final class DefaultPluginManager implements PluginManager {
  50. private final Injector injector;
  51. private final List<Invokable<?>> activates = new LinkedList<Invokable<?>>();
  52. private final List<Invokable<?>> deactivates = new LinkedList<Invokable<?>>();
  53. private DefaultPluginManager(Injector injector) {
  54. this.injector = injector;
  55. }
  56. @Override
  57. public void activate() throws InvokeException {
  58. for (Invokable<?> invokable : activates)
  59. invokable.invoke();
  60. }
  61. @Override
  62. public void deactivate() throws InvokeException {
  63. for (Invokable<?> invokable : deactivates)
  64. invokable.invoke();
  65. }
  66. @Override
  67. public Injector getInjector() {
  68. return injector;
  69. }
  70. public static PluginManager build(Iterable<? extends Class<?>> pluginClasses, Module... modules) throws CyclicPluginDependencyException {
  71. return build(pluginClasses, Arrays.asList(modules));
  72. }
  73. public static PluginManager build(Iterable<? extends Class<?>> pluginClasses, Iterable<? extends Module> modules) throws CyclicPluginDependencyException {
  74. final List<Key<?>> plugins = new LinkedList<Key<?>>();
  75. final Injector injector = load(pluginClasses, plugins, modules);
  76. final DefaultPluginManager pluginManager = new DefaultPluginManager(injector);
  77. final List<Key<?>> order = sort(plugins);
  78. for (final Key<?> key : order) {
  79. Class<?> base = key.getTypeLiteral().getRawType();
  80. while (base != null && base != Object.class) {
  81. for (final Method method : base.getDeclaredMethods()) {
  82. if (method.isAnnotationPresent(OnActivate.class)) {
  83. if (method.getParameterTypes().length != 0)
  84. throw new UnsupportedOperationException("@OnActivate must be put on a method without parameters : " + method);
  85. pluginManager.activates.add(new InvokableMethod(injector, key, method));
  86. }
  87. if (method.isAnnotationPresent(OnClose.class)) {
  88. if (method.getParameterTypes().length != 0)
  89. throw new UnsupportedOperationException("@OnClose must be put on a method without parameters : " + method);
  90. pluginManager.deactivates.add(0, new InvokableMethod(injector, key, method));
  91. }
  92. }
  93. base = base.getSuperclass();
  94. }
  95. }
  96. return pluginManager;
  97. }
  98. private static Key<?> getKey(Class<?> pluginClass) {
  99. return Key.get(pluginClass, Plugin.class);
  100. }
  101. private static Injector load(final Iterable<? extends Class<?>> pluginClasses, final List<Key<?>> plugins, Iterable<? extends Module> modules) {
  102. List<Module> mm = new ArrayList<Module>();
  103. for (Module module : modules)
  104. mm.add(module);
  105. mm.add(new Module() {
  106. @Override
  107. public void configure(Binder binder) {
  108. for (Class pluginClass : pluginClasses) {
  109. Key<?> key = getKey(pluginClass);
  110. plugins.add(key);
  111. binder.bind(key).to(pluginClass).in(Singleton.class);
  112. }
  113. }
  114. });
  115. return Guice.createInjector(Stage.PRODUCTION, mm);
  116. }
  117. private static List<Key<?>> sort(List<Key<?>> plugins) {
  118. final List<Key<?>> order = new LinkedList<Key<?>>();
  119. final DirectedGraph<Key<?>, DefaultEdge> graph = new DefaultDirectedWeightedGraph<Key<?>, DefaultEdge>(DefaultEdge.class);
  120. for (Key<?> pluginKey : plugins) {
  121. graph.addVertex(pluginKey);
  122. {
  123. ActivateAfter activateAfter = pluginKey.getTypeLiteral().getRawType().getAnnotation(ActivateAfter.class);
  124. if (activateAfter != null) {
  125. for (Class<?> pluginToBeActivatedBefore : activateAfter.value()) {
  126. Key<?> key = getKey(pluginToBeActivatedBefore);
  127. if (plugins.contains(key)) {
  128. graph.addVertex(key);
  129. graph.addEdge(key, pluginKey);
  130. }
  131. }
  132. }
  133. }
  134. {
  135. ActivateBefore activateBefore = pluginKey.getTypeLiteral().getRawType().getAnnotation(ActivateBefore.class);
  136. if (activateBefore != null) {
  137. for (Class<?> pluginToBeActivatedAfter : activateBefore.value()) {
  138. Key<?> key = getKey(pluginToBeActivatedAfter);
  139. if (plugins.contains(key)) {
  140. graph.addVertex(key);
  141. graph.addEdge(pluginKey, key);
  142. }
  143. }
  144. }
  145. }
  146. }
  147. if (!graph.vertexSet().isEmpty()) {
  148. CycleDetector<Key<?>, DefaultEdge> detector = new CycleDetector<Key<?>, DefaultEdge>(graph);
  149. if (detector.detectCycles()) {
  150. Set<String> names = new TreeSet<String>();
  151. for (Key<?> key : detector.findCycles())
  152. names.add(key.getTypeLiteral().getRawType().getName());
  153. throw new CyclicPluginDependencyException(names);
  154. }
  155. for (Iterator<Key<?>> c = new TopologicalOrderIterator<Key<?>, DefaultEdge>(graph); c.hasNext();)
  156. order.add(c.next());
  157. }
  158. return order;
  159. }
  160. private static final class InvokableMethod implements Invokable {
  161. private final Injector injector;
  162. private final Key<?> key;
  163. private final Method method;
  164. private InvokableMethod(Injector injector, Key<?> key, Method method) {
  165. this.injector = injector;
  166. this.key = key;
  167. this.method = method;
  168. }
  169. @Override
  170. public Object invoke(Object... args) throws InvokeException {
  171. try {
  172. if (!method.isAccessible())
  173. method.setAccessible(true);
  174. return method.invoke(injector.getInstance(key), args);
  175. } catch (IllegalAccessException e) {
  176. throw new InvokeException(e);
  177. } catch (InvocationTargetException e) {
  178. throw new InvokeException(e.getTargetException());
  179. }
  180. }
  181. }
  182. }