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}