001: /*
002: * Copyright 2002-2005 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 java.lang.reflect.InvocationHandler;
020: import java.lang.reflect.InvocationTargetException;
021: import java.lang.reflect.Method;
022: import java.lang.reflect.Proxy;
023:
024: import oracle.toplink.exceptions.TopLinkException;
025: import oracle.toplink.sessions.Session;
026: import oracle.toplink.sessions.UnitOfWork;
027: import org.apache.commons.logging.Log;
028: import org.apache.commons.logging.LogFactory;
029:
030: /**
031: * Abstract SessionFactory implementation that creates proxies for
032: * "managed" client Sessions and transaction-aware Session references.
033: *
034: * <p>Delegates to two template methods:
035: *
036: * @author Juergen Hoeller
037: * @since 1.2.6
038: * @see #getMasterSession()
039: * @see #createClientSession()
040: */
041: public abstract class AbstractSessionFactory implements SessionFactory {
042:
043: /** Logger available to subclasses */
044: protected final Log logger = LogFactory.getLog(getClass());
045:
046: /**
047: * Create a plain client Session for this factory's master Session.
048: * @see #createClientSession()
049: */
050: public Session createSession() throws TopLinkException {
051: logger.debug("Creating TopLink client Session");
052: return createClientSession();
053: }
054:
055: /**
056: * Create a "managed" client Session reference for an underlying
057: * client Session created for this factory.
058: * @see #createClientSession()
059: */
060: public Session createManagedClientSession() throws TopLinkException {
061: logger.debug("Creating managed TopLink client Session");
062: return (Session) Proxy.newProxyInstance(getClass()
063: .getClassLoader(), new Class[] { Session.class },
064: new ManagedClientInvocationHandler(
065: createClientSession()));
066: }
067:
068: /**
069: * Create a transaction-aware Session reference for this factory's master Session,
070: * expecting transactions to be registered for this SessionFactory.
071: * @see #getMasterSession()
072: * @see oracle.toplink.sessions.Session#getActiveSession()
073: * @see oracle.toplink.sessions.Session#getActiveUnitOfWork()
074: */
075: public Session createTransactionAwareSession()
076: throws TopLinkException {
077: logger.debug("Creating transaction-aware TopLink Session");
078: return createTransactionAwareSession(this );
079: }
080:
081: /**
082: * Create a transaction-aware Session reference for this factory's master Session,
083: * expecting transactions to be registered for the given SessionFactory.
084: * <p>This method is public to allow custom SessionFactory facades to access
085: * it directly, if necessary.
086: * @param sessionFactory the SessionFactory that transactions
087: * are expected to be registered for
088: * @see #getMasterSession()
089: * @see oracle.toplink.sessions.Session#getActiveSession()
090: * @see oracle.toplink.sessions.Session#getActiveUnitOfWork()
091: */
092: public Session createTransactionAwareSession(
093: SessionFactory sessionFactory) throws TopLinkException {
094: return (Session) Proxy.newProxyInstance(getClass()
095: .getClassLoader(), new Class[] { Session.class },
096: new TransactionAwareInvocationHandler(sessionFactory,
097: getMasterSession()));
098: }
099:
100: /**
101: * Return this factory's "master" Session.
102: * For example, a TopLink ServerSession.
103: * <p>Used for creating transaction-aware Session reference.
104: */
105: protected abstract Session getMasterSession();
106:
107: /**
108: * Create a new client Session for this factory's master Session.
109: * For example, a TopLink ClientSession.
110: * <p>Used for creating plain Sessions and "managed" client Sessions.
111: * @throws TopLinkException if creation of a client Session failed
112: */
113: protected abstract Session createClientSession()
114: throws TopLinkException;
115:
116: /**
117: * Invocation handler that decorates a client Session with an "active"
118: * UnitOfWork. For use in situations where Spring's TopLinkTransactionManager
119: * requires a "managed" thread-safe TopLink Session.
120: */
121: private static class ManagedClientInvocationHandler implements
122: InvocationHandler {
123:
124: private final Session target;
125:
126: private final UnitOfWork uow;
127:
128: public ManagedClientInvocationHandler(Session target) {
129: this .target = target;
130: this .uow = this .target.acquireUnitOfWork();
131: }
132:
133: public Object invoke(Object proxy, Method method, Object[] args)
134: throws Throwable {
135: if (method.getName().equals("getActiveSession")) {
136: return this .target;
137: } else if (method.getName().equals("getActiveUnitOfWork")) {
138: return this .uow;
139: } else if (method.getName().equals("release")) {
140: this .uow.release();
141: this .target.release();
142: } else if (method.getName().equals("equals")) {
143: // Only consider equal when proxies are identical.
144: return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE);
145: } else if (method.getName().equals("hashCode")) {
146: // Use hashCode of SessionFactory proxy.
147: return new Integer(hashCode());
148: }
149:
150: // Invoke method on target Session.
151: try {
152: return method.invoke(this .target, args);
153: } catch (InvocationTargetException ex) {
154: throw ex.getTargetException();
155: }
156: }
157: }
158:
159: /**
160: * Invocation handler that delegates <code>getActiveSession</code> calls
161: * to SessionFactoryUtils, for being aware of thread-bound transactions.
162: */
163: private static class TransactionAwareInvocationHandler implements
164: InvocationHandler {
165:
166: private final SessionFactory sessionFactory;
167:
168: private final Session target;
169:
170: public TransactionAwareInvocationHandler(
171: SessionFactory sessionFactory, Session target) {
172: this .sessionFactory = sessionFactory;
173: this .target = target;
174: }
175:
176: public Object invoke(Object proxy, Method method, Object[] args)
177: throws Throwable {
178: // Invocation on Session interface coming in...
179:
180: if (method.getName().equals("getActiveSession")) {
181: // Handle getActiveSession method: return transactional Session, if any.
182: try {
183: return SessionFactoryUtils.doGetSession(
184: this .sessionFactory, false);
185: } catch (IllegalStateException ex) {
186: // getActiveSession is supposed to return the Session itself if no active one found.
187: return this .target;
188: }
189: } else if (method.getName().equals("getActiveUnitOfWork")) {
190: // Handle getActiveUnitOfWork method: return transactional UnitOfWork, if any.
191: try {
192: return SessionFactoryUtils.doGetSession(
193: this .sessionFactory, false)
194: .getActiveUnitOfWork();
195: } catch (IllegalStateException ex) {
196: // getActiveUnitOfWork is supposed to return null if no active one found.
197: return null;
198: }
199: } else if (method.getName().equals("equals")) {
200: // Only consider equal when proxies are identical.
201: return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE);
202: } else if (method.getName().equals("hashCode")) {
203: // Use hashCode of SessionFactory proxy.
204: return new Integer(hashCode());
205: }
206:
207: // Invoke method on target Session.
208: try {
209: return method.invoke(this .target, args);
210: } catch (InvocationTargetException ex) {
211: throw ex.getTargetException();
212: }
213: }
214: }
215:
216: }
|