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.context.ResourceLoaderAware;
027: import org.springframework.context.weaving.LoadTimeWeaverAware;
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, LoadTimeWeaverAware {
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: * Set the PersistenceUnitPostProcessors to be applied to the
151: * PersistenceUnitInfo used for creating this EntityManagerFactory.
152: * <p>Such post-processors can, for example, register further entity
153: * classes and jar files, in addition to the metadata read in from
154: * <code>persistence.xml</code>.
155: * <p><b>NOTE: Only applied if no external PersistenceUnitManager specified.</b>
156: * @see #setPersistenceUnitManager
157: */
158: public void setPersistenceUnitPostProcessors(
159: PersistenceUnitPostProcessor[] postProcessors) {
160: this .internalPersistenceUnitManager
161: .setPersistenceUnitPostProcessors(postProcessors);
162: }
163:
164: /**
165: * Specify the Spring LoadTimeWeaver to use for class instrumentation according
166: * to the JPA class transformer contract.
167: * <p>It is a not required to specify a LoadTimeWeaver: Most providers will be
168: * able to provide a subset of their functionality without class instrumentation
169: * as well, or operate with their VM agent specified on JVM startup.
170: * <p>In terms of Spring-provided weaving options, the most important ones are
171: * InstrumentationLoadTimeWeaver, which requires a Spring-specific (but very general)
172: * VM agent specified on JVM startup, and ReflectiveLoadTimeWeaver, which interacts
173: * with an underlying ClassLoader based on specific extended methods being available
174: * on it (for example, interacting with Spring's TomcatInstrumentableClassLoader).
175: * <p><b>NOTE:</b> As of Spring 2.5, the context's default LoadTimeWeaver (defined
176: * as bean with name "loadTimeWeaver") will be picked up automatically, if available,
177: * removing the need for LoadTimeWeaver configuration on each affected target bean.</b>
178: * Consider using the <code>context:load-time-weaver</code> XML tag for creating
179: * such a shared LoadTimeWeaver (autodetecting the environment by default).
180: * <p><b>NOTE: Only applied if no external PersistenceUnitManager specified.</b>
181: * Otherwise, the external {@link #setPersistenceUnitManager PersistenceUnitManager}
182: * is responsible for the weaving configuration.
183: * @see org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver
184: * @see org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver
185: * @see org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader
186: */
187: public void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver) {
188: this .internalPersistenceUnitManager
189: .setLoadTimeWeaver(loadTimeWeaver);
190: }
191:
192: public void setResourceLoader(ResourceLoader resourceLoader) {
193: this .internalPersistenceUnitManager
194: .setResourceLoader(resourceLoader);
195: this .beanClassLoader = resourceLoader.getClassLoader();
196: }
197:
198: @Override
199: protected EntityManagerFactory createNativeEntityManagerFactory()
200: throws PersistenceException {
201: PersistenceUnitManager managerToUse = this .persistenceUnitManager;
202: if (this .persistenceUnitManager == null) {
203: this .internalPersistenceUnitManager.afterPropertiesSet();
204: managerToUse = this .internalPersistenceUnitManager;
205: }
206:
207: this .persistenceUnitInfo = determinePersistenceUnitInfo(managerToUse);
208:
209: PersistenceProvider provider = getPersistenceProvider();
210: if (provider == null) {
211: String providerClassName = this .persistenceUnitInfo
212: .getPersistenceProviderClassName();
213: if (providerClassName == null) {
214: throw new IllegalArgumentException(
215: "No PersistenceProvider specified in EntityManagerFactory configuration, "
216: + "and chosen PersistenceUnitInfo does not specify a provider class name either");
217: }
218: Class providerClass = ClassUtils.resolveClassName(
219: providerClassName, this .beanClassLoader);
220: provider = (PersistenceProvider) BeanUtils
221: .instantiateClass(providerClass);
222: }
223: if (provider == null) {
224: throw new IllegalStateException(
225: "Unable to determine persistence provider. "
226: + "Please check configuration of "
227: + getClass().getName()
228: + "; "
229: + "ideally specify the appropriate JpaVendorAdapter class for this provider.");
230: }
231:
232: if (logger.isInfoEnabled()) {
233: logger
234: .info("Building JPA container EntityManagerFactory for persistence unit '"
235: + this .persistenceUnitInfo
236: .getPersistenceUnitName() + "'");
237: }
238: this .nativeEntityManagerFactory = provider
239: .createContainerEntityManagerFactory(
240: this .persistenceUnitInfo, getJpaPropertyMap());
241: postProcessEntityManagerFactory(
242: this .nativeEntityManagerFactory,
243: this .persistenceUnitInfo);
244:
245: return this .nativeEntityManagerFactory;
246: }
247:
248: /**
249: * Determine the PersistenceUnitInfo to use for the EntityManagerFactory
250: * created by this bean.
251: * <p>The default implementation reads in all persistence unit infos from
252: * <code>persistence.xml</code>, as defined in the JPA specification.
253: * If no entity manager name was specified, it takes the first info in the
254: * array as returned by the reader. Otherwise, it checks for a matching name.
255: * @param persistenceUnitManager the PersistenceUnitManager to obtain from
256: * @return the chosen PersistenceUnitInfo
257: */
258: protected PersistenceUnitInfo determinePersistenceUnitInfo(
259: PersistenceUnitManager persistenceUnitManager) {
260: if (getPersistenceUnitName() != null) {
261: return persistenceUnitManager
262: .obtainPersistenceUnitInfo(getPersistenceUnitName());
263: } else {
264: return persistenceUnitManager
265: .obtainDefaultPersistenceUnitInfo();
266: }
267: }
268:
269: /**
270: * Hook method allowing subclasses to customize the EntityManagerFactory
271: * after its creation via the PersistenceProvider.
272: * <p>The default implementation is empty.
273: * @param emf the newly created EntityManagerFactory we are working with
274: * @param pui the PersistenceUnitInfo used to configure the EntityManagerFactory
275: * @see javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory
276: */
277: protected void postProcessEntityManagerFactory(
278: EntityManagerFactory emf, PersistenceUnitInfo pui) {
279: }
280:
281: public PersistenceUnitInfo getPersistenceUnitInfo() {
282: return this .persistenceUnitInfo;
283: }
284:
285: public String getPersistenceUnitName() {
286: if (this .persistenceUnitInfo != null) {
287: return this .persistenceUnitInfo.getPersistenceUnitName();
288: }
289: return super .getPersistenceUnitName();
290: }
291:
292: public DataSource getDataSource() {
293: if (this.persistenceUnitInfo != null) {
294: return this.persistenceUnitInfo.getNonJtaDataSource();
295: }
296: return this.internalPersistenceUnitManager
297: .getDefaultDataSource();
298: }
299:
300: }
|