PageRenderTime 4388ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/modules/org.restlet.ext.guice/src/main/java/org/restlet/ext/guice/RestletGuice.java

http://github.com/restlet/restlet-framework-java
Java | 330 lines | 167 code | 42 blank | 121 comment | 16 complexity | c636beb3ebd714e4e781848ee911576d MD5 | raw file
Possible License(s): Apache-2.0, GPL-2.0, CPL-1.0, LGPL-2.1
  1. /**
  2. * Copyright 2005-2020 Talend
  3. *
  4. * The contents of this file are subject to the terms of one of the following
  5. * open source licenses: Apache 2.0 or or EPL 1.0 (the "Licenses"). You can
  6. * select the license that you prefer but you may not use this file except in
  7. * compliance with one of these Licenses.
  8. *
  9. * You can obtain a copy of the Apache 2.0 license at
  10. * http://www.opensource.org/licenses/apache-2.0
  11. *
  12. * You can obtain a copy of the EPL 1.0 license at
  13. * http://www.opensource.org/licenses/eclipse-1.0
  14. *
  15. * See the Licenses for the specific language governing permissions and
  16. * limitations under the Licenses.
  17. *
  18. * Alternatively, you can obtain a royalty free commercial license with less
  19. * limitations, transferable or non-transferable, directly at
  20. * https://restlet.talend.com/
  21. *
  22. * Restlet is a registered trademark of Talend S.A.
  23. */
  24. package org.restlet.ext.guice;
  25. import static java.util.Arrays.asList;
  26. import java.lang.annotation.Annotation;
  27. import java.lang.reflect.Type;
  28. import org.restlet.Application;
  29. import org.restlet.Context;
  30. import org.restlet.Request;
  31. import org.restlet.Response;
  32. import org.restlet.resource.Finder;
  33. import org.restlet.resource.ServerResource;
  34. import com.google.inject.AbstractModule;
  35. import com.google.inject.Guice;
  36. import com.google.inject.Inject;
  37. import com.google.inject.Injector;
  38. import com.google.inject.Key;
  39. import com.google.inject.Provider;
  40. import com.google.inject.ProvisionException;
  41. import com.google.inject.Stage;
  42. /**
  43. * Guice dependency injection for Restlet.
  44. *
  45. * @author Tim Peierls
  46. */
  47. public class RestletGuice {
  48. /**
  49. * A Guice module that implements {@link FinderFactory}. On first use of the
  50. * methods of this facility, if the module hasn't been used to create an
  51. * {@link Injector}, this module creates its own Injector.
  52. */
  53. public static class Module extends AbstractModule implements FinderFactory {
  54. class KeyFinder extends Finder {
  55. private final Class<?> targetClass;
  56. KeyFinder(Type type) {
  57. this.targetClass = (Class<?>) type;
  58. }
  59. @Override
  60. public final Context getContext() {
  61. return getInjector().getInstance(Context.class);
  62. }
  63. protected final Injector getInjector() {
  64. Injector inj = injector;
  65. if (inj == null) {
  66. synchronized (RestletGuice.Module.this) {
  67. inj = injector;
  68. if (inj == null) {
  69. injector = inj = Guice
  70. .createInjector(RestletGuice.Module.this);
  71. }
  72. }
  73. }
  74. return inj;
  75. }
  76. public final Class<? extends ServerResource> getTargetClass() {
  77. // If the key type is a subtype of ServerResource, return it.
  78. Class<ServerResource> src = ServerResource.class;
  79. if (src != null && targetClass != null
  80. && src.isAssignableFrom(targetClass)) {
  81. @SuppressWarnings("unchecked")
  82. Class<? extends ServerResource> result = (Class<? extends ServerResource>) targetClass;
  83. return result;
  84. }
  85. // Otherwise, we can't in general determine the true target
  86. // type, so we revert to the superclass implementation.
  87. // Since we used the no-arg Finder constructor, it will return
  88. // null unless someone has explicitly set a target class. This
  89. // is only relevant to the use of the Router.detach(Class<?>
  90. // targetClass) method; it implies that we cannot detach routes
  91. // that target dependency-injected resources attached as
  92. // non-ServerResource types without explicitly setting a target
  93. // class type. This seems like a *very* minor restriction.
  94. return super.getTargetClass();
  95. }
  96. }
  97. class ServerResourceKeyFinder extends KeyFinder {
  98. private final Key<?> serverResourceKey;
  99. ServerResourceKeyFinder(Key<?> serverResourceKey) {
  100. super(serverResourceKey.getTypeLiteral().getType());
  101. this.serverResourceKey = serverResourceKey;
  102. }
  103. @Override
  104. public ServerResource create(Request request, Response response) {
  105. try {
  106. return ServerResource.class.cast(getInjector().getInstance(
  107. serverResourceKey));
  108. } catch (ClassCastException ex) {
  109. String msg = String.format(
  110. "Must bind %s to ServerResource (or subclass)",
  111. serverResourceKey);
  112. throw new ProvisionException(msg, ex);
  113. }
  114. }
  115. }
  116. //
  117. // FinderFactory methods
  118. //
  119. private static ThreadLocal<Boolean> alreadyBound = new ThreadLocal<Boolean>() {
  120. @Override
  121. protected Boolean initialValue() {
  122. return false;
  123. }
  124. };
  125. @Inject
  126. private volatile Injector injector;
  127. private final Iterable<? extends com.google.inject.Module> modules;
  128. /**
  129. * Creates a RestletGuice.Module that will install the given modules.
  130. */
  131. public Module(com.google.inject.Module... modules) {
  132. this.modules = asList(modules);
  133. }
  134. /**
  135. * Creates a RestletGuice.Module that will install the given modules.
  136. */
  137. public Module(Iterable<? extends com.google.inject.Module> modules) {
  138. this.modules = modules;
  139. }
  140. /**
  141. * If this module is used in more than one injector, we clear the
  142. * thread-local boolean that prevents binding more than once in the same
  143. * thread.
  144. */
  145. @Inject
  146. private void clearAlreadyBound() {
  147. alreadyBound.set(false);
  148. }
  149. @Override
  150. protected final void configure() {
  151. if (injector != null) {
  152. throw new IllegalStateException(
  153. "can't reconfigure with existing Injector");
  154. }
  155. if (!alreadyBound.get()) {
  156. alreadyBound.set(true);
  157. bind(FinderFactory.class).toInstance(this);
  158. bind(Application.class).toProvider(newApplicationProvider());
  159. bind(Context.class).toProvider(newContextProvider());
  160. bind(Request.class).toProvider(newRequestProvider());
  161. bind(Response.class).toProvider(newResponseProvider());
  162. }
  163. for (com.google.inject.Module module : modules) {
  164. install(module);
  165. }
  166. }
  167. public Finder finder(Class<?> cls) {
  168. return new ServerResourceKeyFinder(Key.get(cls));
  169. }
  170. public Finder finder(Class<?> cls, Class<? extends Annotation> qualifier) {
  171. return new ServerResourceKeyFinder(Key.get(cls, qualifier));
  172. }
  173. /**
  174. * Creates a {@link Provider}r for the current {@link Application}.
  175. * Override to use a custom Application provider.
  176. *
  177. * @return A {@link Provider} for the current {@link Application}.
  178. */
  179. protected Provider<Application> newApplicationProvider() {
  180. return new Provider<Application>() {
  181. public Application get() {
  182. return Application.getCurrent();
  183. }
  184. };
  185. }
  186. /**
  187. * Creates a {@link Provider} for the current {@link Context}. Override
  188. * to use a custom Context provider.
  189. *
  190. * @return A {@link Provider} for the current {@link Context}.
  191. */
  192. protected Provider<Context> newContextProvider() {
  193. return new Provider<Context>() {
  194. public Context get() {
  195. return Context.getCurrent();
  196. }
  197. };
  198. }
  199. /**
  200. * Creates a {@link Provider} for the current {@link Request}. Override
  201. * to use a custom Request provider.
  202. *
  203. * @return A {@link Provider} for the current {@link Request}.
  204. */
  205. protected Provider<Request> newRequestProvider() {
  206. return new Provider<Request>() {
  207. public Request get() {
  208. return Request.getCurrent();
  209. }
  210. };
  211. }
  212. /**
  213. * Creates a {@link Provider} for the current {@link Response}. Override
  214. * to use a custom Response provider.
  215. *
  216. * @return A {@link Provider} for the current {@link Response}.
  217. */
  218. protected Provider<Response> newResponseProvider() {
  219. return new Provider<Response>() {
  220. public Response get() {
  221. return Response.getCurrent();
  222. }
  223. };
  224. }
  225. }
  226. /**
  227. * Creates an instance of {@link Injector} from the given modules with
  228. * {@link FinderFactory} bound to an implementation that uses the injector's
  229. * bindings to create Finder instances.
  230. *
  231. * @param modules
  232. * The list of modules.
  233. * @return The injector for the list of modules.
  234. */
  235. public static Injector createInjector(com.google.inject.Module... modules) {
  236. return injectorFor(null, new Module(modules));
  237. }
  238. /**
  239. * Creates an instance of {@link Injector} from the given modules with
  240. * {@link FinderFactory} bound to an implementation that uses the injector's
  241. * bindings to create Finder instances.
  242. *
  243. * @param modules
  244. * The collection of modules.
  245. * @return The injector for the list of modules.
  246. */
  247. public static Injector createInjector(
  248. Iterable<com.google.inject.Module> modules) {
  249. return injectorFor(null, new Module(modules));
  250. }
  251. /**
  252. * Creates an instance of {@link Injector} in the given {@link Stage} from
  253. * the given modules with {@link FinderFactory} bound to an implementation
  254. * that uses the injector's bindings to create {@link Finder} instances.
  255. *
  256. * @param stage
  257. * The {@link Stage}.
  258. * @param modules
  259. * The list of modules.
  260. * @return The injector for the list of modules in the given stage.
  261. */
  262. public static Injector createInjector(Stage stage,
  263. com.google.inject.Module... modules) {
  264. return injectorFor(stage, new Module(modules));
  265. }
  266. /**
  267. * Creates an instance of {@link Injector} in the given {@link Stage} from
  268. * the given modules with {@link FinderFactory} bound to an implementation
  269. * that uses the injector's bindings to create {@link Finder} instances.
  270. *
  271. * @param stage
  272. * The {@link Stage}.
  273. * @param modules
  274. * The list of modules.
  275. * @return The injector for the list of modules in the given stage.
  276. */
  277. public static Injector createInjector(Stage stage,
  278. Iterable<com.google.inject.Module> modules) {
  279. return injectorFor(stage, new Module(modules));
  280. }
  281. private static Injector injectorFor(Stage stage, Module rootModule) {
  282. if (stage == null) {
  283. return Guice.createInjector(rootModule);
  284. } else {
  285. return Guice.createInjector(stage, rootModule);
  286. }
  287. }
  288. }