1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.commons.ognl.test;
21
22 import javassist.ClassPool;
23 import javassist.CtClass;
24 import javassist.CtMethod;
25 import javassist.LoaderClassPath;
26 import org.apache.commons.ognl.ObjectPropertyAccessor;
27 import org.apache.commons.ognl.OgnlContext;
28 import org.apache.commons.ognl.OgnlException;
29 import org.apache.commons.ognl.OgnlRuntime;
30 import org.apache.commons.ognl.enhance.ContextClassLoader;
31 import org.apache.commons.ognl.enhance.EnhancedClassLoader;
32 import org.apache.commons.ognl.test.util.NameFactory;
33
34 import java.lang.reflect.Method;
35 import java.lang.reflect.Modifier;
36 import java.util.HashMap;
37 import java.util.IdentityHashMap;
38 import java.util.Map;
39
40
41
42
43
44 public class CompilingPropertyAccessor
45 extends ObjectPropertyAccessor
46 {
47
48 private static NameFactory NAME_FACTORY = new NameFactory( "ognl.PropertyAccessor", "v" );
49
50 private static Getter NotFoundGetter = new Getter()
51 {
52
53 public Object get( OgnlContext context, Object target, String propertyName )
54 {
55 return null;
56 }
57 };
58
59 private static Getter DefaultGetter = new Getter()
60 {
61
62 public Object get( OgnlContext context, Object target, String propertyName )
63 {
64 try
65 {
66 return OgnlRuntime.getMethodValue( context, target, propertyName, true );
67 }
68 catch ( Exception ex )
69 {
70 throw new RuntimeException( ex );
71 }
72 }
73 };
74
75 private static Map pools = new HashMap();
76
77 private static Map loaders = new HashMap();
78
79 private static java.util.IdentityHashMap PRIMITIVE_WRAPPER_CLASSES = new IdentityHashMap();
80
81 private java.util.IdentityHashMap seenGetMethods = new java.util.IdentityHashMap();
82
83 static
84 {
85 PRIMITIVE_WRAPPER_CLASSES.put( Boolean.TYPE, Boolean.class );
86 PRIMITIVE_WRAPPER_CLASSES.put( Boolean.class, Boolean.TYPE );
87 PRIMITIVE_WRAPPER_CLASSES.put( Byte.TYPE, Byte.class );
88 PRIMITIVE_WRAPPER_CLASSES.put( Byte.class, Byte.TYPE );
89 PRIMITIVE_WRAPPER_CLASSES.put( Character.TYPE, Character.class );
90 PRIMITIVE_WRAPPER_CLASSES.put( Character.class, Character.TYPE );
91 PRIMITIVE_WRAPPER_CLASSES.put( Short.TYPE, Short.class );
92 PRIMITIVE_WRAPPER_CLASSES.put( Short.class, Short.TYPE );
93 PRIMITIVE_WRAPPER_CLASSES.put( Integer.TYPE, Integer.class );
94 PRIMITIVE_WRAPPER_CLASSES.put( Integer.class, Integer.TYPE );
95 PRIMITIVE_WRAPPER_CLASSES.put( Long.TYPE, Long.class );
96 PRIMITIVE_WRAPPER_CLASSES.put( Long.class, Long.TYPE );
97 PRIMITIVE_WRAPPER_CLASSES.put( Float.TYPE, Float.class );
98 PRIMITIVE_WRAPPER_CLASSES.put( Float.class, Float.TYPE );
99 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
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 }