001 /* 002 * $Id: CompilingPropertyAccessor.java 1103095 2011-05-14 13:18:29Z simonetripodi $ 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, 014 * software distributed under the License is distributed on an 015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 016 * KIND, either express or implied. See the License for the 017 * specific language governing permissions and limitations 018 * under the License. 019 */ 020 package org.apache.commons.ognl.test; 021 022 import javassist.ClassPool; 023 import javassist.CtClass; 024 import javassist.CtMethod; 025 import javassist.LoaderClassPath; 026 import org.apache.commons.ognl.ObjectPropertyAccessor; 027 import org.apache.commons.ognl.OgnlContext; 028 import org.apache.commons.ognl.OgnlException; 029 import org.apache.commons.ognl.OgnlRuntime; 030 import org.apache.commons.ognl.enhance.ContextClassLoader; 031 import org.apache.commons.ognl.enhance.EnhancedClassLoader; 032 import org.apache.commons.ognl.test.util.NameFactory; 033 034 import java.lang.reflect.Method; 035 import java.lang.reflect.Modifier; 036 import java.util.HashMap; 037 import java.util.IdentityHashMap; 038 import java.util.Map; 039 040 /** 041 * Implementation of PropertyAccessor that uses Javassist to compile a property accessor specifically tailored to the 042 * property. 043 */ 044 public class CompilingPropertyAccessor 045 extends ObjectPropertyAccessor 046 { 047 048 private static NameFactory NAME_FACTORY = new NameFactory( "ognl.PropertyAccessor", "v" ); 049 050 private static Getter NotFoundGetter = new Getter() 051 { 052 053 public Object get( OgnlContext context, Object target, String propertyName ) 054 { 055 return null; 056 } 057 }; 058 059 private static Getter DefaultGetter = new Getter() 060 { 061 062 public Object get( OgnlContext context, Object target, String propertyName ) 063 { 064 try 065 { 066 return OgnlRuntime.getMethodValue( context, target, propertyName, true ); 067 } 068 catch ( Exception ex ) 069 { 070 throw new RuntimeException( ex ); 071 } 072 } 073 }; 074 075 private static Map pools = new HashMap(); 076 077 private static Map loaders = new HashMap(); 078 079 private static java.util.IdentityHashMap PRIMITIVE_WRAPPER_CLASSES = new IdentityHashMap(); 080 081 private java.util.IdentityHashMap seenGetMethods = new java.util.IdentityHashMap(); 082 083 static 084 { 085 PRIMITIVE_WRAPPER_CLASSES.put( Boolean.TYPE, Boolean.class ); 086 PRIMITIVE_WRAPPER_CLASSES.put( Boolean.class, Boolean.TYPE ); 087 PRIMITIVE_WRAPPER_CLASSES.put( Byte.TYPE, Byte.class ); 088 PRIMITIVE_WRAPPER_CLASSES.put( Byte.class, Byte.TYPE ); 089 PRIMITIVE_WRAPPER_CLASSES.put( Character.TYPE, Character.class ); 090 PRIMITIVE_WRAPPER_CLASSES.put( Character.class, Character.TYPE ); 091 PRIMITIVE_WRAPPER_CLASSES.put( Short.TYPE, Short.class ); 092 PRIMITIVE_WRAPPER_CLASSES.put( Short.class, Short.TYPE ); 093 PRIMITIVE_WRAPPER_CLASSES.put( Integer.TYPE, Integer.class ); 094 PRIMITIVE_WRAPPER_CLASSES.put( Integer.class, Integer.TYPE ); 095 PRIMITIVE_WRAPPER_CLASSES.put( Long.TYPE, Long.class ); 096 PRIMITIVE_WRAPPER_CLASSES.put( Long.class, Long.TYPE ); 097 PRIMITIVE_WRAPPER_CLASSES.put( Float.TYPE, Float.class ); 098 PRIMITIVE_WRAPPER_CLASSES.put( Float.class, Float.TYPE ); 099 PRIMITIVE_WRAPPER_CLASSES.put( Double.TYPE, Double.class ); 100 PRIMITIVE_WRAPPER_CLASSES.put( Double.class, Double.TYPE ); 101 } 102 103 public static Class getPrimitiveWrapperClass( Class primitiveClass ) 104 { 105 return (Class) PRIMITIVE_WRAPPER_CLASSES.get( primitiveClass ); 106 } 107 108 public interface Getter 109 { 110 public Object get( OgnlContext context, Object target, String propertyName ); 111 } 112 113 public static Getter generateGetter( OgnlContext context, String code ) 114 throws OgnlException 115 { 116 String className = NAME_FACTORY.getNewClassName(); 117 118 try 119 { 120 ClassPool pool = (ClassPool) pools.get( context.getClassResolver() ); 121 EnhancedClassLoader loader = (EnhancedClassLoader) loaders.get( context.getClassResolver() ); 122 CtClass newClass; 123 CtClass ognlContextClass; 124 CtClass objectClass; 125 CtClass stringClass; 126 CtMethod method; 127 byte[] byteCode; 128 Class compiledClass; 129 130 if ( ( pool == null ) || ( loader == null ) ) 131 { 132 ClassLoader classLoader = new ContextClassLoader( OgnlContext.class.getClassLoader(), context ); 133 134 pool = ClassPool.getDefault(); 135 pool.insertClassPath( new LoaderClassPath( classLoader ) ); 136 pools.put( context.getClassResolver(), pool ); 137 138 loader = new EnhancedClassLoader( classLoader ); 139 loaders.put( context.getClassResolver(), loader ); 140 } 141 142 newClass = pool.makeClass( className ); 143 ognlContextClass = pool.get( OgnlContext.class.getName() ); 144 objectClass = pool.get( Object.class.getName() ); 145 stringClass = pool.get( String.class.getName() ); 146 147 newClass.addInterface( pool.get( Getter.class.getName() ) ); 148 method = 149 new CtMethod( objectClass, "get", new CtClass[] { ognlContextClass, objectClass, stringClass }, 150 newClass ); 151 method.setBody( "{" + code + "}" ); 152 newClass.addMethod( method ); 153 byteCode = newClass.toBytecode(); 154 compiledClass = loader.defineClass( className, byteCode ); 155 return (Getter) compiledClass.newInstance(); 156 } 157 catch ( Throwable ex ) 158 { 159 throw new OgnlException( "Cannot create class", ex ); 160 } 161 } 162 163 private Getter getGetter( OgnlContext context, Object target, String propertyName ) 164 throws OgnlException 165 { 166 Getter result; 167 Class targetClass = target.getClass(); 168 Map propertyMap; 169 170 if ( ( propertyMap = (Map) seenGetMethods.get( targetClass ) ) == null ) 171 { 172 propertyMap = new HashMap( 101 ); 173 seenGetMethods.put( targetClass, propertyMap ); 174 } 175 if ( ( result = (Getter) propertyMap.get( propertyName ) ) == null ) 176 { 177 try 178 { 179 Method method = OgnlRuntime.getGetMethod( context, targetClass, propertyName ); 180 181 if ( method != null ) 182 { 183 if ( Modifier.isPublic( method.getModifiers() ) ) 184 { 185 if ( method.getReturnType().isPrimitive() ) 186 { 187 propertyMap.put( propertyName, 188 result = 189 generateGetter( context, 190 "java.lang.Object\t\tresult;\n" 191 + targetClass.getName() 192 + "\t" 193 + "t0 = (" 194 + targetClass.getName() 195 + ")$2;\n" 196 + "\n" 197 + "try {\n" 198 + " result = new " 199 + getPrimitiveWrapperClass( method.getReturnType() ).getName() 200 + "(t0." 201 + method.getName() 202 + "());\n" 203 + "} catch (java.lang.Exception ex) {\n" 204 + " throw new java.lang.RuntimeException(ex);\n" 205 + "}\n" + "return result;" ) ); 206 } 207 else 208 { 209 propertyMap.put( propertyName, 210 result = 211 generateGetter( context, 212 "java.lang.Object\t\tresult;\n" 213 + targetClass.getName() 214 + "\t" 215 + "t0 = (" 216 + targetClass.getName() 217 + ")$2;\n" 218 + "\n" 219 + "try {\n" 220 + " result = t0." 221 + method.getName() 222 + "();\n" 223 + "} catch (java.lang.Exception ex) {\n" 224 + " throw new java.lang.RuntimeException(ex);\n" 225 + "}\n" + "return result;" ) ); 226 } 227 } 228 else 229 { 230 propertyMap.put( propertyName, result = DefaultGetter ); 231 } 232 } 233 else 234 { 235 propertyMap.put( propertyName, result = NotFoundGetter ); 236 } 237 } 238 catch ( Exception ex ) 239 { 240 throw new OgnlException( "getting getter", ex ); 241 } 242 } 243 return result; 244 } 245 246 /** 247 * Returns OgnlRuntime.NotFound if the property does not exist. 248 */ 249 public Object getPossibleProperty( Map context, Object target, String name ) 250 throws OgnlException 251 { 252 Object result; 253 OgnlContext ognlContext = (OgnlContext) context; 254 255 if ( context.get( "_compile" ) != null ) 256 { 257 Getter getter = getGetter( ognlContext, target, name ); 258 259 if ( getter != NotFoundGetter ) 260 { 261 result = getter.get( ognlContext, target, name ); 262 } 263 else 264 { 265 try 266 { 267 result = OgnlRuntime.getFieldValue( ognlContext, target, name, true ); 268 } 269 catch ( Exception ex ) 270 { 271 throw new OgnlException( name, ex ); 272 } 273 } 274 } 275 else 276 { 277 result = super.getPossibleProperty( context, target, name ); 278 } 279 return result; 280 } 281 }