View Javadoc

1   /*
2    * $Id: CompilingPropertyAccessor.java 1103095 2011-05-14 13:18:29Z simonetripodi $
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   * http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
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   * Implementation of PropertyAccessor that uses Javassist to compile a property accessor specifically tailored to the
42   * property.
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      * 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 }