AmbariJpaPersistModule.java 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. package com.google.inject.persist.jpa;
  19. import com.google.common.collect.Lists;
  20. import com.google.inject.Inject;
  21. import com.google.inject.Singleton;
  22. import com.google.inject.persist.PersistModule;
  23. import com.google.inject.persist.PersistService;
  24. import com.google.inject.persist.UnitOfWork;
  25. import com.google.inject.persist.finder.DynamicFinder;
  26. import com.google.inject.persist.finder.Finder;
  27. import com.google.inject.util.Providers;
  28. import org.aopalliance.intercept.MethodInterceptor;
  29. import org.aopalliance.intercept.MethodInvocation;
  30. import org.apache.ambari.server.orm.AmbariJpaLocalTxnInterceptor;
  31. import org.apache.ambari.server.orm.AmbariLocalSessionInterceptor;
  32. import org.apache.ambari.server.orm.RequiresSession;
  33. import static com.google.inject.matcher.Matchers.annotatedWith;
  34. import static com.google.inject.matcher.Matchers.any;
  35. import javax.persistence.EntityManager;
  36. import javax.persistence.EntityManagerFactory;
  37. import java.lang.reflect.AccessibleObject;
  38. import java.lang.reflect.InvocationHandler;
  39. import java.lang.reflect.Method;
  40. import java.lang.reflect.Proxy;
  41. import java.util.List;
  42. import java.util.Properties;
  43. /**
  44. * Copy of guice persist module for local modifications
  45. */
  46. public class AmbariJpaPersistModule extends PersistModule {
  47. private final String jpaUnit;
  48. public AmbariJpaPersistModule(String jpaUnit) {
  49. if (null != jpaUnit && jpaUnit.length() > 0) {
  50. this.jpaUnit = jpaUnit;
  51. } else {
  52. throw new IllegalArgumentException("jpaUnit should not be null");
  53. }
  54. }
  55. private Properties properties;
  56. private MethodInterceptor transactionInterceptor;
  57. private MethodInterceptor sessionInterceptor;
  58. @Override protected void configurePersistence() {
  59. bindConstant().annotatedWith(Jpa.class).to(jpaUnit);
  60. if (null != properties) {
  61. bind(Properties.class).annotatedWith(Jpa.class).toInstance(properties);
  62. } else {
  63. bind(Properties.class).annotatedWith(Jpa.class)
  64. .toProvider(Providers.<Properties>of(null));
  65. }
  66. bind(AmbariJpaPersistService.class).in(Singleton.class);
  67. bind(PersistService.class).to(AmbariJpaPersistService.class);
  68. bind(UnitOfWork.class).to(AmbariJpaPersistService.class);
  69. bind(EntityManager.class).toProvider(AmbariJpaPersistService.class);
  70. bind(EntityManagerFactory.class)
  71. .toProvider(JpaPersistService.EntityManagerFactoryProvider.class);
  72. transactionInterceptor = new AmbariJpaLocalTxnInterceptor();
  73. requestInjection(transactionInterceptor);
  74. sessionInterceptor = new AmbariLocalSessionInterceptor();
  75. requestInjection(sessionInterceptor);
  76. // Bind dynamic finders.
  77. for (Class<?> finder : dynamicFinders) {
  78. bindFinder(finder);
  79. }
  80. bindInterceptor(annotatedWith(RequiresSession.class), any(), sessionInterceptor);
  81. bindInterceptor(any(), annotatedWith(RequiresSession.class), sessionInterceptor);
  82. }
  83. @Override protected MethodInterceptor getTransactionInterceptor() {
  84. return transactionInterceptor;
  85. }
  86. /**
  87. * Configures the JPA persistence provider with a set of properties.
  88. *
  89. * @param properties A set of name value pairs that configure a JPA persistence
  90. * provider as per the specification.
  91. */
  92. public AmbariJpaPersistModule properties(Properties properties) {
  93. this.properties = properties;
  94. return this;
  95. }
  96. private final List<Class<?>> dynamicFinders = Lists.newArrayList();
  97. /**
  98. * Adds an interface to this module to use as a dynamic finder.
  99. *
  100. * @param iface Any interface type whose methods are all dynamic finders.
  101. */
  102. public <T> AmbariJpaPersistModule addFinder(Class<T> iface) {
  103. dynamicFinders.add(iface);
  104. return this;
  105. }
  106. private <T> void bindFinder(Class<T> iface) {
  107. if (!isDynamicFinderValid(iface)) {
  108. return;
  109. }
  110. InvocationHandler finderInvoker = new InvocationHandler() {
  111. @Inject
  112. JpaFinderProxy finderProxy;
  113. public Object invoke(final Object thisObject, final Method method, final Object[] args)
  114. throws Throwable {
  115. // Don't intercept non-finder methods like equals and hashcode.
  116. if (!method.isAnnotationPresent(Finder.class)) {
  117. // This is not ideal, we are using the invocation handler's equals
  118. // and hashcode as a proxy (!) for the proxy's equals and hashcode.
  119. return method.invoke(this, args);
  120. }
  121. return finderProxy.invoke(new MethodInvocation() {
  122. public Method getMethod() {
  123. return method;
  124. }
  125. public Object[] getArguments() {
  126. return null == args ? new Object[0] : args;
  127. }
  128. public Object proceed() throws Throwable {
  129. return method.invoke(thisObject, args);
  130. }
  131. public Object getThis() {
  132. throw new UnsupportedOperationException("Bottomless proxies don't expose a this.");
  133. }
  134. public AccessibleObject getStaticPart() {
  135. throw new UnsupportedOperationException();
  136. }
  137. });
  138. }
  139. };
  140. requestInjection(finderInvoker);
  141. @SuppressWarnings("unchecked") // Proxy must produce instance of type given.
  142. T proxy = (T) Proxy
  143. .newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[] { iface },
  144. finderInvoker);
  145. bind(iface).toInstance(proxy);
  146. }
  147. private boolean isDynamicFinderValid(Class<?> iface) {
  148. boolean valid = true;
  149. if (!iface.isInterface()) {
  150. addError(iface + " is not an interface. Dynamic Finders must be interfaces.");
  151. valid = false;
  152. }
  153. for (Method method : iface.getMethods()) {
  154. DynamicFinder finder = DynamicFinder.from(method);
  155. if (null == finder) {
  156. addError("Dynamic Finder methods must be annotated with @Finder, but " + iface
  157. + "." + method.getName() + " was not");
  158. valid = false;
  159. }
  160. }
  161. return valid;
  162. }
  163. }