KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > orm > toplink > LocalSessionFactory


1 /*
2  * Copyright 2002-2007 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.springframework.orm.toplink;
18
19 import java.lang.reflect.Constructor JavaDoc;
20 import java.lang.reflect.Method JavaDoc;
21 import java.util.HashMap JavaDoc;
22 import java.util.Map JavaDoc;
23 import java.util.Properties JavaDoc;
24
25 import javax.sql.DataSource JavaDoc;
26
27 import oracle.toplink.exceptions.TopLinkException;
28 import oracle.toplink.internal.databaseaccess.DatabasePlatform;
29 import oracle.toplink.jndi.JNDIConnector;
30 import oracle.toplink.sessionbroker.SessionBroker;
31 import oracle.toplink.sessions.DatabaseLogin;
32 import oracle.toplink.sessions.DatabaseSession;
33 import oracle.toplink.sessions.SessionLog;
34 import oracle.toplink.threetier.ServerSession;
35 import oracle.toplink.tools.sessionconfiguration.XMLLoader;
36 import oracle.toplink.tools.sessionmanagement.SessionManager;
37 import org.apache.commons.logging.Log;
38 import org.apache.commons.logging.LogFactory;
39
40 import org.springframework.beans.BeanWrapperImpl;
41 import org.springframework.util.ClassUtils;
42 import org.springframework.util.CollectionUtils;
43 import org.springframework.util.ReflectionUtils;
44
45 /**
46  * Convenient JavaBean-style factory for a TopLink SessionFactory instance.
47  * Loads a TopLink <code>sessions.xml</code> file from the class path, exposing a
48  * specific TopLink Session defined there (usually a ServerSession).
49  *
50  * <p>TopLink Session configuration is done using a <code>sessions.xml</code> file.
51  * The most convenient way to create the <code>sessions.xml</code> file is to use
52  * the Oracle TopLink SessionsEditor workbench. The <code>sessions.xml</code> file
53  * contains all runtime configuration and points to a second XML or Class resource
54  * from which to load the actual TopLink project metadata (which defines mappings).
55  *
56  * <p>LocalSessionFactory loads the <code>sessions.xml</code> file during
57  * initialization in order to bootstrap the specified TopLink (Server)Session.
58  * The name of the actual config resource and the name of the Session to be loaded,
59  * if different from <code>sessions.xml</code> and "Session", respectively, can be
60  * configured through bean properties.
61  *
62  * <p>All resources (<code>sessions.xml</code> and Mapping Workbench metadata) are
63  * loaded using <code>ClassLoader.getResourceAsStream</code> calls by TopLink, so
64  * users may need to configure a ClassLoader with appropriate visibility. This is
65  * particularly important in J2EE environments where the TopLink metadata might be
66  * deployed to a different location than the Spring configuration. The ClassLoader
67  * used to search for the TopLink metadata and to load the persistent classes
68  * defined there will default to the the context ClassLoader for the current Thread.
69  *
70  * <p>TopLink's debug logging can be redirected to Commons Logging by passing a
71  * CommonsLoggingSessionLog to the "sessionLog" bean property. Otherwise, TopLink
72  * uses it's own DefaultSessionLog, whose levels are configured in the
73  * <code>sessions.xml</code> file.
74  *
75  * <p>This class has been tested against both TopLink 9.0.4 and TopLink 10.1.3.
76  * It will automatically adapt to the TopLink version encountered: for example,
77  * using an XMLSessionConfigLoader on 10.1.3, but an XMLLoader on 9.0.4.
78  *
79  * <p><b>NOTE:</b> When defining a TopLink SessionFactory in a Spring application
80  * context, you will usually define a bean of type <b>LocalSessionFactoryBean</b>.
81  * LocalSessionFactoryBean is a subclass of this factory, which will automatically
82  * expose the created TopLink SessionFactory instance as bean reference.
83  *
84  * @author Juergen Hoeller
85  * @author <a HREF="mailto:james.x.clark@oracle.com">James Clark</a>
86  * @since 1.2
87  * @see LocalSessionFactoryBean
88  * @see TopLinkTemplate#setSessionFactory
89  * @see TopLinkTransactionManager#setSessionFactory
90  * @see SingleSessionFactory
91  * @see ServerSessionFactory
92  * @see oracle.toplink.threetier.ServerSession
93  * @see oracle.toplink.tools.sessionconfiguration.XMLLoader
94  * @see oracle.toplink.tools.sessionconfiguration.XMLSessionConfigLoader
95  */

96 public class LocalSessionFactory {
97
98     /**
99      * The default location of the <code>sessions.xml</code> TopLink configuration file:
100      * "sessions.xml" in the class path.
101      */

102     public static final String JavaDoc DEFAULT_SESSIONS_XML = "sessions.xml";
103
104     /**
105      * The default session name to look for in the sessions.xml: "Session".
106      */

107     public static final String JavaDoc DEFAULT_SESSION_NAME = "Session";
108
109
110     protected final Log logger = LogFactory.getLog(getClass());
111
112     /**
113      * The classpath location of the sessions TopLink configuration file.
114      */

115     private String JavaDoc configLocation = DEFAULT_SESSIONS_XML;
116
117     /**
118      * The session name to look for in the sessions.xml configuration file.
119      */

120     private String JavaDoc sessionName = DEFAULT_SESSION_NAME;
121
122     /**
123      * The ClassLoader to use to load the sessions.xml and project XML files.
124      */

125     private ClassLoader JavaDoc sessionClassLoader;
126
127     private DatabaseLogin databaseLogin;
128
129     private final Map JavaDoc loginPropertyMap = new HashMap JavaDoc();
130
131     private DataSource JavaDoc dataSource;
132
133     private DatabasePlatform databasePlatform;
134
135     private SessionLog sessionLog;
136
137
138     /**
139      * Set the TopLink <code>sessions.xml</code> configuration file that defines
140      * TopLink Sessions, as class path resource location.
141      * <p>The <code>sessions.xml</code> file will usually be placed in the META-INF
142      * directory or root path of a JAR file, or the <code>WEB-INF/classes</code>
143      * directory of a WAR file (specifying "META-INF/toplink-sessions.xml" or
144      * simply "toplink-sessions.xml" as config location, respectively).
145      * <p>The default config location is "sessions.xml" in the root of the class path.
146      * @param configLocation the class path location of the <code>sessions.xml</code> file
147      */

148     public void setConfigLocation(String JavaDoc configLocation) {
149         this.configLocation = configLocation;
150     }
151
152     /**
153      * Set the name of the TopLink Session, as defined in TopLink's
154      * <code>sessions.xml</code> configuration file.
155      * The default session name is "Session".
156      */

157     public void setSessionName(String JavaDoc sessionName) {
158         this.sessionName = sessionName;
159     }
160
161     /**
162      * Set the ClassLoader that should be used to lookup the config resources.
163      * If nothing is set here, then we will try to use the Thread context ClassLoader
164      * and the ClassLoader that loaded this factory class, in that order.
165      * <p>This ClassLoader will be used to load the TopLink configuration files
166      * and the project metadata. Furthermore, the TopLink ConversionManager will
167      * use this ClassLoader to load all TopLink entity classes. If the latter is not
168      * appropriate, users can configure a pre-login SessionEvent to alter the
169      * ConversionManager ClassLoader that TopLink will use at runtime.
170      */

171     public void setSessionClassLoader(ClassLoader JavaDoc sessionClassLoader) {
172         this.sessionClassLoader = sessionClassLoader;
173     }
174
175     /**
176      * Specify the DatabaseLogin instance that carries the TopLink database
177      * configuration to use. This is an alternative to specifying that information
178      * in a &lt;login&gt; tag in the <code>sessions.xml</code> configuration file,
179      * allowing for configuring a DatabaseLogin instance as standard Spring bean
180      * definition (being able to leverage Spring's placeholder mechanism, etc).
181      * <p>The DatabaseLogin instance can either carry traditional JDBC config properties
182      * or hold a nested TopLink Connector instance, pointing to the connection pool to use.
183      * DatabaseLogin also holds the TopLink DatabasePlatform instance that defines the
184      * database product that TopLink is talking to (for example, HSQLPlatform).
185      * <p><b>WARNING:</b> Overriding the Login instance has been reported to not
186      * work on TopLink 10.1.3.0 and 10.1.3.1. Specify {@link #setLoginProperties
187      * "loginProperties"} or {@link #getLoginPropertyMap "loginPropertyMap[...]"}
188      * entries instead, if you prefer to have the login configuration defined
189      * on the Spring LocalSessionFactory.
190      */

191     public void setDatabaseLogin(DatabaseLogin databaseLogin) {
192         this.databaseLogin = databaseLogin;
193     }
194
195     /**
196      * Specify TopLink login properties, to be passed to
197      * the {@link oracle.toplink.sessions.DatabaseLogin} instance.
198      * <p>Can be populated with a String "value" (parsed via PropertiesEditor)
199      * or a "props" element in XML bean definitions.
200      * @see oracle.toplink.sessions.DatabaseLogin
201      */

202     public void setLoginProperties(Properties JavaDoc loginProperties) {
203         CollectionUtils.mergePropertiesIntoMap(loginProperties, this.loginPropertyMap);
204     }
205
206     /**
207      * Specify TopLink login properties as a Map, to be passed to
208      * the {@link oracle.toplink.sessions.DatabaseLogin} instance.
209      * <p>Can be populated with a "map" or "props" element in XML bean definitions.
210      * @see oracle.toplink.sessions.DatabaseLogin
211      */

212     public void setLoginPropertyMap(Map JavaDoc loginProperties) {
213         if (loginProperties != null) {
214             this.loginPropertyMap.putAll(loginProperties);
215         }
216     }
217
218     /**
219      * Allow Map access to the TopLink login properties to be passed to the
220      * DatabaseLogin instance, with the option to add or override specific entries.
221      * <p>Useful for specifying entries directly, for example via
222      * "loginPropertyMap[tableQualifier]".
223      * @see oracle.toplink.sessions.DatabaseLogin
224      */

225     public Map JavaDoc getLoginPropertyMap() {
226         return this.loginPropertyMap;
227     }
228
229     /**
230      * Specify a standard JDBC DataSource that TopLink should use as connection pool.
231      * This allows for using a shared DataSource definition instead of TopLink's
232      * own connection pool.
233      * <p>A passed-in DataSource will be wrapped in an appropriate TopLink Connector
234      * and registered with the TopLink DatabaseLogin instance (either the default
235      * instance or one passed in through the "databaseLogin" property). The
236      * "usesExternalConnectionPooling" flag will automatically be set to "true".
237      * @see oracle.toplink.sessions.DatabaseLogin#setConnector(oracle.toplink.sessions.Connector)
238      * @see oracle.toplink.sessions.DatabaseLogin#setUsesExternalConnectionPooling(boolean)
239      * @see #setDatabaseLogin(oracle.toplink.sessions.DatabaseLogin)
240      */

241     public void setDataSource(DataSource JavaDoc dataSource) {
242         this.dataSource = dataSource;
243     }
244
245     /**
246      * Specify the TopLink DatabasePlatform instance that the Session should use:
247      * for example, HSQLPlatform. This is an alternative to specifying the platform
248      * in a &lt;login&gt; tag in the <code>sessions.xml</code> configuration file.
249      * <p>A passed-in DatabasePlatform will be registered with the TopLink
250      * DatabaseLogin instance (either the default instance or one passed in
251      * through the "databaseLogin" property).
252      * @see oracle.toplink.internal.databaseaccess.HSQLPlatform
253      * @see oracle.toplink.platform.database.HSQLPlatform
254      */

255     public void setDatabasePlatform(DatabasePlatform databasePlatform) {
256         this.databasePlatform = databasePlatform;
257     }
258
259     /**
260      * Specify a TopLink SessionLog instance to use for detailed logging of the
261      * Session's activities: for example, DefaultSessionLog (which logs to the
262      * console), JavaLog (which logs through JDK 1.4'S <code>java.util.logging</code>,
263      * available as of TopLink 10.1.3), or CommonsLoggingSessionLog /
264      * CommonsLoggingSessionLog904 (which logs through Commons Logging,
265      * on TopLink 10.1.3 and 9.0.4, respectively).
266      * <p>Note that detailed Session logging is usually only useful for debug
267      * logging, with adjustable detail level. As of TopLink 10.1.3, TopLink also
268      * uses different log categories, which allows for fine-grained filtering of
269      * log messages. For standard execution, no SessionLog needs to be specified.
270      * @see oracle.toplink.sessions.DefaultSessionLog
271      * @see oracle.toplink.logging.DefaultSessionLog
272      * @see oracle.toplink.logging.JavaLog
273      * @see org.springframework.orm.toplink.support.CommonsLoggingSessionLog
274      * @see org.springframework.orm.toplink.support.CommonsLoggingSessionLog904
275      */

276     public void setSessionLog(SessionLog sessionLog) {
277         this.sessionLog = sessionLog;
278     }
279
280
281     /**
282      * Create a TopLink SessionFactory according to the configuration settings.
283      * @return the new TopLink SessionFactory
284      * @throws TopLinkException in case of errors
285      */

286     public SessionFactory createSessionFactory() throws TopLinkException {
287         if (logger.isInfoEnabled()) {
288             logger.info("Initializing TopLink SessionFactory from [" + this.configLocation + "]");
289         }
290
291         // Determine class loader to use.
292
ClassLoader JavaDoc classLoader =
293                 (this.sessionClassLoader != null ? this.sessionClassLoader : ClassUtils.getDefaultClassLoader());
294
295         // Initialize the TopLink Session, using the configuration file
296
// and the session name.
297
DatabaseSession session = loadDatabaseSession(this.configLocation, this.sessionName, classLoader);
298
299         // It is possible for SessionManager to return a null Session!
300
if (session == null) {
301             throw new IllegalStateException JavaDoc(
302                     "A session named '" + this.sessionName + "' could not be loaded from resource [" +
303                     this.configLocation + "] using ClassLoader [" + classLoader + "]. " +
304                     "This is most likely a deployment issue: Can the class loader access the resource?");
305         }
306
307         DatabaseLogin login = (this.databaseLogin != null ? this.databaseLogin : session.getLogin());
308
309         // Apply specified login properties to the DatabaseLogin instance.
310
if (this.loginPropertyMap != null) {
311             new BeanWrapperImpl(login).setPropertyValues(this.loginPropertyMap);
312         }
313
314         // Override default connection pool with specified DataSource, if any.
315
if (this.dataSource != null) {
316             login.setConnector(new JNDIConnector(this.dataSource));
317             login.setUsesExternalConnectionPooling(true);
318         }
319
320         // Override default DatabasePlatform with specified one, if any.
321
if (this.databasePlatform != null) {
322             login.usePlatform(this.databasePlatform);
323         }
324
325         // Override default DatabaseLogin instance with specified one, if any.
326
if (this.databaseLogin != null) {
327             setDatabaseLogin(session, this.databaseLogin);
328         }
329
330         // Override default SessionLog with specified one, if any.
331
if (this.sessionLog != null) {
332             session.setSessionLog(this.sessionLog);
333             session.logMessages();
334         }
335
336         // Log in and create corresponding SessionFactory.
337
session.login();
338         return newSessionFactory(session);
339     }
340
341     /**
342      * Handle differences between the <code>Session.setLogin</code> interface
343      * between TopLink 9.0.4 to 10.1.3.
344      * <p>The Login interface was introduced in TopLink 10.1.3.
345      * @param session the DatabaseSession being logged in
346      * @param login the DatabaseLogin injected by Spring
347      * @see oracle.toplink.sessions.DatabaseSession#setLogin
348      */

349     protected void setDatabaseLogin(DatabaseSession session, DatabaseLogin login) {
350         Method JavaDoc setLoginMethod = null;
351         try {
352             // Search for the new 10.1.3 Login interface...
353
Class JavaDoc loginClass = Class.forName("oracle.toplink.sessions.Login");
354             setLoginMethod = DatabaseSession.class.getMethod("setLogin", new Class JavaDoc[] {loginClass});
355             if (logger.isDebugEnabled()) {
356                 logger.debug("Using TopLink 10.1.3 setLogin(Login) API");
357             }
358         }
359         catch (Exception JavaDoc ex) {
360             // TopLink 10.1.3 Login interface not found ->
361
// fall back to TopLink 9.0.4's setLogin(DatabaseLogin)
362
if (logger.isDebugEnabled()) {
363                 logger.debug("Using TopLink 9.0.4 setLogin(DatabaseLogin) API");
364             }
365             session.setLogin(login);
366             return;
367         }
368
369         // Invoke the 10.1.3 version: Session.setLogin(Login)
370
ReflectionUtils.invokeMethod(setLoginMethod, session, new Object JavaDoc[] {login});
371     }
372
373     /**
374      * Load the specified DatabaseSession from the TopLink <code>sessions.xml</code>
375      * configuration file.
376      * @param configLocation the class path location of the <code>sessions.xml</code> file
377      * @param sessionName the name of the TopLink Session in the configuration file
378      * @param sessionClassLoader the class loader to use
379      * @return the DatabaseSession instance
380      * @throws TopLinkException in case of errors
381      */

382     protected DatabaseSession loadDatabaseSession(
383             String JavaDoc configLocation, String JavaDoc sessionName, ClassLoader JavaDoc sessionClassLoader)
384             throws TopLinkException {
385
386         SessionManager manager = getSessionManager();
387
388         // Try to find TopLink 10.1.3 XMLSessionConfigLoader.
389
Method JavaDoc getSessionMethod = null;
390         Object JavaDoc loader = null;
391         try {
392             Class JavaDoc loaderClass = Class.forName("oracle.toplink.tools.sessionconfiguration.XMLSessionConfigLoader");
393             getSessionMethod = SessionManager.class.getMethod("getSession",
394                     new Class JavaDoc[] {loaderClass, String JavaDoc.class, ClassLoader JavaDoc.class, boolean.class, boolean.class, boolean.class});
395             if (logger.isDebugEnabled()) {
396                 logger.debug("Using TopLink 10.1.3 XMLSessionConfigLoader");
397             }
398             Constructor JavaDoc ctor = loaderClass.getConstructor(new Class JavaDoc[] {String JavaDoc.class});
399             loader = ctor.newInstance(new Object JavaDoc[] {configLocation});
400         }
401         catch (Exception JavaDoc ex) {
402             // TopLink 10.1.3 XMLSessionConfigLoader not found ->
403
// fall back to TopLink 9.0.4 XMLLoader.
404
if (logger.isDebugEnabled()) {
405                 logger.debug("Using TopLink 9.0.4 XMLLoader");
406             }
407             XMLLoader xmlLoader = new XMLLoader(configLocation);
408             return (DatabaseSession) manager.getSession(xmlLoader, sessionName, sessionClassLoader, false, false);
409         }
410
411         // TopLink 10.1.3 XMLSessionConfigLoader found -> create loader instance
412
// through reflection and fetch specified Session from SessionManager.
413
// This invocation will check if the ClassLoader passed in is the same
414
// as the one used to a session currently loaded with the same "sessionName"
415
// If the ClassLoaders are different, then this LocalSessionFactory is being
416
// re-loaded after a hot-deploy and the existing DatabaseSession will be logged
417
// out and re-built from scratch.
418
return (DatabaseSession) ReflectionUtils.invokeMethod(getSessionMethod, manager,
419                 new Object JavaDoc[] {loader, sessionName, sessionClassLoader, Boolean.FALSE, Boolean.FALSE, Boolean.TRUE});
420     }
421
422     /**
423      * Return the TopLink SessionManager to use for loading DatabaseSessions.
424      * <p>The default implementation creates a new plain SessionManager instance,
425      * leading to completely independent TopLink Session instances. Could be
426      * overridden to return a shared or pre-configured SessionManager.
427      * @return the TopLink SessionManager instance
428      */

429     protected SessionManager getSessionManager() {
430         return new SessionManager();
431     }
432
433     /**
434      * Create a new SessionFactory for the given TopLink DatabaseSession.
435      * <p>The default implementation creates a ServerSessionFactory for a
436      * ServerSession and a SingleSessionFactory for a plain DatabaseSession.
437      * @param session the TopLink DatabaseSession to create a SessionFactory for
438      * @return the SessionFactory
439      * @throws TopLinkException in case of errors
440      * @see ServerSessionFactory
441      * @see SingleSessionFactory
442      * @see oracle.toplink.threetier.ServerSession
443      * @see oracle.toplink.sessions.DatabaseSession
444      */

445     protected SessionFactory newSessionFactory(DatabaseSession session) {
446         if (session instanceof ServerSession) {
447             return new ServerSessionFactory((ServerSession) session);
448         }
449         else if (session instanceof SessionBroker) {
450             return new SessionBrokerSessionFactory((SessionBroker) session);
451         }
452         else {
453             return new SingleSessionFactory(session);
454         }
455     }
456
457 }
458
Popular Tags