/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package com.caucho.jsp; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; import java.io.Reader; import java.io.Writer; import java.lang.reflect.Method; import java.text.MessageFormat; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.ResourceBundle; import java.util.logging.Level; import java.util.logging.Logger; import javax.el.ELContext; import javax.el.ELContextEvent; import javax.el.ELContextListener; import javax.el.ELResolver; import javax.el.ValueExpression; import javax.servlet.RequestDispatcher; import javax.servlet.Servlet; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import javax.servlet.jsp.ErrorData; import javax.servlet.jsp.JspContext; import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.PageContext; import javax.servlet.jsp.SkipPageException; import javax.servlet.jsp.el.ExpressionEvaluator; import javax.servlet.jsp.el.VariableResolver; import javax.servlet.jsp.jstl.core.Config; import javax.servlet.jsp.jstl.fmt.LocalizationContext; import javax.servlet.jsp.tagext.BodyContent; import javax.servlet.jsp.tagext.JspFragment; import org.w3c.dom.Node; import com.caucho.el.ExprEnv; import com.caucho.jsp.cfg.JspPropertyGroup; import com.caucho.jsp.el.ExpressionEvaluatorImpl; import com.caucho.jsp.el.ImplicitVariableMapper; import com.caucho.jsp.el.JspApplicationContextImpl; import com.caucho.jsp.el.PageContextAttributeMap; import com.caucho.jsp.el.PageContextELResolver; import com.caucho.jsp.el.ServletELContext; import com.caucho.jstl.JstlPageContext; import com.caucho.server.http.AbstractResponseStream; import com.caucho.server.http.CauchoRequest; import com.caucho.server.http.CauchoResponse; import com.caucho.server.http.RequestAdapter; import com.caucho.server.http.ToCharResponseAdapter; import com.caucho.server.webapp.RequestDispatcherImpl; import com.caucho.server.webapp.WebApp; import com.caucho.util.CharBuffer; import com.caucho.util.DisplayableException; import com.caucho.util.HashMapImpl; import com.caucho.util.L10N; import com.caucho.util.NullEnumeration; import com.caucho.vfs.ClientDisconnectException; import com.caucho.vfs.FlushBuffer; import com.caucho.vfs.Path; import com.caucho.vfs.TempCharBuffer; import com.caucho.xpath.VarEnv; public class PageContextImpl extends PageContext implements ExprEnv, JstlPageContext, VariableResolver { private static final Logger log = Logger.getLogger(PageContextImpl.class.getName()); private static final L10N L = new L10N(PageContextImpl.class); private WebApp _webApp; private JspWriterAdapter _jspAdapter = new JspWriterAdapter(); private JspServletOutputStream _jspOutputStream = new JspServletOutputStream(this); private Map<String,Object> _attributes; private Servlet _servlet; private HttpServletRequest _request; private ServletResponse _servletResponse; private CauchoResponse _response; private ToCharResponseAdapter _responseAdapter; private HttpSession _session; private JspWriter _topOut; private JspWriter _out; private String _errorPage; protected boolean _isFilled; private AbstractResponseStream _responseStream; private BodyResponseStream _bodyResponseStream; private int _bufferSize = 8192; private BodyContentImpl _bodyOut; private Locale _locale; private BundleManager _bundleManager; private VarEnv _varEnv; private Node _nodeEnv; private final CharBuffer _cb = new CharBuffer(); private PageELContext _elContextValue; private PageELContext _elContext; private ELResolver _elResolver; private javax.el.FunctionMapper _functionMapper; private PageVariableMapper _variableMapper; private HashMap<String,Method> _functionMap; private ExpressionEvaluatorImpl _expressionEvaluator; private ELContextListener[] _elContextListeners; PageContextImpl() { _attributes = new HashMapImpl<String,Object>(); _bodyResponseStream = new BodyResponseStream(); _bodyResponseStream.start(); } public PageContextImpl(WebApp webApp, Servlet servlet) { this(); if (webApp == null) throw new NullPointerException(); _webApp = webApp; _servlet = servlet; if (servlet instanceof Page) { Page page = (Page) servlet; _functionMap = page._caucho_getFunctionMap(); } else _functionMap = null; } public PageContextImpl(WebApp webApp, HashMap<String,Method> functionMap) { _webApp = webApp; _functionMap = functionMap; } @Override public void initialize(Servlet servlet, ServletRequest request, ServletResponse response, String errorPage, boolean needsSession, int bufferSize, boolean autoFlush) { HttpSession session = null; if (needsSession) session = ((HttpServletRequest) request).getSession(true); WebApp app = (WebApp) request.getServletContext(); _webApp = app; initialize(servlet, app, request, response, errorPage, session, bufferSize, autoFlush, false); } public void initialize(Servlet servlet, WebApp app, ServletRequest request, ServletResponse response, String errorPage, HttpSession session, int bufferSize, boolean autoFlush, boolean isPrintNullAsBlank) { _webApp = app; _servlet = servlet; _request = (HttpServletRequest) request; _servletResponse = response; if (response instanceof CauchoResponse && bufferSize <= TempCharBuffer.SIZE) { _response = (CauchoResponse) response; _responseAdapter = null; } else { // JSP.12.2.3 - JSP must use PrintWriter _responseAdapter = ToCharResponseAdapter.create((HttpServletResponse) response); _response = _responseAdapter; try { // jsp/017m response.setBufferSize(bufferSize); } catch (Exception e) { log.log(Level.FINE, e.toString(), e); } } _responseStream = _response.getResponseStream(); _topOut = _jspAdapter; _responseStream.setAutoFlush(autoFlush); _jspAdapter.init(null, _response, _responseStream); _jspAdapter.setPrintNullAsBlank(isPrintNullAsBlank); if (bufferSize != TempCharBuffer.SIZE) { try { _responseStream.setBufferSize(bufferSize); } catch (Exception e) { log.log(Level.FINE, e.toString(), e); } } // needed for includes from static pages _bufferSize = bufferSize; _session = session; _out = _topOut; _errorPage = errorPage; _locale = null; // jsp/1059, jsp/3147 // XXX: recycling is important for performance reasons _elContext = null; if (_elContextValue != null) _elContextValue.clear(); //if (_attributes.size() > 0) // _attributes.clear(); _isFilled = false; _nodeEnv = null; if (servlet instanceof Page) { Page page = (Page) servlet; _functionMap = page._caucho_getFunctionMap(); } else _functionMap = null; } protected void init() { // XXX: important for performance reasons // jsp/1059, jsp/3147 _elContext = null; if (_elContextValue != null) _elContextValue.clear(); } protected void setOut(JspWriter out) { _out = out; } protected void clearAttributes() { _attributes.clear(); } /** * Returns the page attribute with the given name. * * @param name the attribute name. * * @return the attribute's value. */ public Object getAttribute(String name) { if (name == null) throw new NullPointerException(L.l("getAttribute must have a non-null name")); Object value = _attributes.get(name); if (value != null) return value; else if (! _isFilled) { fillAttribute(); value = _attributes.get(name); } if (value != null) { } else if (name.equals(OUT)) { // jsp/162d return _out; } return value; } /** * Sets the page attribute with the given name. * * @param name the attribute name. * @param attribute the new value */ public void setAttribute(String name, Object attribute) { if (name == null) throw new NullPointerException(L.l("setAttribute must have a non-null name")); if (attribute != null) _attributes.put(name, attribute); else _attributes.remove(name); } /** * Sets the page attribute with the given name. * * @param name the attribute name. * @param attribute the new value */ public Object putAttribute(String name, Object attribute) { if (name == null) throw new NullPointerException(L.l("putAttribute must have a non-null name")); if (attribute != null) return _attributes.put(name, attribute); else return _attributes.remove(name); } /** * Removes a named attribute from the page context. * * @param name the name of the attribute to remove */ public void removeAttribute(String name) { if (name == null) throw new NullPointerException(L.l("removeAttribute must have a non-null name")); _attributes.remove(name); // jsp/162b if (_request != null) _request.removeAttribute(name); if (_session != null) { try { _session.removeAttribute(name); } catch (IllegalStateException e) { // jsp/162f log.log(Level.FINE, e.toString(), e); } } if (_webApp != null) _webApp.removeAttribute(name); } private Enumeration<String> getAttributeNames() { if (! _isFilled) fillAttribute(); return Collections.enumeration(_attributes.keySet()); } /** * Fills the predefined page content _attributes with their values. */ protected void fillAttribute() { _isFilled = true; _attributes.put(PAGE, _servlet); _attributes.put(PAGECONTEXT, this); _attributes.put(REQUEST, getCauchoRequest()); _attributes.put(RESPONSE, getCauchoResponse()); if (_servlet != null) _attributes.put(CONFIG, _servlet.getServletConfig()); if (getSession() != null) _attributes.put(SESSION, getSession()); _attributes.put(APPLICATION, getApplication()); } public Object getAttribute(String name, int scope) { switch (scope) { case PAGE_SCOPE: return getAttribute(name); case REQUEST_SCOPE: return getCauchoRequest().getAttribute(name); case SESSION_SCOPE: { HttpSession session = getSession(); return session != null ? session.getAttribute(name) : null; } case APPLICATION_SCOPE: return getApplication().getAttribute(name); default: throw new IllegalArgumentException(); } } @Override public void setAttribute(String name, Object value, int scope) { switch (scope) { case PAGE_SCOPE: setAttribute(name, value); break; case REQUEST_SCOPE: getCauchoRequest().setAttribute(name, value); break; case SESSION_SCOPE: if (getSession() != null) getSession().setAttribute(name, value); break; case APPLICATION_SCOPE: getApplication().setAttribute(name, value); break; default: throw new IllegalArgumentException(); } } @Override public void removeAttribute(String name, int scope) { if (name == null) throw new NullPointerException(L.l("removeAttribute must have a non-null name")); switch (scope) { case PAGE_SCOPE: if (name != null) _attributes.remove(name); break; case REQUEST_SCOPE: getCauchoRequest().removeAttribute(name); break; case SESSION_SCOPE: if (getSession() != null) getSession().removeAttribute(name); break; case APPLICATION_SCOPE: getApplication().removeAttribute(name); break; default: throw new IllegalArgumentException(); } } public Enumeration<String> getAttributeNames(int scope) { switch (scope) { case PAGE_SCOPE: return getAttributeNames(); case REQUEST_SCOPE: return getCauchoRequest().getAttributeNames(); case SESSION_SCOPE: if (getSession() != null) { return getSession().getAttributeNames(); } else return NullEnumeration.create(); case APPLICATION_SCOPE: return getApplication().getAttributeNames(); default: throw new IllegalArgumentException(); } } public Enumeration<String> getAttributeNamesInScope(int scope) { return getAttributeNames(scope); } /** * Finds an attribute in any of the scopes from page to webApp. * * @param name the attribute name. * * @return the attribute value */ @Override public Object findAttribute(String name) { Object value; if ((value = getAttribute(name)) != null) return value; HttpServletRequest req = getCauchoRequest(); if (req != null && (value = getCauchoRequest().getAttribute(name)) != null) return value; HttpSession session = getSession(); if (session != null) { try { value = session.getAttribute(name); } catch (IllegalStateException e) { // jsp/162e log.log(Level.FINE, e.toString(), e); } if (value != null) return value; } return getServletContext().getAttribute(name); } /** * Return the scope of the named attribute. * * @param name the name of the attribute. * * @return the scope of the attribute */ public int getAttributesScope(String name) { if (getAttribute(name) != null) return PAGE_SCOPE; if (getCauchoRequest().getAttribute(name) != null) return REQUEST_SCOPE; HttpSession session = getSession(); if (session != null && session.getAttribute(name) != null) return SESSION_SCOPE; if (getApplication().getAttribute(name) != null) return APPLICATION_SCOPE; return 0; } /** * Sets the attribute map. */ public Map<String,Object> setMap(Map<String,Object> map) { Map<String,Object> oldMap = _attributes; _attributes = map; return oldMap; } /** * Returns the current writer. */ public JspWriter getOut() { return _out; } /** * Pushes a new BodyContent onto the JspWriter stack. */ public BodyContent pushBody() { BodyContentImpl body; if (_bodyOut != null) { body = _bodyOut; _bodyOut = null; } else body = BodyContentImpl.allocate(); CauchoResponse response = getCauchoResponse(); body.init(_out); _out = body; response.setForbidForward(true); try { _bodyResponseStream.flushBuffer(); } catch (IOException e) { } _bodyResponseStream.start(); _bodyResponseStream.setWriter(body); _bodyResponseStream.setEncoding(response.getCharacterEncoding()); response.setResponseStream(_bodyResponseStream); return body; } /** * Pushes a new writer onto the JspWriter stack. */ public JspWriter pushBody(Writer writer) { if (writer == _out) return null; JspWriter oldWriter = _out; StreamJspWriter jspWriter; jspWriter = new StreamJspWriter(); jspWriter.init(_out, writer); _out = jspWriter; getCauchoResponse().setForbidForward(true); _bodyResponseStream.setWriter(writer); getCauchoResponse().setResponseStream(_bodyResponseStream); return oldWriter; } /** * Pops the BodyContent from the JspWriter stack. * * @return the enclosing writer */ @Override public JspWriter popBody() { BodyContentImpl bodyOut = (BodyContentImpl) _out; _out = bodyOut.getEnclosingWriter(); try { _bodyResponseStream.flushBuffer(); //if (_writeStream != null) // _writeStream.flushBuffer(); } catch (IOException e) { log.log(Level.WARNING, e.toString(), e); } if (_out instanceof StreamJspWriter) { StreamJspWriter writer = (StreamJspWriter) _out; _bodyResponseStream.setWriter(writer.getWriter()); if (_response != null) _bodyResponseStream.setEncoding(_response.getCharacterEncoding()); } else if (_out instanceof JspWriterAdapter) { if (getCauchoResponse() != null) { getCauchoResponse().setResponseStream(_responseStream); getCauchoResponse().setForbidForward(false); } } else if (_out instanceof BodyContentImpl) { BodyContentImpl body = (BodyContentImpl) _out; _bodyResponseStream.setWriter(body.getWriter()); if (_response != null) _bodyResponseStream.setEncoding(_response.getCharacterEncoding()); } return _out; } /** * Pops the BodyContent from the JspWriter stack. * * @return the enclosing writer */ public JspWriter popAndReleaseBody() throws IOException { BodyContentImpl body = (BodyContentImpl) getOut(); JspWriter out = popBody(); releaseBody(body); return out; } public void releaseBody(BodyContentImpl out) throws IOException { if (_bodyOut == null) { out.releaseNoFree(); _bodyOut = out; } else out.release(); } /** * Pops the BodyContent from the JspWriter stack. * * @param oldWriter the old writer */ public JspWriter setWriter(JspWriter oldWriter) { if (_out == oldWriter) return oldWriter; /* if (_out instanceof FlushBuffer) { try { ((FlushBuffer) _out).flushBuffer(); } catch (IOException e) { } } */ try { if (_out instanceof FlushBuffer) ((FlushBuffer) _out).flushBuffer(); } catch (IOException e) { } _out = oldWriter; // jsp/18eg if (_out instanceof StreamJspWriter) { StreamJspWriter writer = (StreamJspWriter) _out; _bodyResponseStream.setWriter(writer.getWriter()); } else if (_out instanceof JspWriterAdapter) { if (getCauchoResponse() != null) { getCauchoResponse().setResponseStream(_responseStream); getCauchoResponse().setForbidForward(false); } } else if (_out instanceof BodyContentImpl) { BodyContentImpl body = (BodyContentImpl) _out; _bodyResponseStream.setWriter(body.getWriter()); } return oldWriter; // getCauchoResponse().setWriter(_os); } /** * Returns the top writer. */ public PrintWriter getTopWriter() throws IOException { CauchoResponse response = getCauchoResponse(); AbstractResponseStream currentStream = response.getResponseStream(); response.setResponseStream(_responseStream); try { return response.getWriter(); } finally { response.setResponseStream(currentStream); } } /** * Returns the response output stream. */ ServletOutputStream getOutputStream() { try { return getCauchoResponse().getOutputStream(); } catch (IOException e) { throw new RuntimeException(e); } } /** * Returns the underlying servlet for the page. */ public Object getPage() { return _servlet; } /** * Returns the servlet request for the page. */ @Override public HttpServletRequest getRequest() { return _request; } /** * Returns the servlet response for the page. */ @Override public HttpServletResponse getResponse() { return getCauchoResponse(); } /** * Returns the servlet response for the page. */ public CauchoResponse getCauchoResponse() { return _response; } /** * Returns the servlet response for the page. */ public HttpServletRequest getCauchoRequest() { return _request; } public HttpSession getSession() { if (_session == null) { HttpServletRequest req = getCauchoRequest(); if (req != null) _session = req.getSession(false); } return _session; } /** * Returns the session, throwing an IllegalStateException if it's * not available. */ public HttpSession getSessionScope() { if (_session == null) _session = getCauchoRequest().getSession(false); if (_session == null) throw new IllegalStateException(L.l("session is not available")); return _session; } public ServletConfig getServletConfig() { return _servlet.getServletConfig(); } /** * Returns the page's servlet context. */ public ServletContext getServletContext() { return _webApp; } /** * Returns the page's webApp. */ public WebApp getApplication() { return _webApp; } /** * Returns the page's error page. */ public String getErrorPage() { return _errorPage; } /** * Sets the page's error page. */ public void setErrorPage(String errorPage) { _errorPage = errorPage; } public Exception getException() { return (Exception) getThrowable(); } /** * Returns the Throwable stored by the error page. */ public Throwable getThrowable() { Throwable exn = (Throwable) getCauchoRequest().getAttribute(EXCEPTION); if (exn == null) exn = (Throwable) getCauchoRequest().getAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE); if (exn == null) exn = (Throwable) getCauchoRequest().getAttribute("javax.servlet.jsp:jspException"); return exn; } public void include(String relativeUrl) throws ServletException, IOException { include(relativeUrl, false); } /** * Include another servlet into the current output stream. * * @param relativeUrl url relative to the current request. */ public void include(String relativeUrl, String query, boolean flush) throws ServletException, IOException { if (! "".equals(query)) { relativeUrl = encode(relativeUrl, query); } include(relativeUrl, flush); } public StringBuilder encode(String relativeUrl) { StringBuilder sb = new StringBuilder(); sb.append(relativeUrl); if (relativeUrl.indexOf('?') >= 0) sb.append('&'); else sb.append('?'); return sb; } private String encode(String relativeUrl, String query) { StringBuilder sb = new StringBuilder(); sb.append(relativeUrl); sb = encode(sb, query); return sb.toString(); } public StringBuilder encode(StringBuilder sb, String query) { int len = query.length(); for (int i = 0; i < len; i++) { char ch = query.charAt(i); switch (ch) { case ' ': sb.append('+'); break; case '+': sb.append("%2b"); break; case '%': sb.append("%25"); break; case '&': sb.append("%26"); break; default: sb.append(ch); break; } } return sb; } /** * Include another servlet into the current output stream. * * @param relativeUrl url relative to the current request. */ public void include(String relativeUrl, boolean flush) throws ServletException, IOException { RequestDispatcher rd = null; HttpServletRequest req = (HttpServletRequest) getCauchoRequest(); HttpServletResponse res = (HttpServletResponse) getResponse(); if (relativeUrl != null && ! relativeUrl.startsWith("/")) { String path = RequestAdapter.getPageServletPath(req); String pathInfo = RequestAdapter.getPagePathInfo(req); String servletPath = req.getServletPath(); if (path != null) { // jsp/15du vs jsp/15lk (tck) if (pathInfo != null && ! path.equals(servletPath)) path += pathInfo; } else if (pathInfo != null) path = pathInfo; else path = "/"; int p = path.lastIndexOf('/'); if (p >= 0) { _cb.clear(); _cb.append(path, 0, p + 1); _cb.append(relativeUrl); rd = getServletContext().getRequestDispatcher(_cb.toString()); } } if (rd == null) rd = req.getRequestDispatcher(relativeUrl); if (rd == null) throw new ServletException(L.l("unknown including page `{0}'.", relativeUrl)); // the FlushBuffer needs to happen to deal with OpenSymphony (Bug#1710) // jsp/17e9, 15lc, 15m4 if (! flush) { } else if (_out instanceof FlushBuffer) ((FlushBuffer) _out).flushBuffer(); else if (flush) _out.flush(); rd.include(req, res); } /** * Include another servlet into the current output stream. * * @param relativeUrl url relative to the current request. */ public void forward(String relativeUrl, String query) throws ServletException, IOException { if (! "".equals(query)) relativeUrl = encode(relativeUrl, query); forward(relativeUrl); } /** * Forward a subrequest relative to the current url. Absolute URLs * are relative to the context root. * * @param relativeUrl url relative to the current file */ public void forward(String relativeUrl) throws ServletException, IOException { if (_bufferSize == 0) { // jsp/15m3, tck if (_out instanceof FlushBuffer) ((FlushBuffer) _out).flushBuffer(); else _out.flush(); } RequestDispatcher rd = null; HttpServletRequest req = (HttpServletRequest) getCauchoRequest(); HttpServletResponse res = (HttpServletResponse) getResponse(); if (res.isCommitted() && ! _webApp.isAllowForwardAfterFlush()) throw new IllegalStateException(L.l("can't forward after writing HTTP headers")); else if (! _webApp.isAllowForwardAfterFlush()) _out.clear(); //jsp/183n jsp/18kl jsp/1625 while (_out instanceof BodyContentImpl) { popBody(); } if (relativeUrl != null && ! relativeUrl.startsWith("/")) { String servletPath = RequestAdapter.getPageServletPath(req); int p = servletPath.lastIndexOf('/'); if (p >= 0) { _cb.clear(); _cb.append(servletPath, 0, p + 1); _cb.append(relativeUrl); rd = getServletContext().getRequestDispatcher(_cb.toString()); } } if (rd == null) rd = req.getRequestDispatcher(relativeUrl); if (rd == null) throw new ServletException(L.l("unknown forwarding page: `{0}'", relativeUrl)); // rd.forward(req, res); rd.forward(req, _servletResponse); //_out.close(); _responseStream.close(); } /** * Handles an exception caught in the JSP page. * * @param e the caught exception */ @Override public void handlePageException(Exception e) throws ServletException, IOException { handlePageException((Throwable) e); } /** * Handles an exception caught in the JSP page. * * @param e the caught exception */ public void handlePageException(Throwable e) throws ServletException, IOException { if (e instanceof SkipPageException) return; HttpServletRequest request = getCauchoRequest(); request.setAttribute("javax.servlet.jsp.jspException", e); CauchoResponse response = getCauchoResponse(); response.setForbidForward(false); response.setResponseStream(_responseStream); response.killCache(); response.setNoCache(true); if (e instanceof ClientDisconnectException) throw (ClientDisconnectException) e; if (! (_servlet instanceof Page)) { } else if (getApplication() == null || getApplication().getJsp() == null || ! getApplication().getJsp().isRecompileOnError()) { } else if (e instanceof OutOfMemoryError) { } else if (e instanceof Error) { try { Path workDir = getApplication().getRootDirectory().lookup("WEB-INF/work"); String className = _servlet.getClass().getName(); Path path = workDir.lookup(className.replace('.', '/') + ".class"); log.warning("Removing " + path + " due to " + e); path.remove(); } catch (Exception e1) { } Page page = (Page) _servlet; page._caucho_unload(); if (! page.isDead()) { page.setDead(); page.destroy(); } } _topOut.clearBuffer(); if (_errorPage != null) { if (log.isLoggable(Level.FINER)) { log.log(Level.FINER, e.toString(), e); } else if (e instanceof DisplayableException) { log.warning(e.getMessage()); } else { log.log(Level.WARNING, e.toString(), e); } request.setAttribute(EXCEPTION, e); request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE, response.getStatus()); request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, e); request.setAttribute(RequestDispatcher.ERROR_MESSAGE, e.getMessage()); request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE, e.getClass()); request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI, request.getRequestURI()); // jsp/01ck response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); try { RequestDispatcher rd = getCauchoRequest().getRequestDispatcher(_errorPage); if (rd instanceof RequestDispatcherImpl) { getCauchoResponse().setHasError(true); ((RequestDispatcherImpl) rd).error(getCauchoRequest(), getCauchoResponse()); } else { if (rd != null) { getCauchoResponse().killCache(); getCauchoResponse().setNoCache(true); rd.forward(getCauchoRequest(), getCauchoResponse()); } else { log.log(Level.FINE, e.toString(), e); throw new ServletException(L.l("`{0}' is an unknown error page. The JSP errorPage directive must refer to a valid URL relative to the current web-app.", _errorPage)); } } // jsp/01ck, server/02ei // response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } catch (FileNotFoundException e2) { log.log(Level.WARNING, e.toString(), e2); throw new ServletException(L.l("`{0}' is an unknown error page. The JSP errorPage directive must refer to a valid URL relative to the current web-app.", _errorPage)); } catch (IOException e2) { log.log(Level.FINE, e.toString(), e2); } return; } /* if (_servlet instanceof Page && ! (e instanceof LineMapException)) { LineMap lineMap = ((Page) _servlet)._caucho_getLineMap(); if (lineMap != null) e = new JspLineException(e, lineMap); } */ if (e instanceof ServletException) { throw (ServletException) e; } else if (e instanceof IOException) { throw (IOException) e; } else if (e instanceof RuntimeException) { throw (RuntimeException) e; } else if (e instanceof Error) { throw (Error) e; } else { throw new ServletException(e); } } /** * Returns the error data */ @Override public ErrorData getErrorData() { String uri = (String) getCauchoRequest().getAttribute(RequestDispatcher.ERROR_REQUEST_URI); if (uri == null) return null; Integer status = (Integer) getCauchoRequest().getAttribute(RequestDispatcher.ERROR_STATUS_CODE); return new ErrorData(getThrowable(), status == null ? 0 : status.intValue(), (String) getCauchoRequest().getAttribute(RequestDispatcher.ERROR_REQUEST_URI), (String) getCauchoRequest().getAttribute(RequestDispatcher.ERROR_SERVLET_NAME)); } /** * Returns the variable resolver */ public javax.servlet.jsp.el.VariableResolver getVariableResolver() { return this; } /** * Returns the expression evaluator */ public ExpressionEvaluator getExpressionEvaluator() { if (_expressionEvaluator == null) _expressionEvaluator = new ExpressionEvaluatorImpl(getELContext()); return _expressionEvaluator; } /** * Returns the expression evaluator */ public ELContext getELContext() { if (_elContext != null) return _elContext; if (_elContextValue == null) { WebApp webApp = getApplication(); JspApplicationContextImpl jspContext = webApp.getJspApplicationContext(); if (_elResolver == null) { ELResolver[] resolverArray = jspContext.getELResolverArray(); _elResolver = new PageContextELResolver(this, resolverArray); } if (_functionMapper == null) _functionMapper = new PageFunctionMapper(); if (_variableMapper == null) _variableMapper = new PageVariableMapper(); _elContextValue = new PageELContext(); if (_elContextListeners == null) _elContextListeners = jspContext.getELListenerArray(); } _elContext = _elContextValue; if (_elContextListeners.length > 0) { ELContextEvent event = new ELContextEvent(_elContext); for (int i = 0; i < _elContextListeners.length; i++) { _elContextListeners[i].contextCreated(event); } } return _elContext; } /** * Releases the context. */ @Override public void release() { try { _servlet = null; if (_attributes.size() > 0) _attributes.clear(); /* XXX: if (! autoFlush && response instanceof Response) ((Response) response).setDisableAutoFlush(false); */ getCauchoResponse().setResponseStream(_responseStream); // getCauchoResponse().setFlushBuffer(null); _request = null; _webApp = null; _session = null; while (_out instanceof AbstractJspWriter) { if (_out instanceof AbstractJspWriter) _out = ((AbstractJspWriter) _out).popWriter(); } _out = null; _topOut = null; _nodeEnv = null; _responseStream = null; ToCharResponseAdapter resAdapt = _responseAdapter; _responseAdapter = null; _servletResponse = null; _response = null; if (_elContext != null) _elContext.clear(); _jspOutputStream.release(); if (resAdapt != null) { // jsp/15l3 resAdapt.finish(); //_responseAdapter.close(); ToCharResponseAdapter.free(resAdapt); } /* // server/137q if (! _hasException && responseStream != null) responseStream.close(); */ } catch (IOException e) { _out = null; } } /** * Returns the localized message appropriate for the current context. */ public String getLocalizedMessage(String key, Object []args, String basename) { Object lc = basename; if (lc == null) lc = getAttribute("caucho.bundle"); if (lc == null) lc = Config.find(this, Config.FMT_LOCALIZATION_CONTEXT); return getLocalizedMessage(lc, key, args, basename); } /** * Returns the localized message appropriate for the current context. */ public String getLocalizedMessage(Object lc, String key, Object []args, String basename) { String bundleString = null; // jsp/1c51, jsp/1c54 String prefix = (String) getAttribute("caucho.bundle.prefix"); // jsp/1c3x if (key == null) key = ""; if (prefix != null) key = prefix + key; if (lc == null) { lc = basename; if (lc == null || "".equals(lc)) lc = getAttribute("caucho.bundle"); if (lc == null) lc = Config.find(this, Config.FMT_LOCALIZATION_CONTEXT); } Locale locale = null; if (lc instanceof LocalizationContext) { ResourceBundle bundle = ((LocalizationContext) lc).getResourceBundle(); locale = ((LocalizationContext) lc).getLocale(); try { if (bundle != null) bundleString = bundle.getString(key); } catch (Exception e) { } } else if (lc instanceof String) { LocalizationContext loc = getBundle((String) lc); locale = loc.getLocale(); ResourceBundle bundle = loc.getResourceBundle(); try { if (bundle != null) bundleString = bundle.getString(key); } catch (Exception e) { } } if (bundleString == null) return "???" + key + "???"; else if (args == null || args.length == 0) return bundleString; if (locale == null) locale = getLocale(); if (locale != null) return new MessageFormat(bundleString, locale).format(args); else return new MessageFormat(bundleString).format(args); } /** * Returns the localized message appropriate for the current context. */ public LocalizationContext getBundle(String name) { Object localeObj = Config.find(this, Config.FMT_LOCALE); LocalizationContext bundle = null; BundleManager manager = getBundleManager(); if (localeObj instanceof Locale) { Locale locale = (Locale) localeObj; bundle = manager.getBundle(name, locale); } else if (localeObj instanceof String) { Locale locale = getLocale((String) localeObj, null); bundle = manager.getBundle(name, locale); } else { String acceptLanguage = getCauchoRequest().getHeader("Accept-Language"); if (acceptLanguage != null) { String cacheKey = name + acceptLanguage; bundle = manager.getBundle(name, cacheKey, getCauchoRequest().getLocales()); } } if (bundle != null) return bundle; Object fallback = Config.find(this, Config.FMT_FALLBACK_LOCALE); if (fallback instanceof Locale) { Locale locale = (Locale) fallback; bundle = manager.getBundle(name, locale); } else if (fallback instanceof String) { String localeName = (String) fallback; Locale locale = getLocale(localeName, null); bundle = manager.getBundle(name, locale); } if (bundle != null) return bundle; bundle = manager.getBundle(name); if (bundle != null) return bundle; else { return BundleManager.NULL_BUNDLE; } } /** * Returns the currently active locale. */ public Locale getLocale() { if (_locale != null) return _locale; _locale = getLocaleImpl(); if (_locale != null) getResponse().setLocale(_locale); return _locale; } /** * Returns the currently active locale. */ private Locale getLocaleImpl() { Object localeObj = Config.find(this, Config.FMT_LOCALIZATION_CONTEXT); LocalizationContext lc; Locale locale = null; if (localeObj instanceof LocalizationContext) { lc = (LocalizationContext) localeObj; locale = lc.getLocale(); if (locale != null) return locale; } localeObj = Config.find(this, Config.FMT_LOCALE); if (localeObj instanceof Locale) return (Locale) localeObj; else if (localeObj instanceof String) return getLocale((String) localeObj, null); lc = (LocalizationContext) getAttribute("caucho.bundle"); if (lc != null) locale = lc.getLocale(); if (locale != null) return locale; String acceptLanguage = getCauchoRequest().getHeader("Accept-Language"); if (acceptLanguage != null) { Enumeration e = getCauchoRequest().getLocales(); if (e != null && e.hasMoreElements()) locale = (Locale) e.nextElement(); } localeObj = Config.find(this, Config.FMT_FALLBACK_LOCALE); if (localeObj instanceof Locale) return (Locale) localeObj; else if (localeObj instanceof String) return getLocale((String) localeObj, null); else return null; } public static Locale getLocale(String value, String variant) { Locale locale = null; int len = value.length(); CharBuffer cb = new CharBuffer(); int i = 0; char ch = 0; for (; i < len && (ch = value.charAt(i)) != '_' && ch != '-'; i++) cb.append(ch); String language = cb.toString(); if (ch == '_' || ch == '-') { cb.clear(); for (i++; i < len && (ch = value.charAt(i)) != '_' && ch != '-'; i++) cb.append(ch); String country = cb.toString(); if (variant != null && ! variant.equals("")) return new Locale(language, country, variant); else return new Locale(language, country); } else if (variant != null && ! variant.equals("")) return new Locale(language, "", variant); else return new Locale(language, ""); } public static void printBody(BodyContentImpl body, boolean isEscaped) throws IOException { JspWriter out = body.getEnclosingWriter(); CharBuffer string = body.getCharBuffer(); char []cBuf = string.getBuffer(); int length = string.length() - 1; for (; length >= 0; length--) { char ch = cBuf[length]; if (ch != ' ' && ch != '\n' && ch != '\t' && ch != '\r') break; } length++; int i; for (i = 0; i < length; i++) { char ch = cBuf[i]; if (ch != ' ' && ch != '\n' && ch != '\t' && ch != '\r') break; } if (! isEscaped) { out.write(cBuf, i, length - i); return; } for (; i < length; i++) { char ch = cBuf[i]; switch (ch) { case '<': out.write("<"); break; case '>': out.write(">"); break; case '&': out.write("&"); break; case '\'': out.write("'"); break; case '"': out.write("""); break; default: out.write((char) ch); break; } } } /** * Evaluates the fragment, returing the string value. */ public String invoke(JspFragment fragment) throws JspException, IOException { if (fragment == null) return ""; BodyContentImpl body = (BodyContentImpl) pushBody(); try { fragment.invoke(body); return body.getString(); } finally { popBody(); body.release(); } } /** * Evaluates the fragment, returing the string value. */ public String invokeTrim(JspFragment fragment) throws JspException, IOException { if (fragment == null) return ""; BodyContentImpl body = (BodyContentImpl) pushBody(); try { fragment.invoke(body); return body.getTrimString(); } finally { popBody(); body.release(); } } /** * Evaluates the fragment, returing a reader */ public Reader invokeReader(JspFragment fragment) throws JspException, IOException { if (fragment == null) return null; BodyContentImpl body = (BodyContentImpl) pushBody(); try { fragment.invoke(body); return body.getReader(); } finally { popBody(); body.release(); } } /** * Parses a boolean value. */ static public boolean toBoolean(String value) { if (value == null) return false; else if (value.equalsIgnoreCase("true")) return true; else if (value.equalsIgnoreCase("false")) return false; else return value.trim().equalsIgnoreCase("true"); } /** * Set/Remove a page attribute. */ public void defaultSetOrRemove(String var, Object value) { if (value != null) putAttribute(var, value); else removeAttribute(var); } /** * Set/Remove a page attribute. */ public void pageSetOrRemove(String var, Object value) { if (value != null) _attributes.put(var, value); else _attributes.remove(var); } /** * Set/Remove a request attribute. */ public void requestSetOrRemove(String var, Object value) { if (value != null) getCauchoRequest().setAttribute(var, value); else getCauchoRequest().removeAttribute(var); } /** * Set/Remove a session attribute. */ public void sessionSetOrRemove(String var, Object value) { if (value != null) getSession().setAttribute(var, value); else getSession().removeAttribute(var); } /** * Set/Remove an webApp attribute. */ public void applicationSetOrRemove(String var, Object value) { if (value != null) getApplication().setAttribute(var, value); else getApplication().removeAttribute(var); } /** * Returns true if the EL ignores exceptions */ public boolean isIgnoreException() { JspPropertyGroup jsp = getApplication().getJsp(); return (jsp == null || jsp.isIgnoreELException()); } /** * Returns the XPath variable environment corresponding to this page */ public VarEnv getVarEnv() { if (_varEnv == null) _varEnv = new PageVarEnv(); return _varEnv; } /** * Returns the XPath node environment corresponding to this page */ public Node getNodeEnv() { return _nodeEnv; } /** * Returns the XPath node environment corresponding to this page */ public void setNodeEnv(Node node) { _nodeEnv = node; } /** * Creates an expression. */ public ValueExpression createExpr(ValueExpression expr, String exprString, Class type) { if (_variableMapper == null || _variableMapper.isConstant()) return expr; return JspUtil.createValueExpression(getELContext(), type, exprString); } /** * Finds an attribute in any of the scopes from page to webApp. * * @param name the attribute name. * * @return the attribute value */ public Object resolveVariable(String name) throws javax.el.ELException { Object value = findAttribute(name); if (value != null) return value; ELContext elContext = getELContext(); /* if (_elContext == null) _elContext = EL.getEnvironment(); */ return elContext.getELResolver().getValue(elContext, null, name); } /** * Returns the bundle manager. */ private BundleManager getBundleManager() { if (_bundleManager == null) _bundleManager = BundleManager.create(); return _bundleManager; } /** * Represents the XPath environment for this page. */ public class PageVarEnv extends VarEnv { /** * Returns the value corresponding to the name. */ public Object getValue(String name) { Object value = findAttribute(name); if (value != null) return value; int p = name.indexOf(':'); if (p < 0) { // jsp/1m43, TCK throw new javax.el.ELException(L.l("'{0}' is an unknown variable", name)); } String prefix = name.substring(0, p); String suffix = name.substring(p + 1); if (prefix.equals("param")) return getCauchoRequest().getParameter(suffix); else if (prefix.equals("header")) return ((HttpServletRequest) getCauchoRequest()).getHeader(suffix); else if (prefix.equals("cookie")) { Cookie cookie; HttpServletRequest request = (HttpServletRequest) getCauchoRequest(); if (request instanceof CauchoRequest) cookie = ((CauchoRequest) request).getCookie(suffix); else cookie = null; if (cookie != null) return cookie.getValue(); else return null; } else if (prefix.equals("initParam")) return getApplication().getInitParameter(suffix); else if (prefix.equals("pageScope")) { return getAttribute(suffix); } else if (prefix.equals("requestScope")) return getCauchoRequest().getAttribute(suffix); else if (prefix.equals("sessionScope")) return getSession().getAttribute(suffix); else if (prefix.equals("applicationScope")) return getApplication().getAttribute(suffix); else return null; } } static class StringArrayEnum implements Enumeration { private int _index; private String []_values; StringArrayEnum(String []values) { _values = values; } public boolean hasMoreElements() { return _index < _values.length; } public Object nextElement() { return _values[_index++]; } } public class PageELContext extends ServletELContext { private HashMap<Class,Object> _contextMap; public PageELContext() { } @Override public Object getContext(Class key) { if (key == JspContext.class) return PageContextImpl.this; if (key == null) throw new NullPointerException(); else if (_contextMap != null) return _contextMap.get(key); else return null; } @Override public void putContext(Class key, Object contextObject) { if (key == null) throw new NullPointerException(); if (_contextMap == null) _contextMap = new HashMap<Class,Object>(8); _contextMap.put(key, contextObject); } public void clear() { if (_contextMap != null) _contextMap.clear(); setLocale(null); } public PageContextImpl getPageContext() { return PageContextImpl.this; } @Override public ServletContext getApplication() { return PageContextImpl.this.getApplication(); } @Override public Object getApplicationScope() { return new PageContextAttributeMap(PageContextImpl.this, APPLICATION_SCOPE); } @Override public HttpServletRequest getRequest() { return (HttpServletRequest) PageContextImpl.this.getRequest(); } @Override public Object getRequestScope() { return new PageContextAttributeMap(PageContextImpl.this, REQUEST_SCOPE); } @Override public Object getSessionScope() { return new PageContextAttributeMap(PageContextImpl.this, SESSION_SCOPE); } public ELResolver getELResolver() { return _elResolver; } public javax.el.FunctionMapper getFunctionMapper() { return _functionMapper; } public javax.el.VariableMapper getVariableMapper() { return _variableMapper; } public String toString() { return getClass().getSimpleName() + "[" + PageContextImpl.this + "]"; } } public class PageFunctionMapper extends javax.el.FunctionMapper { public Method resolveFunction(String prefix, String localName) { if (_functionMap != null) return _functionMap.get(prefix + ":" + localName); else return null; } } public class PageVariableMapper extends ImplicitVariableMapper { private HashMap<String,ValueExpression> _map; final boolean isConstant() { return _map == null || _map.size() == 0; } public ValueExpression resolveVariable(String var) { if (_map != null) { ValueExpression expr = _map.get(var); if (expr != null) return expr; } Object value = PageContextImpl.this.resolveVariable(var); if (value instanceof ValueExpression) return (ValueExpression) value; ValueExpression expr; expr = super.resolveVariable(var); return expr; } public ValueExpression setVariable(String var, ValueExpression expr) { // ValueExpression oldValue = getVariable(var); if (_map == null) { _map = new HashMap<String,ValueExpression>(); } _map.put(var, expr); return expr; } public String toString() { return getClass().getSimpleName() + "[" + PageContextImpl.this + "]"; } } }