/* * 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 com.caucho.server.http.CauchoRequest; import com.caucho.server.http.CauchoResponse; import com.caucho.server.http.RequestAdapter; import com.caucho.server.http.ResponseAdapter; import com.caucho.server.webapp.WebApp; import com.caucho.util.L10N; import com.caucho.vfs.ClientDisconnectException; import com.caucho.vfs.JarPath; import com.caucho.vfs.Path; import com.caucho.vfs.Vfs; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.FileNotFoundException; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; /** * Base servlet for both JSP and XTP. It's primarily responsible for * returning the proper error messages when things go wrong. * * <p>The manager create the compiled JSP and XTP pages. The manager * returns a Page object which is actually executed. * * @see JspManager * @see XtpManager * @see Page */ abstract public class QServlet implements Servlet { static final String COPYRIGHT = "Copyright(c) 1998-2011 Caucho Technology. All rights reserved."; private static final Logger log = Logger.getLogger(QServlet.class.getName()); private static final L10N L = new L10N(QServlet.class); protected WebApp _webApp; private PageManager _manager; private ServletConfig _config; /** * Initialize the servlet. If necessary, convert the ServletContext * to a CauchoWebApp. Also, read the configuration Registry * it it hasn't been read yet. */ public void init(ServletConfig config) throws ServletException { ServletContext cxt = config.getServletContext(); _webApp = (WebApp) cxt; _config = config; log.finer(config.getServletName() + " init"); } /** * JspServlet and XtpServlet will set the PageManager with this method. */ protected void setManager(PageManager manager) { _manager = manager; } protected PageManager getManager() { return _manager; } /** * Override the Servlet method to return the generated application. */ public ServletContext getServletContext() { return _webApp; } /** * Returns the config. */ public ServletConfig getServletConfig() { return _config; } /** * Returns the init parameter */ public String getInitParameter(String name) { return _config.getInitParameter(name); } /** * The service method gets the JSP/XTP page and executes it. The * request and response objects are converted to Caucho objects so * other servlet runners will produce the same results as the Caucho * servlet runner. */ public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { CauchoRequest request; CauchoResponse response; ResponseAdapter resAdapt = null; if (req instanceof CauchoRequest) request = (CauchoRequest) req; else request = RequestAdapter.create((HttpServletRequest) req, _webApp); if (res instanceof CauchoResponse) response = (CauchoResponse) res; else { resAdapt = ResponseAdapter.create((HttpServletResponse) res); response = resAdapt; } Page page = null; try { page = getPage(request, response); if (page == null) { response.sendError(HttpServletResponse.SC_NOT_FOUND); return; } page.service(request, response); } catch (JspParseException e) { if (e.getErrorPage() != null) forwardErrorPage(request, response, e, e.getErrorPage()); else throw new ServletException(e); } catch (ClientDisconnectException e) { throw e; } catch (Throwable e) { if (page != null && page.getErrorPage() != null && forwardErrorPage(request, response, e, page.getErrorPage())) { } else if (e instanceof IOException) { log.log(Level.FINE, e.toString(), e); throw (IOException) e; } else if (e instanceof ServletException) { log.log(Level.FINE, e.toString(), e); throw (ServletException) e; } else { log.log(Level.FINE, e.toString(), e); throw new ServletException(e); } } if (resAdapt != null) { resAdapt.close(); ResponseAdapter.free(resAdapt); } } /** * Creates and returns a new page. * * @param request the servlet request * @param response the servlet response * * @return the compiled page */ public Page getPage(HttpServletRequest request, HttpServletResponse response) throws Exception { try { Page page = getSubPage(request, response); return page; } catch (JspParseException e) { if (e.getErrorPage() != null) { if (e.getCause() != null && ! (e instanceof JspLineParseException)) forwardErrorPage(request, response, e.getCause(), e.getErrorPage()); else forwardErrorPage(request, response, e, e.getErrorPage()); return null; } else throw e; } } /** * Given a request and response, returns the compiled Page. For example, * JspManager will return the JspPage and XtpManager will * return the XtpPage. * * @param req servlet request for generating the page. * @param res servlet response to any needed error messages. */ private Page getSubPage(HttpServletRequest req, HttpServletResponse res) throws Exception { CauchoRequest cauchoRequest = null; initGetPage(); /* if (! _webApp.isActive()) throw new UnavailableException("JSP compilation unavailable during restart", 10); */ if (req instanceof CauchoRequest) cauchoRequest = (CauchoRequest) req; String servletPath; if (cauchoRequest != null) servletPath = cauchoRequest.getPageServletPath(); else servletPath = RequestAdapter.getPageServletPath(req); if (servletPath == null) servletPath = "/"; String uri; if (cauchoRequest != null) uri = cauchoRequest.getPageURI(); else uri = RequestAdapter.getPageURI(req); Path appDir = _webApp.getRootDirectory(); String realPath; Path subcontext; ServletConfig config = null; String jspPath = (String) req.getAttribute("caucho.jsp.jsp-file"); if (jspPath != null) { req.removeAttribute("caucho.jsp.jsp-file"); subcontext = getPagePath(jspPath); return _manager.getPage(uri, jspPath, subcontext, config); } String pathInfo; if (cauchoRequest != null) pathInfo = cauchoRequest.getPagePathInfo(); else pathInfo = RequestAdapter.getPagePathInfo(req); subcontext = getPagePath(servletPath); if (subcontext != null) return _manager.getPage(servletPath, subcontext); if (pathInfo == null) { realPath = _webApp.getRealPath(servletPath); subcontext = appDir.lookupNative(realPath); return _manager.getPage(servletPath, subcontext); } subcontext = getPagePath(servletPath + pathInfo); if (subcontext != null) return _manager.getPage(servletPath + pathInfo, subcontext); // If servlet path exists, can't use pathInfo to lookup servlet, // because /jsp/WEB-INF would be a security hole if (servletPath != null && ! servletPath.equals("")) { // server/0035 throw new FileNotFoundException(L.l("{0} was not found on this server.", uri)); // return null; } subcontext = getPagePath(pathInfo); if (subcontext != null) return _manager.getPage(pathInfo, subcontext); subcontext = getPagePath(uri); if (subcontext == null) throw new FileNotFoundException(L.l("{0} was not found on this server.", uri)); return _manager.getPage(uri, subcontext); } private void initGetPage() { // marks the listener array as used _webApp.getJspApplicationContext().getELListenerArray(); } public Page getPage(String uri, String pageURI, ServletConfig config) throws Exception { Path path = getPagePath(pageURI); if (path == null) return null; return _manager.getPage(uri, pageURI, path, config); } /** * Returns the path to be used as the servlet name. */ private Path getPagePath(String pathName) { Path rootDir = _webApp.getRootDirectory(); String realPath = _webApp.getRealPath(pathName); Path path = rootDir.lookupNative(realPath); if (path.isFile() && path.canRead()) return path; java.net.URL url; ClassLoader loader = _webApp.getClassLoader(); if (loader != null) { url = _webApp.getClassLoader().getResource(pathName); String name = url != null ? url.toString() : null; path = null; if (url != null && (name.endsWith(".jar") || name.endsWith(".zip"))) path = JarPath.create(Vfs.lookup(url.toString())).lookup(pathName); else if (url != null) path = Vfs.lookup(url.toString()); if (path != null && path.isFile() && path.canRead()) return path; } url = ClassLoader.getSystemResource(pathName); String name = url != null ? url.toString() : null; path = null; if (url != null && (name.endsWith(".jar") || name.endsWith(".zip"))) path = JarPath.create(Vfs.lookup(url.toString())).lookup(pathName); else if (url != null) path = Vfs.lookup(url.toString()); if (path != null && path.isFile() && path.canRead()) return path; else return null; } /** * Remove the page from any cache. */ public void killPage(HttpServletRequest request, HttpServletResponse response, Page page) { _manager.killPage(request, response, page); } /** * Forwards an error to the proper error page. * * @param req the servlet request * @param res the servlet response * @param e the exception * @param errorPage the error page */ private boolean forwardErrorPage(HttpServletRequest req, HttpServletResponse res, Throwable e, String errorPage) throws ServletException, IOException { req.setAttribute("javax.servlet.jsp.jspException", e); req.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE, e); req.setAttribute(RequestDispatcher.ERROR_REQUEST_URI, req.getRequestURI()); if (res instanceof CauchoResponse) { CauchoResponse cauchoResponse = (CauchoResponse) res; cauchoResponse.killCache(); cauchoResponse.setNoCache(true); } RequestDispatcher rd = req.getRequestDispatcher(errorPage); if (rd == null) return false; rd.forward(req, res); return true; } public void destroy() { _manager.destroy(); log.finer(_config.getServletName() + " destroy"); } }