/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.drill.jdbc.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Proxy; import java.util.IdentityHashMap; import java.util.Map; /** * Manager of proxy classes and instances. * Managing includes creating proxies and tracking to re-use proxies. */ class ProxiesManager { private final InvocationReporter reporter; private Map<Class<?>, Class<Proxy>> interfacesToProxyClassesMap = new IdentityHashMap<>(); /** Map of proxied original objects from proxied JDBC driver to * proxy objects to be returned by tracing proxy driver. */ private Map<Object, Object> proxiedsToProxiesMap = new IdentityHashMap<>(); public ProxiesManager( final InvocationReporter reporter ) { this.reporter = reporter; } private Class<Proxy> getProxyClassForInterface( final Class<?> interfaceType ) { assert interfaceType.isInterface(); Class<Proxy> proxyReturnClass = interfacesToProxyClassesMap.get( interfaceType ); if ( null == proxyReturnClass ) { // Suppressed because we know getProxyClass returns class extending Proxy. @SuppressWarnings("unchecked") Class<Proxy> newProxyReturnClass = (Class<Proxy>) Proxy.getProxyClass( interfaceType.getClassLoader(), new Class[] { interfaceType }); interfacesToProxyClassesMap.put( interfaceType, newProxyReturnClass ); proxyReturnClass = newProxyReturnClass; } return proxyReturnClass; } /** * Creates or retrieves proxy instance to be returned for given original * instance. * @param originalInstance * the original object * @param declaredType * the declared type of source of the original object; interface type */ public <INTF> INTF getProxyInstanceForOriginal( final INTF originalInstance, final Class<?> declaredType ) { final INTF proxyInstance; assert declaredType.isAssignableFrom( originalInstance.getClass() ) : "toBeProxied is of class (" + originalInstance.getClass().getName() + ") that doesn't implement specified interface " + declaredType.getName(); // Suppressed because checked immediately above. @SuppressWarnings("unchecked") final INTF existingProxy = (INTF) proxiedsToProxiesMap.get( originalInstance ); if ( null != existingProxy ) { // Repeated occurrence of original--return same proxy instance as before. proxyInstance = existingProxy; } else { // Original we haven't seen yet--create proxy instance and return that. Class<Proxy> proxyReturnClass = getProxyClassForInterface( declaredType ); // Create tracing handler instance for this proxy/original pair. final InvocationHandler callHandler = new TracingInvocationHandler<INTF>( this, reporter, originalInstance, declaredType ); try { // Suppressed because we know that proxy class implements INTF. @SuppressWarnings("unchecked") INTF newProxyInstance = (INTF) proxyReturnClass .getConstructor( new Class[] { InvocationHandler.class } ) .newInstance( new Object[] { callHandler } ); proxiedsToProxiesMap.put( originalInstance, newProxyInstance ); proxyInstance = newProxyInstance; } catch ( InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e ) { throw new RuntimeException( "Error creating proxy for " + declaredType + ": " + e , e ); } } return proxyInstance; } } // class ProxiesManager