001 // Copyright 2011 The Apache Software Foundation 002 // 003 // Licensed under the Apache License, Version 2.0 (the "License"); 004 // you may not use this file except in compliance with the License. 005 // You may obtain a copy of the License at 006 // 007 // http://www.apache.org/licenses/LICENSE-2.0 008 // 009 // Unless required by applicable law or agreed to in writing, software 010 // distributed under the License is distributed on an "AS IS" BASIS, 011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 // See the License for the specific language governing permissions and 013 // limitations under the License. 014 015 package org.apache.tapestry5.internal.jpa; 016 017 import org.apache.tapestry5.func.F; 018 import org.apache.tapestry5.func.Mapper; 019 import org.apache.tapestry5.func.Predicate; 020 import org.apache.tapestry5.ioc.Resource; 021 import org.apache.tapestry5.ioc.annotations.Local; 022 import org.apache.tapestry5.ioc.annotations.PostInjection; 023 import org.apache.tapestry5.ioc.annotations.Symbol; 024 import org.apache.tapestry5.ioc.internal.util.CollectionFactory; 025 import org.apache.tapestry5.ioc.internal.util.InternalUtils; 026 import org.apache.tapestry5.ioc.services.RegistryShutdownHub; 027 import org.apache.tapestry5.jpa.*; 028 import org.slf4j.Logger; 029 030 import javax.persistence.EntityManager; 031 import javax.persistence.EntityManagerFactory; 032 import javax.persistence.spi.PersistenceProvider; 033 import javax.persistence.spi.PersistenceProviderResolver; 034 import javax.persistence.spi.PersistenceProviderResolverHolder; 035 import javax.persistence.spi.PersistenceUnitInfo; 036 import java.io.InputStream; 037 import java.util.Collections; 038 import java.util.List; 039 import java.util.Map; 040 import java.util.Map.Entry; 041 import java.util.Set; 042 043 public class EntityManagerSourceImpl implements EntityManagerSource 044 { 045 private final Map<String, EntityManagerFactory> entityManagerFactories = CollectionFactory 046 .newMap(); 047 048 private final Logger logger; 049 050 private final List<TapestryPersistenceUnitInfo> persistenceUnitInfos; 051 052 public EntityManagerSourceImpl(Logger logger, @Symbol(JpaSymbols.PERSISTENCE_DESCRIPTOR) 053 final Resource persistenceDescriptor, @Local 054 PersistenceUnitConfigurer packageNamePersistenceUnitConfigurer, 055 Map<String, PersistenceUnitConfigurer> configuration) 056 { 057 this.logger = logger; 058 059 List<TapestryPersistenceUnitInfo> persistenceUnitInfos = parsePersistenceUnitInfos(persistenceDescriptor); 060 061 final Map<String, PersistenceUnitConfigurer> remainingConfigurations = configure(configuration, persistenceUnitInfos); 062 063 configureRemaining(persistenceUnitInfos, remainingConfigurations); 064 065 if (persistenceUnitInfos.size() == 1) 066 { 067 packageNamePersistenceUnitConfigurer.configure(persistenceUnitInfos.get(0)); 068 } else 069 { 070 validateUnitInfos(persistenceUnitInfos); 071 } 072 073 this.persistenceUnitInfos = persistenceUnitInfos; 074 } 075 076 @PostInjection 077 public void listenForShutdown(RegistryShutdownHub hub) 078 { 079 hub.addRegistryShutdownListener(new Runnable() 080 { 081 public void run() 082 { 083 registryDidShutdown(); 084 } 085 }); 086 } 087 088 private void validateUnitInfos(List<TapestryPersistenceUnitInfo> persistenceUnitInfos) 089 { 090 final List<String> affectedUnits = F.flow(persistenceUnitInfos).filter(new Predicate<TapestryPersistenceUnitInfo>() 091 { 092 public boolean accept(TapestryPersistenceUnitInfo info) 093 { 094 return !info.excludeUnlistedClasses(); 095 } 096 }).map(new Mapper<TapestryPersistenceUnitInfo, String>() 097 { 098 public String map(TapestryPersistenceUnitInfo info) 099 { 100 return info.getPersistenceUnitName(); 101 } 102 }).toList(); 103 104 if (0 < affectedUnits.size()) 105 { 106 throw new RuntimeException( 107 String.format( 108 "Persistence units '%s' are configured to include managed classes that have not been explicitly listed. " + 109 "This is forbidden when multiple persistence units are used in the same application. " + 110 "Please configure persistence units to exclude unlisted managed classes (e.g. by removing <exclude-unlisted-classes> element) " + 111 "and include them explicitly.", 112 InternalUtils.join(affectedUnits))); 113 } 114 } 115 116 private List<TapestryPersistenceUnitInfo> parsePersistenceUnitInfos(Resource persistenceDescriptor) 117 { 118 List<TapestryPersistenceUnitInfo> persistenceUnitInfos = CollectionFactory.newList(); 119 120 if (persistenceDescriptor.exists()) 121 { 122 final PersistenceParser parser = new PersistenceParser(); 123 124 InputStream inputStream = null; 125 try 126 { 127 inputStream = persistenceDescriptor.openStream(); 128 persistenceUnitInfos = parser.parse(inputStream); 129 } catch (Exception e) 130 { 131 throw new RuntimeException(e); 132 } finally 133 { 134 InternalUtils.close(inputStream); 135 } 136 137 } 138 return persistenceUnitInfos; 139 } 140 141 private Map<String, PersistenceUnitConfigurer> configure(Map<String, PersistenceUnitConfigurer> configuration, List<TapestryPersistenceUnitInfo> persistenceUnitInfos) 142 { 143 final Map<String, PersistenceUnitConfigurer> remainingConfigurations = CollectionFactory.newMap(configuration); 144 145 for (final TapestryPersistenceUnitInfo info : persistenceUnitInfos) 146 { 147 final String unitName = info.getPersistenceUnitName(); 148 149 final PersistenceUnitConfigurer configurer = configuration.get(unitName); 150 151 if (configurer != null) 152 { 153 configurer.configure(info); 154 155 remainingConfigurations.remove(unitName); 156 } 157 } 158 159 return remainingConfigurations; 160 } 161 162 163 private void configureRemaining(List<TapestryPersistenceUnitInfo> persistenceUnitInfos, Map<String, PersistenceUnitConfigurer> remainingConfigurations) 164 { 165 for (Entry<String, PersistenceUnitConfigurer> entry : remainingConfigurations.entrySet()) 166 { 167 final PersistenceUnitInfoImpl info = new PersistenceUnitInfoImpl(entry.getKey()); 168 169 final PersistenceUnitConfigurer configurer = entry.getValue(); 170 configurer.configure(info); 171 172 persistenceUnitInfos.add(info); 173 } 174 } 175 176 /** 177 * {@inheritDoc} 178 */ 179 public EntityManagerFactory getEntityManagerFactory(final String persistenceUnitName) 180 { 181 EntityManagerFactory emf = entityManagerFactories.get(persistenceUnitName); 182 183 if (emf == null) 184 { 185 emf = createEntityManagerFactory(persistenceUnitName); 186 187 entityManagerFactories.put(persistenceUnitName, emf); 188 } 189 190 return emf; 191 } 192 193 @SuppressWarnings("unchecked") 194 EntityManagerFactory createEntityManagerFactory(final String persistenceUnitName) 195 { 196 197 for (final TapestryPersistenceUnitInfo info : persistenceUnitInfos) 198 { 199 if (info.getPersistenceUnitName().equals(persistenceUnitName)) 200 { 201 final Map properties = info.getEntityManagerProperties() == null ? CollectionFactory.newCaseInsensitiveMap() : info.getEntityManagerProperties(); 202 properties.put(JpaConstants.PERSISTENCE_UNIT_NAME, persistenceUnitName); 203 204 String providerClassName = info.getPersistenceProviderClassName(); 205 206 final PersistenceProvider persistenceProvider = getPersistenceProvider(persistenceUnitName, providerClassName); 207 208 return persistenceProvider.createContainerEntityManagerFactory(info, properties); 209 } 210 } 211 212 throw new IllegalStateException(String.format( 213 "Failed to create EntityManagerFactory for persistence unit '%s'", 214 persistenceUnitName)); 215 } 216 217 private PersistenceProvider getPersistenceProvider(final String persistenceUnitName, final String providerClassName) 218 { 219 final PersistenceProviderResolver resolver = PersistenceProviderResolverHolder 220 .getPersistenceProviderResolver(); 221 222 final List<PersistenceProvider> providers = resolver.getPersistenceProviders(); 223 224 if (providers.isEmpty()) 225 { 226 throw new IllegalStateException( 227 "No PersistenceProvider implementation available in the runtime environment."); 228 } 229 230 if(1 < providers.size() && providerClassName == null) 231 { 232 throw new IllegalStateException( 233 String.format("Persistence providers [%s] are available in the runtime environment " + 234 "but no provider class is defined for the persistence unit %s.", InternalUtils.join(toProviderClasses(providers)), persistenceUnitName)); 235 } 236 237 if(providerClassName != null) 238 { 239 return findPersistenceProviderByName(providers, providerClassName); 240 } 241 242 return providers.get(0); 243 } 244 245 private PersistenceProvider findPersistenceProviderByName(final List<PersistenceProvider> providers, final String providerClassName) 246 { 247 PersistenceProvider provider = F.flow(providers).filter(new Predicate<PersistenceProvider>() { 248 @Override 249 public boolean accept(PersistenceProvider next) { 250 return next.getClass().getName().equals(providerClassName); 251 } 252 }).first(); 253 254 if(provider == null) 255 { 256 throw new IllegalStateException( 257 String.format("No persistence provider of type %s found in the runtime environment. " + 258 "Following providers are available: [%s]", providerClassName, InternalUtils.join(toProviderClasses(providers)))); 259 } 260 261 return provider; 262 } 263 264 private List<Class> toProviderClasses(final List<PersistenceProvider> providers) 265 { 266 return F.flow(providers).map(new Mapper<PersistenceProvider, Class>() { 267 @Override 268 public Class map(PersistenceProvider element) { 269 return element.getClass(); 270 } 271 }).toList(); 272 } 273 274 public EntityManager create(final String persistenceUnitName) 275 { 276 return getEntityManagerFactory(persistenceUnitName).createEntityManager(); 277 } 278 279 private void registryDidShutdown() 280 { 281 final Set<Entry<String, EntityManagerFactory>> entrySet = entityManagerFactories.entrySet(); 282 283 for (final Entry<String, EntityManagerFactory> entry : entrySet) 284 { 285 final EntityManagerFactory emf = entry.getValue(); 286 try 287 { 288 emf.close(); 289 } catch (final Exception e) 290 { 291 logger.error(String.format( 292 "Failed to close EntityManagerFactory for persistence unit '%s'", 293 entry.getKey()), e); 294 } 295 } 296 297 entityManagerFactories.clear(); 298 299 } 300 301 public List<PersistenceUnitInfo> getPersistenceUnitInfos() 302 { 303 return Collections.<PersistenceUnitInfo>unmodifiableList(persistenceUnitInfos); 304 } 305 306 }