1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
61 private static ExpressionEvaluatorImpl elExprEval
62 = new ExpressionEvaluatorImpl(false);
63
64
65 private VariableResolverImpl variableResolver;
66
67 private BodyContentImpl[] outs;
68 private int depth;
69
70
71 private Servlet servlet;
72 private ServletConfig config;
73 private ServletContext context;
74 private String errorPageURL;
75
76
77 private transient Hashtable attributes;
78
79
80 private transient ServletRequest request;
81 private transient ServletResponse response;
82 private transient HttpSession session;
83 private boolean isIncluded;
84
85
86 private transient JspWriter out;
87 private transient JspWriterImpl baseOut;
88
89
90
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
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
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
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
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
167 } else {
168
169
170
171
172
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
412
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
459
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
533
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
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
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
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
724
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
739
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
757
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
795
796
797
798
799
800
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
816
817 Object newException = request.getAttribute("javax.servlet.error.exception");
818
819
820 if ((newException != null) && (newException == t)) {
821 request.removeAttribute("javax.servlet.error.exception");
822 }
823
824
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
832
833
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("<");
877 } else if (c == '>') {
878 sb.append(">");
879 } else if (c == '\'') {
880 sb.append("'");
881 } else if (c == '&') {
882 sb.append("&");
883 } else if (c == '"') {
884 sb.append(""");
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 }