View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.el.convert;
20  
21  import javax.el.ELContext;
22  import javax.el.ELException;
23  import javax.el.ELResolver;
24  import javax.el.PropertyNotFoundException;
25  import javax.el.PropertyNotWritableException;
26  import javax.faces.context.FacesContext;
27  import javax.faces.el.EvaluationException;
28  import javax.faces.el.VariableResolver;
29  import java.beans.FeatureDescriptor;
30  import java.util.Collection;
31  import java.util.HashSet;
32  import java.util.Iterator;
33  import org.apache.myfaces.el.VariableResolverImpl;
34  
35  /**
36   * Wrapper that converts a VariableResolver into an ELResolver. See JSF 1.2 spec section 5.6.1.5
37   * 
38   * @author Stan Silvert (latest modification by $Author: lu4242 $)
39   * @author Mathias Broekelmann
40   * @version $Revision: 1215315 $ $Date: 2011-12-16 17:06:49 -0500 (Fri, 16 Dec 2011) $
41   */
42  @SuppressWarnings("deprecation")
43  public final class VariableResolverToELResolver extends ELResolver
44  {
45  
46      // holds a flag to check if this instance is already called in current thread 
47      private static final ThreadLocal<Collection<String>> propertyGuardThreadLocal
48              = new ThreadLocal<Collection<String>>();
49  
50      /**
51       * Gets the Collection<String> value of the propertyGuardThreadLocal.
52       * If the value from the ThreadLocal ist null, a new Collection<String>
53       * will be created.
54       *
55       * NOTE that we should not accomplish this by setting an initialValue on the
56       * ThreadLocal, because this will automatically be set on the ThreadLocalMap
57       * and thus can propably cause a memory leak.
58       *
59       * @return
60       */
61      private static Collection<String> getPropertyGuard()
62      {
63          Collection<String> propertyGuard = propertyGuardThreadLocal.get();
64  
65          if (propertyGuard == null)
66          {
67              propertyGuard = new HashSet<String>();
68              propertyGuardThreadLocal.set(propertyGuard);
69          }
70  
71          return propertyGuard;
72      }
73      
74      private VariableResolver variableResolver;
75      private boolean isDefaultLegacyVariableResolver;
76  
77      /**
78       * Creates a new instance of VariableResolverToELResolver
79       */
80      public VariableResolverToELResolver(final VariableResolver variableResolver)
81      {
82          this.variableResolver = variableResolver;
83          this.isDefaultLegacyVariableResolver = (this.variableResolver instanceof VariableResolverImpl);
84      }
85      
86      /**
87       * @return the variableResolver
88       */
89      public VariableResolver getVariableResolver()
90      {
91          return variableResolver;
92      }
93  
94      @Override
95      public Object getValue(ELContext context, Object base, Object property) throws NullPointerException,
96              PropertyNotFoundException, ELException
97      {
98          if (isDefaultLegacyVariableResolver)
99          {
100             // Skip this one because it causes previous EL Resolvers already processed
101             // before this one to be called again. This behavior is only valid if it 
102             // is installed a custom VariableResolver.
103             return null;
104         }
105         if (base != null)
106         {
107             return null;
108         }
109         if (property == null)
110         {
111             throw new PropertyNotFoundException();
112         }
113 
114         context.setPropertyResolved(true);
115 
116         if (!(property instanceof String))
117         {
118             return null;
119         }
120 
121         final String strProperty = (String) property;
122 
123         Collection<String> propertyGuard = getPropertyGuard();
124 
125         Object result = null;
126         try
127         {
128             // only call the resolver if we haven't done it in current stack
129             if(!propertyGuard.contains(strProperty))
130             {
131                 propertyGuard.add(strProperty);
132                 result = variableResolver.resolveVariable(facesContext(context), strProperty);
133             }
134         }
135         catch (javax.faces.el.PropertyNotFoundException e)
136         {
137             context.setPropertyResolved(false);
138             throw new PropertyNotFoundException(e.getMessage(), e);
139         }
140         catch (EvaluationException e)
141         {
142             context.setPropertyResolved(false);
143             throw new ELException(e.getMessage(), e);
144         }
145         catch (RuntimeException e)
146         {
147             context.setPropertyResolved(false);
148             throw e;
149         }
150         finally
151         {
152             propertyGuard.remove(strProperty);
153 
154             // if the propertyGuard is empty, remove the ThreadLocal
155             // in order to prevent a memory leak
156             if (propertyGuard.isEmpty())
157             {
158                 propertyGuardThreadLocal.remove();
159             }
160 
161             // set property resolved to false in any case if result is null
162             context.setPropertyResolved(result != null);
163         }
164 
165         return result;
166     }
167 
168     // get the FacesContext from the ELContext
169     private static FacesContext facesContext(ELContext context)
170     {
171         return (FacesContext) context.getContext(FacesContext.class);
172     }
173 
174     @Override
175     public Class<?> getCommonPropertyType(ELContext context, Object base)
176     {
177         if (isDefaultLegacyVariableResolver)
178         {
179             return null;
180         }
181         if (base != null)
182         {
183             return null;
184         }
185 
186         return String.class;
187     }
188 
189     @Override
190     public void setValue(ELContext context, Object base, Object property, Object value) throws NullPointerException,
191             PropertyNotFoundException, PropertyNotWritableException, ELException
192     {
193         if (isDefaultLegacyVariableResolver)
194         {
195             return;
196         }
197         if ((base == null) && (property == null))
198         {
199             throw new PropertyNotFoundException();
200         }
201     }
202 
203     @Override
204     public boolean isReadOnly(ELContext context, Object base, Object property) throws NullPointerException,
205             PropertyNotFoundException, ELException
206     {
207         if (isDefaultLegacyVariableResolver)
208         {
209             return false;
210         }
211         if ((base == null) && (property == null))
212         {
213             throw new PropertyNotFoundException();
214         }
215 
216         return false;
217     }
218 
219     @Override
220     public Class<?> getType(ELContext context, Object base, Object property) throws NullPointerException,
221             PropertyNotFoundException, ELException
222     {
223         if (isDefaultLegacyVariableResolver)
224         {
225             return null;
226         }
227         if ((base == null) && (property == null))
228         {
229             throw new PropertyNotFoundException();
230         }
231 
232         return null;
233     }
234 
235     @Override
236     public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base)
237     {
238         if (isDefaultLegacyVariableResolver)
239         {
240             return null;
241         }
242         return null;
243     }
244 
245 }