PageRenderTime 460ms CodeModel.GetById 41ms RepoModel.GetById 2ms app.codeStats 0ms

/extensions/eclipse/src/main/java/org/ops4j/peaberry/eclipse/GuiceExtensionFactory.java

https://github.com/sonatype/sonatype-peaberry
Java | 204 lines | 91 code | 27 blank | 86 comment | 10 complexity | d61bf6dd31db173df47dd3c73453cbe5 MD5 | raw file
  1. /**
  2. * Copyright (C) 2009 Stuart McCulloch
  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 org.ops4j.peaberry.eclipse;
  17. import static com.google.inject.Guice.createInjector;
  18. import static org.eclipse.core.runtime.ContributorFactoryOSGi.resolve;
  19. import static org.eclipse.core.runtime.RegistryFactory.getRegistry;
  20. import static org.ops4j.peaberry.Peaberry.osgiModule;
  21. import static org.ops4j.peaberry.eclipse.EclipseRegistry.eclipseRegistry;
  22. import java.util.ArrayList;
  23. import java.util.HashMap;
  24. import java.util.List;
  25. import java.util.Map;
  26. import java.util.logging.Logger;
  27. import org.eclipse.core.runtime.CoreException;
  28. import org.eclipse.core.runtime.IConfigurationElement;
  29. import org.eclipse.core.runtime.IContributor;
  30. import org.eclipse.core.runtime.IExecutableExtension;
  31. import org.eclipse.core.runtime.IExecutableExtensionFactory;
  32. import org.eclipse.core.runtime.IStatus;
  33. import org.eclipse.core.runtime.InvalidRegistryObjectException;
  34. import org.eclipse.core.runtime.Status;
  35. import com.google.inject.Injector;
  36. import com.google.inject.Module;
  37. /**
  38. * {@link IExecutableExtensionFactory} that creates an injected extension based
  39. * on the class named in the adapter data section. If the data section is empty
  40. * the class is assumed to be in the {@code id} attribute. The factory searches
  41. * the {@code org.ops4j.peaberry.eclipse.modules} extension for {@link Module}s
  42. * belonging to the same plug-in and uses them to create a new {@link Injector}.
  43. * This injector creates the injected extension and is then cached so it can be
  44. * re-used for other extensions in the same plug-in.
  45. * <p>
  46. * To use the factory put it in front of your class name in the extension XML.
  47. * Or replace your class with the factory and put your class in the {@code id}
  48. * attribute instead. Because the implementation will be injected based on the
  49. * bindings you could even replace your class name with one of its interfaces,
  50. * and that interface will then be used to lookup the correct implementation.
  51. * <p>
  52. * Here's a more detailed walkthrough, based on the example RCP Mail Template:
  53. *
  54. * <pre> {@literal <}extension point="org.eclipse.ui.views"{@literal >}
  55. * {@literal <}view name="Message"
  56. * allowMultiple="true"
  57. * icon="icons/sample2.gif"
  58. * class="example.ViewImpl"
  59. * id="example.view" /{@literal >}
  60. * {@literal <}/extension{@literal >}</pre>
  61. * becomes:
  62. *
  63. * <pre> {@literal <}extension point="org.eclipse.ui.views"{@literal >}
  64. * {@literal <}view name="Message"
  65. * allowMultiple="true"
  66. * icon="icons/sample2.gif"
  67. * class="org.ops4j.peaberry.eclipse.GuiceExtensionFactory:example.ViewImpl"
  68. * id="example.view" /{@literal >}
  69. * {@literal <}/extension{@literal >}
  70. * {@literal <}extension point="org.ops4j.peaberry.eclipse.modules"{@literal >}
  71. * {@literal <}module class="example.ViewModule" /{@literal >}
  72. * {@literal <}/extension{@literal >}</pre>
  73. * Here's the same example with the class in the {@code id} attribute:
  74. *
  75. * <pre> {@literal <}extension point="org.eclipse.ui.views"{@literal >}
  76. * {@literal <}view name="Message"
  77. * allowMultiple="true"
  78. * icon="icons/sample2.gif"
  79. * class="org.ops4j.peaberry.eclipse.GuiceExtensionFactory"
  80. * id="example.ViewImpl" /{@literal >}
  81. * {@literal <}/extension{@literal >}
  82. * {@literal <}extension point="org.ops4j.peaberry.eclipse.modules"{@literal >}
  83. * {@literal <}module class="example.ViewModule" /{@literal >}
  84. * {@literal <}/extension{@literal >}</pre>
  85. * and again, this time using an interface instead of the implementation:
  86. *
  87. * <pre> {@literal <}extension point="org.eclipse.ui.views"{@literal >}
  88. * {@literal <}view name="Message"
  89. * allowMultiple="true"
  90. * icon="icons/sample2.gif"
  91. * class="org.ops4j.peaberry.eclipse.GuiceExtensionFactory:org.eclipse.ui.IViewPart"
  92. * id="example.view" /{@literal >}
  93. * {@literal <}/extension{@literal >}
  94. * {@literal <}extension point="org.ops4j.peaberry.eclipse.modules"{@literal >}
  95. * {@literal <}module class="example.ViewModule" /{@literal >}
  96. * {@literal <}/extension{@literal >}</pre>
  97. *
  98. * @author mcculls@gmail.com (Stuart McCulloch)
  99. */
  100. public final class GuiceExtensionFactory
  101. implements IExecutableExtension, IExecutableExtensionFactory {
  102. public static final String POINT_ID = "org.ops4j.peaberry.eclipse.modules";
  103. private static final Logger LOGGER = Logger.getLogger(GuiceExtensionFactory.class.getName());
  104. // re-use injectors for extensions with the same contributor
  105. private static final Map<IContributor, Injector> INJECTORS =
  106. new HashMap<IContributor, Injector>();
  107. private IConfigurationElement configuration;
  108. private IContributor contributor;
  109. private String clazzName;
  110. public void setInitializationData(final IConfigurationElement config, final String name,
  111. final Object data) {
  112. configuration = config;
  113. // find contributor while still valid
  114. contributor = config.getContributor();
  115. // if there's no (string-based) adapter data then class must be under "id"
  116. clazzName = data instanceof String ? (String) data : config.getAttribute("id");
  117. }
  118. public Object create()
  119. throws CoreException {
  120. if (null == clazzName) {
  121. throw newCoreException("Configuration is missing class information");
  122. }
  123. final Class<?> clazz;
  124. try {
  125. clazz = resolve(contributor).loadClass(clazzName);
  126. } catch (final InvalidRegistryObjectException e) {
  127. throw newCoreException(e);
  128. } catch (final ClassNotFoundException e) {
  129. throw newCoreException(e);
  130. }
  131. final Object o = getInjector().getInstance(clazz);
  132. if (o instanceof IExecutableExtension) {
  133. ((IExecutableExtension) o).setInitializationData(configuration, null, null);
  134. }
  135. return o;
  136. }
  137. /**
  138. * Remove any {@link Injector}s belonging to uninstalled bundles.
  139. */
  140. public static void cleanup() {
  141. synchronized (INJECTORS) { // LOCK
  142. for (final IContributor c : INJECTORS.keySet()) {
  143. if (null == resolve(c)) {
  144. INJECTORS.remove(c);
  145. }
  146. }
  147. } // UNLOCK
  148. }
  149. private Injector getInjector()
  150. throws CoreException {
  151. synchronized (INJECTORS) { // LOCK
  152. Injector injector = INJECTORS.get(contributor);
  153. if (null == injector) {
  154. final List<Module> modules = new ArrayList<Module>();
  155. // first add the OSGi service registry and Eclipse extension bindings
  156. modules.add(osgiModule(resolve(contributor).getBundleContext(), eclipseRegistry()));
  157. // now add any module extensions contributed by the current plug-in
  158. for (final IConfigurationElement e : getRegistry().getConfigurationElementsFor(POINT_ID)) {
  159. if (contributor.equals(e.getContributor())) {
  160. modules.add((Module) e.createExecutableExtension("class"));
  161. }
  162. }
  163. injector = createInjector(modules);
  164. INJECTORS.put(contributor, injector);
  165. }
  166. return injector;
  167. } // UNLOCK
  168. }
  169. private CoreException newCoreException(final Throwable e) {
  170. LOGGER.warning(e.getMessage());
  171. return new CoreException(new Status(IStatus.ERROR, contributor.getName(), e.getMessage(), e));
  172. }
  173. private CoreException newCoreException(final String message) {
  174. LOGGER.warning(message);
  175. return new CoreException(new Status(IStatus.ERROR, contributor.getName(), message));
  176. }
  177. }