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 -> Persistence.createEntityManagerFactory(n));
187
188    } else {
189      final PersistenceProvider persistenceProvider = getPersistenceProvider(persistenceUnitInfo);
190      assert persistenceProvider != null;
191      returnValue =
192        emfs.computeIfAbsent(name,
193                             n -> {
194                               final Map<String, Object> properties = new HashMap<>();
195                               properties.put("javax.persistence.bean.manager",
196                                              CDI.current().getBeanManager());
197                               return
198                                 persistenceProvider.createContainerEntityManagerFactory(persistenceUnitInfo,
199                                                                                         properties);
200                             });
201    }
202    return returnValue;
203  }
204
205
206  /*
207   * Inner and nested classes.
208   */
209
210
211  private static final class EntityManagerFactoryResourceReference implements ResourceReference<EntityManagerFactory> {
212
213    private final Map<String, EntityManagerFactory> emfs;
214
215    private final String name;
216
217    private EntityManagerFactoryResourceReference(final Map<String, EntityManagerFactory> emfs,
218                                                  final String name) {
219      super();
220      this.emfs = Objects.requireNonNull(emfs);
221      this.name = Objects.requireNonNull(name);
222    }
223
224    @Override
225    public final EntityManagerFactory getInstance() {
226      final PersistenceUnitInfo persistenceUnitInfo = getPersistenceUnitInfo(this.name);
227      assert persistenceUnitInfo != null;
228      final EntityManagerFactory returnValue;
229      if (PersistenceUnitTransactionType.RESOURCE_LOCAL.equals(persistenceUnitInfo.getTransactionType())) {
230        returnValue = getOrCreateEntityManagerFactory(emfs, null, name);
231      } else {
232        returnValue = getOrCreateEntityManagerFactory(emfs, persistenceUnitInfo, name);
233      }
234      return returnValue;
235    }
236
237    @Override
238    public final void release() {
239      final EntityManagerFactory emf = this.emfs.remove(this.name);
240      if (emf != null && emf.isOpen()) {
241        emf.close();
242      }
243    }
244  }
245
246  private static final class EntityManagerResourceReference implements ResourceReference<EntityManager> {
247
248    private final Map<String, EntityManagerFactory> emfs;
249
250    private final String name;
251
252    private volatile EntityManager em;
253
254    private EntityManagerResourceReference(final Map<String, EntityManagerFactory> emfs,
255                                           final String name) {
256      super();
257      this.emfs = Objects.requireNonNull(emfs);
258      this.name = Objects.requireNonNull(name);
259    }
260
261    @Override
262    public final EntityManager getInstance() {
263      EntityManager returnValue = this.em;
264      if (returnValue == null) {
265        final PersistenceUnitInfo persistenceUnitInfo = getPersistenceUnitInfo(this.name);
266        assert persistenceUnitInfo != null;
267        final EntityManagerFactory emf;
268        if (PersistenceUnitTransactionType.RESOURCE_LOCAL.equals(persistenceUnitInfo.getTransactionType())) {
269          emf = getOrCreateEntityManagerFactory(this.emfs, null, this.name);
270        } else {
271          emf = getOrCreateEntityManagerFactory(this.emfs, persistenceUnitInfo, this.name);
272        }
273        assert emf != null;
274        returnValue = emf.createEntityManager();
275        assert returnValue != null;
276        this.em = returnValue;
277      }
278      return returnValue;
279    }
280
281    @Override
282    public final void release() {
283      final EntityManager em = this.em;
284      if (em != null && em.isOpen()) {
285        em.close();
286      }
287    }
288
289  }
290
291}