001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2018 microBean.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
014 * implied.  See the License for the specific language governing
015 * permissions and limitations under the License.
016 */
017package org.microbean.jpa.weld;
018
019import java.util.Collection;
020import java.util.HashMap;
021import java.util.Iterator;
022import java.util.Map;
023import java.util.Map.Entry;
024import java.util.Objects;
025
026import java.util.concurrent.ConcurrentHashMap;
027
028import javax.enterprise.inject.literal.NamedLiteral;
029
030import javax.enterprise.inject.spi.Annotated;
031import javax.enterprise.inject.spi.AnnotatedField;
032import javax.enterprise.inject.spi.CDI;
033import javax.enterprise.inject.spi.InjectionPoint;
034
035import javax.persistence.EntityManager;
036import javax.persistence.EntityManagerFactory;
037import javax.persistence.Persistence;
038import javax.persistence.PersistenceContext;
039import javax.persistence.PersistenceException;
040import javax.persistence.PersistenceUnit;
041
042import javax.persistence.spi.PersistenceProvider;
043import javax.persistence.spi.PersistenceUnitInfo;
044import javax.persistence.spi.PersistenceUnitTransactionType;
045
046import org.jboss.weld.injection.spi.ResourceReference;
047import org.jboss.weld.injection.spi.ResourceReferenceFactory;
048
049public final class JpaInjectionServices implements org.jboss.weld.injection.spi.JpaInjectionServices {
050
051  private volatile Map<String, EntityManagerFactory> emfs;
052
053  public JpaInjectionServices() {
054    super();
055  }
056
057  @Override
058  public final ResourceReferenceFactory<EntityManager> registerPersistenceContextInjectionPoint(final InjectionPoint injectionPoint) {
059    Objects.requireNonNull(injectionPoint);
060    final Annotated annotatedMember = injectionPoint.getAnnotated();
061    assert annotatedMember != null;
062    final PersistenceContext persistenceContextAnnotation = annotatedMember.getAnnotation(PersistenceContext.class);
063    if (persistenceContextAnnotation == null) {
064      throw new IllegalArgumentException("injectionPoint.getAnnotated().getAnnotation(PersistenceContext.class) == null");
065    }
066    final String name;
067    final String n = persistenceContextAnnotation.unitName();
068    if (n.isEmpty()) {
069      if (annotatedMember instanceof AnnotatedField) {
070        name = ((AnnotatedField<?>)annotatedMember).getJavaMember().getName();
071      } else {
072        name = n;
073      }
074    } else {
075      name = n;
076    }
077    synchronized (this) {
078      if (this.emfs == null) {
079        this.emfs = new ConcurrentHashMap<>();
080      }
081    }
082    return () -> new EntityManagerResourceReference(this.emfs, name);
083  }
084
085  @Override
086  public final ResourceReferenceFactory<EntityManagerFactory> registerPersistenceUnitInjectionPoint(final InjectionPoint injectionPoint) {
087    Objects.requireNonNull(injectionPoint);
088    final Annotated annotatedMember = injectionPoint.getAnnotated();
089    assert annotatedMember != null;
090    final PersistenceUnit persistenceUnitAnnotation = annotatedMember.getAnnotation(PersistenceUnit.class);
091    if (persistenceUnitAnnotation == null) {
092      throw new IllegalArgumentException("injectionPoint.getAnnotated().getAnnotation(PersistenceUnit.class) == null");
093    }
094    final String name;
095    final String n = persistenceUnitAnnotation.unitName();
096    if (n.isEmpty()) {
097      if (annotatedMember instanceof AnnotatedField) {
098        name = ((AnnotatedField<?>)annotatedMember).getJavaMember().getName();
099      } else {
100        name = n;
101      }
102    } else {
103      name = n;
104    }
105    synchronized (this) {
106      if (this.emfs == null) {
107        this.emfs = new ConcurrentHashMap<>();
108      }
109    }
110    return () -> new EntityManagerFactoryResourceReference(this.emfs, name);
111  }
112
113  @Override
114  public final void cleanup() {
115    final Map<? extends String, ? extends EntityManagerFactory> emfs = this.emfs;
116    if (emfs != null && !emfs.isEmpty()) {
117      final Collection<? extends Entry<? extends String, ? extends EntityManagerFactory>> entries = emfs.entrySet();
118      assert entries != null;
119      assert !entries.isEmpty();
120      final Iterator<? extends Entry<? extends String, ? extends EntityManagerFactory>> iterator = entries.iterator();
121      assert iterator != null;
122      assert iterator.hasNext();
123      while (iterator.hasNext()) {
124        final Entry<? extends String, ? extends EntityManagerFactory> entry = iterator.next();
125        assert entry != null;
126        final EntityManagerFactory emf = entry.getValue();
127        assert emf != null;
128        if (emf.isOpen()) {
129          emf.close();
130        }
131        iterator.remove();
132      }
133    }
134  }
135  
136  @Deprecated
137  @Override
138  public final EntityManager resolvePersistenceContext(final InjectionPoint injectionPoint) {
139    return this.registerPersistenceContextInjectionPoint(injectionPoint).createResource().getInstance();
140  }
141
142  @Deprecated
143  @Override
144  public final EntityManagerFactory resolvePersistenceUnit(final InjectionPoint injectionPoint) {
145    return this.registerPersistenceUnitInjectionPoint(injectionPoint).createResource().getInstance();
146  }
147  
148
149  /*
150   * Static methods.
151   */
152
153  
154  private static final PersistenceProvider getPersistenceProvider(final PersistenceUnitInfo persistenceUnitInfo) {
155    final String providerClassName = Objects.requireNonNull(persistenceUnitInfo).getPersistenceProviderClassName();
156    final PersistenceProvider persistenceProvider;
157    if (providerClassName == null) {
158      persistenceProvider = CDI.current().select(PersistenceProvider.class).get();
159    } else {
160      try {
161        persistenceProvider =
162          (PersistenceProvider)CDI.current().select(Class.forName(providerClassName,
163                                                                  true,
164                                                                  Thread.currentThread().getContextClassLoader())).get();
165      } catch (final ReflectiveOperationException exception) {
166        throw new PersistenceException(exception.getMessage(), exception);
167      }
168    }
169    return persistenceProvider;
170  }
171
172  private static final PersistenceUnitInfo getPersistenceUnitInfo(final String name) {
173    return CDI.current().select(PersistenceUnitInfo.class,
174                                NamedLiteral.of(Objects.requireNonNull(name))).get();
175  }
176
177  private static final EntityManagerFactory getOrCreateEntityManagerFactory(final Map<String, EntityManagerFactory> emfs,
178                                                                            final PersistenceUnitInfo persistenceUnitInfo,
179                                                                            final String name) {
180    Objects.requireNonNull(emfs);
181    Objects.requireNonNull(name);
182    final EntityManagerFactory returnValue;
183    if (persistenceUnitInfo == null) {
184      returnValue =
185        emfs.computeIfAbsent(name,
186                             n -> {
187                               return
188                               Persistence.createEntityManagerFactory(n);
189                             });
190
191    } else {
192      final PersistenceProvider persistenceProvider = getPersistenceProvider(persistenceUnitInfo);
193      assert persistenceProvider != null;
194      returnValue =
195        emfs.computeIfAbsent(name,
196                             n -> {
197                               final Map<String, Object> properties = new HashMap<>();
198                               properties.put("javax.persistence.bean.manager",
199                                              CDI.current().getBeanManager());
200                               return
201                                 persistenceProvider.createContainerEntityManagerFactory(persistenceUnitInfo,
202                                                                                       properties);
203                             });
204    }
205    return returnValue;
206  }
207
208
209  /*
210   * Inner and nested classes.
211   */
212
213
214  private static final class EntityManagerFactoryResourceReference implements ResourceReference<EntityManagerFactory> {
215
216    private final Map<String, EntityManagerFactory> emfs;
217
218    private final String name;
219
220    private EntityManagerFactoryResourceReference(final Map<String, EntityManagerFactory> emfs,
221                                                  final String name) {
222      super();
223      this.emfs = Objects.requireNonNull(emfs);
224      this.name = Objects.requireNonNull(name);
225    }
226
227    @Override
228    public final EntityManagerFactory getInstance() {
229      final PersistenceUnitInfo persistenceUnitInfo = getPersistenceUnitInfo(this.name);
230      assert persistenceUnitInfo != null;
231      final EntityManagerFactory returnValue;
232      if (PersistenceUnitTransactionType.RESOURCE_LOCAL.equals(persistenceUnitInfo.getTransactionType())) {
233        returnValue = getOrCreateEntityManagerFactory(emfs, null, name);
234      } else {
235        returnValue = getOrCreateEntityManagerFactory(emfs, persistenceUnitInfo, name);
236      }
237      return returnValue;
238    }
239
240    @Override
241    public final void release() {
242      final EntityManagerFactory emf = this.emfs.remove(this.name);
243      if (emf != null && emf.isOpen()) {
244        emf.close();
245      }
246    }
247  }
248
249  private static final class EntityManagerResourceReference implements ResourceReference<EntityManager> {
250
251    private final Map<String, EntityManagerFactory> emfs;
252
253    private final String name;
254
255    private volatile EntityManager em;
256
257    private EntityManagerResourceReference(final Map<String, EntityManagerFactory> emfs,
258                                           final String name) {
259      super();
260      this.emfs = Objects.requireNonNull(emfs);
261      this.name = Objects.requireNonNull(name);
262    }
263
264    @Override
265    public final EntityManager getInstance() {
266      EntityManager returnValue = this.em;
267      if (returnValue == null) {
268        final PersistenceUnitInfo persistenceUnitInfo = getPersistenceUnitInfo(this.name);
269        assert persistenceUnitInfo != null;
270        final EntityManagerFactory emf;
271        if (PersistenceUnitTransactionType.RESOURCE_LOCAL.equals(persistenceUnitInfo.getTransactionType())) {
272          emf = getOrCreateEntityManagerFactory(this.emfs, null, this.name);
273        } else {
274          emf = getOrCreateEntityManagerFactory(this.emfs, persistenceUnitInfo, this.name);
275        }
276        assert emf != null;
277        returnValue = emf.createEntityManager();
278        assert returnValue != null;
279        this.em = returnValue;
280      }
281      return returnValue;
282    }
283
284    @Override
285    public final void release() {
286      final EntityManager em = this.em;
287      if (em != null && em.isOpen()) {
288        em.close();
289      }
290    }
291
292  }
293
294}