001: /*
002: * Copyright 2002-2006 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.toplink;
018:
019: import oracle.toplink.exceptions.ConcurrencyException;
020: import oracle.toplink.exceptions.ConversionException;
021: import oracle.toplink.exceptions.DatabaseException;
022: import oracle.toplink.exceptions.OptimisticLockException;
023: import oracle.toplink.exceptions.QueryException;
024: import oracle.toplink.exceptions.TopLinkException;
025: import oracle.toplink.sessions.Session;
026: import org.apache.commons.logging.Log;
027: import org.apache.commons.logging.LogFactory;
028:
029: import org.springframework.dao.ConcurrencyFailureException;
030: import org.springframework.dao.DataAccessException;
031: import org.springframework.dao.DataAccessResourceFailureException;
032: import org.springframework.dao.TypeMismatchDataAccessException;
033: import org.springframework.transaction.support.TransactionSynchronizationAdapter;
034: import org.springframework.transaction.support.TransactionSynchronizationManager;
035: import org.springframework.util.Assert;
036:
037: /**
038: * Helper class featuring methods for TopLink Session handling,
039: * allowing for reuse of TopLink Session instances within transactions.
040: * Also provides support for exception translation.
041: *
042: * <p>Mainly intended for internal use within the framework.
043: *
044: * @author Juergen Hoeller
045: * @author <a href="mailto:james.x.clark@oracle.com">James Clark</a>
046: * @since 1.2
047: */
048: public abstract class SessionFactoryUtils {
049:
050: private static final Log logger = LogFactory
051: .getLog(SessionFactoryUtils.class);
052:
053: /**
054: * Get a TopLink Session for the given SessionFactory. Is aware of and will
055: * return any existing corresponding Session bound to the current thread, for
056: * example when using TopLinkTransactionManager. Will create a new Session
057: * otherwise, if "allowCreate" is <code>true</code>.
058: * <p>This is the <code>getSession</code> method used by typical data access code,
059: * in combination with <code>releaseSession</code> called when done with
060: * the Session. Note that TopLinkTemplate allows to write data access code
061: * without caring about such resource handling.
062: * @param sessionFactory TopLink SessionFactory to create the session with
063: * @param allowCreate if a non-transactional Session should be created when no
064: * transactional Session can be found for the current thread
065: * @return the TopLink Session
066: * @throws DataAccessResourceFailureException if the Session couldn't be created
067: * @throws IllegalStateException if no thread-bound Session found and
068: * "allowCreate" is <code>false</code>
069: * @see #releaseSession
070: * @see TopLinkTemplate
071: */
072: public static Session getSession(SessionFactory sessionFactory,
073: boolean allowCreate)
074: throws DataAccessResourceFailureException,
075: IllegalStateException {
076:
077: try {
078: return doGetSession(sessionFactory, allowCreate);
079: } catch (TopLinkException ex) {
080: throw new DataAccessResourceFailureException(
081: "Could not open TopLink Session", ex);
082: }
083: }
084:
085: /**
086: * Get a TopLink Session for the given SessionFactory. Is aware of and will
087: * return any existing corresponding Session bound to the current thread, for
088: * example when using TopLinkTransactionManager. Will create a new Session
089: * otherwise, if "allowCreate" is <code>true</code>.
090: * <p>Same as <code>getSession</code>, but throwing the original TopLinkException.
091: * @param sessionFactory TopLink SessionFactory to create the session with
092: * @param allowCreate if a non-transactional Session should be created when no
093: * transactional Session can be found for the current thread
094: * @return the TopLink Session
095: * @throws TopLinkException if the Session couldn't be created
096: * @throws IllegalStateException if no thread-bound Session found and
097: * "allowCreate" is <code>false</code>
098: * @see #releaseSession
099: * @see TopLinkTemplate
100: */
101: public static Session doGetSession(SessionFactory sessionFactory,
102: boolean allowCreate) throws TopLinkException,
103: IllegalStateException {
104:
105: Assert.notNull(sessionFactory, "No SessionFactory specified");
106:
107: SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager
108: .getResource(sessionFactory);
109: if (sessionHolder != null) {
110: return sessionHolder.getSession();
111: }
112:
113: if (!allowCreate
114: && !TransactionSynchronizationManager
115: .isSynchronizationActive()) {
116: throw new IllegalStateException(
117: "No TopLink Session bound to thread, "
118: + "and configuration does not allow creation of non-transactional one here");
119: }
120:
121: logger.debug("Creating TopLink Session");
122: Session session = sessionFactory.createSession();
123:
124: if (TransactionSynchronizationManager.isSynchronizationActive()) {
125: logger
126: .debug("Registering new Spring transaction synchronization for new TopLink Session");
127: // Use same Session for further TopLink actions within the transaction.
128: // Thread object will get removed by synchronization at transaction completion.
129: sessionHolder = new SessionHolder(session);
130: sessionHolder.setSynchronizedWithTransaction(true);
131: TransactionSynchronizationManager
132: .registerSynchronization(new SessionSynchronization(
133: sessionHolder, sessionFactory));
134: TransactionSynchronizationManager.bindResource(
135: sessionFactory, sessionHolder);
136: }
137:
138: return session;
139: }
140:
141: /**
142: * Return whether the given TopLink Session is transactional, that is,
143: * bound to the current thread by Spring's transaction facilities.
144: * @param session the TopLink Session to check
145: * @param sessionFactory TopLink SessionFactory that the Session was created with
146: * (can be <code>null</code>)
147: * @return whether the Session is transactional
148: */
149: public static boolean isSessionTransactional(Session session,
150: SessionFactory sessionFactory) {
151: if (sessionFactory == null) {
152: return false;
153: }
154: SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager
155: .getResource(sessionFactory);
156: return (sessionHolder != null && session == sessionHolder
157: .getSession());
158: }
159:
160: /**
161: * Convert the given TopLinkException to an appropriate exception from the
162: * <code>org.springframework.dao</code> hierarchy.
163: * @param ex TopLinkException that occured
164: * @return the corresponding DataAccessException instance
165: */
166: public static DataAccessException convertTopLinkAccessException(
167: TopLinkException ex) {
168: if (ex instanceof DatabaseException) {
169: // SQLException during TopLink access: only passed in here from custom code,
170: // as TopLinkTemplate will use SQLExceptionTranslator-based handling.
171: return new TopLinkJdbcException((DatabaseException) ex);
172: }
173: if (ex instanceof OptimisticLockException) {
174: return new TopLinkOptimisticLockingFailureException(
175: (OptimisticLockException) ex);
176: }
177: if (ex instanceof QueryException) {
178: return new TopLinkQueryException((QueryException) ex);
179: }
180: if (ex instanceof ConcurrencyException) {
181: return new ConcurrencyFailureException(ex.getMessage(), ex);
182: }
183: if (ex instanceof ConversionException) {
184: return new TypeMismatchDataAccessException(ex.getMessage(),
185: ex);
186: }
187: // fallback
188: return new TopLinkSystemException(ex);
189: }
190:
191: /**
192: * Close the given Session, created via the given factory,
193: * if it is not managed externally (i.e. not bound to the thread).
194: * @param session the TopLink Session to close
195: * @param sessionFactory TopLink SessionFactory that the Session was created with
196: * (can be <code>null</code>)
197: */
198: public static void releaseSession(Session session,
199: SessionFactory sessionFactory) {
200: if (session == null) {
201: return;
202: }
203: // Only release non-transactional Sessions.
204: if (!isSessionTransactional(session, sessionFactory)) {
205: doRelease(session);
206: }
207: }
208:
209: /**
210: * Perform the actual releasing of the TopLink Session.
211: * @param session the TopLink Session to release
212: */
213: private static void doRelease(Session session) {
214: if (session != null) {
215: logger.debug("Closing TopLink Session");
216: try {
217: session.release();
218: } catch (TopLinkException ex) {
219: logger.debug("Could not close TopLink Session", ex);
220: } catch (Throwable ex) {
221: logger
222: .debug(
223: "Unexpected exception on closing TopLink Session",
224: ex);
225: }
226: }
227: }
228:
229: /**
230: * Callback for resource cleanup at the end of a Spring-managed JTA transaction,
231: * i.e. when participating in a JtaTransactionManager transaction.
232: * @see org.springframework.transaction.jta.JtaTransactionManager
233: */
234: private static class SessionSynchronization extends
235: TransactionSynchronizationAdapter {
236:
237: private final SessionHolder sessionHolder;
238:
239: private final SessionFactory sessionFactory;
240:
241: private boolean holderActive = true;
242:
243: private SessionSynchronization(SessionHolder sessionHolder,
244: SessionFactory sessionFactory) {
245: this .sessionHolder = sessionHolder;
246: this .sessionFactory = sessionFactory;
247: }
248:
249: public void suspend() {
250: if (this .holderActive) {
251: TransactionSynchronizationManager
252: .unbindResource(this .sessionFactory);
253: }
254: }
255:
256: public void resume() {
257: if (this .holderActive) {
258: TransactionSynchronizationManager.bindResource(
259: this .sessionFactory, this .sessionHolder);
260: }
261: }
262:
263: public void beforeCompletion() {
264: TransactionSynchronizationManager
265: .unbindResource(this .sessionFactory);
266: this .holderActive = false;
267: }
268:
269: public void afterCompletion(int status) {
270: releaseSession(this.sessionHolder.getSession(),
271: this.sessionFactory);
272: }
273: }
274:
275: }
|