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 015package org.apache.tapestry5.internal.jpa; 016 017import org.apache.tapestry5.func.F; 018import org.apache.tapestry5.func.Mapper; 019import org.apache.tapestry5.func.Predicate; 020import org.apache.tapestry5.ioc.Resource; 021import org.apache.tapestry5.ioc.annotations.Local; 022import org.apache.tapestry5.ioc.annotations.PostInjection; 023import org.apache.tapestry5.ioc.annotations.Symbol; 024import org.apache.tapestry5.ioc.internal.util.CollectionFactory; 025import org.apache.tapestry5.ioc.internal.util.InternalUtils; 026import org.apache.tapestry5.ioc.services.RegistryShutdownHub; 027import org.apache.tapestry5.jpa.*; 028import org.slf4j.Logger; 029 030import javax.persistence.EntityManager; 031import javax.persistence.EntityManagerFactory; 032import javax.persistence.spi.PersistenceProvider; 033import javax.persistence.spi.PersistenceProviderResolver; 034import javax.persistence.spi.PersistenceProviderResolverHolder; 035import javax.persistence.spi.PersistenceUnitInfo; 036import java.io.InputStream; 037import java.util.Collections; 038import java.util.List; 039import java.util.Map; 040import java.util.Map.Entry; 041import java.util.Set; 042 043public 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 @Override 082 public void run() 083 { 084 registryDidShutdown(); 085 } 086 }); 087 } 088 089 private void validateUnitInfos(List<TapestryPersistenceUnitInfo> persistenceUnitInfos) 090 { 091 final List<String> affectedUnits = F.flow(persistenceUnitInfos).filter(new Predicate<TapestryPersistenceUnitInfo>() 092 { 093 @Override 094 public boolean accept(TapestryPersistenceUnitInfo info) 095 { 096 return !info.excludeUnlistedClasses(); 097 } 098 }).map(new Mapper<TapestryPersistenceUnitInfo, String>() 099 { 100 @Override 101 public String map(TapestryPersistenceUnitInfo info) 102 { 103 return info.getPersistenceUnitName(); 104 } 105 }).toList(); 106 107 if (0 < affectedUnits.size()) 108 { 109 throw new RuntimeException( 110 String.format( 111 "Persistence units '%s' are configured to include managed classes that have not been explicitly listed. " + 112 "This is forbidden when multiple persistence units are used in the same application. " + 113 "Please configure persistence units to exclude unlisted managed classes (e.g. by removing <exclude-unlisted-classes> element) " + 114 "and include them explicitly.", 115 InternalUtils.join(affectedUnits))); 116 } 117 } 118 119 private List<TapestryPersistenceUnitInfo> parsePersistenceUnitInfos(Resource persistenceDescriptor) 120 { 121 List<TapestryPersistenceUnitInfo> persistenceUnitInfos = CollectionFactory.newList(); 122 123 if (persistenceDescriptor.exists()) 124 { 125 final PersistenceParser parser = new PersistenceParser(); 126 127 InputStream inputStream = null; 128 try 129 { 130 inputStream = persistenceDescriptor.openStream(); 131 persistenceUnitInfos = parser.parse(inputStream); 132 } catch (Exception e) 133 { 134 throw new RuntimeException(e); 135 } finally 136 { 137 InternalUtils.close(inputStream); 138 } 139 140 } 141 return persistenceUnitInfos; 142 } 143 144 private Map<String, PersistenceUnitConfigurer> configure(Map<String, PersistenceUnitConfigurer> configuration, List<TapestryPersistenceUnitInfo> persistenceUnitInfos) 145 { 146 final Map<String, PersistenceUnitConfigurer> remainingConfigurations = CollectionFactory.newMap(configuration); 147 148 for (final TapestryPersistenceUnitInfo info : persistenceUnitInfos) 149 { 150 final String unitName = info.getPersistenceUnitName(); 151 152 final PersistenceUnitConfigurer configurer = configuration.get(unitName); 153 154 if (configurer != null) 155 { 156 configurer.configure(info); 157 158 remainingConfigurations.remove(unitName); 159 } 160 } 161 162 return remainingConfigurations; 163 } 164 165 166 private void configureRemaining(List<TapestryPersistenceUnitInfo> persistenceUnitInfos, Map<String, PersistenceUnitConfigurer> remainingConfigurations) 167 { 168 for (Entry<String, PersistenceUnitConfigurer> entry : remainingConfigurations.entrySet()) 169 { 170 final PersistenceUnitInfoImpl info = new PersistenceUnitInfoImpl(entry.getKey()); 171 172 final PersistenceUnitConfigurer configurer = entry.getValue(); 173 configurer.configure(info); 174 175 persistenceUnitInfos.add(info); 176 } 177 } 178 179 /** 180 * {@inheritDoc} 181 */ 182 @Override 183 public EntityManagerFactory getEntityManagerFactory(final String persistenceUnitName) 184 { 185 EntityManagerFactory emf = entityManagerFactories.get(persistenceUnitName); 186 187 if (emf == null) 188 { 189 emf = createEntityManagerFactory(persistenceUnitName); 190 191 entityManagerFactories.put(persistenceUnitName, emf); 192 } 193 194 return emf; 195 } 196 197 @SuppressWarnings("unchecked") 198 EntityManagerFactory createEntityManagerFactory(final String persistenceUnitName) 199 { 200 201 for (final TapestryPersistenceUnitInfo info : persistenceUnitInfos) 202 { 203 if (info.getPersistenceUnitName().equals(persistenceUnitName)) 204 { 205 final Map properties = info.getEntityManagerProperties() == null ? CollectionFactory.newCaseInsensitiveMap() : info.getEntityManagerProperties(); 206 properties.put(JpaConstants.PERSISTENCE_UNIT_NAME, persistenceUnitName); 207 208 String providerClassName = info.getPersistenceProviderClassName(); 209 210 final PersistenceProvider persistenceProvider = getPersistenceProvider(persistenceUnitName, providerClassName); 211 212 return persistenceProvider.createContainerEntityManagerFactory(info, properties); 213 } 214 } 215 216 throw new IllegalStateException(String.format( 217 "Failed to create EntityManagerFactory for persistence unit '%s'", 218 persistenceUnitName)); 219 } 220 221 private PersistenceProvider getPersistenceProvider(final String persistenceUnitName, final String providerClassName) 222 { 223 final PersistenceProviderResolver resolver = PersistenceProviderResolverHolder 224 .getPersistenceProviderResolver(); 225 226 final List<PersistenceProvider> providers = resolver.getPersistenceProviders(); 227 228 if (providers.isEmpty()) 229 { 230 throw new IllegalStateException( 231 "No PersistenceProvider implementation available in the runtime environment."); 232 } 233 234 if(1 < providers.size() && providerClassName == null) 235 { 236 throw new IllegalStateException( 237 String.format("Persistence providers [%s] are available in the runtime environment " + 238 "but no provider class is defined for the persistence unit %s.", InternalUtils.join(toProviderClasses(providers)), persistenceUnitName)); 239 } 240 241 if(providerClassName != null) 242 { 243 return findPersistenceProviderByName(providers, providerClassName); 244 } 245 246 return providers.get(0); 247 } 248 249 private PersistenceProvider findPersistenceProviderByName(final List<PersistenceProvider> providers, final String providerClassName) 250 { 251 PersistenceProvider provider = F.flow(providers).filter(new Predicate<PersistenceProvider>() { 252 @Override 253 public boolean accept(PersistenceProvider next) { 254 return next.getClass().getName().equals(providerClassName); 255 } 256 }).first(); 257 258 if(provider == null) 259 { 260 throw new IllegalStateException( 261 String.format("No persistence provider of type %s found in the runtime environment. " + 262 "Following providers are available: [%s]", providerClassName, InternalUtils.join(toProviderClasses(providers)))); 263 } 264 265 return provider; 266 } 267 268 private List<Class> toProviderClasses(final List<PersistenceProvider> providers) 269 { 270 return F.flow(providers).map(new Mapper<PersistenceProvider, Class>() { 271 @Override 272 public Class map(PersistenceProvider element) { 273 return element.getClass(); 274 } 275 }).toList(); 276 } 277 278 @Override 279 public EntityManager create(final String persistenceUnitName) 280 { 281 return getEntityManagerFactory(persistenceUnitName).createEntityManager(); 282 } 283 284 private void registryDidShutdown() 285 { 286 final Set<Entry<String, EntityManagerFactory>> entrySet = entityManagerFactories.entrySet(); 287 288 for (final Entry<String, EntityManagerFactory> entry : entrySet) 289 { 290 final EntityManagerFactory emf = entry.getValue(); 291 try 292 { 293 emf.close(); 294 } catch (final Exception e) 295 { 296 logger.error(String.format( 297 "Failed to close EntityManagerFactory for persistence unit '%s'", 298 entry.getKey()), e); 299 } 300 } 301 302 entityManagerFactories.clear(); 303 304 } 305 306 @Override 307 public List<PersistenceUnitInfo> getPersistenceUnitInfos() 308 { 309 return Collections.<PersistenceUnitInfo>unmodifiableList(persistenceUnitInfos); 310 } 311 312}