All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.maxifier.mxcache.proxy.MxGenericProxyFactory Maven / Gradle / Ivy

/*
 * Copyright (c) 2008-2014 Maxifier Ltd. All Rights Reserved.
 */
package com.maxifier.mxcache.proxy;

import com.maxifier.mxcache.asm.Opcodes;
import com.maxifier.mxcache.asm.Type;
import com.maxifier.mxcache.asm.commons.Method;
import com.maxifier.mxcache.util.ClassGenerator;
import com.maxifier.mxcache.util.MxConstructorGenerator;
import com.maxifier.mxcache.util.MxGeneratorAdapter;
import gnu.trove.map.hash.THashMap;
import gnu.trove.set.hash.THashSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import java.lang.reflect.Constructor;
import java.util.*;

import static com.maxifier.mxcache.util.CodegenHelper.CLASS_TYPE;
import static com.maxifier.mxcache.util.CodegenHelper.CONSTRUCTOR_NAME;

/**
 * @author Alexander Kochurov ([email protected])
 */
public final class MxGenericProxyFactory extends MxAbstractProxyFactory {
    private static final Logger logger = LoggerFactory.getLogger(MxGenericProxyFactory.class);

    private static final Type MX_GENERIC_PROXY_TYPE = Type.getType(MxGenericProxy.class);
    private static final Method MX_GENERIC_PROXY_CTOR = new Method(CONSTRUCTOR_NAME, Type.VOID_TYPE, new Type[]{RESOLVABLE_TYPE, CLASS_TYPE, CLASS_TYPE, CLASS_TYPE});

    private final Class sourceInterface;
    private final Class containerClass;

    private final Map, Constructor> proxyClasses = new THashMap, Constructor>();

    MxGenericProxyFactory(@Nonnull Class sourceInterface, @Nonnull Class containerClass) {
        if (!sourceInterface.isInterface()) {
            throw new IllegalArgumentException("Only interface can be used as proxy source but " + sourceInterface + " was passed");
        }
        if (!Resolvable.class.isAssignableFrom(containerClass)) {
            throw new IllegalArgumentException("Container " + containerClass + " should implement " + Resolvable.class);
        }
        this.sourceInterface = sourceInterface;
        this.containerClass = containerClass;
    }

    private synchronized Constructor getOrCreateProxy(Class cls) {
        Constructor proxyInfo = proxyClasses.get(cls);
        if (proxyInfo == null) {
            Class proxyClass = createProxyClass(cls, containerClass);
            try {
                proxyInfo = proxyClass.getConstructor(containerClass);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException(e);
            }
            proxyClasses.put(cls, proxyInfo);
        }
        return proxyInfo;
    }

    public Class getSourceInterface() {
        return sourceInterface;
    }

    public Class getContainerClass() {
        return containerClass;
    }

    /**
     * 

* Creates proxy-object that implements given interface {@link #getSourceInterface()} and all its child interfaces * of passed type. It is not guaranteed that proxy will extend this class if it is not interface. * Proxy stores a link to container of {@link #getContainerClass()} type. The proxy class has two constructors: * default (for serialization) and single parameter-constructor {@link #getContainerClass()}. *

* Proxy overrides {@code toString()} method that returns toString() of stored object from container. *

* Proxies for the same container with same target interface and value from container will be considered equal * in terms of equals and hashCode. *

* * @param srcClass this type will be used in order to add all child interfaces of getSourceInterface() to proxy * @param container container of proxyed objects * @return created proxy */ public T createProxy(Class srcClass, C container) { try { return getOrCreateProxy(srcClass).newInstance(container); } catch (Exception e) { throw new RuntimeException("Cannot create proxy for " + container, e); } } private final class MethodInfo { private final Class[] argTypes; private final Class returnType; private final String name; private final int hash; MethodInfo(Class[] argTypes, Class returnType, String name) { this.argTypes = argTypes; this.returnType = returnType; this.name = name; hash = Arrays.hashCode(argTypes) ^ returnType.hashCode() ^ name.hashCode(); } @Override public int hashCode() { return hash; } @SuppressWarnings({"unchecked"}) @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj == null || obj.getClass() != this.getClass()) { return false; } MethodInfo info = (MethodInfo) obj; return hash == info.hash && returnType == info.returnType && name.equals(info.name) && Arrays.equals(argTypes, info.argTypes); } } private Class createProxyClass(Class sourceClass, Class containerClass) { long start = System.currentTimeMillis(); try { Set> interfaces = getAllProxiedInterfaces(sourceClass); String proxyClassName = createProxyClassName(sourceClass); Type containerType = Type.getType(containerClass); ClassGenerator proxyClass = new ClassGenerator(Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, proxyClassName, MxGenericProxy.class, interfaces.toArray(new Class[interfaces.size()])); createProxyConstructor(sourceClass, containerClass, proxyClass, containerType); createMethodProxies(containerClass, interfaces, proxyClass, containerType); //noinspection unchecked Class newClass = proxyClass.toClass(sourceClass.getClassLoader()); long end = System.currentTimeMillis(); logger.debug("Generated generic proxy for " + sourceClass + "[" + sourceInterface + ", " + interfaces.size() + "] with container " + containerClass + " in " + (end - start) + " ms"); return newClass; } catch (Exception e) { throw new RuntimeException(e); } } private void createMethodProxies(Class containerClass, Set> interfaces, ClassGenerator generator, Type containerType) { Set methods = new THashSet(); for (Class intf : interfaces) { for (java.lang.reflect.Method sourceMethod : intf.getMethods()) { if (sourceMethod.getDeclaringClass().equals(intf)) { Class returnType = sourceMethod.getReturnType(); Class[] args = sourceMethod.getParameterTypes(); if (methods.add(new MethodInfo(args, returnType, sourceMethod.getName()))) { createProxyMethod(containerClass, generator, containerType, intf, sourceMethod); } } } } } private Set> getAllProxiedInterfaces(Class sourceClass) { Set> interfaces = new THashSet>(); Queue iq = new LinkedList(); Set passed = new THashSet(); iq.add(sourceClass); if (sourceClass.isInterface()) { //noinspection unchecked interfaces.add(sourceClass); } //noinspection ManualArrayToCollectionCopy for (Class cls : sourceInterface.getInterfaces()) { //noinspection unchecked interfaces.add(cls); } while (!iq.isEmpty()) { Class c = iq.poll(); if (c.getSuperclass() != null && passed.add(c.getSuperclass())) { iq.add(c.getSuperclass()); } for (Class cls : c.getInterfaces()) { if (passed.add(cls)) { iq.add(cls); } if (sourceInterface.isAssignableFrom(cls)) { //noinspection unchecked interfaces.add((Class) cls); } } } return interfaces; } private void createProxyMethod(Class containerClass, ClassGenerator generator, Type containerType, Class intf, java.lang.reflect.Method sourceMethod) { Method sourceMethod0 = Method.getMethod(sourceMethod); MxGeneratorAdapter method = generator.defineMethod(Opcodes.ACC_PUBLIC, sourceMethod0); method.start(); method.loadThis(); method.getField(MX_GENERIC_PROXY_TYPE, VALUE_FIELD_NAME, RESOLVABLE_TYPE); method.checkCast(containerType); method.invokeVirtual(Type.getType(containerClass), GETTER); method.loadArgs(); method.invokeInterface(Type.getType(intf), sourceMethod0); method.returnValue(); method.endMethod(); } private void createProxyConstructor(Class sourceClass, Class containerClass, ClassGenerator generator, Type containerType) { MxConstructorGenerator ctor = generator.defineConstructor(Opcodes.ACC_PUBLIC, containerType); ctor.start(); ctor.loadThis(); ctor.loadArg(0); ctor.push(Type.getType(sourceClass)); ctor.push(Type.getType(containerClass)); ctor.push(Type.getType(sourceInterface)); ctor.invokeConstructor(MX_GENERIC_PROXY_TYPE, MX_GENERIC_PROXY_CTOR); ctor.returnValue(); ctor.endMethod(); } @Override public String toString() { return "ProxyFactory for " + sourceInterface + " wrapped in " + containerClass; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof MxGenericProxyFactory)) { return false; } MxGenericProxyFactory that = (MxGenericProxyFactory) o; return containerClass == that.containerClass && sourceInterface == that.sourceInterface; } @Override public int hashCode() { return 31 * sourceInterface.hashCode() + containerClass.hashCode(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy