Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.caucho.server.webapp.ErrorPageManager Maven / Gradle / Ivy
/*
* Copyright (c) 1998-2018 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.server.webapp;
import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.AsyncContext;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestWrapper;
import javax.servlet.ServletResponse;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.caucho.VersionFactory;
import com.caucho.config.LineException;
import com.caucho.env.meter.MeterService;
import com.caucho.env.meter.TimeSensor;
import com.caucho.env.shutdown.ExitCode;
import com.caucho.env.shutdown.ShutdownSystem;
import com.caucho.i18n.CharacterEncoding;
import com.caucho.java.LineMap;
import com.caucho.java.LineMapException;
import com.caucho.java.ScriptStackTrace;
import com.caucho.server.cluster.ServletService;
import com.caucho.server.dispatch.BadRequestException;
import com.caucho.server.host.Host;
import com.caucho.server.http.CauchoRequest;
import com.caucho.server.http.CauchoResponse;
import com.caucho.server.http.HttpServletRequestImpl;
import com.caucho.server.http.HttpServletResponseImpl;
import com.caucho.server.resin.Resin;
import com.caucho.server.util.CauchoSystem;
import com.caucho.util.Alarm;
import com.caucho.util.CharBuffer;
import com.caucho.util.CompileException;
import com.caucho.util.CurrentTime;
import com.caucho.util.DisplayableException;
import com.caucho.util.L10N;
import com.caucho.util.LineCompileException;
import com.caucho.util.QDate;
import com.caucho.vfs.ClientDisconnectException;
import com.caucho.vfs.Encoding;
import com.caucho.vfs.Vfs;
/**
* Represents the final servlet in a filter chain.
*/
public class ErrorPageManager {
private final static L10N L = new L10N(ErrorPageManager.class);
private final static Logger log
= Logger.getLogger(ErrorPageManager.class.getName());
public static final char []MSIE_PADDING;
public static String JSP_EXCEPTION = "javax.servlet.jsp.jspException";
public static String SHUTDOWN = "com.caucho.shutdown";
private final ServletService _server;
private final Host _host;
private final WebApp _webApp;
private WebAppContainer _appContainer;
private HashMap _errorPageMap = new HashMap();
private String _defaultLocation;
private ErrorPageManager _parent;
/**
* Create error page manager.
*/
public ErrorPageManager(ServletService server)
{
this(server, null, null);
}
/**
* Create error page manager.
*/
public ErrorPageManager(ServletService server, WebApp webApp)
{
this(server, null, webApp);
}
/**
* Create error page manager.
*/
public ErrorPageManager(ServletService server, Host host, WebApp app)
{
_webApp = app;
_server = server;
_host = host;
if (_server == null)
throw new IllegalStateException(L.l("{0} requires an active {1}",
getClass().getSimpleName(),
ServletService.class.getSimpleName()));
}
/**
* Sets the manager parent.
*/
public void setParent(ErrorPageManager parent)
{
_parent = parent;
}
/**
* Gets the manager parent.
*/
public ErrorPageManager getParent()
{
return _parent;
}
/**
* Adds an error page.
*/
public void addErrorPage(ErrorPage errorPage)
{
if (errorPage.getExceptionType() != null) {
_errorPageMap.put(errorPage.getExceptionType(),
errorPage.getLocation());
}
else if (errorPage.getErrorCode() < 0) {
_defaultLocation = errorPage.getLocation();
}
else
_errorPageMap.put(new Integer(errorPage.getErrorCode()),
errorPage.getLocation());
}
/**
* Sets the webApp container.
*/
public void setWebAppContainer(WebAppContainer appContainer)
{
_appContainer = appContainer;
}
/**
* Returns true if we should return a development-friendly error page.
*/
protected boolean isDevelopmentModeErrorPage()
{
return _server.isDevelopmentModeErrorPage();
}
/**
* Returns true if we should return a development-friendly error page.
*/
protected boolean isErrorPageServerId()
{
return _server.isErrorPageServerId() || isDevelopmentModeErrorPage();
}
/**
* Displays a parse error.
*/
public void sendServletError(Throwable e,
ServletRequest req,
ServletResponse res)
throws IOException
{
try {
sendServletErrorImpl(e, req, res);
} finally {
if (res instanceof CauchoResponse)
((CauchoResponse) res).close();
}
}
public void sendServletErrorImpl(Throwable e,
ServletRequest req,
ServletResponse res)
throws IOException
{
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
Throwable rootExn = e;
Throwable errorPageExn = null;
LineMap lineMap = null;
try {
if (response != null) {
response.reset();
}
} catch (IllegalStateException e1) {
}
if (req.isAsyncStarted()) {
AsyncContext async = req.getAsyncContext();
if (async != null)
async.complete();
}
if (response instanceof HttpServletResponseImpl) {
HttpServletResponseImpl resFacade = (HttpServletResponseImpl) response;
resFacade.killCache();
resFacade.setNoCache(true);
}
if (rootExn instanceof ClientDisconnectException)
throw (ClientDisconnectException) rootExn;
String location = null;
String title = "500 Servlet Exception";
boolean isBadRequest = false;
boolean doStackTrace = true;
boolean isCompileException = false;
boolean isServletException = false;
Throwable compileException = null;
String lineMessage = null;
boolean lookupErrorPage = true;
while (true) {
if (rootExn instanceof LineMapException)
lineMap = ((LineMapException) rootExn).getLineMap();
if (lookupErrorPage) {
errorPageExn = rootExn;
}
if (rootExn instanceof DisplayableException) {
doStackTrace = false;
isCompileException = true;
if (compileException == null)
compileException = rootExn;
}
else if (rootExn instanceof CompileException) {
doStackTrace = false;
isCompileException = true;
// use outer exception because it might have added more location info
/*
if (rootExn instanceof LineCompileException) {
compileException = rootExn;
isLineCompileException = true;
}
else if (compileException == null) // ! isLineCompileException)
compileException = rootExn;
*/
if (compileException == null) // ! isLineCompileException)
compileException = rootExn;
}
else if (rootExn instanceof LineException) {
if (lineMessage == null)
lineMessage = rootExn.getMessage();
}
if (rootExn instanceof BadRequestException) {
isBadRequest = true;
}
if (rootExn instanceof OutOfMemoryError) {
String msg = "TcpSocketLink OutOfMemory";
ShutdownSystem.shutdownOutOfMemory(msg);
}
if (location != null || ! lookupErrorPage) {
}
else if (rootExn instanceof LineMapException
&& rootExn instanceof ServletException
&& ! (rootExn instanceof LineCompileException)
&& rootExn.getCause() != null) {
// hack to deal with JSP wrapping
}
else if (! isServletException) {
// SRV.9.9.2 Servlet 2.4
//location = getErrorPage(rootExn, ServletException.class);
location = getErrorPage(rootExn);
isServletException = true;
}
else {
location = getErrorPage(rootExn);
lookupErrorPage = false;
}
if (location != null)
lookupErrorPage = false;
if (isBadRequest)
break;
Throwable cause = null;
if (rootExn instanceof ServletException
&& ! (rootExn instanceof LineCompileException))
cause = ((ServletException) rootExn).getRootCause();
else {
lookupErrorPage = false;
cause = rootExn.getCause();
}
if (cause != null)
rootExn = cause;
else {
break;
}
}
if (location == null && lookupErrorPage) {
location = getErrorPage(rootExn);
}
if (location == null && rootExn instanceof BadRequestException) {
location = getErrorPage(400);
}
else if (location == null) {
location = getErrorPage(500);
}
if (location == null && _defaultLocation == null && _parent != null) {
_parent.sendServletError(e, req, res);
return;
}
if (isBadRequest) {
// server/05a0, server/0532
if (rootExn instanceof CompileException)
title = rootExn.getMessage();
else
title = String.valueOf(rootExn);
doStackTrace = false;
isBadRequest = true;
if (request instanceof CauchoRequest)
((CauchoRequest) request).killKeepalive("bad request: " + rootExn);
response.resetBuffer();
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
/*
if (location == null)
log.warning(e.toString());
*/
}
else if (rootExn instanceof UnavailableException) {
UnavailableException unAvail = (UnavailableException) rootExn;
if (unAvail.isPermanent()) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
title = "404 Not Found";
if (location == null)
location = getErrorPage(HttpServletResponse.SC_NOT_FOUND);
}
else {
response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
title = "503 Unavailable";
if (unAvail.getUnavailableSeconds() > 0)
response.setIntHeader("Retry-After",
unAvail.getUnavailableSeconds());
if (location == null)
location = getErrorPage(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
}
}
/*
else if (_app != null && app.getServer().isClosed()) {
response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
title = "503 Unavailable";
}
*/
else {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
if (location == null)
location = _defaultLocation;
Level level = location == null ? Level.WARNING : Level.FINE;
if (log.isLoggable(Level.FINER))
log.log(level, e.toString(), e);
else if (isCompileException) {
level = location == null ? Level.WARNING : Level.INFO;
if (isBadRequest)
log.log(level, BadRequestException.class.getSimpleName() + ": " + compileException.getMessage());
else
log.log(level, compileException.getMessage());
}
else if (! doStackTrace)
log.log(level, rootExn.toString());
else
log.log(level, e.toString(), e);
if (location != null) {
if (errorPageExn == null)
errorPageExn = rootExn;
request.setAttribute(JSP_EXCEPTION, errorPageExn);
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, errorPageExn);
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE, errorPageExn.getClass());
if (request instanceof HttpServletRequest)
request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI,
((HttpServletRequest) request).getRequestURI());
String servletName = getServletName(request);
if (servletName != null)
request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME, servletName);
request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE, new Integer(500));
request.setAttribute(RequestDispatcher.ERROR_MESSAGE, errorPageExn.getMessage());
try {
RequestDispatcher disp = null;
// can't use filters because of error pages due to filters
// or security.
WebApp webApp = getWebApp();
if (webApp != null)
disp = webApp.getRequestDispatcher(location);
else if (_host != null)
disp = _host.getWebAppContainer().getRequestDispatcher(location);
if (disp != null) {
((RequestDispatcherImpl) disp).error(request, response);
return;
}
} catch (Throwable e1) {
log.log(Level.INFO, e1.toString(), e1);
rootExn = e1;
}
}
response.setContentType("text/html");
String encoding = CharacterEncoding.getLocalEncoding();
if (encoding != null)
response.setCharacterEncoding(encoding);
else {
Locale locale = Locale.getDefault();
if (! "ISO-8859-1".equals(Encoding.getMimeName(locale)))
response.setLocale(Locale.getDefault());
else
response.setCharacterEncoding("utf-8");
}
PrintWriter out;
try {
out = response.getWriter();
} catch (IllegalStateException e1) {
log.log(Level.FINEST, e1.toString(), e1);
out = new PrintWriter(new OutputStreamWriter(response.getOutputStream()));
}
if (isDevelopmentModeErrorPage()) {
out.println("");
if (! response.isCommitted())
out.println("" + escapeHtml(title) + " ");
out.println("");
out.println("" + escapeHtml(title) + " ");
out.println("");
if (log.isLoggable(Level.FINE) && ! CurrentTime.isTest())
doStackTrace = true;
if (doStackTrace) {
out.println("");
out.print("[show] ");
}
if (compileException instanceof DisplayableException) {
// ioc/0000
// XXX: dispExn.print doesn't normalize user.name
// dispExn.print(out);
out.println(escapeHtml(compileException.getMessage()));
}
else if (compileException != null)
out.println(escapeHtml(compileException.getMessage()));
else
out.println(escapeHtml(rootExn.toString()));
if (doStackTrace) {
out.println("");
printStackTrace(out, lineMessage, e, rootExn, lineMap);
out.println(" ");
}
/*
*if (doStackTrace || log.isLoggable(Level.FINE)) {
printStackTrace(out, lineMessage, e, rootExn, lineMap);
}
*/
out.println("
");
printVersion(out);
out.println("");
}
else { // non-development mode
out.println("");
out.println("Server Error ");
out.println("");
out.println("Server Error ");
out.println("The server is temporarily unavailable due to an");
out.println("internal error. Please notify the system administrator");
out.println("of this problem.
");
out.println("");
out.println("Date: " + QDate.formatISO8601(CurrentTime.getCurrentTime()));
out.println("
");
printVersion(out);
out.println("");
}
String userAgent = request.getHeader("User-Agent");
if (userAgent != null && userAgent.indexOf("MSIE") >= 0) {
out.print(MSIE_PADDING);
}
out.close();
}
private void printVersion(PrintWriter out)
throws IOException
{
ServletService server = _server;
String version = null;
if (server == null) {
}
else if (server.getServerHeader() != null) {
version = server.getServerHeader();
}
else if (CauchoSystem.isTesting()) {
}
else
version = VersionFactory.getFullVersion();
if (version != null) {
out.println("
");
out.println("");
out.println(version);
if (server != null && isErrorPageServerId()) {
out.println("Server: '" + server.getServerId() + "'");
}
out.println(" ");
}
}
private String getServletName(ServletRequest request)
{
if (request instanceof HttpServletRequestImpl)
return ((HttpServletRequestImpl) request).getServletName();
else if (request instanceof ServletRequestWrapper)
return getServletName(((ServletRequestWrapper) request).getRequest());
/*
else if (request instanceof CauchoRequest)
return getServletName(((CauchoRequest) request).getAbstractHttpRequest());
*/
else {
return null;
}
}
/**
* Sends an HTTP error to the browser.
*
* @param code the HTTP error code
* @param message a string message
*/
public void sendError(CauchoRequest request,
CauchoResponse response,
int code, String message)
throws IOException
{
try {
sendErrorImpl(request, response, code, message);
} finally {
response.close();
}
}
/**
* Sends an HTTP error to the browser.
*
* @param code the HTTP error code
* @param message a string message
*/
public void sendErrorImpl(CauchoRequest request,
CauchoResponse response,
int code, String message)
throws IOException
{
response.resetBuffer();
/* XXX: if we've already got an error, won't this just mask it?
if (responseStream.isCommitted())
throw new IllegalStateException("response can't sendError() after commit");
*/
response.setStatus(code, message);
try {
if (handleErrorStatus(request, response, code, message)
|| code == HttpServletResponse.SC_NOT_MODIFIED) {
return;
}
response.setContentType("text/html; charset=utf-8");
boolean isOutputStreamWrapper = false;
PrintWriter out;
try {
out = response.getWriter();
} catch (IllegalStateException e) {
log.log(Level.ALL, e.toString(), e);
out = Vfs.openWrite(response.getOutputStream()).getPrintWriter();
isOutputStreamWrapper = true;
}
out.println("");
if (! response.isCommitted()) {
out.print("");
out.print(code);
out.print(" ");
out.print(escapeHtml(message));
out.println(" ");
}
out.println("");
out.print("");
out.print(code);
out.print(" ");
out.print(escapeHtml(message));
out.println(" ");
if (code == HttpServletResponse.SC_NOT_FOUND) {
out.println(L.l("{0} was not found on this server.",
escapeHtml(request.getPageURI())));
}
printVersion(out);
out.println("");
String userAgent = request.getHeader("User-Agent");
if (userAgent != null && userAgent.indexOf("MSIE") >= 0) {
out.write(MSIE_PADDING, 0, MSIE_PADDING.length);
}
if (isOutputStreamWrapper) {
out.flush();
out.close();
}
} catch (Exception e) {
log.log(Level.WARNING, e.toString(), e);
}
}
/**
* Handles an error status code.
*
* @return true if we've forwarded to an error page.
*/
private boolean handleErrorStatus(CauchoRequest request,
CauchoResponse response,
int code, String message)
throws ServletException, IOException
{
if (code == HttpServletResponse.SC_OK
|| code == HttpServletResponse.SC_MOVED_TEMPORARILY
|| code == HttpServletResponse.SC_NOT_MODIFIED)
return false;
if (request.getRequestDepth(0) > 16)
return false;
else if (request.getAttribute(RequestDispatcher.ERROR_REQUEST_URI) != null) {
return false;
}
response.killCache();
String location = getErrorPage(code);
if (location == null)
location = _defaultLocation;
if (location == null && _parent != null)
return _parent.handleErrorStatus(request, response, code, message);
WebApp webApp = getWebApp();
if (webApp == null && _host == null)
return false;
if (location != null && ! location.equals(request.getRequestURI())) {
request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,
new Integer(code));
request.setAttribute(RequestDispatcher.ERROR_MESSAGE,
message);
request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI,
request.getRequestURI());
String servletName = getServletName(request);
if (servletName != null)
request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME, servletName);
try {
RequestDispatcher disp = null;
// can't use filters because of error pages due to filters
// or security.
if (webApp != null)
disp = webApp.getRequestDispatcher(location);
else if (_host != null)
disp = _host.getWebAppContainer().getRequestDispatcher(location);
//disp.forward(request, this, "GET", false);
if (disp != null) {
((RequestDispatcherImpl) disp).error(request, response);
}
else
return false;
} catch (Throwable e) {
sendServletError(e, request, response);
}
return true;
}
return false;
}
/**
* Returns the URL of an error page for the given exception.
*/
String getErrorPage(Throwable e)
{
return getErrorPage(e, Throwable.class);
}
/**
* Returns the URL of an error page for the given exception.
*/
String getErrorPage(Throwable e, Class> limit)
{
Class> cl = e.getClass();
for (; cl != null; cl = cl.getSuperclass()) {
String location = (String) _errorPageMap.get(cl.getName());
if (location != null)
return location;
if (cl == limit)
break;
}
for (cl = e.getClass(); cl != null; cl = cl.getSuperclass()) {
String name = cl.getName();
int p = name.lastIndexOf('.');
if (p > 0) {
name = name.substring(p + 1);
String location = (String) _errorPageMap.get(name);
if (location != null)
return location;
}
if (cl == limit)
break;
}
return null;
}
private WebApp getWebApp()
{
if (_webApp != null)
return _webApp;
else if (_host != null)
return _host.getWebAppContainer().findWebAppByURI("/");
else
return null;
}
/**
* Returns the URL of an error page for the given exception.
*/
String getErrorPage(int code)
{
Integer key = new Integer(code);
String location = (String) _errorPageMap.get(key);
if (location != null)
return location;
return (String) _errorPageMap.get(new Integer(0));
}
/**
* Escapes HTML symbols in a stack trace.
*/
private void printStackTrace(PrintWriter out,
String lineMessage,
Throwable e,
Throwable rootExn,
LineMap lineMap)
{
CharArrayWriter writer = new CharArrayWriter();
PrintWriter pw = new PrintWriter(writer);
if (lineMessage != null)
pw.println(lineMessage);
if (lineMap != null)
lineMap.printStackTrace(e, pw);
else
ScriptStackTrace.printStackTrace(e, pw);
pw.close();
char []array = writer.toCharArray();
out.print(escapeHtml(new String(array)));
}
/**
* Escapes special symbols in a string. For example '<' becomes '<'
*/
private String escapeHtml(String s)
{
if (s == null)
return null;
if (CurrentTime.isTest()) {
s = normalizeForTesting(s);
}
CharBuffer cb = new CharBuffer();
int lineCharacter = 0;
boolean startsWithSpace = false;
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
lineCharacter++;
if (ch == '<')
cb.append("<");
else if (ch == '&')
cb.append("&");
/*
else if (ch == '%')
cb.append("%25");
*/
else if (ch == '\n' || ch == '\r') {
lineCharacter = 0;
cb.append(ch);
startsWithSpace = false;
}
else if (lineCharacter > 70 && ch == ' ' && ! startsWithSpace) {
lineCharacter = 0;
cb.append('\n');
for (; i + 1 < s.length() && s.charAt(i + 1) == ' '; i++) {
}
}
else if (lineCharacter == 1 && (ch == ' ' || ch == '\t')) {
cb.append((char) ch);
startsWithSpace = true;
}
else
cb.append(ch);
}
return cb.toString();
}
private String normalizeForTesting(String s)
{
String userName = System.getProperty("user.name");
if ("caucho".equals(userName))
return s;
int p;
while ((p = s.indexOf(userName)) >= 0) {
String head = s.substring(0, p);
String tail = s.substring(p + userName.length());
s = head + "caucho" + tail;
}
return s;
}
public String toString()
{
return getClass().getSimpleName() + "[" + _webApp + "]";
}
static {
MSIE_PADDING = ("\n\n\n\n" +
"\n").toCharArray();
}
}