View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.struts2.jasper.runtime;
19  
20  import org.apache.commons.el.VariableResolverImpl;
21  import org.apache.struts2.jasper.compiler.Localizer;
22  
23  import javax.servlet.*;
24  import javax.servlet.http.HttpSession;
25  import javax.servlet.jsp.JspContext;
26  import javax.servlet.jsp.JspWriter;
27  import javax.servlet.jsp.PageContext;
28  import javax.servlet.jsp.el.ELException;
29  import javax.servlet.jsp.el.ExpressionEvaluator;
30  import javax.servlet.jsp.el.VariableResolver;
31  import javax.servlet.jsp.tagext.BodyContent;
32  import javax.servlet.jsp.tagext.VariableInfo;
33  import java.io.IOException;
34  import java.io.Writer;
35  import java.util.*;
36  
37  /***
38   * Implementation of a JSP Context Wrapper.
39   * <p/>
40   * The JSP Context Wrapper is a JspContext created and maintained by a tag
41   * handler implementation. It wraps the Invoking JSP Context, that is, the
42   * JspContext instance passed to the tag handler by the invoking page via
43   * setJspContext().
44   *
45   * @author Kin-man Chung
46   * @author Jan Luehe
47   */
48  public class JspContextWrapper
49          extends PageContext implements VariableResolver {
50  
51      // Invoking JSP context
52      private PageContext invokingJspCtxt;
53  
54      private transient Hashtable pageAttributes;
55  
56      // ArrayList of NESTED scripting variables
57      private ArrayList nestedVars;
58  
59      // ArrayList of AT_BEGIN scripting variables
60      private ArrayList atBeginVars;
61  
62      // ArrayList of AT_END scripting variables
63      private ArrayList atEndVars;
64  
65      private Map aliases;
66  
67      private Hashtable originalNestedVars;
68  
69      /***
70       * The variable resolver, for evaluating EL expressions.
71       */
72      private VariableResolverImpl variableResolver
73              = new VariableResolverImpl(this);
74  
75      public JspContextWrapper(JspContext jspContext, ArrayList nestedVars,
76                               ArrayList atBeginVars, ArrayList atEndVars,
77                               Map aliases) {
78          this.invokingJspCtxt = (PageContext) jspContext;
79          this.nestedVars = nestedVars;
80          this.atBeginVars = atBeginVars;
81          this.atEndVars = atEndVars;
82          this.pageAttributes = new Hashtable(16);
83          this.aliases = aliases;
84  
85          if (nestedVars != null) {
86              this.originalNestedVars = new Hashtable(nestedVars.size());
87          }
88          syncBeginTagFile();
89      }
90  
91      public void initialize(Servlet servlet, ServletRequest request,
92                             ServletResponse response, String errorPageURL,
93                             boolean needsSession, int bufferSize,
94                             boolean autoFlush)
95              throws IOException, IllegalStateException, IllegalArgumentException {
96      }
97  
98      public Object getAttribute(String name) {
99  
100         if (name == null) {
101             throw new NullPointerException(
102                     Localizer.getMessage("jsp.error.attribute.null_name"));
103         }
104 
105         return pageAttributes.get(name);
106     }
107 
108     public Object getAttribute(String name, int scope) {
109 
110         if (name == null) {
111             throw new NullPointerException(
112                     Localizer.getMessage("jsp.error.attribute.null_name"));
113         }
114 
115         if (scope == PAGE_SCOPE) {
116             return pageAttributes.get(name);
117         }
118 
119         return invokingJspCtxt.getAttribute(name, scope);
120     }
121 
122     public void setAttribute(String name, Object value) {
123 
124         if (name == null) {
125             throw new NullPointerException(
126                     Localizer.getMessage("jsp.error.attribute.null_name"));
127         }
128 
129         if (value != null) {
130             pageAttributes.put(name, value);
131         } else {
132             removeAttribute(name, PAGE_SCOPE);
133         }
134     }
135 
136     public void setAttribute(String name, Object value, int scope) {
137 
138         if (name == null) {
139             throw new NullPointerException(
140                     Localizer.getMessage("jsp.error.attribute.null_name"));
141         }
142 
143         if (scope == PAGE_SCOPE) {
144             if (value != null) {
145                 pageAttributes.put(name, value);
146             } else {
147                 removeAttribute(name, PAGE_SCOPE);
148             }
149         } else {
150             invokingJspCtxt.setAttribute(name, value, scope);
151         }
152     }
153 
154     public Object findAttribute(String name) {
155 
156         if (name == null) {
157             throw new NullPointerException(
158                     Localizer.getMessage("jsp.error.attribute.null_name"));
159         }
160 
161         Object o = pageAttributes.get(name);
162         if (o == null) {
163             o = invokingJspCtxt.getAttribute(name, REQUEST_SCOPE);
164             if (o == null) {
165                 if (getSession() != null) {
166                     o = invokingJspCtxt.getAttribute(name, SESSION_SCOPE);
167                 }
168                 if (o == null) {
169                     o = invokingJspCtxt.getAttribute(name, APPLICATION_SCOPE);
170                 }
171             }
172         }
173 
174         return o;
175     }
176 
177     public void removeAttribute(String name) {
178 
179         if (name == null) {
180             throw new NullPointerException(
181                     Localizer.getMessage("jsp.error.attribute.null_name"));
182         }
183 
184         pageAttributes.remove(name);
185         invokingJspCtxt.removeAttribute(name, REQUEST_SCOPE);
186         if (getSession() != null) {
187             invokingJspCtxt.removeAttribute(name, SESSION_SCOPE);
188         }
189         invokingJspCtxt.removeAttribute(name, APPLICATION_SCOPE);
190     }
191 
192     public void removeAttribute(String name, int scope) {
193 
194         if (name == null) {
195             throw new NullPointerException(
196                     Localizer.getMessage("jsp.error.attribute.null_name"));
197         }
198 
199         if (scope == PAGE_SCOPE) {
200             pageAttributes.remove(name);
201         } else {
202             invokingJspCtxt.removeAttribute(name, scope);
203         }
204     }
205 
206     public int getAttributesScope(String name) {
207 
208         if (name == null) {
209             throw new NullPointerException(
210                     Localizer.getMessage("jsp.error.attribute.null_name"));
211         }
212 
213         if (pageAttributes.get(name) != null) {
214             return PAGE_SCOPE;
215         } else {
216             return invokingJspCtxt.getAttributesScope(name);
217         }
218     }
219 
220     public Enumeration getAttributeNamesInScope(int scope) {
221         if (scope == PAGE_SCOPE) {
222             return pageAttributes.keys();
223         }
224 
225         return invokingJspCtxt.getAttributeNamesInScope(scope);
226     }
227 
228     public void release() {
229         invokingJspCtxt.release();
230     }
231 
232     public JspWriter getOut() {
233         return invokingJspCtxt.getOut();
234     }
235 
236     public HttpSession getSession() {
237         return invokingJspCtxt.getSession();
238     }
239 
240     public Object getPage() {
241         return invokingJspCtxt.getPage();
242     }
243 
244     public ServletRequest getRequest() {
245         return invokingJspCtxt.getRequest();
246     }
247 
248     public ServletResponse getResponse() {
249         return invokingJspCtxt.getResponse();
250     }
251 
252     public Exception getException() {
253         return invokingJspCtxt.getException();
254     }
255 
256     public ServletConfig getServletConfig() {
257         return invokingJspCtxt.getServletConfig();
258     }
259 
260     public ServletContext getServletContext() {
261         return invokingJspCtxt.getServletContext();
262     }
263 
264     public void forward(String relativeUrlPath)
265             throws ServletException, IOException {
266         invokingJspCtxt.forward(relativeUrlPath);
267     }
268 
269     public void include(String relativeUrlPath)
270             throws ServletException, IOException {
271         invokingJspCtxt.include(relativeUrlPath);
272     }
273 
274     public void include(String relativeUrlPath, boolean flush)
275             throws ServletException, IOException {
276         include(relativeUrlPath, false); // XXX
277     }
278 
279     public VariableResolver getVariableResolver() {
280         return this;
281     }
282 
283     public BodyContent pushBody() {
284         return invokingJspCtxt.pushBody();
285     }
286 
287     public JspWriter pushBody(Writer writer) {
288         return invokingJspCtxt.pushBody(writer);
289     }
290 
291     public JspWriter popBody() {
292         return invokingJspCtxt.popBody();
293     }
294 
295     public ExpressionEvaluator getExpressionEvaluator() {
296         return invokingJspCtxt.getExpressionEvaluator();
297     }
298 
299     public void handlePageException(Exception ex)
300             throws IOException, ServletException {
301         // Should never be called since handleException() called with a
302         // Throwable in the generated servlet.
303         handlePageException((Throwable) ex);
304     }
305 
306     public void handlePageException(Throwable t)
307             throws IOException, ServletException {
308         invokingJspCtxt.handlePageException(t);
309     }
310 
311     /***
312      * VariableResolver interface
313      */
314     public Object resolveVariable(String pName) throws ELException {
315         return variableResolver.resolveVariable(pName);
316     }
317 
318     /***
319      * Synchronize variables at begin of tag file
320      */
321     public void syncBeginTagFile() {
322         saveNestedVariables();
323     }
324 
325     /***
326      * Synchronize variables before fragment invokation
327      */
328     public void syncBeforeInvoke() {
329         copyTagToPageScope(VariableInfo.NESTED);
330         copyTagToPageScope(VariableInfo.AT_BEGIN);
331     }
332 
333     /***
334      * Synchronize variables at end of tag file
335      */
336     public void syncEndTagFile() {
337         copyTagToPageScope(VariableInfo.AT_BEGIN);
338         copyTagToPageScope(VariableInfo.AT_END);
339         restoreNestedVariables();
340     }
341 
342     /***
343      * Copies the variables of the given scope from the virtual page scope of
344      * this JSP context wrapper to the page scope of the invoking JSP context.
345      *
346      * @param scope variable scope (one of NESTED, AT_BEGIN, or AT_END)
347      */
348     private void copyTagToPageScope(int scope) {
349         Iterator iter = null;
350 
351         switch (scope) {
352             case VariableInfo.NESTED:
353                 if (nestedVars != null) {
354                     iter = nestedVars.iterator();
355                 }
356                 break;
357             case VariableInfo.AT_BEGIN:
358                 if (atBeginVars != null) {
359                     iter = atBeginVars.iterator();
360                 }
361                 break;
362             case VariableInfo.AT_END:
363                 if (atEndVars != null) {
364                     iter = atEndVars.iterator();
365                 }
366                 break;
367         }
368 
369         while ((iter != null) && iter.hasNext()) {
370             String varName = (String) iter.next();
371             Object obj = getAttribute(varName);
372             varName = findAlias(varName);
373             if (obj != null) {
374                 invokingJspCtxt.setAttribute(varName, obj);
375             } else {
376                 invokingJspCtxt.removeAttribute(varName, PAGE_SCOPE);
377             }
378         }
379     }
380 
381     /***
382      * Saves the values of any NESTED variables that are present in
383      * the invoking JSP context, so they can later be restored.
384      */
385     private void saveNestedVariables() {
386         if (nestedVars != null) {
387             Iterator iter = nestedVars.iterator();
388             while (iter.hasNext()) {
389                 String varName = (String) iter.next();
390                 varName = findAlias(varName);
391                 Object obj = invokingJspCtxt.getAttribute(varName);
392                 if (obj != null) {
393                     originalNestedVars.put(varName, obj);
394                 }
395             }
396         }
397     }
398 
399     /***
400      * Restores the values of any NESTED variables in the invoking JSP
401      * context.
402      */
403     private void restoreNestedVariables() {
404         if (nestedVars != null) {
405             Iterator iter = nestedVars.iterator();
406             while (iter.hasNext()) {
407                 String varName = (String) iter.next();
408                 varName = findAlias(varName);
409                 Object obj = originalNestedVars.get(varName);
410                 if (obj != null) {
411                     invokingJspCtxt.setAttribute(varName, obj);
412                 } else {
413                     invokingJspCtxt.removeAttribute(varName, PAGE_SCOPE);
414                 }
415             }
416         }
417     }
418 
419     /***
420      * Checks to see if the given variable name is used as an alias, and if so,
421      * returns the variable name for which it is used as an alias.
422      *
423      * @param varName The variable name to check
424      * @return The variable name for which varName is used as an alias, or
425      *         varName if it is not being used as an alias
426      */
427     private String findAlias(String varName) {
428 
429         if (aliases == null)
430             return varName;
431 
432         String alias = (String) aliases.get(varName);
433         if (alias == null) {
434             return varName;
435         }
436         return alias;
437     }
438 }
439