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 private EntityManagerFactory createEntityManagerFactory(final String persistenceUnitName) 194 { 195 final PersistenceProvider persistenceProvider = getPersistenceProvider(); 196 197 for (final TapestryPersistenceUnitInfo info : persistenceUnitInfos) 198 { 199 if (info.getPersistenceUnitName().equals(persistenceUnitName)) 200 { 201 final Map<String, String> properties = CollectionFactory.newCaseInsensitiveMap(); 202 properties.put(JpaConstants.PERSISTENCE_UNIT_NAME, persistenceUnitName); 203 204 return persistenceProvider.createContainerEntityManagerFactory(info, properties); 205 } 206 } 207 208 throw new IllegalStateException(String.format( 209 "Failed to create EntityManagerFactory for persistence unit '%s'", 210 persistenceUnitName)); 211 } 212 213 private PersistenceProvider getPersistenceProvider() 214 { 215 final PersistenceProviderResolver resolver = PersistenceProviderResolverHolder 216 .getPersistenceProviderResolver(); 217 218 final List<PersistenceProvider> providers = resolver.getPersistenceProviders(); 219 220 if (providers.isEmpty()) 221 throw new IllegalStateException( 222 "No PersistenceProvider implementation available in the runtime environment."); 223 224 return providers.get(0); 225 } 226 227 public EntityManager create(final String persistenceUnitName) 228 { 229 return getEntityManagerFactory(persistenceUnitName).createEntityManager(); 230 } 231 232 private void registryDidShutdown() 233 { 234 final Set<Entry<String, EntityManagerFactory>> entrySet = entityManagerFactories.entrySet(); 235 236 for (final Entry<String, EntityManagerFactory> entry : entrySet) 237 { 238 final EntityManagerFactory emf = entry.getValue(); 239 try 240 { 241 emf.close(); 242 } catch (final Exception e) 243 { 244 logger.error(String.format( 245 "Failed to close EntityManagerFactory for persistence unit '%s'", 246 entry.getKey()), e); 247 } 248 } 249 250 entityManagerFactories.clear(); 251 252 } 253 254 public List<PersistenceUnitInfo> getPersistenceUnitInfos() 255 { 256 return Collections.<PersistenceUnitInfo>unmodifiableList(persistenceUnitInfos); 257 } 258 259 }