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.ExpressionEvaluatorImpl;
21  import org.apache.commons.el.VariableResolverImpl;
22  import org.apache.struts2.jasper.Constants;
23  import org.apache.struts2.jasper.compiler.Localizer;
24  import org.apache.struts2.jasper.security.SecurityUtil;
25  
26  import javax.servlet.*;
27  import javax.servlet.http.HttpServletRequest;
28  import javax.servlet.http.HttpServletResponse;
29  import javax.servlet.http.HttpSession;
30  import javax.servlet.jsp.JspException;
31  import javax.servlet.jsp.JspFactory;
32  import javax.servlet.jsp.JspWriter;
33  import javax.servlet.jsp.PageContext;
34  import javax.servlet.jsp.el.ELException;
35  import javax.servlet.jsp.el.ExpressionEvaluator;
36  import javax.servlet.jsp.el.VariableResolver;
37  import javax.servlet.jsp.tagext.BodyContent;
38  import java.io.IOException;
39  import java.io.Writer;
40  import java.security.AccessController;
41  import java.security.PrivilegedAction;
42  import java.security.PrivilegedActionException;
43  import java.security.PrivilegedExceptionAction;
44  import java.util.Enumeration;
45  import java.util.Hashtable;
46  
47  /***
48   * Implementation of the PageContext class from the JSP spec.
49   * Also doubles as a VariableResolver for the EL.
50   *
51   * @author Anil K. Vijendran
52   * @author Larry Cable
53   * @author Hans Bergsten
54   * @author Pierre Delisle
55   * @author Mark Roth
56   * @author Jan Luehe
57   */
58  public class PageContextImpl extends PageContext implements VariableResolver {
59  
60      // The expression evaluator, for evaluating EL expressions.
61      private static ExpressionEvaluatorImpl elExprEval
62              = new ExpressionEvaluatorImpl(false);
63  
64      // The variable resolver, for evaluating EL expressions.
65      private VariableResolverImpl variableResolver;
66  
67      private BodyContentImpl[] outs;
68      private int depth;
69  
70      // per-servlet state
71      private Servlet servlet;
72      private ServletConfig config;
73      private ServletContext context;
74      private String errorPageURL;
75  
76      // page-scope attributes
77      private transient Hashtable attributes;
78  
79      // per-request state
80      private transient ServletRequest request;
81      private transient ServletResponse response;
82      private transient HttpSession session;
83      private boolean isIncluded;
84  
85      // initial output stream
86      private transient JspWriter out;
87      private transient JspWriterImpl baseOut;
88  
89      /*
90       * Constructor.
91       */
92      PageContextImpl(JspFactory factory) {
93          this.variableResolver = new VariableResolverImpl(this);
94          this.outs = new BodyContentImpl[0];
95          this.attributes = new Hashtable(16);
96          this.depth = -1;
97      }
98  
99      public void initialize(Servlet servlet,
100                            ServletRequest request,
101                            ServletResponse response,
102                            String errorPageURL,
103                            boolean needsSession,
104                            int bufferSize,
105                            boolean autoFlush) throws IOException {
106 
107         _initialize(servlet, request, response, errorPageURL, needsSession,
108                 bufferSize, autoFlush);
109     }
110 
111     private void _initialize(Servlet servlet,
112                              ServletRequest request,
113                              ServletResponse response,
114                              String errorPageURL,
115                              boolean needsSession,
116                              int bufferSize,
117                              boolean autoFlush) throws IOException {
118 
119         // initialize state
120         this.servlet = servlet;
121         this.config = servlet.getServletConfig();
122         this.context = config.getServletContext();
123         this.errorPageURL = errorPageURL;
124         this.request = request;
125         this.response = response;
126 
127 
128         // Setup session (if required)
129         if (request instanceof HttpServletRequest && needsSession)
130             this.session = ((HttpServletRequest) request).getSession();
131         if (needsSession && session == null)
132             throw new IllegalStateException
133                     ("Page needs a session and none is available");
134 
135         // initialize the initial out ...
136         depth = -1;
137         if (this.baseOut == null) {
138             this.baseOut = new JspWriterImpl(response, bufferSize, autoFlush);
139         } else {
140             this.baseOut.init(response, bufferSize, autoFlush);
141         }
142         this.out = baseOut;
143 
144         // register names/values as per spec
145         setAttribute(OUT, this.out);
146         setAttribute(REQUEST, request);
147         setAttribute(RESPONSE, response);
148 
149         if (session != null)
150             setAttribute(SESSION, session);
151 
152         setAttribute(PAGE, servlet);
153         setAttribute(CONFIG, config);
154         setAttribute(PAGECONTEXT, this);
155         setAttribute(APPLICATION, context);
156 
157         isIncluded = request.getAttribute(
158                 "javax.servlet.include.servlet_path") != null;
159     }
160 
161     public void release() {
162         out = baseOut;
163         try {
164             if (isIncluded) {
165                 ((JspWriterImpl) out).flushBuffer();
166                 // push it into the including jspWriter
167             } else {
168                 // Old code:
169                 //out.flush();
170                 // Do not flush the buffer even if we're not included (i.e.
171                 // we are the main page. The servlet will flush it and close
172                 // the stream.
173                 ((JspWriterImpl) out).flushBuffer();
174             }
175         } catch (IOException ex) {
176             context.log(
177                     "PageContextImpl.release(): Internal error flushing buffer");
178         }
179 
180         servlet = null;
181         config = null;
182         context = null;
183         errorPageURL = null;
184         request = null;
185         response = null;
186         depth = -1;
187         baseOut.recycle();
188         session = null;
189 
190         attributes.clear();
191     }
192 
193     public Object getAttribute(final String name) {
194 
195         if (name == null) {
196             throw new NullPointerException(
197                     Localizer.getMessage("jsp.error.attribute.null_name"));
198         }
199 
200         if (SecurityUtil.isPackageProtectionEnabled()) {
201             return AccessController.doPrivileged(new PrivilegedAction() {
202                 public Object run() {
203                     return doGetAttribute(name);
204                 }
205             });
206         } else {
207             return doGetAttribute(name);
208         }
209 
210     }
211 
212     private Object doGetAttribute(String name) {
213         return attributes.get(name);
214     }
215 
216     public Object getAttribute(final String name, final int scope) {
217 
218         if (name == null) {
219             throw new NullPointerException(
220                     Localizer.getMessage("jsp.error.attribute.null_name"));
221         }
222 
223         if (SecurityUtil.isPackageProtectionEnabled()) {
224             return AccessController.doPrivileged(new PrivilegedAction() {
225                 public Object run() {
226                     return doGetAttribute(name, scope);
227                 }
228             });
229         } else {
230             return doGetAttribute(name, scope);
231         }
232 
233     }
234 
235     private Object doGetAttribute(String name, int scope) {
236         switch (scope) {
237             case PAGE_SCOPE:
238                 return attributes.get(name);
239 
240             case REQUEST_SCOPE:
241                 return request.getAttribute(name);
242 
243             case SESSION_SCOPE:
244                 if (session == null) {
245                     throw new IllegalStateException(
246                             Localizer.getMessage("jsp.error.page.noSession"));
247                 }
248                 return session.getAttribute(name);
249 
250             case APPLICATION_SCOPE:
251                 return context.getAttribute(name);
252 
253             default:
254                 throw new IllegalArgumentException("Invalid scope");
255         }
256     }
257 
258     public void setAttribute(final String name, final Object attribute) {
259 
260         if (name == null) {
261             throw new NullPointerException(
262                     Localizer.getMessage("jsp.error.attribute.null_name"));
263         }
264 
265         if (SecurityUtil.isPackageProtectionEnabled()) {
266             AccessController.doPrivileged(new PrivilegedAction() {
267                 public Object run() {
268                     doSetAttribute(name, attribute);
269                     return null;
270                 }
271             });
272         } else {
273             doSetAttribute(name, attribute);
274         }
275     }
276 
277     private void doSetAttribute(String name, Object attribute) {
278         if (attribute != null) {
279             attributes.put(name, attribute);
280         } else {
281             removeAttribute(name, PAGE_SCOPE);
282         }
283     }
284 
285     public void setAttribute(final String name, final Object o, final int scope) {
286 
287         if (name == null) {
288             throw new NullPointerException(
289                     Localizer.getMessage("jsp.error.attribute.null_name"));
290         }
291 
292         if (SecurityUtil.isPackageProtectionEnabled()) {
293             AccessController.doPrivileged(new PrivilegedAction() {
294                 public Object run() {
295                     doSetAttribute(name, o, scope);
296                     return null;
297                 }
298             });
299         } else {
300             doSetAttribute(name, o, scope);
301         }
302 
303     }
304 
305     private void doSetAttribute(String name, Object o, int scope) {
306         if (o != null) {
307             switch (scope) {
308                 case PAGE_SCOPE:
309                     attributes.put(name, o);
310                     break;
311 
312                 case REQUEST_SCOPE:
313                     request.setAttribute(name, o);
314                     break;
315 
316                 case SESSION_SCOPE:
317                     if (session == null) {
318                         throw new IllegalStateException(
319                                 Localizer.getMessage("jsp.error.page.noSession"));
320                     }
321                     session.setAttribute(name, o);
322                     break;
323 
324                 case APPLICATION_SCOPE:
325                     context.setAttribute(name, o);
326                     break;
327 
328                 default:
329                     throw new IllegalArgumentException("Invalid scope");
330             }
331         } else {
332             removeAttribute(name, scope);
333         }
334     }
335 
336     public void removeAttribute(final String name, final int scope) {
337 
338         if (name == null) {
339             throw new NullPointerException(
340                     Localizer.getMessage("jsp.error.attribute.null_name"));
341         }
342         if (SecurityUtil.isPackageProtectionEnabled()) {
343             AccessController.doPrivileged(new PrivilegedAction() {
344                 public Object run() {
345                     doRemoveAttribute(name, scope);
346                     return null;
347                 }
348             });
349         } else {
350             doRemoveAttribute(name, scope);
351         }
352     }
353 
354     private void doRemoveAttribute(String name, int scope) {
355         switch (scope) {
356             case PAGE_SCOPE:
357                 attributes.remove(name);
358                 break;
359 
360             case REQUEST_SCOPE:
361                 request.removeAttribute(name);
362                 break;
363 
364             case SESSION_SCOPE:
365                 if (session == null) {
366                     throw new IllegalStateException(
367                             Localizer.getMessage("jsp.error.page.noSession"));
368                 }
369                 session.removeAttribute(name);
370                 break;
371 
372             case APPLICATION_SCOPE:
373                 context.removeAttribute(name);
374                 break;
375 
376             default:
377                 throw new IllegalArgumentException("Invalid scope");
378         }
379     }
380 
381     public int getAttributesScope(final String name) {
382 
383         if (name == null) {
384             throw new NullPointerException(
385                     Localizer.getMessage("jsp.error.attribute.null_name"));
386         }
387 
388         if (SecurityUtil.isPackageProtectionEnabled()) {
389             return ((Integer) AccessController.doPrivileged(new PrivilegedAction() {
390                 public Object run() {
391                     return new Integer(doGetAttributeScope(name));
392                 }
393             })).intValue();
394         } else {
395             return doGetAttributeScope(name);
396         }
397     }
398 
399     private int doGetAttributeScope(String name) {
400         if (attributes.get(name) != null)
401             return PAGE_SCOPE;
402 
403         if (request.getAttribute(name) != null)
404             return REQUEST_SCOPE;
405 
406         if (session != null) {
407             try {
408                 if (session.getAttribute(name) != null)
409                     return SESSION_SCOPE;
410             } catch (IllegalStateException ise) {
411                 // Session has been invalidated.
412                 // Ignore and fall through to application scope.
413             }
414         }
415 
416         if (context.getAttribute(name) != null)
417             return APPLICATION_SCOPE;
418 
419         return 0;
420     }
421 
422     public Object findAttribute(final String name) {
423         if (SecurityUtil.isPackageProtectionEnabled()) {
424             return AccessController.doPrivileged(new PrivilegedAction() {
425                 public Object run() {
426                     if (name == null) {
427                         throw new NullPointerException(
428                                 Localizer.getMessage("jsp.error.attribute.null_name"));
429                     }
430 
431                     return doFindAttribute(name);
432                 }
433             });
434         } else {
435             if (name == null) {
436                 throw new NullPointerException(
437                         Localizer.getMessage("jsp.error.attribute.null_name"));
438             }
439 
440             return doFindAttribute(name);
441         }
442     }
443 
444     private Object doFindAttribute(String name) {
445 
446         Object o = attributes.get(name);
447         if (o != null)
448             return o;
449 
450         o = request.getAttribute(name);
451         if (o != null)
452             return o;
453 
454         if (session != null) {
455             try {
456                 o = session.getAttribute(name);
457             } catch (IllegalStateException ise) {
458                 // Session has been invalidated.
459                 // Ignore and fall through to application scope.
460             }
461             if (o != null)
462                 return o;
463         }
464 
465         return context.getAttribute(name);
466     }
467 
468 
469     public Enumeration getAttributeNamesInScope(final int scope) {
470         if (SecurityUtil.isPackageProtectionEnabled()) {
471             return (Enumeration)
472                     AccessController.doPrivileged(new PrivilegedAction() {
473                         public Object run() {
474                             return doGetAttributeNamesInScope(scope);
475                         }
476                     });
477         } else {
478             return doGetAttributeNamesInScope(scope);
479         }
480     }
481 
482     private Enumeration doGetAttributeNamesInScope(int scope) {
483         switch (scope) {
484             case PAGE_SCOPE:
485                 return attributes.keys();
486 
487             case REQUEST_SCOPE:
488                 return request.getAttributeNames();
489 
490             case SESSION_SCOPE:
491                 if (session == null) {
492                     throw new IllegalStateException(
493                             Localizer.getMessage("jsp.error.page.noSession"));
494                 }
495                 return session.getAttributeNames();
496 
497             case APPLICATION_SCOPE:
498                 return context.getAttributeNames();
499 
500             default:
501                 throw new IllegalArgumentException("Invalid scope");
502         }
503     }
504 
505     public void removeAttribute(final String name) {
506 
507         if (name == null) {
508             throw new NullPointerException(
509                     Localizer.getMessage("jsp.error.attribute.null_name"));
510         }
511 
512         if (SecurityUtil.isPackageProtectionEnabled()) {
513             AccessController.doPrivileged(new PrivilegedAction() {
514                 public Object run() {
515                     doRemoveAttribute(name);
516                     return null;
517                 }
518             });
519         } else {
520             doRemoveAttribute(name);
521         }
522     }
523 
524 
525     private void doRemoveAttribute(String name) {
526         removeAttribute(name, PAGE_SCOPE);
527         removeAttribute(name, REQUEST_SCOPE);
528         if (session != null) {
529             try {
530                 removeAttribute(name, SESSION_SCOPE);
531             } catch (IllegalStateException ise) {
532                 // Session has been invalidated.
533                 // Ignore and fall throw to application scope.
534             }
535         }
536         removeAttribute(name, APPLICATION_SCOPE);
537     }
538 
539     public JspWriter getOut() {
540         return out;
541     }
542 
543     public HttpSession getSession() {
544         return session;
545     }
546 
547     public Servlet getServlet() {
548         return servlet;
549     }
550 
551     public ServletConfig getServletConfig() {
552         return config;
553     }
554 
555     public ServletContext getServletContext() {
556         return config.getServletContext();
557     }
558 
559     public ServletRequest getRequest() {
560         return request;
561     }
562 
563     public ServletResponse getResponse() {
564         return response;
565     }
566 
567 
568     /***
569      * Returns the exception associated with this page
570      * context, if any.
571      * <p/>
572      * Added wrapping for Throwables to avoid ClassCastException:
573      * see Bugzilla 31171 for details.
574      *
575      * @return The Exception associated with this page context, if any.
576      */
577     public Exception getException() {
578         Throwable t = JspRuntimeLibrary.getThrowable(request);
579 
580         // Only wrap if needed
581         if ((t != null) && (!(t instanceof Exception))) {
582             t = new JspException(t);
583         }
584 
585         return (Exception) t;
586     }
587 
588 
589     public Object getPage() {
590         return servlet;
591     }
592 
593 
594     private final String getAbsolutePathRelativeToContext(String relativeUrlPath) {
595         String path = relativeUrlPath;
596 
597         if (!path.startsWith("/")) {
598             String uri = (String)
599                     request.getAttribute("javax.servlet.include.servlet_path");
600             if (uri == null)
601                 uri = ((HttpServletRequest) request).getServletPath();
602             String baseURI = uri.substring(0, uri.lastIndexOf('/'));
603             path = baseURI + '/' + path;
604         }
605 
606         return path;
607     }
608 
609     public void include(String relativeUrlPath)
610             throws ServletException, IOException {
611         JspRuntimeLibrary.include(request, response, relativeUrlPath, out,
612                 true);
613     }
614 
615     public void include(final String relativeUrlPath, final boolean flush)
616             throws ServletException, IOException {
617         if (SecurityUtil.isPackageProtectionEnabled()) {
618             try {
619                 AccessController.doPrivileged(new PrivilegedExceptionAction() {
620                     public Object run() throws Exception {
621                         doInclude(relativeUrlPath, flush);
622                         return null;
623                     }
624                 });
625             } catch (PrivilegedActionException e) {
626                 Exception ex = e.getException();
627                 if (ex instanceof IOException) {
628                     throw (IOException) ex;
629                 } else {
630                     throw (ServletException) ex;
631                 }
632             }
633         } else {
634             doInclude(relativeUrlPath, flush);
635         }
636     }
637 
638     private void doInclude(String relativeUrlPath, boolean flush)
639             throws ServletException, IOException {
640         JspRuntimeLibrary.include(request, response, relativeUrlPath, out,
641                 flush);
642     }
643 
644     public VariableResolver getVariableResolver() {
645         return this;
646     }
647 
648     public void forward(final String relativeUrlPath)
649             throws ServletException, IOException {
650         if (SecurityUtil.isPackageProtectionEnabled()) {
651             try {
652                 AccessController.doPrivileged(new PrivilegedExceptionAction() {
653                     public Object run() throws Exception {
654                         doForward(relativeUrlPath);
655                         return null;
656                     }
657                 });
658             } catch (PrivilegedActionException e) {
659                 Exception ex = e.getException();
660                 if (ex instanceof IOException) {
661                     throw (IOException) ex;
662                 } else {
663                     throw (ServletException) ex;
664                 }
665             }
666         } else {
667             doForward(relativeUrlPath);
668         }
669     }
670 
671     private void doForward(String relativeUrlPath)
672             throws ServletException, IOException {
673 
674         // JSP.4.5 If the buffer was flushed, throw IllegalStateException
675         try {
676             out.clear();
677         } catch (IOException ex) {
678             IllegalStateException ise =
679                     new IllegalStateException(Localizer.getMessage(
680                             "jsp.error.attempt_to_clear_flushed_buffer"));
681             ise.initCause(ex);
682             throw ise;
683         }
684 
685         // Make sure that the response object is not the wrapper for include
686         while (response instanceof ServletResponseWrapperInclude) {
687             response = ((ServletResponseWrapperInclude) response).getResponse();
688         }
689 
690         final String path = getAbsolutePathRelativeToContext(relativeUrlPath);
691         String includeUri
692                 = (String) request.getAttribute(Constants.INC_SERVLET_PATH);
693 
694         if (includeUri != null)
695             request.removeAttribute(Constants.INC_SERVLET_PATH);
696         try {
697             context.getRequestDispatcher(path).forward(request, response);
698         } finally {
699             if (includeUri != null)
700                 request.setAttribute(Constants.INC_SERVLET_PATH, includeUri);
701             request.setAttribute(Constants.FORWARD_SEEN, "true");
702         }
703     }
704 
705     public BodyContent pushBody() {
706         return (BodyContent) pushBody(null);
707     }
708 
709     public JspWriter pushBody(Writer writer) {
710         depth++;
711         if (depth >= outs.length) {
712             BodyContentImpl[] newOuts = new BodyContentImpl[depth + 1];
713             for (int i = 0; i < outs.length; i++) {
714                 newOuts[i] = outs[i];
715             }
716             newOuts[depth] = new BodyContentImpl(out);
717             outs = newOuts;
718         }
719 
720         outs[depth].setWriter(writer);
721         out = outs[depth];
722 
723         // Update the value of the "out" attribute in the page scope
724         // attribute namespace of this PageContext
725         setAttribute(OUT, out);
726 
727         return outs[depth];
728     }
729 
730     public JspWriter popBody() {
731         depth--;
732         if (depth >= 0) {
733             out = outs[depth];
734         } else {
735             out = baseOut;
736         }
737 
738         // Update the value of the "out" attribute in the page scope
739         // attribute namespace of this PageContext
740         setAttribute(OUT, out);
741 
742         return out;
743     }
744 
745     /***
746      * Provides programmatic access to the ExpressionEvaluator.
747      * The JSP Container must return a valid instance of an
748      * ExpressionEvaluator that can parse EL expressions.
749      */
750     public ExpressionEvaluator getExpressionEvaluator() {
751         return elExprEval;
752     }
753 
754     public void handlePageException(Exception ex)
755             throws IOException, ServletException {
756         // Should never be called since handleException() called with a
757         // Throwable in the generated servlet.
758         handlePageException((Throwable) ex);
759     }
760 
761     public void handlePageException(final Throwable t)
762             throws IOException, ServletException {
763         if (t == null)
764             throw new NullPointerException("null Throwable");
765 
766         if (SecurityUtil.isPackageProtectionEnabled()) {
767             try {
768                 AccessController.doPrivileged(new PrivilegedExceptionAction() {
769                     public Object run() throws Exception {
770                         doHandlePageException(t);
771                         return null;
772                     }
773                 });
774             } catch (PrivilegedActionException e) {
775                 Exception ex = e.getException();
776                 if (ex instanceof IOException) {
777                     throw (IOException) ex;
778                 } else {
779                     throw (ServletException) ex;
780                 }
781             }
782         } else {
783             doHandlePageException(t);
784         }
785 
786     }
787 
788     private void doHandlePageException(Throwable t)
789             throws IOException, ServletException {
790 
791         if (errorPageURL != null && !errorPageURL.equals("")) {
792 
793             /*
794              * Set request attributes.
795              * Do not set the javax.servlet.error.exception attribute here
796              * (instead, set in the generated servlet code for the error page)
797              * in order to prevent the ErrorReportValve, which is invoked as
798              * part of forwarding the request to the error page, from
799              * throwing it if the response has not been committed (the response
800              * will have been committed if the error page is a JSP page).
801              */
802             request.setAttribute("javax.servlet.jsp.jspException", t);
803             request.setAttribute("javax.servlet.error.status_code",
804                     new Integer(HttpServletResponse.SC_INTERNAL_SERVER_ERROR));
805             request.setAttribute("javax.servlet.error.request_uri",
806                     ((HttpServletRequest) request).getRequestURI());
807             request.setAttribute("javax.servlet.error.servlet_name",
808                     config.getServletName());
809             try {
810                 forward(errorPageURL);
811             } catch (IllegalStateException ise) {
812                 include(errorPageURL);
813             }
814 
815             // The error page could be inside an include.
816 
817             Object newException = request.getAttribute("javax.servlet.error.exception");
818 
819             // t==null means the attribute was not set.
820             if ((newException != null) && (newException == t)) {
821                 request.removeAttribute("javax.servlet.error.exception");
822             }
823 
824             // now clear the error code - to prevent double handling.
825             request.removeAttribute("javax.servlet.error.status_code");
826             request.removeAttribute("javax.servlet.error.request_uri");
827             request.removeAttribute("javax.servlet.error.status_code");
828             request.removeAttribute("javax.servlet.jsp.jspException");
829 
830         } else {
831             // Otherwise throw the exception wrapped inside a ServletException.
832             // Set the exception as the root cause in the ServletException
833             // to get a stack trace for the real problem
834             if (t instanceof IOException) throw (IOException) t;
835             if (t instanceof ServletException) throw (ServletException) t;
836             if (t instanceof RuntimeException) throw (RuntimeException) t;
837 
838             Throwable rootCause = null;
839             if (t instanceof JspException) {
840                 rootCause = ((JspException) t).getRootCause();
841             } else if (t instanceof ELException) {
842                 rootCause = ((ELException) t).getRootCause();
843             }
844 
845             if (rootCause != null) {
846                 throw new ServletException(t.getClass().getName() + ": " +
847                         t.getMessage(), rootCause);
848             }
849 
850             throw new ServletException(t);
851         }
852     }
853 
854     /***
855      * VariableResolver interface
856      */
857     public Object resolveVariable(String pName) throws ELException {
858         return variableResolver.resolveVariable(pName);
859     }
860 
861     /***
862      * XML-escape the given string.
863      *
864      * @param s The string to escape
865      * @return The escape string
866      */
867     private static String XmlEscape(String s) {
868         if (s == null) {
869             return null;
870         }
871 
872         StringBuffer sb = new StringBuffer();
873         for (int i = 0; i < s.length(); i++) {
874             char c = s.charAt(i);
875             if (c == '<') {
876                 sb.append("&lt;");
877             } else if (c == '>') {
878                 sb.append("&gt;");
879             } else if (c == '\'') {
880                 sb.append("&#039;");        // &apos;
881             } else if (c == '&') {
882                 sb.append("&amp;");
883             } else if (c == '"') {
884                 sb.append("&#034;");        // &quot;
885             } else {
886                 sb.append(c);
887             }
888         }
889         return sb.toString();
890     }
891 
892     /***
893      * Proprietary method to evaluate EL expressions.
894      * XXX - This method should go away once the EL interpreter moves
895      * out of JSTL and into its own project.  For now, this is necessary
896      * because the standard machinery is too slow.
897      *
898      * @param expression   The expression to be evaluated
899      * @param expectedType The expected resulting type
900      * @param pageContext  The page context
901      * @param functionMap  Maps prefix and name to Method
902      * @return The result of the evaluation
903      */
904     public static Object proprietaryEvaluate(final String expression,
905                                              final Class expectedType,
906                                              final PageContext pageContext,
907                                              final ProtectedFunctionMapper functionMap,
908                                              final boolean escape)
909             throws ELException {
910         Object retValue;
911         if (SecurityUtil.isPackageProtectionEnabled()) {
912             try {
913                 retValue = AccessController.doPrivileged(
914                         new PrivilegedExceptionAction() {
915 
916                             public Object run() throws Exception {
917                                 return elExprEval.evaluate(expression,
918                                         expectedType,
919                                         pageContext.getVariableResolver(),
920                                         functionMap);
921                             }
922                         });
923             } catch (PrivilegedActionException ex) {
924                 Exception realEx = ex.getException();
925                 if (realEx instanceof ELException) {
926                     throw (ELException) realEx;
927                 } else {
928                     throw new ELException(realEx);
929                 }
930             }
931         } else {
932             retValue = elExprEval.evaluate(expression,
933                     expectedType,
934                     pageContext.getVariableResolver(),
935                     functionMap);
936         }
937         if (escape) {
938             retValue = XmlEscape(retValue.toString());
939         }
940 
941         return retValue;
942     }
943 
944 }