| package com.intellij.remoteServer.agent.impl; |
| |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.util.containers.hash.HashSet; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.lang.reflect.*; |
| import java.net.URL; |
| import java.net.URLClassLoader; |
| import java.util.Arrays; |
| import java.util.Set; |
| |
| /** |
| * @author michael.golubev |
| */ |
| public class RemoteAgentReflectiveProxyFactory extends RemoteAgentProxyFactoryBase { |
| |
| private static final Logger LOG = Logger.getInstance("#" + RemoteAgentReflectiveProxyFactory.class.getName()); |
| |
| private final RemoteAgentClassLoaderCache myClassLoaderCache; |
| |
| public RemoteAgentReflectiveProxyFactory(@Nullable RemoteAgentClassLoaderCache classLoaderCache, |
| CallerClassLoaderProvider callerClassLoaderProvider) { |
| super(callerClassLoaderProvider); |
| myClassLoaderCache = classLoaderCache; |
| } |
| |
| @Override |
| protected ClassLoader createAgentClassLoader(URL[] agentLibraryUrls) throws Exception { |
| Set<URL> urls = new HashSet<URL>(); |
| urls.addAll(Arrays.asList(agentLibraryUrls)); |
| return myClassLoaderCache == null |
| ? new URLClassLoader(urls.toArray(new URL[urls.size()]), null) |
| : myClassLoaderCache.getOrCreateClassLoader(urls); |
| } |
| |
| @Override |
| protected InvocationHandler createInvocationHandler(Object agentImpl, ClassLoader agentClassLoader, ClassLoader callerClassLoader) { |
| return new ReflectiveInvocationHandler(agentImpl, agentClassLoader, callerClassLoader); |
| } |
| |
| private static class ReflectiveInvocationHandler implements InvocationHandler { |
| |
| private final Object myTarget; |
| private final ClassLoader myTargetClassLoader; |
| private final ClassLoader mySourceClassLoader; |
| |
| public ReflectiveInvocationHandler(Object target, ClassLoader targetClassLoader, ClassLoader sourceClassLoader) { |
| myTarget = target; |
| myTargetClassLoader = targetClassLoader; |
| mySourceClassLoader = sourceClassLoader; |
| } |
| |
| @Nullable |
| @Override |
| public Object invoke(Object proxy, final Method method, final Object[] args) { |
| ClassLoader initialClassLoader = Thread.currentThread().getContextClassLoader(); |
| try { |
| Thread.currentThread().setContextClassLoader(myTargetClassLoader); |
| |
| Class<?>[] parameterTypes = method.getParameterTypes(); |
| Class<?>[] delegateParameterTypes = new Class<?>[parameterTypes.length]; |
| |
| Object[] delegateArgs = new Object[parameterTypes.length]; |
| for (int i = 0; i < parameterTypes.length; i++) { |
| Mirror parameterMirror = new Mirror(parameterTypes[i], args[i], mySourceClassLoader, myTargetClassLoader); |
| delegateParameterTypes[i] = parameterMirror.getMirrorType(); |
| delegateArgs[i] = parameterMirror.getMirrorValue(); |
| } |
| |
| Method delegateMethod = myTarget.getClass().getMethod(method.getName(), delegateParameterTypes); |
| delegateMethod.setAccessible(true); |
| |
| Object result = delegateMethod.invoke(myTarget, delegateArgs); |
| Mirror resultMirror = new Mirror(delegateMethod.getReturnType(), result, myTargetClassLoader, mySourceClassLoader); |
| return resultMirror.getMirrorValue(); |
| } |
| catch (IllegalAccessException e) { |
| LOG.error(e); |
| return null; |
| } |
| catch (InvocationTargetException e) { |
| LOG.error(e); |
| return null; |
| } |
| catch (NoSuchMethodException e) { |
| LOG.error(e); |
| return null; |
| } |
| catch (ClassNotFoundException e) { |
| LOG.error(e); |
| return null; |
| } |
| finally { |
| Thread.currentThread().setContextClassLoader(initialClassLoader); |
| } |
| } |
| } |
| |
| private static class Mirror { |
| |
| private final Class<?> myMirrorType; |
| |
| private final Object myMirrorValue; |
| |
| public Mirror(Class<?> type, Object value, ClassLoader classLoader, ClassLoader mirrorClassLoader) throws ClassNotFoundException { |
| if (type.isArray()) { |
| Class<?> componentType = type.getComponentType(); |
| Mirror componentMirror = new Mirror(componentType, null, classLoader, mirrorClassLoader); |
| int length = value == null ? 0 : Array.getLength(value); |
| Object mirrorValue = Array.newInstance(componentMirror.getMirrorType(), length); |
| for (int i = 0; i < length; i++) { |
| Mirror itemMirror = new Mirror(componentType, Array.get(value, i), classLoader, mirrorClassLoader); |
| Array.set(mirrorValue, i, itemMirror.getMirrorValue()); |
| } |
| myMirrorType = mirrorValue.getClass(); |
| myMirrorValue = value == null ? null : mirrorValue; |
| } |
| else if (type.isInterface()) { |
| myMirrorType = mirrorClassLoader.loadClass(type.getName()); |
| myMirrorValue = value == null ? null |
| : Proxy.newProxyInstance(mirrorClassLoader, |
| new Class[]{myMirrorType}, |
| new ReflectiveInvocationHandler(value, classLoader, mirrorClassLoader)); |
| } |
| else { |
| myMirrorType = type; |
| myMirrorValue = value; |
| } |
| } |
| |
| public Class<?> getMirrorType() { |
| return myMirrorType; |
| } |
| |
| public Object getMirrorValue() { |
| return myMirrorValue; |
| } |
| } |
| } |