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 }