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.jca.cci.connection;
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 javax.resource.NotSupportedException;
025: import javax.resource.ResourceException;
026: import javax.resource.cci.Connection;
027: import javax.resource.cci.ConnectionFactory;
028: import javax.resource.cci.ConnectionSpec;
029:
030: import org.apache.commons.logging.Log;
031: import org.apache.commons.logging.LogFactory;
032:
033: import org.springframework.beans.factory.DisposableBean;
034: import org.springframework.util.Assert;
035:
036: /**
037: * A CCI ConnectionFactory adapter that returns the same Connection on all
038: * <code>getConnection</code> calls, and ignores calls to
039: * <code>Connection.close()</code>.
040: *
041: * <p>Useful for testing and standalone environments, to keep using the same
042: * Connection for multiple CciTemplate calls, without having a pooling
043: * ConnectionFactory, also spanning any number of transactions.
044: *
045: * <p>You can either pass in a CCI Connection directly, or let this
046: * factory lazily create a Connection via a given target ConnectionFactory.
047: *
048: * @author Juergen Hoeller
049: * @since 1.2
050: * @see #getConnection()
051: * @see javax.resource.cci.Connection#close()
052: * @see org.springframework.jca.cci.core.CciTemplate
053: */
054: public class SingleConnectionFactory extends
055: DelegatingConnectionFactory implements DisposableBean {
056:
057: protected final Log logger = LogFactory.getLog(getClass());
058:
059: /** Wrapped Connection */
060: private Connection target;
061:
062: /** Proxy Connection */
063: private Connection connection;
064:
065: /** Synchronization monitor for the shared Connection */
066: private final Object connectionMonitor = new Object();
067:
068: /**
069: * Create a new SingleConnectionFactory for bean-style usage.
070: * @see #setTargetConnectionFactory
071: */
072: public SingleConnectionFactory() {
073: }
074:
075: /**
076: * Create a new SingleConnectionFactory that always returns the
077: * given Connection.
078: * @param target the single Connection
079: */
080: public SingleConnectionFactory(Connection target) {
081: Assert.notNull(target, "Target Connection must not be null");
082: this .target = target;
083: this .connection = getCloseSuppressingConnectionProxy(target);
084: }
085:
086: /**
087: * Create a new SingleConnectionFactory that always returns a single
088: * Connection that it will lazily create via the given target
089: * ConnectionFactory.
090: * @param targetConnectionFactory the target ConnectionFactory
091: */
092: public SingleConnectionFactory(
093: ConnectionFactory targetConnectionFactory) {
094: Assert.notNull(targetConnectionFactory,
095: "Target ConnectionFactory must not be null");
096: setTargetConnectionFactory(targetConnectionFactory);
097: }
098:
099: /**
100: * Make sure a Connection or ConnectionFactory has been set.
101: */
102: public void afterPropertiesSet() {
103: if (this .connection == null
104: && getTargetConnectionFactory() == null) {
105: throw new IllegalArgumentException(
106: "Connection or targetConnectionFactory is required");
107: }
108: }
109:
110: public Connection getConnection() throws ResourceException {
111: synchronized (this .connectionMonitor) {
112: if (this .connection == null) {
113: initConnection();
114: }
115: return this .connection;
116: }
117: }
118:
119: public Connection getConnection(ConnectionSpec connectionSpec)
120: throws ResourceException {
121: throw new NotSupportedException(
122: "SingleConnectionFactory does not support custom ConnectionSpec");
123: }
124:
125: /**
126: * Close the underlying Connection.
127: * The provider of this ConnectionFactory needs to care for proper shutdown.
128: * <p>As this bean implements DisposableBean, a bean factory will
129: * automatically invoke this on destruction of its cached singletons.
130: */
131: public void destroy() {
132: resetConnection();
133: }
134:
135: /**
136: * Initialize the single underlying Connection.
137: * <p>Closes and reinitializes the Connection if an underlying
138: * Connection is present already.
139: * @throws javax.resource.ResourceException if thrown by CCI API methods
140: */
141: public void initConnection() throws ResourceException {
142: if (getTargetConnectionFactory() == null) {
143: throw new IllegalStateException(
144: "targetConnectionFactory is required for lazily initializing a Connection");
145: }
146: synchronized (this .connectionMonitor) {
147: if (this .target != null) {
148: closeConnection(this .target);
149: }
150: this .target = doCreateConnection();
151: prepareConnection(this .target);
152: if (logger.isInfoEnabled()) {
153: logger.info("Established shared CCI Connection: "
154: + this .target);
155: }
156: this .connection = getCloseSuppressingConnectionProxy(this .target);
157: }
158: }
159:
160: /**
161: * Reset the underlying shared Connection, to be reinitialized on next access.
162: */
163: public void resetConnection() {
164: synchronized (this .connectionMonitor) {
165: if (this .target != null) {
166: closeConnection(this .target);
167: }
168: this .target = null;
169: this .connection = null;
170: }
171: }
172:
173: /**
174: * Create a CCI Connection via this template's ConnectionFactory.
175: * @return the new CCI Connection
176: * @throws javax.resource.ResourceException if thrown by CCI API methods
177: */
178: protected Connection doCreateConnection() throws ResourceException {
179: return getTargetConnectionFactory().getConnection();
180: }
181:
182: /**
183: * Prepare the given Connection before it is exposed.
184: * <p>The default implementation is empty. Can be overridden in subclasses.
185: * @param con the Connection to prepare
186: */
187: protected void prepareConnection(Connection con)
188: throws ResourceException {
189: }
190:
191: /**
192: * Close the given Connection.
193: * @param con the Connection to close
194: */
195: protected void closeConnection(Connection con) {
196: try {
197: con.close();
198: } catch (Throwable ex) {
199: logger.warn("Could not close shared CCI Connection", ex);
200: }
201: }
202:
203: /**
204: * Wrap the given Connection with a proxy that delegates every method call to it
205: * but suppresses close calls. This is useful for allowing application code to
206: * handle a special framework Connection just like an ordinary Connection from a
207: * CCI ConnectionFactory.
208: * @param target the original Connection to wrap
209: * @return the wrapped Connection
210: */
211: protected Connection getCloseSuppressingConnectionProxy(
212: Connection target) {
213: return (Connection) Proxy.newProxyInstance(Connection.class
214: .getClassLoader(), new Class[] { Connection.class },
215: new CloseSuppressingInvocationHandler(target));
216: }
217:
218: /**
219: * Invocation handler that suppresses close calls on CCI Connections.
220: */
221: private static class CloseSuppressingInvocationHandler implements
222: InvocationHandler {
223:
224: private final Connection target;
225:
226: private CloseSuppressingInvocationHandler(Connection target) {
227: this .target = target;
228: }
229:
230: public Object invoke(Object proxy, Method method, Object[] args)
231: throws Throwable {
232: if (method.getName().equals("equals")) {
233: // Only consider equal when proxies are identical.
234: return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE);
235: } else if (method.getName().equals("hashCode")) {
236: // Use hashCode of Connection proxy.
237: return new Integer(hashCode());
238: } else if (method.getName().equals("close")) {
239: // Handle close method: don't pass the call on.
240: return null;
241: }
242: try {
243: return method.invoke(this .target, args);
244: } catch (InvocationTargetException ex) {
245: throw ex.getTargetException();
246: }
247: }
248: }
249:
250: }
|