Source Code Cross Referenced for LazyConnectionDataSourceProxy.java in  » J2EE » spring-framework-2.0.6 » org » springframework » jdbc » datasource » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » J2EE » spring framework 2.0.6 » org.springframework.jdbc.datasource 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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.jdbc.datasource;
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:        import java.sql.Connection;
024:        import java.sql.SQLException;
025:
026:        import javax.sql.DataSource;
027:
028:        import org.apache.commons.logging.Log;
029:        import org.apache.commons.logging.LogFactory;
030:
031:        /**
032:         * Proxy for a target DataSource, fetching actual JDBC Connections lazily,
033:         * i.e. not until first creation of a Statement. Connection initialization
034:         * properties like auto-commit mode, transaction isolation and read-only mode
035:         * will be kept and applied to the actual JDBC Connection as soon as an
036:         * actual Connection is fetched (if ever). Consequently, commit and rollback
037:         * calls will be ignored if no Statements have been created.
038:         *
039:         * <p>This DataSource proxy allows to avoid fetching JDBC Connections from
040:         * a pool unless actually necessary. JDBC transaction control can happen
041:         * without fetching a Connection from the pool or communicating with the
042:         * database; this will be done lazily on first creation of a JDBC Statement.
043:         *
044:         * <p><b>If you configure both a LazyConnectionDataSourceProxy and a
045:         * TransactionAwareDataSourceProxy, make sure that the latter is the outermost
046:         * DataSource.</b> In such a scenario, data access code will talk to the
047:         * transaction-aware DataSource, which will in turn work with the
048:         * LazyConnectionDataSourceProxy.
049:         *
050:         * <p>Lazy fetching of physical JDBC Connections is particularly beneficial
051:         * in a generic transaction demarcation environment. It allows you to demarcate
052:         * transactions on all methods that could potentially perform data access,
053:         * without paying a performance penalty if no actual data access happens.
054:         *
055:         * <p>This DataSource proxy gives you behavior analogous to JTA and a
056:         * transactional JNDI DataSource (as provided by the J2EE server), even
057:         * with a local transaction strategy like DataSourceTransactionManager or
058:         * HibernateTransactionManager. It does not add value with Spring's
059:         * JtaTransactionManager as transaction strategy.
060:         *
061:         * <p>Lazy fetching of JDBC Connections is also recommended for read-only
062:         * operations with Hibernate, in particular if the chances of resolving the
063:         * result in the second-level cache are high. This avoids the need to
064:         * communicate with the database at all for such read-only operations.
065:         * You will get the same effect with non-transactional reads, but lazy fetching
066:         * of JDBC Connections allows you to still perform reads in transactions.
067:         *
068:         * <p><b>NOTE:</b> This DataSource proxy needs to return wrapped Connections to
069:         * handle lazy fetching of an actual JDBC Connection. Therefore, the returned
070:         * Connections cannot be cast to a native JDBC Connection type like OracleConnection,
071:         * or to a connection pool implementation type. Use a corresponding
072:         * NativeJdbcExtractor to retrieve the native JDBC Connection.
073:         *
074:         * @author Juergen Hoeller
075:         * @since 1.1.4
076:         * @see ConnectionProxy
077:         * @see DataSourceTransactionManager
078:         * @see org.springframework.orm.hibernate.HibernateTransactionManager
079:         * @see org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor
080:         */
081:        public class LazyConnectionDataSourceProxy extends DelegatingDataSource {
082:
083:            private static final Log logger = LogFactory
084:                    .getLog(LazyConnectionDataSourceProxy.class);
085:
086:            private Boolean defaultAutoCommit;
087:
088:            private Integer defaultTransactionIsolation;
089:
090:            /**
091:             * Create a new LazyConnectionDataSourceProxy.
092:             * @see #setTargetDataSource
093:             */
094:            public LazyConnectionDataSourceProxy() {
095:            }
096:
097:            /**
098:             * Create a new LazyConnectionDataSourceProxy.
099:             * @param targetDataSource the target DataSource
100:             */
101:            public LazyConnectionDataSourceProxy(DataSource targetDataSource) {
102:                setTargetDataSource(targetDataSource);
103:                afterPropertiesSet();
104:            }
105:
106:            /**
107:             * Set the default auto-commit mode to expose when no target Connection
108:             * has been fetched yet (-> actual JDBC Connection default not known yet).
109:             * <p>If not specified, the default gets determined by checking a target
110:             * Connection on startup. If that check fails, the default will be determined
111:             * lazily on first access of a Connection.
112:             * @see java.sql.Connection#getAutoCommit
113:             */
114:            public void setDefaultAutoCommit(boolean defaultAutoCommit) {
115:                this .defaultAutoCommit = new Boolean(defaultAutoCommit);
116:            }
117:
118:            /**
119:             * Set the default transaction isolation level to expose when no target Connection
120:             * has been fetched yet (-> actual JDBC Connection default not known yet).
121:             * <p>If not specified, the default gets determined by checking a target
122:             * Connection on startup. If that check fails, the default will be determined
123:             * lazily on first access of a Connection.
124:             * @see java.sql.Connection#getTransactionIsolation
125:             */
126:            public void setDefaultTransactionIsolation(
127:                    int defaultTransactionIsolation) {
128:                this .defaultTransactionIsolation = new Integer(
129:                        defaultTransactionIsolation);
130:            }
131:
132:            public void afterPropertiesSet() {
133:                super .afterPropertiesSet();
134:
135:                // Determine default auto-commit and transaction isolation
136:                // via a Connection from the target DataSource, if possible.
137:                if (this .defaultAutoCommit == null
138:                        || this .defaultTransactionIsolation == null) {
139:                    try {
140:                        Connection con = getTargetDataSource().getConnection();
141:                        try {
142:                            checkDefaultConnectionProperties(con);
143:                        } finally {
144:                            con.close();
145:                        }
146:                    } catch (SQLException ex) {
147:                        logger
148:                                .warn(
149:                                        "Could not retrieve default auto-commit and transaction isolation settings",
150:                                        ex);
151:                    }
152:                }
153:            }
154:
155:            /**
156:             * Check the default connection properties (auto-commit, transaction isolation),
157:             * keeping them to be able to expose them correctly without fetching an actual
158:             * JDBC Connection from the target DataSource.
159:             * <p>This will be invoked once on startup, but also for each retrieval of a
160:             * target Connection. If the check failed on startup (because the database was
161:             * down), we'll lazily retrieve those settings.
162:             * @param con the Connection to use for checking
163:             * @throws SQLException if thrown by Connection methods
164:             */
165:            protected synchronized void checkDefaultConnectionProperties(
166:                    Connection con) throws SQLException {
167:                if (this .defaultAutoCommit == null) {
168:                    this .defaultAutoCommit = new Boolean(con.getAutoCommit());
169:                }
170:                if (this .defaultTransactionIsolation == null) {
171:                    this .defaultTransactionIsolation = new Integer(con
172:                            .getTransactionIsolation());
173:                }
174:            }
175:
176:            /**
177:             * Expose the default auto-commit value.
178:             */
179:            protected Boolean defaultAutoCommit() {
180:                return defaultAutoCommit;
181:            }
182:
183:            /**
184:             * Expose the default transaction isolation value.
185:             */
186:            protected Integer defaultTransactionIsolation() {
187:                return defaultTransactionIsolation;
188:            }
189:
190:            /**
191:             * Return a Connection handle that lazily fetches an actual JDBC Connection
192:             * when asked for a Statement (or PreparedStatement or CallableStatement).
193:             * <p>The returned Connection handle implements the ConnectionProxy interface,
194:             * allowing to retrieve the underlying target Connection.
195:             * @return a lazy Connection handle
196:             * @see ConnectionProxy#getTargetConnection
197:             */
198:            public Connection getConnection() throws SQLException {
199:                return (Connection) Proxy.newProxyInstance(
200:                        ConnectionProxy.class.getClassLoader(),
201:                        new Class[] { ConnectionProxy.class },
202:                        new LazyConnectionInvocationHandler());
203:            }
204:
205:            /**
206:             * Return a Connection handle that lazily fetches an actual JDBC Connection
207:             * when asked for a Statement (or PreparedStatement or CallableStatement).
208:             * <p>The returned Connection handle implements the ConnectionProxy interface,
209:             * allowing to retrieve the underlying target Connection.
210:             * @param username the per-Connection username
211:             * @param password the per-Connection password
212:             * @return a lazy Connection handle
213:             * @see ConnectionProxy#getTargetConnection
214:             */
215:            public Connection getConnection(String username, String password)
216:                    throws SQLException {
217:                return (Connection) Proxy
218:                        .newProxyInstance(ConnectionProxy.class
219:                                .getClassLoader(),
220:                                new Class[] { ConnectionProxy.class },
221:                                new LazyConnectionInvocationHandler(username,
222:                                        password));
223:            }
224:
225:            /**
226:             * Invocation handler that defers fetching an actual JDBC Connection
227:             * until first creation of a Statement.
228:             */
229:            private class LazyConnectionInvocationHandler implements 
230:                    InvocationHandler {
231:
232:                private String username;
233:
234:                private String password;
235:
236:                private Boolean readOnly = Boolean.FALSE;
237:
238:                private Integer transactionIsolation;
239:
240:                private Boolean autoCommit;
241:
242:                private boolean closed = false;
243:
244:                private Connection target;
245:
246:                public LazyConnectionInvocationHandler() {
247:                    this .autoCommit = defaultAutoCommit();
248:                    this .transactionIsolation = defaultTransactionIsolation();
249:                }
250:
251:                public LazyConnectionInvocationHandler(String username,
252:                        String password) {
253:                    this ();
254:                    this .username = username;
255:                    this .password = password;
256:                }
257:
258:                public Object invoke(Object proxy, Method method, Object[] args)
259:                        throws Throwable {
260:                    // Invocation on ConnectionProxy interface coming in...
261:
262:                    if (method.getName().equals("getTargetConnection")) {
263:                        // Handle getTargetConnection method: return underlying connection.
264:                        return getTargetConnection(method);
265:                    } else if (method.getName().equals("equals")) {
266:                        // We must avoid fetching a target Connection for "equals".
267:                        // Only consider equal when proxies are identical.
268:                        return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE);
269:                    } else if (method.getName().equals("hashCode")) {
270:                        // We must avoid fetching a target Connection for "hashCode",
271:                        // and we must return the same hash code even when the target
272:                        // Connection has been fetched: use hashCode of Connection proxy.
273:                        return new Integer(hashCode());
274:                    }
275:
276:                    if (!hasTargetConnection()) {
277:                        // No physical target Connection kept yet ->
278:                        // resolve transaction demarcation methods without fetching
279:                        // a physical JDBC Connection until absolutely necessary.
280:
281:                        if (method.getName().equals("toString")) {
282:                            return "Lazy Connection proxy for target DataSource ["
283:                                    + getTargetDataSource() + "]";
284:                        } else if (method.getName().equals("isReadOnly")) {
285:                            return this .readOnly;
286:                        } else if (method.getName().equals("setReadOnly")) {
287:                            this .readOnly = (Boolean) args[0];
288:                            return null;
289:                        } else if (method.getName().equals(
290:                                "getTransactionIsolation")) {
291:                            if (this .transactionIsolation != null) {
292:                                return this .transactionIsolation;
293:                            }
294:                            // Else fetch actual Connection and check there,
295:                            // because we didn't have a default specified.
296:                        } else if (method.getName().equals(
297:                                "setTransactionIsolation")) {
298:                            this .transactionIsolation = (Integer) args[0];
299:                            return null;
300:                        } else if (method.getName().equals("getAutoCommit")) {
301:                            if (this .autoCommit != null) {
302:                                return this .autoCommit;
303:                            }
304:                            // Else fetch actual Connection and check there,
305:                            // because we didn't have a default specified.
306:                        } else if (method.getName().equals("setAutoCommit")) {
307:                            this .autoCommit = (Boolean) args[0];
308:                            return null;
309:                        } else if (method.getName().equals("commit")) {
310:                            // Ignore: no statements created yet.
311:                            return null;
312:                        } else if (method.getName().equals("rollback")) {
313:                            // Ignore: no statements created yet.
314:                            return null;
315:                        } else if (method.getName().equals("getWarnings")) {
316:                            return null;
317:                        } else if (method.getName().equals("clearWarnings")) {
318:                            return null;
319:                        } else if (method.getName().equals("isClosed")) {
320:                            return (this .closed ? Boolean.TRUE : Boolean.FALSE);
321:                        } else if (method.getName().equals("close")) {
322:                            // Ignore: no target connection yet.
323:                            this .closed = true;
324:                            return null;
325:                        } else if (this .closed) {
326:                            // Connection proxy closed, without ever having fetched a
327:                            // physical JDBC Connection: throw corresponding SQLException.
328:                            throw new SQLException(
329:                                    "Illegal operation: connection is closed");
330:                        }
331:                    }
332:
333:                    // Target Connection already fetched,
334:                    // or target Connection necessary for current operation ->
335:                    // invoke method on target connection.
336:                    try {
337:                        return method.invoke(getTargetConnection(method), args);
338:                    } catch (InvocationTargetException ex) {
339:                        throw ex.getTargetException();
340:                    }
341:                }
342:
343:                /**
344:                 * Return whether the proxy currently holds a target Connection.
345:                 */
346:                private boolean hasTargetConnection() {
347:                    return (this .target != null);
348:                }
349:
350:                /**
351:                 * Return the target Connection, fetching it and initializing it if necessary.
352:                 */
353:                private Connection getTargetConnection(Method operation)
354:                        throws SQLException {
355:                    if (this .target == null) {
356:                        // No target Connection held -> fetch one.
357:                        if (logger.isDebugEnabled()) {
358:                            logger
359:                                    .debug("Connecting to database for operation '"
360:                                            + operation.getName() + "'");
361:                        }
362:
363:                        // Fetch physical Connection from DataSource.
364:                        this .target = (this .username != null) ? getTargetDataSource()
365:                                .getConnection(this .username, this .password)
366:                                : getTargetDataSource().getConnection();
367:
368:                        // If we still lack default connection properties, check them now.
369:                        checkDefaultConnectionProperties(this .target);
370:
371:                        // Apply kept transaction settings, if any.
372:                        if (this .readOnly.booleanValue()) {
373:                            this .target.setReadOnly(this .readOnly
374:                                    .booleanValue());
375:                        }
376:                        if (this .transactionIsolation != null
377:                                && !this .transactionIsolation
378:                                        .equals(defaultTransactionIsolation())) {
379:                            this .target
380:                                    .setTransactionIsolation(this .transactionIsolation
381:                                            .intValue());
382:                        }
383:                        if (this .autoCommit != null
384:                                && this .autoCommit.booleanValue() != this .target
385:                                        .getAutoCommit()) {
386:                            this .target.setAutoCommit(this .autoCommit
387:                                    .booleanValue());
388:                        }
389:                    }
390:
391:                    else {
392:                        // Target Connection already held -> return it.
393:                        if (logger.isDebugEnabled()) {
394:                            logger
395:                                    .debug("Using existing database connection for operation '"
396:                                            + operation.getName() + "'");
397:                        }
398:                    }
399:
400:                    return this.target;
401:                }
402:            }
403:
404:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.