KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > jca > cci > connection > SingleConnectionFactory


1 /*
2  * Copyright 2002-2006 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.jca.cci.connection;
18
19 import java.lang.reflect.InvocationHandler JavaDoc;
20 import java.lang.reflect.InvocationTargetException JavaDoc;
21 import java.lang.reflect.Method JavaDoc;
22 import java.lang.reflect.Proxy JavaDoc;
23
24 import javax.resource.NotSupportedException JavaDoc;
25 import javax.resource.ResourceException JavaDoc;
26 import javax.resource.cci.Connection JavaDoc;
27 import javax.resource.cci.ConnectionFactory JavaDoc;
28 import javax.resource.cci.ConnectionSpec JavaDoc;
29
30 import org.apache.commons.logging.Log;
31 import org.apache.commons.logging.LogFactory;
32
33 import org.springframework.beans.factory.DisposableBean;
34 import org.springframework.util.Assert;
35
36 /**
37  * A CCI ConnectionFactory adapter that returns the same Connection on all
38  * <code>getConnection</code> calls, and ignores calls to
39  * <code>Connection.close()</code>.
40  *
41  * <p>Useful for testing and standalone environments, to keep using the same
42  * Connection for multiple CciTemplate calls, without having a pooling
43  * ConnectionFactory, also spanning any number of transactions.
44  *
45  * <p>You can either pass in a CCI Connection directly, or let this
46  * factory lazily create a Connection via a given target ConnectionFactory.
47  *
48  * @author Juergen Hoeller
49  * @since 1.2
50  * @see #getConnection()
51  * @see javax.resource.cci.Connection#close()
52  * @see org.springframework.jca.cci.core.CciTemplate
53  */

54 public class SingleConnectionFactory extends DelegatingConnectionFactory implements DisposableBean {
55
56     protected final Log logger = LogFactory.getLog(getClass());
57
58     /** Wrapped Connection */
59     private Connection JavaDoc target;
60
61     /** Proxy Connection */
62     private Connection JavaDoc connection;
63
64     /** Synchronization monitor for the shared Connection */
65     private final Object JavaDoc connectionMonitor = new Object JavaDoc();
66
67
68     /**
69      * Create a new SingleConnectionFactory for bean-style usage.
70      * @see #setTargetConnectionFactory
71      */

72     public SingleConnectionFactory() {
73     }
74
75     /**
76      * Create a new SingleConnectionFactory that always returns the
77      * given Connection.
78      * @param target the single Connection
79      */

80     public SingleConnectionFactory(Connection JavaDoc target) {
81         Assert.notNull(target, "Target Connection must not be null");
82         this.target = target;
83         this.connection = getCloseSuppressingConnectionProxy(target);
84     }
85
86     /**
87      * Create a new SingleConnectionFactory that always returns a single
88      * Connection that it will lazily create via the given target
89      * ConnectionFactory.
90      * @param targetConnectionFactory the target ConnectionFactory
91      */

92     public SingleConnectionFactory(ConnectionFactory JavaDoc targetConnectionFactory) {
93         Assert.notNull(targetConnectionFactory, "Target ConnectionFactory must not be null");
94         setTargetConnectionFactory(targetConnectionFactory);
95     }
96
97     /**
98      * Make sure a Connection or ConnectionFactory has been set.
99      */

100     public void afterPropertiesSet() {
101         if (this.connection == null && getTargetConnectionFactory() == null) {
102             throw new IllegalArgumentException JavaDoc("Connection or targetConnectionFactory is required");
103         }
104     }
105
106
107     public Connection JavaDoc getConnection() throws ResourceException JavaDoc {
108         synchronized (this.connectionMonitor) {
109             if (this.connection == null) {
110                 initConnection();
111             }
112             return this.connection;
113         }
114     }
115
116     public Connection JavaDoc getConnection(ConnectionSpec JavaDoc connectionSpec) throws ResourceException JavaDoc {
117         throw new NotSupportedException JavaDoc(
118                 "SingleConnectionFactory does not support custom ConnectionSpec");
119     }
120
121     /**
122      * Close the underlying Connection.
123      * The provider of this ConnectionFactory needs to care for proper shutdown.
124      * <p>As this bean implements DisposableBean, a bean factory will
125      * automatically invoke this on destruction of its cached singletons.
126      */

127     public void destroy() {
128         resetConnection();
129     }
130
131
132     /**
133      * Initialize the single underlying Connection.
134      * <p>Closes and reinitializes the Connection if an underlying
135      * Connection is present already.
136      * @throws javax.resource.ResourceException if thrown by CCI API methods
137      */

138     public void initConnection() throws ResourceException JavaDoc {
139         if (getTargetConnectionFactory() == null) {
140             throw new IllegalStateException JavaDoc("targetConnectionFactory is required for lazily initializing a Connection");
141         }
142         synchronized (this.connectionMonitor) {
143             if (this.target != null) {
144                 closeConnection(this.target);
145             }
146             this.target = doCreateConnection();
147             prepareConnection(this.target);
148             if (logger.isInfoEnabled()) {
149                 logger.info("Established shared CCI Connection: " + this.target);
150             }
151             this.connection = getCloseSuppressingConnectionProxy(this.target);
152         }
153     }
154
155     /**
156      * Reset the underlying shared Connection, to be reinitialized on next access.
157      */

158     public void resetConnection() {
159         synchronized (this.connectionMonitor) {
160             if (this.target != null) {
161                 closeConnection(this.target);
162             }
163             this.target = null;
164             this.connection = null;
165         }
166     }
167
168     /**
169      * Create a CCI Connection via this template's ConnectionFactory.
170      * @return the new CCI Connection
171      * @throws javax.resource.ResourceException if thrown by CCI API methods
172      */

173     protected Connection JavaDoc doCreateConnection() throws ResourceException JavaDoc {
174         return getTargetConnectionFactory().getConnection();
175     }
176
177     /**
178      * Prepare the given Connection before it is exposed.
179      * <p>The default implementation is empty. Can be overridden in subclasses.
180      * @param con the Connection to prepare
181      */

182     protected void prepareConnection(Connection JavaDoc con) throws ResourceException JavaDoc {
183     }
184
185     /**
186      * Close the given Connection.
187      * @param con the Connection to close
188      */

189     protected void closeConnection(Connection JavaDoc con) {
190         try {
191             con.close();
192         }
193         catch (Throwable JavaDoc ex) {
194             logger.warn("Could not close shared CCI Connection", ex);
195         }
196     }
197
198     /**
199      * Wrap the given Connection with a proxy that delegates every method call to it
200      * but suppresses close calls. This is useful for allowing application code to
201      * handle a special framework Connection just like an ordinary Connection from a
202      * CCI ConnectionFactory.
203      * @param target the original Connection to wrap
204      * @return the wrapped Connection
205      */

206     protected Connection JavaDoc getCloseSuppressingConnectionProxy(Connection JavaDoc target) {
207         return (Connection JavaDoc) Proxy.newProxyInstance(
208                 Connection JavaDoc.class.getClassLoader(),
209                 new Class JavaDoc[] {Connection JavaDoc.class},
210                 new CloseSuppressingInvocationHandler(target));
211     }
212
213
214     /**
215      * Invocation handler that suppresses close calls on CCI Connections.
216      */

217     private static class CloseSuppressingInvocationHandler implements InvocationHandler JavaDoc {
218
219         private final Connection JavaDoc target;
220
221         private CloseSuppressingInvocationHandler(Connection JavaDoc target) {
222             this.target = target;
223         }
224
225         public Object JavaDoc invoke(Object JavaDoc proxy, Method JavaDoc method, Object JavaDoc[] args) throws Throwable JavaDoc {
226             if (method.getName().equals("equals")) {
227                 // Only consider equal when proxies are identical.
228
return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE);
229             }
230             else if (method.getName().equals("hashCode")) {
231                 // Use hashCode of Connection proxy.
232
return new Integer JavaDoc(hashCode());
233             }
234             else if (method.getName().equals("close")) {
235                 // Handle close method: don't pass the call on.
236
return null;
237             }
238             try {
239                 return method.invoke(this.target, args);
240             }
241             catch (InvocationTargetException JavaDoc ex) {
242                 throw ex.getTargetException();
243             }
244         }
245     }
246
247 }
248
Popular Tags