Package org.jboss.forge.furnace.proxy

Source Code of org.jboss.forge.furnace.proxy.Proxies

/*
* Copyright 2012 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Eclipse Public License version 1.0, available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.jboss.forge.furnace.proxy;

import java.lang.reflect.Method;

import javassist.util.proxy.MethodFilter;
import javassist.util.proxy.ProxyFactory;
import javassist.util.proxy.ProxyObject;

/**
* @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a>
*/
public class Proxies
{
   /**
    * Create a proxy for the given {@link Class} type.
    */
   @SuppressWarnings("unchecked")
   public static <T> T enhance(final ClassLoader loader, Object instance, ForgeProxy handler)
   {
      MethodFilter filter = new MethodFilter()
      {
         @Override
         public boolean isHandled(Method method)
         {
            String name = method.getName();
            Class<?>[] parameterTypes = method.getParameterTypes();
            if (!method.getDeclaringClass().getName().contains("java.lang")
                     || ("clone".equals(name) && parameterTypes.length == 0)
                     || ("equals".equals(name) && parameterTypes.length == 1)
                     || ("hashCode".equals(name) && parameterTypes.length == 0)
                     || ("toString".equals(name) && parameterTypes.length == 0))
               return true;
            return false;
         }
      };

      Object enhancedResult = null;

      ProxyFactory f = new ProxyFactory()
      {
         @Override
         protected ClassLoader getClassLoader()
         {
            return loader;
         }
      };

      f.setUseCache(true);

      Class<?>[] hierarchy = null;
      Class<?> unwrappedInstanceType = Proxies.unwrapProxyTypes(instance.getClass(), loader);
      hierarchy = ProxyTypeInspector.getCompatibleClassHierarchy(loader, unwrappedInstanceType);
      if (hierarchy == null || hierarchy.length == 0)
         throw new IllegalArgumentException("Must specify at least one non-final type to enhance for Object: "
                  + instance + " of type " + instance.getClass());

      Class<?> first = hierarchy[0];
      if (!first.isInterface())
      {
         f.setSuperclass(Proxies.unwrapProxyTypes(first, loader));
         hierarchy = Arrays.shiftLeft(hierarchy, new Class<?>[hierarchy.length - 1]);
      }

      int index = Arrays.indexOf(hierarchy, ProxyObject.class);
      if (index >= 0)
      {
         hierarchy = Arrays.removeElementAtIndex(hierarchy, index);
      }

      if (!Proxies.isProxyType(first) && !Arrays.contains(hierarchy, ForgeProxy.class))
         hierarchy = Arrays.append(hierarchy, ForgeProxy.class);

      if (hierarchy.length > 0)
         f.setInterfaces(hierarchy);

      f.setFilter(filter);

      Class<?> c = f.createClass();

      try
      {
         enhancedResult = c.newInstance();
      }
      catch (InstantiationException e)
      {
         throw new IllegalStateException(
                  "Could not instantiate proxy for object [" + instance + "] of type [" + unwrappedInstanceType
                           + "]. For optimal proxy compatability, ensure " +
                           "that this type is an interface, or a class with a default constructor.", e);
      }
      catch (IllegalAccessException e)
      {
         throw new IllegalStateException(e);
      }

      ((ProxyObject) enhancedResult).setHandler(handler);

      return (T) enhancedResult;
   }

   /**
    * Create a proxy for the given {@link Class} type.
    */
   @SuppressWarnings("unchecked")
   public static <T> T enhance(Class<T> type, ForgeProxy handler)
   {
      MethodFilter filter = new MethodFilter()
      {
         @Override
         public boolean isHandled(Method method)
         {
            String name = method.getName();
            Class<?>[] parameterTypes = method.getParameterTypes();
            if (!method.getDeclaringClass().getName().contains("java.lang")
                     || ("clone".equals(name) && parameterTypes.length == 0)
                     || ("equals".equals(name) && parameterTypes.length == 1)
                     || ("hashCode".equals(name) && parameterTypes.length == 0)
                     || ("toString".equals(name) && parameterTypes.length == 0))
               return true;
            return false;
         }
      };

      Object enhancedResult = null;

      ProxyFactory f = new ProxyFactory();

      f.setUseCache(true);

      if (type.isInterface() && !ForgeProxy.class.isAssignableFrom(type))
         f.setInterfaces(new Class<?>[] { type, ForgeProxy.class });
      else if (type.isInterface())
         f.setInterfaces(new Class<?>[] { type });
      else
      {
         if (Proxies.isProxyType(type))
            f.setSuperclass(unwrapProxyTypes(type));
         else
         {
            f.setSuperclass(type);
            f.setInterfaces(new Class<?>[] { ForgeProxy.class });
         }
      }

      f.setFilter(filter);
      Class<?> c;
      try
      {
         c = f.createClass();
      }
      catch (RuntimeException e)
      {
         throw e;
      }

      try
      {
         enhancedResult = c.newInstance();
      }
      catch (InstantiationException e)
      {
         throw new IllegalStateException(
                  "Could not instantiate proxy for type [" + type
                           + "]. For optimal proxy compatability, ensure " +
                           "that this type is an interface, or a class with a default constructor.", e);
      }
      catch (IllegalAccessException e)
      {
         throw new IllegalStateException(e);
      }

      ((ProxyObject) enhancedResult).setHandler(handler);

      return (T) enhancedResult;
   }

   public static boolean isProxyType(Class<?> type)
   {
      if (type.getName().contains("$$EnhancerByCGLIB$$") || type.getName().contains("_javassist_"))
      {
         return true;
      }
      return false;
   }

   /**
    * Returns the delegate object, if the given object was created via {@link Proxies}, otherwise it returns the given
    * object, unchanged.
    */
   @SuppressWarnings("unchecked")
   public static <T> T unwrap(Object object)
   {
      T result = (T) object;

      if (object != null)
      {
         while (isForgeProxy(result))
         {
            try
            {
               Method method = result.getClass().getMethod("getDelegate");
               method.setAccessible(true);
               result = (T) method.invoke(result);
            }
            catch (Exception e)
            {
               break;
            }
         }

         if (result == null)
            result = (T) object;
      }
      return result;
   }

   @SuppressWarnings("unchecked")
   public static <T> T unwrapOnce(Object object)
   {
      T result = (T) object;

      if (object != null)
      {
         if (isForgeProxy(result))
         {
            try
            {
               Method method = result.getClass().getMethod("getDelegate");
               method.setAccessible(true);
               result = (T) method.invoke(result);
            }
            catch (Exception e)
            {
            }
         }

         if (result == null)
            result = (T) object;
      }
      return result;
   }

   /**
    * Returns true if the given object was created via {@link Proxies}.
    */
   public static boolean isForgeProxy(Object object)
   {
      if (object != null)
      {
         Class<?>[] interfaces = object.getClass().getInterfaces();
         if (interfaces != null)
         {
            for (Class<?> iface : interfaces)
            {
               if (iface.getName().equals(ForgeProxy.class.getName()))
               {
                  return true;
               }
            }
         }
      }
      return false;
   }

   public static Class<?> unwrapProxyTypes(Class<?> type, ClassLoader... loaders)
   {
      Class<?> result = type;

      if (isProxyType(result))
      {
         Class<?> superclass = result.getSuperclass();
         while (superclass != null && !superclass.getName().equals(Object.class.getName()) && isProxyType(superclass))
         {
            superclass = superclass.getSuperclass();
         }

         if (superclass != null && !superclass.getName().equals(Object.class.getName()))
            return superclass;

         String typeName = unwrapProxyClassName(result);
         for (ClassLoader loader : loaders)
         {
            try
            {
               result = loader.loadClass(typeName);
               break;
            }
            catch (ClassNotFoundException e)
            {
            }
         }
      }
      return result;
   }

   /**
    * Unwraps the proxy type if javassist or CGLib is used
    *
    * @param type the class type
    * @return the unproxied class name
    */
   public static String unwrapProxyClassName(Class<?> type)
   {
      String typeName;
      if (type.getName().contains("$$EnhancerByCGLIB$$"))
      {
         typeName = type.getName().replaceAll("^(.*)\\$\\$EnhancerByCGLIB\\$\\$.*", "$1");
      }
      else if (type.getName().contains("_javassist_"))
      {
         typeName = type.getName().replaceAll("^(.*)_\\$\\$_javassist_.*", "$1");
      }
      else
      {
         typeName = type.getName();
      }
      return typeName;
   }

   /**
    * This method tests if two proxied objects are equivalent.
    *
    * It does so by comparing the class names and the hashCode, since they may be loaded from different classloaders.
    *
    */
   public static boolean areEquivalent(Object proxiedObj, Object anotherProxiedObj)
   {
      if (proxiedObj == null && anotherProxiedObj == null)
      {
         return true;
      }
      else if (proxiedObj == null || anotherProxiedObj == null)
      {
         return false;
      }
      else
      {
         Object unproxiedObj = unwrap(proxiedObj);
         Object anotherUnproxiedObj = unwrap(anotherProxiedObj);

         boolean sameClassName = unwrapProxyClassName(unproxiedObj.getClass()).equals(
                  unwrapProxyClassName(anotherUnproxiedObj.getClass()));
         if (sameClassName)
         {
            if (unproxiedObj.getClass().isEnum())
            {
               // Enum hashCode is different if loaded from different classloaders and cannot be overriden.
               Enum<?> enumLeft = Enum.class.cast(unproxiedObj);
               Enum<?> enumRight = Enum.class.cast(anotherUnproxiedObj);
               return (enumLeft.name().equals(enumRight.name())) && (enumLeft.ordinal() == enumRight.ordinal());
            }
            else
            {
               return (unproxiedObj.hashCode() == anotherUnproxiedObj.hashCode());
            }
         }
         else
         {
            return false;
         }
      }
   }

   /**
    * Checks if a proxied object is an instance of the specified {@link Class}
    */
   public static boolean isInstance(Class<?> type, Object proxiedObject)
   {
      return type.isInstance(unwrap(proxiedObject));
   }

   /**
    * Determine whether or not a given {@link Class} type is instantiable.
    */
   public static boolean isInstantiable(Class<?> type)
   {
      if (type != null)
      {
         try
         {
            if (type.isInterface())
               return true;
            type.getConstructor();
            return true;
         }
         catch (SecurityException e)
         {
            return false;
         }
         catch (NoSuchMethodException e)
         {
            return false;
         }
      }
      return false;
   }

   /**
    * Determine if the given {@link Class} type does not require {@link ClassLoader} proxying.
    */
   public static boolean isPassthroughType(Class<?> type)
   {
      boolean result = type.getName().startsWith("[L")
               || type.getName().matches("^(java\\.lang).*")
               || type.getName().matches("^(java\\.io).*")
               || type.getName().matches("^(java\\.net).*")
               || type.isPrimitive();

      result = result && !(Iterable.class.getName().equals(type.getName()));

      return result;
   }

   public static boolean isLanguageType(Class<?> type)
   {
      boolean result = type.getName().startsWith("[L")
               || type.getName().matches("^(java\\.).*")
               || type.isPrimitive();

      return result;
   }
}
TOP

Related Classes of org.jboss.forge.furnace.proxy.Proxies

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.