001/*
002 * Copyright 2002-2012 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 *      https://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
017package org.springframework.jdbc.support.nativejdbc;
018
019import java.lang.reflect.Method;
020import java.sql.CallableStatement;
021import java.sql.Connection;
022import java.sql.PreparedStatement;
023import java.sql.ResultSet;
024import java.sql.SQLException;
025import java.sql.Statement;
026
027import org.springframework.util.ReflectionUtils;
028
029/**
030 * Implementation of the {@link NativeJdbcExtractor} interface for JBoss,
031 * supporting JBoss Application Server 3.2.4+. As of Spring 3.1.1, it also
032 * supports JBoss 7.
033 *
034 * <p>Returns the underlying native Connection, Statement, etc to
035 * application code instead of JBoss' wrapper implementations.
036 * The returned JDBC classes can then safely be cast, e.g. to
037 * {@code oracle.jdbc.OracleConnection}.
038 *
039 * <p>This NativeJdbcExtractor can be set just to <i>allow</i> working with
040 * a JBoss connection pool: If a given object is not a JBoss wrapper,
041 * it will be returned as-is.
042 *
043 * @author Juergen Hoeller
044 * @since 03.01.2004
045 * @see org.jboss.resource.adapter.jdbc.WrappedConnection#getUnderlyingConnection
046 * @see org.jboss.resource.adapter.jdbc.WrappedStatement#getUnderlyingStatement
047 * @see org.jboss.resource.adapter.jdbc.WrappedResultSet#getUnderlyingResultSet
048 */
049public class JBossNativeJdbcExtractor extends NativeJdbcExtractorAdapter {
050
051        // JBoss 7
052        private static final String JBOSS_JCA_PREFIX = "org.jboss.jca.adapters.jdbc.";
053
054        // JBoss <= 6
055        private static final String JBOSS_RESOURCE_PREFIX = "org.jboss.resource.adapter.jdbc.";
056
057
058        private Class<?> wrappedConnectionClass;
059
060        private Class<?> wrappedStatementClass;
061
062        private Class<?> wrappedResultSetClass;
063
064        private Method getUnderlyingConnectionMethod;
065
066        private Method getUnderlyingStatementMethod;
067
068        private Method getUnderlyingResultSetMethod;
069
070
071        /**
072         * This constructor retrieves JBoss JDBC wrapper classes,
073         * so we can get the underlying vendor connection using reflection.
074         */
075        public JBossNativeJdbcExtractor() {
076                String prefix = JBOSS_JCA_PREFIX;
077                try {
078                        // trying JBoss 7 jca package first...
079                        this.wrappedConnectionClass = getClass().getClassLoader().loadClass(prefix + "WrappedConnection");
080                }
081                catch (ClassNotFoundException ex) {
082                        // JBoss 7 jca package not found -> try traditional resource package.
083                        prefix = JBOSS_RESOURCE_PREFIX;
084                        try {
085                                this.wrappedConnectionClass = getClass().getClassLoader().loadClass(prefix + "WrappedConnection");
086                        }
087                        catch (ClassNotFoundException ex2) {
088                                throw new IllegalStateException("Could not initialize JBossNativeJdbcExtractor: neither JBoss 7's [" +
089                                                JBOSS_JCA_PREFIX + ".WrappedConnection] nor traditional JBoss [" + JBOSS_RESOURCE_PREFIX +
090                                                ".WrappedConnection] found");
091                        }
092                }
093                try {
094                        this.wrappedStatementClass = getClass().getClassLoader().loadClass(prefix + "WrappedStatement");
095                        this.wrappedResultSetClass = getClass().getClassLoader().loadClass(prefix + "WrappedResultSet");
096                        this.getUnderlyingConnectionMethod =
097                                this.wrappedConnectionClass.getMethod("getUnderlyingConnection", (Class[]) null);
098                        this.getUnderlyingStatementMethod =
099                                this.wrappedStatementClass.getMethod("getUnderlyingStatement", (Class[]) null);
100                        this.getUnderlyingResultSetMethod =
101                                this.wrappedResultSetClass.getMethod("getUnderlyingResultSet", (Class[]) null);
102                }
103                catch (Exception ex) {
104                        throw new IllegalStateException(
105                                        "Could not initialize JBossNativeJdbcExtractor because of missing JBoss API methods/classes: " + ex);
106                }
107        }
108
109
110        /**
111         * Retrieve the Connection via JBoss' {@code getUnderlyingConnection} method.
112         */
113        @Override
114        protected Connection doGetNativeConnection(Connection con) throws SQLException {
115                if (this.wrappedConnectionClass.isAssignableFrom(con.getClass())) {
116                        return (Connection) ReflectionUtils.invokeJdbcMethod(this.getUnderlyingConnectionMethod, con);
117                }
118                return con;
119        }
120
121        /**
122         * Retrieve the Connection via JBoss' {@code getUnderlyingStatement} method.
123         */
124        @Override
125        public Statement getNativeStatement(Statement stmt) throws SQLException {
126                if (this.wrappedStatementClass.isAssignableFrom(stmt.getClass())) {
127                        return (Statement) ReflectionUtils.invokeJdbcMethod(this.getUnderlyingStatementMethod, stmt);
128                }
129                return stmt;
130        }
131
132        /**
133         * Retrieve the Connection via JBoss' {@code getUnderlyingStatement} method.
134         */
135        @Override
136        public PreparedStatement getNativePreparedStatement(PreparedStatement ps) throws SQLException {
137                return (PreparedStatement) getNativeStatement(ps);
138        }
139
140        /**
141         * Retrieve the Connection via JBoss' {@code getUnderlyingStatement} method.
142         */
143        @Override
144        public CallableStatement getNativeCallableStatement(CallableStatement cs) throws SQLException {
145                return (CallableStatement) getNativeStatement(cs);
146        }
147
148        /**
149         * Retrieve the Connection via JBoss' {@code getUnderlyingResultSet} method.
150         */
151        @Override
152        public ResultSet getNativeResultSet(ResultSet rs) throws SQLException {
153                if (this.wrappedResultSetClass.isAssignableFrom(rs.getClass())) {
154                        return (ResultSet) ReflectionUtils.invokeJdbcMethod(this.getUnderlyingResultSetMethod, rs);
155                }
156                return rs;
157        }
158
159}