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 }