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}