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.jdo;
018:
019: import java.io.IOException;
020: import java.util.HashMap;
021: import java.util.Map;
022: import java.util.Properties;
023:
024: import javax.jdo.JDOException;
025: import javax.jdo.JDOHelper;
026: import javax.jdo.PersistenceManagerFactory;
027:
028: import org.apache.commons.logging.Log;
029: import org.apache.commons.logging.LogFactory;
030:
031: import org.springframework.beans.factory.BeanClassLoaderAware;
032: import org.springframework.beans.factory.DisposableBean;
033: import org.springframework.beans.factory.FactoryBean;
034: import org.springframework.beans.factory.InitializingBean;
035: import org.springframework.core.io.Resource;
036: import org.springframework.core.io.support.PropertiesLoaderUtils;
037: import org.springframework.dao.DataAccessException;
038: import org.springframework.dao.support.PersistenceExceptionTranslator;
039: import org.springframework.util.CollectionUtils;
040:
041: /**
042: * {@link org.springframework.beans.factory.FactoryBean} that creates a
043: * JDO {@link javax.jdo.PersistenceManagerFactory}. This is the usual way to
044: * set up a shared JDO PersistenceManagerFactory in a Spring application context;
045: * the PersistenceManagerFactory can then be passed to JDO-based DAOs via
046: * dependency injection. Note that switching to a JNDI lookup or to a bean-style
047: * PersistenceManagerFactory instance is just a matter of configuration!
048: *
049: * <p>Configuration settings can either be read from a properties file,
050: * specified as "configLocation", or locally specified. Properties
051: * specified as "jdoProperties" here will override any settings in a file.
052: * On JDO 2.1, you may alternatively specify a "persistenceManagerFactoryName",
053: * referring to a PMF definition in "META-INF/jdoconfig.xml"
054: * (see {@link #setPersistenceManagerFactoryName}).
055: *
056: * <p><b>NOTE: This class requires JDO 2.0 or higher, as of Spring 2.5.</b>
057: *
058: * <p>This class also implements the
059: * {@link org.springframework.dao.support.PersistenceExceptionTranslator}
060: * interface, as autodetected by Spring's
061: * {@link org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor},
062: * for AOP-based translation of native exceptions to Spring DataAccessExceptions.
063: * Hence, the presence of a LocalPersistenceManagerFactoryBean automatically enables
064: * a PersistenceExceptionTranslationPostProcessor to translate JDO exceptions.
065: *
066: * <p><b>Alternative: Configuration of a PersistenceManagerFactory provider bean</b>
067: *
068: * <p>As alternative to the properties-driven approach that this FactoryBean offers
069: * (which is analogous to using the standard JDOHelper class with a Properties
070: * object that is populated with standard JDO properties), you can set up an
071: * instance of your PersistenceManagerFactory implementation class directly.
072: *
073: * <p>Like a DataSource, a PersistenceManagerFactory is encouraged to
074: * support bean-style configuration, which makes it very easy to set up as
075: * Spring-managed bean. The implementation class becomes the bean class;
076: * the remaining properties are applied as bean properties (starting with
077: * lower-case characters, in contrast to the corresponding JDO properties).
078: *
079: * <p>For example, in case of <a href="http://www.jpox.org">JPOX</a>:
080: *
081: * <p><pre>
082: * <bean id="persistenceManagerFactory" class="org.jpox.PersistenceManagerFactoryImpl" destroy-method="close">
083: * <property name="connectionFactory" ref="dataSource"/>
084: * <property name="nontransactionalRead" value="true"/>
085: * </bean>
086: * </pre>
087: *
088: * <p>Note that such direct setup of a PersistenceManagerFactory implementation
089: * is the only way to pass an external connection factory (i.e. a JDBC DataSource)
090: * into a JDO PersistenceManagerFactory. With the standard properties-driven approach,
091: * you can only use an internal connection pool or a JNDI DataSource.
092: *
093: * <p>The <code>close()</code> method is standardized in JDO; don't forget to
094: * specify it as "destroy-method" for any PersistenceManagerFactory instance.
095: * Note that this FactoryBean will automatically invoke <code>close()</code> for
096: * the PersistenceManagerFactory that it creates, without any special configuration.
097: *
098: * @author Juergen Hoeller
099: * @since 03.06.2003
100: * @see JdoTemplate#setPersistenceManagerFactory
101: * @see JdoTransactionManager#setPersistenceManagerFactory
102: * @see org.springframework.jndi.JndiObjectFactoryBean
103: * @see javax.jdo.JDOHelper#getPersistenceManagerFactory
104: * @see javax.jdo.PersistenceManagerFactory#setConnectionFactory
105: * @see javax.jdo.PersistenceManagerFactory#close()
106: * @see org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor
107: */
108: public class LocalPersistenceManagerFactoryBean implements FactoryBean,
109: BeanClassLoaderAware, InitializingBean, DisposableBean,
110: PersistenceExceptionTranslator {
111:
112: protected final Log logger = LogFactory.getLog(getClass());
113:
114: private String persistenceManagerFactoryName;
115:
116: private Resource configLocation;
117:
118: private final Map jdoPropertyMap = new HashMap();
119:
120: private ClassLoader beanClassLoader;
121:
122: private PersistenceManagerFactory persistenceManagerFactory;
123:
124: private JdoDialect jdoDialect;
125:
126: /**
127: * Specify the name of the desired PersistenceManagerFactory.
128: * <p>This may either be a properties resource in the classpath if such a resource exists
129: * (JDO 2.0), or a PMF definition with that name from "META-INF/jdoconfig.xml" (JDO 2.1),
130: * or a JPA EntityManagerFactory cast to a PersistenceManagerFactory based on the
131: * persistence-unit name from "META-INF/persistence.xml" (JDO 2.1 / JPA 1.0).
132: * <p>Default is none: Either 'persistenceManagerFactoryName' or 'configLocation'
133: * or 'jdoProperties' needs to be specified.
134: * @see #setConfigLocation
135: * @see #setJdoProperties
136: */
137: public void setPersistenceManagerFactoryName(
138: String persistenceManagerFactoryName) {
139: this .persistenceManagerFactoryName = persistenceManagerFactoryName;
140: }
141:
142: /**
143: * Set the location of the JDO properties config file, for example
144: * as classpath resource "classpath:kodo.properties".
145: * <p>Note: Can be omitted when all necessary properties are
146: * specified locally via this bean.
147: */
148: public void setConfigLocation(Resource configLocation) {
149: this .configLocation = configLocation;
150: }
151:
152: /**
153: * Set JDO properties, such as"javax.jdo.PersistenceManagerFactoryClass".
154: * <p>Can be used to override values in a JDO properties config file,
155: * or to specify all necessary properties locally.
156: * <p>Can be populated with a String "value" (parsed via PropertiesEditor)
157: * or a "props" element in XML bean definitions.
158: */
159: public void setJdoProperties(Properties jdoProperties) {
160: CollectionUtils.mergePropertiesIntoMap(jdoProperties,
161: this .jdoPropertyMap);
162: }
163:
164: /**
165: * Specify JDO properties as a Map, to be passed into
166: * <code>JDOHelper.getPersistenceManagerFactory</code> (if any).
167: * <p>Can be populated with a "map" or "props" element in XML bean definitions.
168: * @see javax.jdo.JDOHelper#getPersistenceManagerFactory(java.util.Map)
169: */
170: public void setJdoPropertyMap(Map jdoProperties) {
171: if (jdoProperties != null) {
172: this .jdoPropertyMap.putAll(jdoProperties);
173: }
174: }
175:
176: /**
177: * Allow Map access to the JDO properties to be passed to the JDOHelper,
178: * with the option to add or override specific entries.
179: * <p>Useful for specifying entries directly, for example via
180: * "jdoPropertyMap[myKey]".
181: */
182: public Map getJdoPropertyMap() {
183: return this .jdoPropertyMap;
184: }
185:
186: /**
187: * Set the JDO dialect to use for the PersistenceExceptionTranslator
188: * functionality of this factory.
189: * <p>Default is a DefaultJdoDialect based on the PersistenceManagerFactory's
190: * underlying DataSource, if any.
191: * @see JdoDialect#translateException
192: * @see #translateExceptionIfPossible
193: * @see org.springframework.dao.support.PersistenceExceptionTranslator
194: */
195: public void setJdoDialect(JdoDialect jdoDialect) {
196: this .jdoDialect = jdoDialect;
197: }
198:
199: public void setBeanClassLoader(ClassLoader beanClassLoader) {
200: this .beanClassLoader = beanClassLoader;
201: }
202:
203: /**
204: * Initialize the PersistenceManagerFactory for the given location.
205: * @throws IllegalArgumentException in case of illegal property values
206: * @throws IOException if the properties could not be loaded from the given location
207: * @throws JDOException in case of JDO initialization errors
208: */
209: public void afterPropertiesSet() throws IllegalArgumentException,
210: IOException, JDOException {
211: if (this .persistenceManagerFactoryName != null) {
212: if (this .configLocation != null
213: || !this .jdoPropertyMap.isEmpty()) {
214: throw new IllegalStateException(
215: "'configLocation'/'jdoProperties' not supported in "
216: + "combination with 'persistenceManagerFactoryName' - specify one or the other, not both");
217: }
218: if (logger.isInfoEnabled()) {
219: logger
220: .info("Building new JDO PersistenceManagerFactory for name '"
221: + this .persistenceManagerFactoryName
222: + "'");
223: }
224: this .persistenceManagerFactory = newPersistenceManagerFactory(this .persistenceManagerFactoryName);
225: }
226:
227: else {
228: Map mergedProps = new HashMap();
229:
230: if (this .configLocation != null) {
231: if (logger.isInfoEnabled()) {
232: logger.info("Loading JDO config from ["
233: + this .configLocation + "]");
234: }
235: mergedProps.putAll(PropertiesLoaderUtils
236: .loadProperties(this .configLocation));
237: }
238:
239: mergedProps.putAll(this .jdoPropertyMap);
240:
241: // Build PersistenceManagerFactory instance.
242: logger.info("Building new JDO PersistenceManagerFactory");
243: this .persistenceManagerFactory = newPersistenceManagerFactory(mergedProps);
244: }
245:
246: // Build default JdoDialect if none explicitly specified.
247: if (this .jdoDialect == null) {
248: this .jdoDialect = new DefaultJdoDialect(
249: this .persistenceManagerFactory
250: .getConnectionFactory());
251: }
252: }
253:
254: /**
255: * Subclasses can override this to perform custom initialization of the
256: * PersistenceManagerFactory instance, creating it for the specified name.
257: * <p>The default implementation invokes JDOHelper's
258: * <code>getPersistenceManagerFactory(String)</code> method.
259: * A custom implementation could prepare the instance in a specific way,
260: * or use a custom PersistenceManagerFactory implementation.
261: * @param name the name of the desired PersistenceManagerFactory
262: * @return the PersistenceManagerFactory instance
263: * @see javax.jdo.JDOHelper#getPersistenceManagerFactory(String)
264: */
265: protected PersistenceManagerFactory newPersistenceManagerFactory(
266: String name) {
267: return JDOHelper.getPersistenceManagerFactory(name,
268: this .beanClassLoader);
269: }
270:
271: /**
272: * Subclasses can override this to perform custom initialization of the
273: * PersistenceManagerFactory instance, creating it via the given Properties
274: * that got prepared by this LocalPersistenceManagerFactoryBean.
275: * <p>The default implementation invokes JDOHelper's
276: * <code>getPersistenceManagerFactory(Map)</code> method.
277: * A custom implementation could prepare the instance in a specific way,
278: * or use a custom PersistenceManagerFactory implementation.
279: * @param props the merged properties prepared by this LocalPersistenceManagerFactoryBean
280: * @return the PersistenceManagerFactory instance
281: * @see javax.jdo.JDOHelper#getPersistenceManagerFactory(java.util.Map)
282: */
283: protected PersistenceManagerFactory newPersistenceManagerFactory(
284: Map props) {
285: return JDOHelper.getPersistenceManagerFactory(props,
286: this .beanClassLoader);
287: }
288:
289: /**
290: * Return the singleton PersistenceManagerFactory.
291: */
292: public Object getObject() {
293: return this .persistenceManagerFactory;
294: }
295:
296: public Class getObjectType() {
297: return (this .persistenceManagerFactory != null ? this .persistenceManagerFactory
298: .getClass()
299: : PersistenceManagerFactory.class);
300: }
301:
302: public boolean isSingleton() {
303: return true;
304: }
305:
306: /**
307: * Implementation of the PersistenceExceptionTranslator interface,
308: * as autodetected by Spring's PersistenceExceptionTranslationPostProcessor.
309: * <p>Converts the exception if it is a JDOException, preferably using a specified
310: * JdoDialect. Else returns <code>null</code> to indicate an unknown exception.
311: * @see org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor
312: * @see JdoDialect#translateException
313: * @see PersistenceManagerFactoryUtils#convertJdoAccessException
314: */
315: public DataAccessException translateExceptionIfPossible(
316: RuntimeException ex) {
317: if (ex instanceof JDOException) {
318: if (this .jdoDialect != null) {
319: return this .jdoDialect
320: .translateException((JDOException) ex);
321: } else {
322: return PersistenceManagerFactoryUtils
323: .convertJdoAccessException((JDOException) ex);
324: }
325: }
326: return null;
327: }
328:
329: /**
330: * Close the PersistenceManagerFactory on bean factory shutdown.
331: */
332: public void destroy() {
333: logger.info("Closing JDO PersistenceManagerFactory");
334: this.persistenceManagerFactory.close();
335: }
336:
337: }
|