Package play.utils

Source Code of play.utils.JavaWithCaching$ClassAndAnnotation

package play.utils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.FutureTask;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.bytecode.SourceFileAttribute;
import play.Play;
import play.classloading.ApplicationClassloaderState;
import play.classloading.enhancers.LocalvariablesNamesEnhancer.LocalVariablesNamesTracer;
import play.data.binding.Binder;
import play.data.binding.ParamNode;
import play.data.binding.RootParamNode;
import play.exceptions.UnexpectedException;
import play.mvc.After;
import play.mvc.Before;
import play.mvc.Finally;
import play.mvc.With;

/**
* Java utils
*/
public class Java {

    protected static JavaWithCaching _javaWithCaching = new JavaWithCaching();
    protected static ApplicationClassloaderState _lastKnownApplicationClassloaderState = Play.classloader.currentState;
    protected static Object _javaWithCachingLock = new Object();

    protected static JavaWithCaching getJavaWithCaching() {
        synchronized( _javaWithCachingLock ) {
            // has the state of the ApplicationClassloader changed?
            ApplicationClassloaderState currentApplicationClasloaderState = Play.classloader.currentState;
            if( !currentApplicationClasloaderState.equals( _lastKnownApplicationClassloaderState )) {
                // it has changed.
                // we must drop our current _javaWithCaching and create a new one...
                // and start the caching over again.
                _lastKnownApplicationClassloaderState = currentApplicationClasloaderState;
                _javaWithCaching = new JavaWithCaching();

            }
            return _javaWithCaching;
        }
    }


    public static String[] extractInfosFromByteCode(byte[] code) {
        try {
            CtClass ctClass = ClassPool.getDefault().makeClass(new ByteArrayInputStream(code));
            String sourceName = ((SourceFileAttribute) ctClass.getClassFile().getAttribute("SourceFile")).getFileName();
            return new String[]{ctClass.getName(), sourceName};
        } catch (Exception e) {
            throw new UnexpectedException("Cannot read a scala generated class using javassist", e);
        }
    }

    /**
     * Try to discover what is hidden under a FutureTask (hack)
     */
    public static Object extractUnderlyingCallable(FutureTask<?> futureTask) {
        try {
            Field syncField = FutureTask.class.getDeclaredField("sync");
            syncField.setAccessible(true);
            Object sync = syncField.get(futureTask);
            Field callableField = sync.getClass().getDeclaredField("callable");
            callableField.setAccessible(true);
            Object callable = callableField.get(sync);
            if (callable.getClass().getSimpleName().equals("RunnableAdapter")) {
                Field taskField = callable.getClass().getDeclaredField("task");
                taskField.setAccessible(true);
                return taskField.get(callable);
            }
            return callable;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Find the first public static method of a controller class
     * @param name The method name
     * @param clazz The class
     * @return The method or null
     */
    public static Method findActionMethod(String name, Class clazz) {
        while (!clazz.getName().equals("java.lang.Object")) {
            for (Method m : clazz.getDeclaredMethods()) {
                if (m.getName().equalsIgnoreCase(name) && Modifier.isPublic(m.getModifiers())) {
                    // Check that it is not an intercepter
                    if (!m.isAnnotationPresent(Before.class) && !m.isAnnotationPresent(After.class) && !m.isAnnotationPresent(Finally.class)) {
                        return m;
                    }
                }
            }
            clazz = clazz.getSuperclass();
        }
        return null;
    }


    /**
     * Invoke a static method
     * @param clazz The class
     * @param method The method name
     * @return The result
     * @throws java.lang.Exception
     */
    public static Object invokeStatic(Class<?> clazz, String method) throws Exception {
        return invokeStatic(clazz, method, new Object[0]);
    }

    public static Object invokeStatic(String clazz, String method) throws Exception {
        return invokeStatic(Play.classloader.loadClass(clazz), method, new Object[0]);
    }

    /**
     * Invoke a static method with args
     * @param clazz The class
     * @param method The method name
     * @param args Arguments
     * @return The result
     * @throws java.lang.Exception
     */
    public static Object invokeStatic(Class<?> clazz, String method, Object... args) throws Exception {
        Class[] types = new Class[args.length];
        for (int i = 0; i < args.length; i++) {
            types[i] = args[i].getClass();
        }
        Method m = clazz.getDeclaredMethod(method, types);
        m.setAccessible(true);
        return m.invoke(null, args);
    }

    public static Object invokeStaticOrParent(Class<?> clazz, String method, Object... args) throws Exception {
        Class[] types = new Class[args.length];
        for (int i = 0; i < args.length; i++) {
            types[i] = args[i].getClass();
        }
        Method m = null;
        while (!clazz.equals(Object.class) && m == null) {
            try {
                m = clazz.getDeclaredMethod(method, types);
            } catch (Exception e) {
                clazz = clazz.getSuperclass();
            }
        }
        if (m != null) {
            m.setAccessible(true);
            if (Modifier.isStatic(m.getModifiers())) {
                return m.invoke(null, args);
            } else {
                Object instance = m.getDeclaringClass().getDeclaredField("MODULE$").get(null);
                return m.invoke(instance, args);
            }
        }
        throw new NoSuchMethodException(method);
    }

    public static Object invokeChildOrStatic(Class<?> clazz, String method, Object... args) throws Exception {

        Class invokedClass = null;
        List<Class> assignableClasses = Play.classloader.getAssignableClasses(clazz);
        if(assignableClasses.size() == 0)
        {
            invokedClass = clazz;
        }
        else
        {
            invokedClass = assignableClasses.get(0);
        }
       
        return Java.invokeStaticOrParent(invokedClass, method, args);
    }

    public static Object invokeStatic(Method method, Map<String, String[]> args) throws Exception {
        return method.invoke(null, prepareArgs(method, args));
    }

    public static Object invokeStatic(Method method, Object[] args) throws Exception {
        return method.invoke(null, args);
    }

    static Object[] prepareArgs(Method method, Map<String, String[]> args) throws Exception {
        String[] paramsNames = parameterNames(method);
        if (paramsNames == null && method.getParameterTypes().length > 0) {
            throw new UnexpectedException("Parameter names not found for method " + method);
        }

        RootParamNode rootParamNode = ParamNode.convert(args);

        Object[] rArgs = new Object[method.getParameterTypes().length];
        for (int i = 0; i < method.getParameterTypes().length; i++) {
            rArgs[i] = Binder.bind(rootParamNode, paramsNames[i], method.getParameterTypes()[i], method.getGenericParameterTypes()[i], method.getParameterAnnotations()[i]);
        }
        return rArgs;
    }

    /**
     * Retrieve parameter names of a method
     */
    public static String[] parameterNames(Method method) throws Exception {
        try {
            return (String[]) method.getDeclaringClass().getDeclaredField("$" + method.getName() + LocalVariablesNamesTracer.computeMethodHash(method.getParameterTypes())).get(null);
        } catch (Exception e) {
            throw new UnexpectedException("Cannot read parameter names for " + method);
        }
    }

    public static String rawMethodSignature(Method method) {
        StringBuilder sig = new StringBuilder();
        sig.append(method.getDeclaringClass().getName());
        sig.append(".");
        sig.append(method.getName());
        sig.append('(');
        for (Class clazz : method.getParameterTypes()) {
            sig.append(rawJavaType(clazz));
        }
        sig.append(")");
        sig.append(rawJavaType(method.getReturnType()));
        return sig.toString();
    }

    public static String rawJavaType(Class clazz) {
        if (clazz.getName().equals("void")) {
            return "V";
        }
        if (clazz.getName().equals("boolean")) {
            return "Z";
        }
        if (clazz.getName().equals("byte")) {
            return "B";
        }
        if (clazz.getName().equals("char")) {
            return "C";
        }
        if (clazz.getName().equals("double")) {
            return "D";
        }
        if (clazz.getName().equals("float")) {
            return "F";
        }
        if (clazz.getName().equals("int")) {
            return "I";
        }
        if (clazz.getName().equals("long")) {
            return "J";
        }
        if (clazz.getName().equals("short")) {
            return "S";
        }
        if (clazz.getName().startsWith("[")) {
            return clazz.getName().replace('.', '/');
        }
        return "L" + (clazz.getName().replace('.', '/')) + ";";
    }

    /**
     * Find all annotated method from a class
     * @param clazz The class
     * @param annotationType The annotation class
     * @return A list of method object
     */
    public static List<Method> findAllAnnotatedMethods(Class<?> clazz, Class<? extends Annotation> annotationType) {

        return getJavaWithCaching().findAllAnnotatedMethods(clazz, annotationType);
    }

    /**
     * Find all annotated method from a class
     * @param classes The classes
     * @param annotationType The annotation class
     * @return A list of method object
     */
    public static List<Method> findAllAnnotatedMethods(List<Class> classes, Class<? extends Annotation> annotationType) {
        List<Method> methods = new ArrayList<Method>();
        for (Class clazz : classes) {
            methods.addAll(findAllAnnotatedMethods(clazz, annotationType));
        }
        return methods;
    }

    public static void findAllFields(Class clazz, Set<Field> found) {
        Field[] fields = clazz.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            found.add(fields[i]);
        }
        Class sClazz = clazz.getSuperclass();
        if (sClazz != null && sClazz != Object.class) {
            findAllFields(sClazz, found);
        }
    }
    /** cache */
    private static Map<Field, FieldWrapper> wrappers = new HashMap<Field, FieldWrapper>();

    public static FieldWrapper getFieldWrapper(Field field) {
        if (wrappers.get(field) == null) {
            FieldWrapper fw = new FieldWrapper(field);
            if (play.Logger.isTraceEnabled()) {
                play.Logger.trace("caching %s", fw);
            }
            wrappers.put(field, fw);
        }
        return wrappers.get(field);
    }

    public static byte[] serialize(Object o) throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oo = new ObjectOutputStream(baos);
        oo.writeObject(o);
        oo.flush();
        oo.close();
        return baos.toByteArray();
    }

    public static Object deserialize(byte[] b) throws Exception {
        ByteArrayInputStream bais = new ByteArrayInputStream(b);
        ObjectInputStream oi = new ObjectInputStream(bais);
        return oi.readObject();
    }

    /**
     * Field accessor
     * set and get value for a property, using the getter/setter when it exists or direct access otherwise.
     * final, native or static properties are safely ignored
     */
    public static class FieldWrapper {

        final static int unwritableModifiers = Modifier.FINAL | Modifier.NATIVE | Modifier.STATIC;
        private Method setter;
        private Method getter;
        private Field field;
        private boolean writable;
        private boolean accessible;

        private FieldWrapper(Method setter, Method getter) {
            this.setter = setter;
            this.getter = getter;
        }

        private FieldWrapper(Field field) {
            this.field = field;
            accessible = field.isAccessible();
            writable = ((field.getModifiers() & unwritableModifiers) == 0);
            String property = field.getName();
            try {
                String setterMethod = "set" + property.substring(0, 1).toUpperCase() + property.substring(1);
                setter = field.getDeclaringClass().getMethod(setterMethod, field.getType());
            } catch (Exception ex) {
            }
            try {
                String getterMethod = "get" + property.substring(0, 1).toUpperCase() + property.substring(1);
                getter = field.getDeclaringClass().getMethod(getterMethod);
            } catch (Exception ex) {
            }
        }

        public boolean isModifiable() {
            return writable;
        }

        public void setValue(Object instance, Object value) {
            if (!writable) {
                return;
            }
            try {
                if (setter != null) {
                    if (play.Logger.isTraceEnabled()) {
                        play.Logger.trace("invoke setter %s on %s with value %s", setter, instance, value);
                    }
                    setter.invoke(instance, value);
                } else {
                    if (!accessible) {
                        field.setAccessible(true);
                    }
                    if (play.Logger.isTraceEnabled()) {
                        play.Logger.trace("field.set(%s, %s)", instance, value);
                    }
                    field.set(instance, value);
                    if (!accessible) {
                        field.setAccessible(accessible);
                    }
                }
            } catch (Exception ex) {
                play.Logger.info("ERROR: when setting value for field %s - %s", field, ex);
            }
        }

        public Object getValue(Object instance) {
            try {
                if (getter != null) {
                    return getter.invoke(instance);
                } else {
                    return field.get(instance);
                }
            } catch (Exception ex) {
                play.Logger.info("ERROR: when getting value for field %s - %s", field, ex);
            }
            return null;
        }

        @Override
        public String toString() {
            return "FieldWrapper (" + (writable ? "RW" : "R ") + ") for " + field;
        }
    }

}


/**
* This is an internal class uses only by the Java-class.
* It contains functionality with caching..
*
* The idea is that the Java-objects creates a new instance of JavaWithCaching,
* each time something new is compiled..
*
*/
class JavaWithCaching {

    /**
     * Class uses as key for storing info about the relation between a Class and an Annotation
     */
    private static class ClassAndAnnotation {
        private final Class<?> clazz;
        private final Class<? extends Annotation> annotation;

        private ClassAndAnnotation(Class<?> clazz, Class<? extends Annotation> annotation) {
            this.clazz = clazz;
            this.annotation = annotation;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            ClassAndAnnotation that = (ClassAndAnnotation) o;

            if (annotation != null ? !annotation.equals(that.annotation) : that.annotation != null) return false;
            if (clazz != null ? !clazz.equals(that.clazz) : that.clazz != null) return false;

            return true;
        }

        @Override
        public int hashCode() {
            int result = clazz != null ? clazz.hashCode() : 0;
            result = 31 * result + (annotation != null ? annotation.hashCode() : 0);
            return result;
        }
    }


    // cache follows..

    private final Object classAndAnnotationsLock = new Object();
    private final Map<ClassAndAnnotation, List<Method>> classAndAnnotation2Methods = new HashMap<ClassAndAnnotation, List<Method>>();
    private final Map<Class<?>, List<Method>> class2AllMethodsWithAnnotations = new HashMap<Class<?>, List<Method>>();

    /**
     * Find all annotated method from a class
     * @param clazz The class
     * @param annotationType The annotation class
     * @return A list of method object
     */
    public List<Method> findAllAnnotatedMethods(Class<?> clazz, Class<? extends Annotation> annotationType) {

        if( clazz == null ) {
            return new ArrayList<Method>(0);
        }

        synchronized( classAndAnnotationsLock ) {

            // first look in cache

            ClassAndAnnotation key = new ClassAndAnnotation(clazz, annotationType);

            List<Method> methods = classAndAnnotation2Methods.get( key );
            if( methods != null ) {
                // cache hit
                return methods;
            }
            // have to resolve it.
            methods = new ArrayList<Method>();
            // get list of all annotated methods on this class..
            for( Method method : findAllAnnotatedMethods( clazz)) {
                if (method.isAnnotationPresent(annotationType)) {
                    methods.add(method);
                }
            }

            // store it in cache
            classAndAnnotation2Methods.put( key, methods);

            return methods;
        }
    }

    /**
     * Find all annotated method from a class
     * @param clazz The class
     * @return A list of method object
     */
    public List<Method> findAllAnnotatedMethods(Class<?> clazz) {
        synchronized( classAndAnnotationsLock ) {
            // first check the cache..
            List<Method> methods = class2AllMethodsWithAnnotations.get(clazz);
            if( methods != null ) {
                // cache hit
                return methods;
            }
            //have to resolve it..
            methods = new ArrayList<Method>();
            // Clazz can be null if we are looking at an interface / annotation
            while (clazz != null && !clazz.equals(Object.class)) {
                for (Method method : clazz.getDeclaredMethods()) {
                    if (method.getAnnotations().length > 0) {
                        methods.add(method);
                    }
                }
                if (clazz.isAnnotationPresent(With.class)) {
                    for (Class withClass : clazz.getAnnotation(With.class).value()) {
                        methods.addAll(findAllAnnotatedMethods(withClass ));
                    }
                }
                clazz = clazz.getSuperclass();
            }

            //store it in the cache.
            class2AllMethodsWithAnnotations.put(clazz, methods);
            return methods;
        }
    }


}
TOP

Related Classes of play.utils.JavaWithCaching$ClassAndAnnotation

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.