/* * Copyright 2008-2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.data.jdbc.support.oracle; import java.sql.Connection; import java.sql.SQLException; import java.lang.reflect.Proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.dao.NonTransientDataAccessResourceException; import org.springframework.data.jdbc.support.ConnectionContextProvider; import org.springframework.data.jdbc.support.ConnectionPreparer; import org.springframework.data.jdbc.support.ConnectionUsernamePasswordProvider; import org.springframework.data.jdbc.support.ConnectionUsernameProvider; import org.springframework.util.Assert; import org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor; import org.springframework.jdbc.datasource.ConnectionProxy; import oracle.jdbc.OracleConnection; /** * A ConnectionPreparer that will delegate to a ConnectionContextProvider to obtaing the current user name * to be used for the proxy connection. * * @author Thomas Risberg * @since 1.0 */ public class ProxyConnectionPreparer implements ConnectionPreparer { private ConnectionContextProvider contextProvider; NativeJdbcExtractor jdbcExtractor; public void setContextProvider(ConnectionContextProvider contextProvider) { this.contextProvider = contextProvider; } public void setJdbcExtractor(NativeJdbcExtractor jdbcExtractor) { this.jdbcExtractor = jdbcExtractor; } public Connection prepare(Connection connection) { Assert.notNull(contextProvider, "You must provide a ConnectionContextProvider implementation that provides the username"); OracleConnection oraCon; if (contextProvider instanceof ConnectionUsernamePasswordProvider || contextProvider instanceof ConnectionUsernameProvider) { if (connection instanceof OracleConnection) { oraCon = (OracleConnection) connection; } else { if (jdbcExtractor != null) { try { Connection nativeCon = jdbcExtractor.getNativeConnection(connection); if (nativeCon instanceof OracleConnection) { oraCon = (OracleConnection) connection; } else { throw new NonTransientDataAccessResourceException("Native connection is not of type OracleConnection"); } } catch (SQLException e) { throw new NonTransientDataAccessResourceException("Unable to access native connection: " + e.getMessage(), e); } } else { throw new NonTransientDataAccessResourceException("Provided connection is not of type OracleConnection and no NativeJdbcExtractor provided"); } } } else { throw new InvalidDataAccessApiUsageException("The provided ContextProvider must implement one of the CurrenUsernameProvider or CurrentUsernamePasswordProvider interfaces"); } try { return doPrepareUserNameProxyConnection((ConnectionUsernameProvider)contextProvider, oraCon); } catch (SQLException e) { System.out.println("!!! " + e); throw new NonTransientDataAccessResourceException("Unable to prepare user name proxy connection: " + e.getMessage(), e); } } private Connection doPrepareUserNameProxyConnection(ConnectionUsernameProvider contextProvider, OracleConnection oraCon) throws SQLException { String proxyUserName = contextProvider.getUserName(); String proxyPassword = null; if (contextProvider instanceof ConnectionUsernamePasswordProvider) { proxyPassword = ((ConnectionUsernamePasswordProvider)contextProvider).getPassword(); } if (proxyUserName != null) { java.util.Properties proxyProperties = new java.util.Properties(); proxyProperties.setProperty(OracleConnection.PROXY_USER_NAME, proxyUserName); if (proxyPassword != null) { proxyProperties.setProperty(OracleConnection.PROXY_USER_PASSWORD, proxyPassword); } oraCon.openProxySession(OracleConnection.PROXYTYPE_USER_NAME, proxyProperties); return getUserNameConnectionProxyWrapper(oraCon); } return oraCon; } /** * Wrap the given Connection with a proxy that delegates every method call to it * and resets user name proxy for close calls. * @param target the original Connection to wrap * @return the wrapped Connection */ protected Connection getUserNameConnectionProxyWrapper(Connection target) { return (Connection) Proxy.newProxyInstance( ConnectionProxy.class.getClassLoader(), new Class[] {ConnectionProxy.class}, new UserNameConnectionProxyInvocationHandler(target)); } /** * Invocation handler that intercepts close calls on JDBC Connections and resets the user name proxy. */ private static class UserNameConnectionProxyInvocationHandler implements InvocationHandler { private final Connection target; public UserNameConnectionProxyInvocationHandler(Connection target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // Invocation on ConnectionProxy interface coming in... if (method.getName().equals("getTargetConnection")) { // Handle getTargetConnection method: return underlying Connection. return this.target; } else if (method.getName().equals("equals")) { // Only consider equal when proxies are identical. return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE); } else if (method.getName().equals("hashCode")) { // Use hashCode of Connection proxy. return new Integer(hashCode()); } else if (method.getName().equals("close")) { // Handle close method: cose proxy if its an Oracle Connection. if (target instanceof OracleConnection) { ((OracleConnection)target).close(OracleConnection.PROXY_SESSION); } target.close(); return null; } // Invoke method on target Connection. try { return method.invoke(this.target, args); } catch (InvocationTargetException ex) { throw ex.getTargetException(); } } } }