001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.orm.jpa;
018:
019: import javax.persistence.EntityManagerFactory;
020: import javax.persistence.PersistenceException;
021: import javax.persistence.spi.PersistenceProvider;
022: import javax.persistence.spi.PersistenceUnitInfo;
023: import javax.sql.DataSource;
024:
025: import org.springframework.beans.BeanUtils;
026: import org.springframework.beans.factory.BeanClassLoaderAware;
027: import org.springframework.context.ResourceLoaderAware;
028: import org.springframework.core.io.ResourceLoader;
029: import org.springframework.instrument.classloading.LoadTimeWeaver;
030: import org.springframework.jdbc.datasource.lookup.SingleDataSourceLookup;
031: import org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager;
032: import org.springframework.orm.jpa.persistenceunit.PersistenceUnitManager;
033: import org.springframework.orm.jpa.persistenceunit.PersistenceUnitPostProcessor;
034: import org.springframework.util.ClassUtils;
035:
036: /**
037: * {@link org.springframework.beans.factory.FactoryBean} that creates a JPA
038: * {@link javax.persistence.EntityManagerFactory} according to JPA's standard
039: * <i>container</i> bootstrap contract. This is the most powerful way to set
040: * up a shared JPA EntityManagerFactory in a Spring application context;
041: * the EntityManagerFactory can then be passed to JPA-based DAOs via
042: * dependency injection. Note that switching to a JNDI lookup or to a
043: * {@link org.springframework.orm.jpa.LocalEntityManagerFactoryBean}
044: * definition is just a matter of configuration!
045: *
046: * <p>As with {@link LocalEntityManagerFactoryBean}, configuration settings
047: * are usually read in from a <code>META-INF/persistence.xml</code> config file,
048: * residing in the class path, according to the general JPA configuration contract.
049: * However, this FactoryBean is more flexible in that you can override the location
050: * of the <code>persistence.xml</code> file, specify the JDBC DataSources to link to,
051: * etc. Furthermore, it allows for pluggable class instrumentation through Spring's
052: * {@link org.springframework.instrument.classloading.LoadTimeWeaver} abstraction,
053: * instead of being tied to a special VM agent specified on JVM startup.
054: *
055: * <p>Internally, this FactoryBean parses the <code>persistence.xml</code> file
056: * itself and creates a corresponding {@link javax.persistence.spi.PersistenceUnitInfo}
057: * object (with further configuration merged in, such as JDBC DataSources and the
058: * Spring LoadTimeWeaver), to be passed to the chosen JPA
059: * {@link javax.persistence.spi.PersistenceProvider}. This corresponds to a
060: * local JPA container with full support for the standard JPA container contract.
061: *
062: * <p>The exposed EntityManagerFactory object will implement all the interfaces of
063: * the underlying native EntityManagerFactory returned by the PersistenceProvider,
064: * plus the {@link EntityManagerFactoryInfo} interface which exposes additional
065: * metadata as assembled by this FactoryBean.
066: *
067: * @author Juergen Hoeller
068: * @author Rod Johnson
069: * @since 2.0
070: * @see #setPersistenceXmlLocation
071: * @see #setJpaProperties
072: * @see #setJpaVendorAdapter
073: * @see #setLoadTimeWeaver
074: * @see #setDataSource
075: * @see EntityManagerFactoryInfo
076: * @see LocalEntityManagerFactoryBean
077: * @see org.springframework.orm.jpa.support.SharedEntityManagerBean
078: * @see javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory
079: */
080: public class LocalContainerEntityManagerFactoryBean extends
081: AbstractEntityManagerFactoryBean implements
082: ResourceLoaderAware, BeanClassLoaderAware {
083:
084: private PersistenceUnitManager persistenceUnitManager;
085:
086: private final DefaultPersistenceUnitManager internalPersistenceUnitManager = new DefaultPersistenceUnitManager();
087:
088: private ClassLoader beanClassLoader = ClassUtils
089: .getDefaultClassLoader();
090:
091: private PersistenceUnitInfo persistenceUnitInfo;
092:
093: /**
094: * Set the PersistenceUnitManager to use for obtaining the JPA persistence unit
095: * that this FactoryBean is supposed to build an EntityManagerFactory for.
096: * <p>The default is to rely on the local settings specified on this FactoryBean,
097: * such as "persistenceXmlLocation", "dataSource" and "loadTimeWeaver".
098: * <p>For reuse of existing persistence unit configuration or more advanced forms
099: * of custom persistence unit handling, consider defining a separate
100: * PersistenceUnitManager bean (typically a DefaultPersistenceUnitManager instance)
101: * and linking it in here. <code>persistence.xml</code> location, DataSource
102: * configuration and LoadTimeWeaver will be defined on that separate
103: * DefaultPersistenceUnitManager bean in such a scenario.
104: * @see #setPersistenceXmlLocation
105: * @see #setDataSource
106: * @see #setLoadTimeWeaver
107: * @see org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager
108: */
109: public void setPersistenceUnitManager(
110: PersistenceUnitManager persistenceUnitManager) {
111: this .persistenceUnitManager = persistenceUnitManager;
112: }
113:
114: /**
115: * Set the location of the <code>persistence.xml</code> file
116: * we want to use. This is a Spring resource location.
117: * <p>Default is "classpath:META-INF/persistence.xml".
118: * <p><b>NOTE: Only applied if no external PersistenceUnitManager specified.</b>
119: * @param persistenceXmlLocation a Spring resource String
120: * identifying the location of the <code>persistence.xml</code> file
121: * that this LocalContainerEntityManagerFactoryBean should parse
122: * @see #setPersistenceUnitManager
123: */
124: public void setPersistenceXmlLocation(String persistenceXmlLocation) {
125: this .internalPersistenceUnitManager
126: .setPersistenceXmlLocations(new String[] { persistenceXmlLocation });
127: }
128:
129: /**
130: * Specify the JDBC DataSource that the JPA persistence provider is supposed
131: * to use for accessing the database. This is an alternative to keeping the
132: * JDBC configuration in <code>persistence.xml</code>, passing in a Spring-managed
133: * DataSource instead.
134: * <p>In JPA speak, a DataSource passed in here will be used as "nonJtaDataSource"
135: * on the PersistenceUnitInfo passed to the PersistenceProvider, overriding
136: * data source configuration in <code>persistence.xml</code> (if any).
137: * <p><b>NOTE: Only applied if no external PersistenceUnitManager specified.</b>
138: * @see javax.persistence.spi.PersistenceUnitInfo#getNonJtaDataSource()
139: * @see #setPersistenceUnitManager
140: */
141: public void setDataSource(DataSource dataSource) {
142: this .internalPersistenceUnitManager
143: .setDataSourceLookup(new SingleDataSourceLookup(
144: dataSource));
145: this .internalPersistenceUnitManager
146: .setDefaultDataSource(dataSource);
147: }
148:
149: /**
150: * Specify the Spring LoadTimeWeaver to use for class instrumentation according
151: * to the JPA class transformer contract.
152: * <p>It is a not required to specify a LoadTimeWeaver: Most providers will be
153: * able to provide a subset of their functionality without class instrumentation
154: * as well, or operate with their VM agent specified on JVM startup.
155: * <p>In terms of Spring-provided weaving options, the most important ones are
156: * InstrumentationLoadTimeWeaver, which requires a Spring-specific (but very general)
157: * VM agent specified on JVM startup, and ReflectiveLoadTimeWeaver, which interacts
158: * with an underlying ClassLoader based on specific extended methods being available
159: * on it (for example, interacting with Spring's TomcatInstrumentableClassLoader).
160: * <p><b>NOTE: Only applied if no external PersistenceUnitManager specified.</b>
161: * @see org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver
162: * @see org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver
163: * @see org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader
164: * @see #setPersistenceUnitManager
165: */
166: public void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver) {
167: this .internalPersistenceUnitManager
168: .setLoadTimeWeaver(loadTimeWeaver);
169: }
170:
171: /**
172: * Set the PersistenceUnitPostProcessors to be applied to the
173: * PersistenceUnitInfo used for creating this EntityManagerFactory.
174: * <p>Such post-processors can, for example, register further entity
175: * classes and jar files, in addition to the metadata read in from
176: * <code>persistence.xml</code>.
177: * <p><b>NOTE: Only applied if no external PersistenceUnitManager specified.</b>
178: * @see #setPersistenceUnitManager
179: */
180: public void setPersistenceUnitPostProcessors(
181: PersistenceUnitPostProcessor[] postProcessors) {
182: this .internalPersistenceUnitManager
183: .setPersistenceUnitPostProcessors(postProcessors);
184: }
185:
186: public void setResourceLoader(ResourceLoader resourceLoader) {
187: this .internalPersistenceUnitManager
188: .setResourceLoader(resourceLoader);
189: }
190:
191: public void setBeanClassLoader(ClassLoader beanClassLoader) {
192: this .beanClassLoader = beanClassLoader;
193: }
194:
195: @Override
196: protected EntityManagerFactory createNativeEntityManagerFactory()
197: throws PersistenceException {
198: PersistenceUnitManager managerToUse = this .persistenceUnitManager;
199: if (this .persistenceUnitManager == null) {
200: this .internalPersistenceUnitManager.afterPropertiesSet();
201: managerToUse = this .internalPersistenceUnitManager;
202: }
203:
204: this .persistenceUnitInfo = determinePersistenceUnitInfo(managerToUse);
205:
206: PersistenceProvider provider = getPersistenceProvider();
207: if (provider == null) {
208: String providerClassName = this .persistenceUnitInfo
209: .getPersistenceProviderClassName();
210: if (providerClassName == null) {
211: throw new IllegalArgumentException(
212: "No PersistenceProvider specified in EntityManagerFactory configuration, "
213: + "and chosen PersistenceUnitInfo does not specify a provider class name either");
214: }
215: Class providerClass = ClassUtils.resolveClassName(
216: providerClassName, this .beanClassLoader);
217: provider = (PersistenceProvider) BeanUtils
218: .instantiateClass(providerClass);
219: }
220: if (provider == null) {
221: throw new IllegalStateException(
222: "Unable to determine persistence provider. "
223: + "Please check configuration of "
224: + getClass().getName()
225: + "; "
226: + "ideally specify the appropriate JpaVendorAdapter class for this provider.");
227: }
228:
229: if (logger.isInfoEnabled()) {
230: logger
231: .info("Building JPA container EntityManagerFactory for persistence unit '"
232: + this .persistenceUnitInfo
233: .getPersistenceUnitName() + "'");
234: }
235: this .nativeEntityManagerFactory = provider
236: .createContainerEntityManagerFactory(
237: this .persistenceUnitInfo, getJpaPropertyMap());
238: postProcessEntityManagerFactory(
239: this .nativeEntityManagerFactory,
240: this .persistenceUnitInfo);
241:
242: return this .nativeEntityManagerFactory;
243: }
244:
245: /**
246: * Determine the PersistenceUnitInfo to use for the EntityManagerFactory
247: * created by this bean.
248: * <p>The default implementation reads in all persistence unit infos from
249: * <code>persistence.xml</code>, as defined in the JPA specification.
250: * If no entity manager name was specified, it takes the first info in the
251: * array as returned by the reader. Otherwise, it checks for a matching name.
252: * @param persistenceUnitManager the PersistenceUnitManager to obtain from
253: * @return the chosen PersistenceUnitInfo
254: */
255: protected PersistenceUnitInfo determinePersistenceUnitInfo(
256: PersistenceUnitManager persistenceUnitManager) {
257: if (getPersistenceUnitName() != null) {
258: return persistenceUnitManager
259: .obtainPersistenceUnitInfo(getPersistenceUnitName());
260: } else {
261: return persistenceUnitManager
262: .obtainDefaultPersistenceUnitInfo();
263: }
264: }
265:
266: /**
267: * Hook method allowing subclasses to customize the EntityManagerFactory
268: * after its creation via the PersistenceProvider.
269: * <p>The default implementation is empty.
270: * @param emf the newly created EntityManagerFactory we are working with
271: * @param pui the PersistenceUnitInfo used to configure the EntityManagerFactory
272: * @see javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory
273: */
274: protected void postProcessEntityManagerFactory(
275: EntityManagerFactory emf, PersistenceUnitInfo pui) {
276: }
277:
278: public PersistenceUnitInfo getPersistenceUnitInfo() {
279: return this .persistenceUnitInfo;
280: }
281:
282: public String getPersistenceUnitName() {
283: if (this .persistenceUnitInfo != null) {
284: return this .persistenceUnitInfo.getPersistenceUnitName();
285: }
286: return super .getPersistenceUnitName();
287: }
288:
289: public DataSource getDataSource() {
290: if (this.persistenceUnitInfo != null) {
291: return this.persistenceUnitInfo.getNonJtaDataSource();
292: }
293: return this.internalPersistenceUnitManager
294: .getDefaultDataSource();
295: }
296:
297: }
|