/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
* (c) 2001 - 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.vfny.geoserver;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.util.logging.Logger;
import javax.servlet.http.HttpServletRequest;
import org.geoserver.config.GeoServer;
import org.vfny.geoserver.util.Requests;
import org.vfny.geoserver.util.ResponseUtils;
/**
* Represents a standard OGC service exception. Able to turn itself into the
* proper xml response.
*
* <p>
* JG - here is my guess on what the parameters do:
* </p>
* <pre><code>
* [?xml version="1.0" ?
* [ServiceExceptionReport
* version="1.2.0"
* xmlns="http://www.opengis.net/ogc"
* xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
* xsi:schemaLocation="http://www.opengis.net/ogc <i>SchemaBaseUrl</i> wfs/1.0.0/OGC-exception.xsd"]
* [ServiceException code="<i>code</i>"
* locator="<i>locator</i>"]
* </i>preMessage<i>:<i>getMessage()</i>
* <i>stack trace</i>
* [/ServiceException]
* [/ServiceExceptionReport]
* </code></pre>
*
* <p>
* Where:
* </p>
*
* <ul>
* <li>
* code: is a diagnostic code
* </li>
* <li>
* locator: is the java class that caused the problem
* </li>
* <li>
* preMessage: is your chance to place things in user terms
* </li>
* <li>
* message: is the exception message
* </li>
* <li>
* stack trace: is the exception strack trace
* </li>
* </ul>
*
* <p>
* Java Exception have recently developed the ability to contain other
* exceptions. By calling initCause on your ServiceConfig Exception you can
* get the real exception included in the stacktrace above.
* </p>
*
* @author Gabriel Rold?n
* @author Chris Holmes
*
* @task REVISIT: Take a request in the constructor? This would make it so we
* do not have to rely on schemas.opengis.net being available, as it
* will just reference the geoserver instance that created it. But to
* do this we need the request, as that's how we figure out the baseUrl.
* Would probably not be that hard to get the request included, and
* would lead to better error reporting...
*
* @deprecated use {@link org.geoserver.platform.ServiceException}
*/
public class ServiceException extends org.geoserver.platform.ServiceException {
/** Class logger */
private static Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.vfny.geoserver.responses");
/** message inserted by GeoServer as to what it thinks happened */
protected String preMessage = new String();
/** full classpath of originating GeoServer class */
protected String locator = new String();
/**
* Empty constructor.
*/
public ServiceException() {
super((String) null);
}
/**
* Empty constructor.
*
* @param message The message for the .
*/
public ServiceException(String message) {
super(message, (String) null);
}
/**
* This should be the most used entry point.
*
* @param message User message
* @param cause The origional exception that caused failure
*/
public ServiceException(String message, Throwable cause) {
super(message, cause, null);
}
/**
* Empty constructor.
*
* @param e The message for the .
*/
public ServiceException(Throwable e) {
super(e, null);
}
/**
* Empty constructor.
*
* @param message The message for the .
* @param locator The message for the .
*/
public ServiceException(String message, String locator) {
super(message);
this.locator = locator;
}
public ServiceException(String message, String code, String locator) {
super(message, code, locator);
this.locator = locator;
}
/**
* DOCUMENT ME!
*
* @param e The message for the .
* @param preMessage The message to tack on the front.
* @param locator The message for the .
*/
public ServiceException(Throwable e, String preMessage, String locator) {
this(e);
this.preMessage = preMessage;
this.locator = locator;
}
public ServiceException(ServiceException e) {
super(e.getMessage(), e.getCause(), e.getLocator());
this.preMessage = e.preMessage;
}
/**
* DOCUMENT ME!
*
* @param testString DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
protected boolean isEmpty(String testString) {
return (testString == null) || testString.equals("");
}
public String getLocator() {
return locator;
}
/**
* DOCUMENT ME!
*
* @param printStackTrace DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
//public String getXmlResponse() {
// return getXmlResponse(true);
//}
/**
* gets the message, encoding it with the proper escaped xml characters. If
* requested it prints the whole stack trace in the response.
*
* @param printStackTrace set to <tt>true</tt> if the full stack trace
* should be returned to client apps.
*
* @return The message of this error, with xml escapes.
*
* @task REVISIT: The stack trace printing is not that efficient, but it
* should be relatively small. Once we convert errors to print
* directly to the servlet output stream we can make it faster.
*/
public String getXmlMessage(boolean printStackTrace) {
String indent = " ";
StringBuffer mesg = new StringBuffer();
//this distinction no longer so much applies, as we don't always
//throw Service exceptions for all expected exceptions.
//if (!isEmpty(this.preMessage)) {
// mesg.append(this.preMessage + ": ");
//}
//mesg.append(ResponseUtils.encodeXML(this.getMessage()) + "\n");
// if (printStackTrace) {
if (printStackTrace) {
mesg.append(createStackTrace());
} else {
mesg.append(this.getMessage());
}
return ResponseUtils.encodeXML(mesg.toString());
}
private String createStackTrace() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintWriter writer = new PrintWriter(baos);
Throwable cause = getCause();
if (cause == null) {
this.printStackTrace(writer);
} else {
cause.printStackTrace(writer);
}
writer.flush();
return baos.toString();
}
/**
* Return request type.
*
* @param printStackTrace whether the stack trace should be included.
* @param request DOCUMENT ME!
*
* @return The ServiceExceptionReport of this error.
*
* @task REVISIT: Our error handling should actually have knowledge of the
* app configuration, so that we can set the ogc error report to
* validate right (reference our own schema), and to put the correct
* mime type here.
*/
public String getXmlResponse(boolean printStackTrace, HttpServletRequest request,
GeoServer geoserver) {
//Perhaps not the best place to do this, but it's by far the best place to ensure
//that all logged errors get recorded in the same way, as there all must return
//xml responses.
LOGGER.warning("encountered error: " + getMessage() + "\nStackTrace: " + createStackTrace());
String indent = " ";
StringBuffer returnXml = new StringBuffer("<?xml version=\"1.0\" ?>\n");
returnXml.append("<ServiceExceptionReport\n");
returnXml.append(indent + "version=\"1.2.0\"\n");
returnXml.append(indent + "xmlns=\"http://www.opengis.net/ogc\"\n");
returnXml.append(indent + "xmlns:xsi=\"http://www.w3.org/2001/" + "XMLSchema-instance\"\n");
returnXml.append(indent);
returnXml.append("xsi:schemaLocation=\"http://www.opengis.net/ogc ");
returnXml.append(Requests.getSchemaBaseUrl(request, geoserver)
+ "/wfs/1.0.0/OGC-exception.xsd\">\n");
//REVISIT: handle multiple service exceptions? must refactor class.
returnXml.append(indent + "<ServiceException");
if (!isEmpty(getCode())) {
returnXml.append(" code=\"" + getCode() + "\"");
}
if (!isEmpty(this.locator)) {
returnXml.append(" locator=\"" + this.locator + "\"");
}
returnXml.append(">\n" + indent + indent);
returnXml.append(getXmlMessage(printStackTrace));
returnXml.append(indent + "</ServiceException>\n");
returnXml.append("</ServiceExceptionReport>");
LOGGER.fine("return wfs exception is " + returnXml);
return returnXml.toString();
}
/**
* Returns the mime type that should be exposed to the client
* when sending the exception message.
*
* <p>
* Defaults to <code>geoserver.getMimeType()</code>
* </p>
*
*
*/
public String getMimeType(GeoServer geoserver) {
return "text/xml; charset=" + geoserver.getSettings().getCharset();
}
}