| /** |
| * 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.cxf.common.util; |
| |
| import java.lang.reflect.InvocationHandler; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Proxy; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| |
| import org.apache.cxf.common.classloader.ClassLoaderUtils; |
| import org.apache.cxf.common.logging.LogUtils; |
| |
| /** |
| * |
| */ |
| public class ProxyHelper { |
| static final ProxyHelper HELPER; |
| static { |
| ProxyHelper theHelper; |
| try { |
| theHelper = new CglibProxyHelper(); |
| } catch (Throwable ex) { |
| theHelper = new ProxyHelper(); |
| } |
| HELPER = theHelper; |
| } |
| |
| private static final Logger LOG = LogUtils.getL7dLogger(ProxyHelper.class); |
| |
| protected ProxyClassLoaderCache proxyClassLoaderCache = |
| new ProxyClassLoaderCache(); |
| |
| |
| protected ProxyHelper() { |
| } |
| |
| protected Object getProxyInternal(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler) { |
| ClassLoader combinedLoader = getClassLoaderForInterfaces(loader, interfaces); |
| return Proxy.newProxyInstance(combinedLoader, interfaces, handler); |
| } |
| |
| /** |
| * Return a classloader that can see all the given interfaces If the given loader can see all interfaces |
| * then it is used. If not then a combined classloader of all interface classloaders is returned. |
| * |
| * @param loader use supplied class loader |
| * @param interfaces |
| * @return classloader that sees all interfaces |
| */ |
| private ClassLoader getClassLoaderForInterfaces(final ClassLoader loader, final Class<?>[] interfaces) { |
| if (canSeeAllInterfaces(loader, interfaces)) { |
| LOG.log(Level.FINE, "current classloader " + loader + " can see all interface"); |
| return loader; |
| } |
| String sortedNameFromInterfaceArray = getSortedNameFromInterfaceArray(interfaces); |
| ClassLoader cachedLoader = proxyClassLoaderCache.getProxyClassLoader(loader, interfaces); |
| if (canSeeAllInterfaces(cachedLoader, interfaces)) { |
| LOG.log(Level.FINE, "find required loader from ProxyClassLoader cache with key" |
| + sortedNameFromInterfaceArray); |
| return cachedLoader; |
| } else { |
| LOG.log(Level.FINE, "find a loader from ProxyClassLoader cache with interfaces " |
| + sortedNameFromInterfaceArray |
| + " but can't see all interfaces"); |
| for (Class<?> currentInterface : interfaces) { |
| String ifName = currentInterface.getName(); |
| |
| if (!ifName.startsWith("org.apache.cxf") && !ifName.startsWith("java")) { |
| // remove the stale ProxyClassLoader and recreate one |
| proxyClassLoaderCache.removeStaleProxyClassLoader(currentInterface); |
| cachedLoader = proxyClassLoaderCache.getProxyClassLoader(loader, interfaces); |
| |
| } |
| } |
| } |
| |
| return cachedLoader; |
| } |
| |
| private String getSortedNameFromInterfaceArray(Class<?>[] interfaces) { |
| SortedArraySet<String> arraySet = new SortedArraySet<>(); |
| for (Class<?> currentInterface : interfaces) { |
| arraySet.add(currentInterface.getName() + ClassLoaderUtils.getClassLoaderName(currentInterface)); |
| } |
| return arraySet.toString(); |
| } |
| |
| |
| private boolean canSeeAllInterfaces(ClassLoader loader, Class<?>[] interfaces) { |
| for (Class<?> currentInterface : interfaces) { |
| String ifName = currentInterface.getName(); |
| try { |
| Class<?> ifClass = Class.forName(ifName, true, loader); |
| if (ifClass != currentInterface) { |
| return false; |
| } |
| //we need to check all the params/returns as well as the Proxy creation |
| //will try to create methods for all of this even if they aren't used |
| //by the client and not available in the clients classloader |
| for (Method m : ifClass.getMethods()) { |
| Class<?> returnType = m.getReturnType(); |
| if (!returnType.isPrimitive()) { |
| Class.forName(returnType.getName(), true, loader); |
| } |
| for (Class<?> p : m.getParameterTypes()) { |
| if (!p.isPrimitive()) { |
| Class.forName(p.getName(), true, loader); |
| } |
| } |
| } |
| } catch (NoClassDefFoundError | ClassNotFoundException e) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| public static Object getProxy(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler) { |
| return HELPER.getProxyInternal(loader, interfaces, handler); |
| } |
| |
| } |