| /* |
| * $Id$ |
| * 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.commons.ognl.test; |
| |
| import javassist.ClassPool; |
| import javassist.CtClass; |
| import javassist.CtMethod; |
| import javassist.LoaderClassPath; |
| import org.apache.commons.ognl.ObjectPropertyAccessor; |
| import org.apache.commons.ognl.OgnlContext; |
| import org.apache.commons.ognl.OgnlException; |
| import org.apache.commons.ognl.OgnlRuntime; |
| import org.apache.commons.ognl.enhance.ContextClassLoader; |
| import org.apache.commons.ognl.enhance.EnhancedClassLoader; |
| import org.apache.commons.ognl.test.util.NameFactory; |
| |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| import java.util.HashMap; |
| import java.util.IdentityHashMap; |
| import java.util.Map; |
| |
| /** |
| * Implementation of PropertyAccessor that uses Javassist to compile a property accessor specifically tailored to the |
| * property. |
| */ |
| public class CompilingPropertyAccessor |
| extends ObjectPropertyAccessor |
| { |
| |
| private static final NameFactory NAME_FACTORY = new NameFactory( "ognl.PropertyAccessor", "v" ); |
| |
| private static final Getter NOT_FOUND_GETTER = (context, target, propertyName) -> null; |
| |
| private static final Getter DEFAULT_GETTER = (context, target, propertyName) -> { |
| try |
| { |
| return OgnlRuntime.getMethodValue( context, target, propertyName, true ); |
| } |
| catch ( Exception ex ) |
| { |
| throw new RuntimeException( ex ); |
| } |
| }; |
| |
| private static final Map POOLS = new HashMap(); |
| |
| private static final Map LOADERS = new HashMap(); |
| |
| private static final java.util.IdentityHashMap PRIMITIVE_WRAPPER_CLASSES = new IdentityHashMap(); |
| |
| private final java.util.IdentityHashMap seenGetMethods = new java.util.IdentityHashMap(); |
| |
| static |
| { |
| PRIMITIVE_WRAPPER_CLASSES.put( Boolean.TYPE, Boolean.class ); |
| PRIMITIVE_WRAPPER_CLASSES.put( Boolean.class, Boolean.TYPE ); |
| PRIMITIVE_WRAPPER_CLASSES.put( Byte.TYPE, Byte.class ); |
| PRIMITIVE_WRAPPER_CLASSES.put( Byte.class, Byte.TYPE ); |
| PRIMITIVE_WRAPPER_CLASSES.put( Character.TYPE, Character.class ); |
| PRIMITIVE_WRAPPER_CLASSES.put( Character.class, Character.TYPE ); |
| PRIMITIVE_WRAPPER_CLASSES.put( Short.TYPE, Short.class ); |
| PRIMITIVE_WRAPPER_CLASSES.put( Short.class, Short.TYPE ); |
| PRIMITIVE_WRAPPER_CLASSES.put( Integer.TYPE, Integer.class ); |
| PRIMITIVE_WRAPPER_CLASSES.put( Integer.class, Integer.TYPE ); |
| PRIMITIVE_WRAPPER_CLASSES.put( Long.TYPE, Long.class ); |
| PRIMITIVE_WRAPPER_CLASSES.put( Long.class, Long.TYPE ); |
| PRIMITIVE_WRAPPER_CLASSES.put( Float.TYPE, Float.class ); |
| PRIMITIVE_WRAPPER_CLASSES.put( Float.class, Float.TYPE ); |
| PRIMITIVE_WRAPPER_CLASSES.put( Double.TYPE, Double.class ); |
| PRIMITIVE_WRAPPER_CLASSES.put( Double.class, Double.TYPE ); |
| } |
| |
| public static Class getPrimitiveWrapperClass( Class primitiveClass ) |
| { |
| return (Class) PRIMITIVE_WRAPPER_CLASSES.get( primitiveClass ); |
| } |
| |
| public interface Getter |
| { |
| Object get( OgnlContext context, Object target, String propertyName ); |
| } |
| |
| public static Getter generateGetter( OgnlContext context, String code ) |
| throws OgnlException |
| { |
| String className = NAME_FACTORY.getNewClassName(); |
| |
| try |
| { |
| ClassPool pool = (ClassPool) POOLS.get( context.getClassResolver() ); |
| EnhancedClassLoader loader = (EnhancedClassLoader) LOADERS.get( context.getClassResolver() ); |
| CtClass newClass; |
| CtClass ognlContextClass; |
| CtClass objectClass; |
| CtClass stringClass; |
| CtMethod method; |
| byte[] byteCode; |
| Class compiledClass; |
| |
| if ( ( pool == null ) || ( loader == null ) ) |
| { |
| ClassLoader classLoader = new ContextClassLoader( OgnlContext.class.getClassLoader(), context ); |
| |
| pool = ClassPool.getDefault(); |
| pool.insertClassPath( new LoaderClassPath( classLoader ) ); |
| POOLS.put( context.getClassResolver(), pool ); |
| |
| loader = new EnhancedClassLoader( classLoader ); |
| LOADERS.put( context.getClassResolver(), loader ); |
| } |
| |
| newClass = pool.makeClass( className ); |
| ognlContextClass = pool.get( OgnlContext.class.getName() ); |
| objectClass = pool.get( Object.class.getName() ); |
| stringClass = pool.get( String.class.getName() ); |
| |
| newClass.addInterface( pool.get( Getter.class.getName() ) ); |
| method = |
| new CtMethod( objectClass, "get", new CtClass[] { ognlContextClass, objectClass, stringClass }, |
| newClass ); |
| method.setBody( "{" + code + "}" ); |
| newClass.addMethod( method ); |
| byteCode = newClass.toBytecode(); |
| compiledClass = loader.defineClass( className, byteCode ); |
| return (Getter) compiledClass.newInstance(); |
| } |
| catch ( Throwable ex ) |
| { |
| throw new OgnlException( "Cannot create class", ex ); |
| } |
| } |
| |
| private Getter getGetter( OgnlContext context, Object target, String propertyName ) |
| throws OgnlException |
| { |
| Getter result; |
| Class targetClass = target.getClass(); |
| Map propertyMap; |
| |
| if ( ( propertyMap = (Map) seenGetMethods.get( targetClass ) ) == null ) |
| { |
| propertyMap = new HashMap( 101 ); |
| seenGetMethods.put( targetClass, propertyMap ); |
| } |
| if ( ( result = (Getter) propertyMap.get( propertyName ) ) == null ) |
| { |
| try |
| { |
| Method method = OgnlRuntime.getGetMethod( context, targetClass, propertyName ); |
| |
| if ( method != null ) |
| { |
| if ( Modifier.isPublic( method.getModifiers() ) ) |
| { |
| if ( method.getReturnType().isPrimitive() ) |
| { |
| propertyMap.put( propertyName, |
| result = |
| generateGetter( context, |
| "java.lang.Object\t\tresult;\n" |
| + targetClass.getName() |
| + "\t" |
| + "t0 = (" |
| + targetClass.getName() |
| + ")$2;\n" |
| + "\n" |
| + "try {\n" |
| + " result = new " |
| + getPrimitiveWrapperClass( method.getReturnType() ).getName() |
| + "(t0." |
| + method.getName() |
| + "());\n" |
| + "} catch (java.lang.Exception ex) {\n" |
| + " throw new java.lang.RuntimeException(ex);\n" |
| + "}\n" + "return result;" ) ); |
| } |
| else |
| { |
| propertyMap.put( propertyName, |
| result = |
| generateGetter( context, |
| "java.lang.Object\t\tresult;\n" |
| + targetClass.getName() |
| + "\t" |
| + "t0 = (" |
| + targetClass.getName() |
| + ")$2;\n" |
| + "\n" |
| + "try {\n" |
| + " result = t0." |
| + method.getName() |
| + "();\n" |
| + "} catch (java.lang.Exception ex) {\n" |
| + " throw new java.lang.RuntimeException(ex);\n" |
| + "}\n" + "return result;" ) ); |
| } |
| } |
| else |
| { |
| propertyMap.put( propertyName, result = DEFAULT_GETTER ); |
| } |
| } |
| else |
| { |
| propertyMap.put( propertyName, result = NOT_FOUND_GETTER ); |
| } |
| } |
| catch ( Exception ex ) |
| { |
| throw new OgnlException( "getting getter", ex ); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Returns OgnlRuntime.NotFound if the property does not exist. |
| */ |
| public Object getPossibleProperty( Map context, Object target, String name ) |
| throws OgnlException |
| { |
| Object result; |
| OgnlContext ognlContext = (OgnlContext) context; |
| |
| if ( context.get( "_compile" ) != null ) |
| { |
| Getter getter = getGetter( ognlContext, target, name ); |
| |
| if ( getter != NOT_FOUND_GETTER ) |
| { |
| result = getter.get( ognlContext, target, name ); |
| } |
| else |
| { |
| try |
| { |
| result = OgnlRuntime.getFieldValue( ognlContext, target, name, true ); |
| } |
| catch ( Exception ex ) |
| { |
| throw new OgnlException( name, ex ); |
| } |
| } |
| } |
| else |
| { |
| result = super.getPossibleProperty( context, target, name ); |
| } |
| return result; |
| } |
| } |