001: /* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
002: * This code is licensed under the GPL 2.0 license, availible at the root
003: * application directory.
004: */
005: package org.vfny.geoserver;
006:
007: import org.vfny.geoserver.global.GeoServer;
008: import org.vfny.geoserver.util.Requests;
009: import org.vfny.geoserver.util.ResponseUtils;
010: import java.io.ByteArrayOutputStream;
011: import java.io.PrintWriter;
012: import java.util.logging.Logger;
013: import javax.servlet.http.HttpServletRequest;
014:
015: /**
016: * Represents a standard OGC service exception. Able to turn itself into the
017: * proper xml response.
018: *
019: * <p>
020: * JG - here is my guess on what the parameters do:
021: * </p>
022: * <pre><code>
023: * [?xml version="1.0" ?
024: * [ServiceExceptionReport
025: * version="1.2.0"
026: * xmlns="http://www.opengis.net/ogc"
027: * xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
028: * xsi:schemaLocation="http://www.opengis.net/ogc <i>SchemaBaseUrl</i> wfs/1.0.0/OGC-exception.xsd"]
029: * [ServiceException code="<i>code</i>"
030: * locator="<i>locator</i>"]
031: * </i>preMessage<i>:<i>getMessage()</i>
032: * <i>stack trace</i>
033: * [/ServiceException]
034: * [/ServiceExceptionReport]
035: * </code></pre>
036: *
037: * <p>
038: * Where:
039: * </p>
040: *
041: * <ul>
042: * <li>
043: * code: is a diagnostic code
044: * </li>
045: * <li>
046: * locator: is the java class that caused the problem
047: * </li>
048: * <li>
049: * preMessage: is your chance to place things in user terms
050: * </li>
051: * <li>
052: * message: is the exception message
053: * </li>
054: * <li>
055: * stack trace: is the exception strack trace
056: * </li>
057: * </ul>
058: *
059: * <p>
060: * Java Exception have recently developed the ability to contain other
061: * exceptions. By calling initCause on your ServiceConfig Exception you can
062: * get the real exception included in the stacktrace above.
063: * </p>
064: *
065: * @author Gabriel Rold?n
066: * @author Chris Holmes
067: *
068: * @task REVISIT: Take a request in the constructor? This would make it so we
069: * do not have to rely on schemas.opengis.net being available, as it
070: * will just reference the geoserver instance that created it. But to
071: * do this we need the request, as that's how we figure out the baseUrl.
072: * Would probably not be that hard to get the request included, and
073: * would lead to better error reporting...
074: */
075: public class ServiceException extends
076: org.geoserver.platform.ServiceException {
077: /** Class logger */
078: private static Logger LOGGER = org.geotools.util.logging.Logging
079: .getLogger("org.vfny.geoserver.responses");
080:
081: /** message inserted by GeoServer as to what it thinks happened */
082: protected String preMessage = new String();
083:
084: /** full classpath of originating GeoServer class */
085: protected String locator = new String();
086:
087: /**
088: * Empty constructor.
089: */
090: public ServiceException() {
091: super ((String) null);
092: }
093:
094: /**
095: * Empty constructor.
096: *
097: * @param message The message for the .
098: */
099: public ServiceException(String message) {
100: super (message, (String) null);
101: }
102:
103: /**
104: * This should be the most used entry point.
105: *
106: * @param message User message
107: * @param cause The origional exception that caused failure
108: */
109: public ServiceException(String message, Throwable cause) {
110: super (message, cause, null);
111: }
112:
113: /**
114: * Empty constructor.
115: *
116: * @param e The message for the .
117: */
118: public ServiceException(Throwable e) {
119: super (e, null);
120: }
121:
122: /**
123: * Empty constructor.
124: *
125: * @param message The message for the .
126: * @param locator The message for the .
127: */
128: public ServiceException(String message, String locator) {
129: super (message);
130:
131: this .locator = locator;
132: }
133:
134: /**
135: * DOCUMENT ME!
136: *
137: * @param e The message for the .
138: * @param preMessage The message to tack on the front.
139: * @param locator The message for the .
140: */
141: public ServiceException(Throwable e, String preMessage,
142: String locator) {
143: this (e);
144:
145: this .preMessage = preMessage;
146:
147: this .locator = locator;
148: }
149:
150: public ServiceException(ServiceException e) {
151: super (e.getMessage(), e.getCause(), e.getLocator());
152: this .preMessage = e.preMessage;
153: }
154:
155: /**
156: * DOCUMENT ME!
157: *
158: * @param testString DOCUMENT ME!
159: *
160: * @return DOCUMENT ME!
161: */
162: protected boolean isEmpty(String testString) {
163: return (testString == null) || testString.equals("");
164: }
165:
166: public String getLocator() {
167: return locator;
168: }
169:
170: /**
171: * DOCUMENT ME!
172: *
173: * @param printStackTrace DOCUMENT ME!
174: *
175: * @return DOCUMENT ME!
176: */
177:
178: //public String getXmlResponse() {
179: // return getXmlResponse(true);
180: //}
181: /**
182: * gets the message, encoding it with the proper escaped xml characters. If
183: * requested it prints the whole stack trace in the response.
184: *
185: * @param printStackTrace set to <tt>true</tt> if the full stack trace
186: * should be returned to client apps.
187: *
188: * @return The message of this error, with xml escapes.
189: *
190: * @task REVISIT: The stack trace printing is not that efficient, but it
191: * should be relatively small. Once we convert errors to print
192: * directly to the servlet output stream we can make it faster.
193: */
194: public String getXmlMessage(boolean printStackTrace) {
195: String indent = " ";
196: StringBuffer mesg = new StringBuffer();
197:
198: //this distinction no longer so much applies, as we don't always
199: //throw Service exceptions for all expected exceptions.
200: //if (!isEmpty(this.preMessage)) {
201: // mesg.append(this.preMessage + ": ");
202: //}
203: //mesg.append(ResponseUtils.encodeXML(this.getMessage()) + "\n");
204: // if (printStackTrace) {
205: if (printStackTrace) {
206: mesg.append(createStackTrace());
207: } else {
208: mesg.append(this .getMessage());
209: }
210:
211: return ResponseUtils.encodeXML(mesg.toString());
212: }
213:
214: private String createStackTrace() {
215: ByteArrayOutputStream baos = new ByteArrayOutputStream();
216: PrintWriter writer = new PrintWriter(baos);
217: Throwable cause = getCause();
218:
219: if (cause == null) {
220: this .printStackTrace(writer);
221: } else {
222: cause.printStackTrace(writer);
223: }
224:
225: writer.flush();
226:
227: return baos.toString();
228: }
229:
230: /**
231: * Return request type.
232: *
233: * @param printStackTrace whether the stack trace should be included.
234: * @param request DOCUMENT ME!
235: *
236: * @return The ServiceExceptionReport of this error.
237: *
238: * @task REVISIT: Our error handling should actually have knowledge of the
239: * app configuration, so that we can set the ogc error report to
240: * validate right (reference our own schema), and to put the correct
241: * mime type here.
242: */
243: public String getXmlResponse(boolean printStackTrace,
244: HttpServletRequest request, GeoServer geoserver) {
245: //Perhaps not the best place to do this, but it's by far the best place to ensure
246: //that all logged errors get recorded in the same way, as there all must return
247: //xml responses.
248: LOGGER.warning("encountered error: " + getMessage()
249: + "\nStackTrace: " + createStackTrace());
250:
251: String indent = " ";
252:
253: StringBuffer returnXml = new StringBuffer(
254: "<?xml version=\"1.0\" ?>\n");
255:
256: returnXml.append("<ServiceExceptionReport\n");
257:
258: returnXml.append(indent + "version=\"1.2.0\"\n");
259:
260: returnXml.append(indent
261: + "xmlns=\"http://www.opengis.net/ogc\"\n");
262:
263: returnXml.append(indent + "xmlns:xsi=\"http://www.w3.org/2001/"
264: + "XMLSchema-instance\"\n");
265:
266: returnXml.append(indent);
267:
268: returnXml
269: .append("xsi:schemaLocation=\"http://www.opengis.net/ogc ");
270:
271: returnXml.append(Requests.getSchemaBaseUrl(request, geoserver)
272: + "/wfs/1.0.0/OGC-exception.xsd\">\n");
273:
274: //REVISIT: handle multiple service exceptions? must refactor class.
275: returnXml.append(indent + "<ServiceException");
276:
277: if (!isEmpty(getCode())) {
278: returnXml.append(" code=\"" + getCode() + "\"");
279: }
280:
281: if (!isEmpty(this .locator)) {
282: returnXml.append(" locator=\"" + this .locator + "\"");
283: }
284:
285: returnXml.append(">\n" + indent + indent);
286: returnXml.append(getXmlMessage(printStackTrace));
287:
288: returnXml.append(indent + "</ServiceException>\n");
289:
290: returnXml.append("</ServiceExceptionReport>");
291:
292: LOGGER.fine("return wfs exception is " + returnXml);
293:
294: return returnXml.toString();
295: }
296:
297: /**
298: * Returns the mime type that should be exposed to the client
299: * when sending the exception message.
300: *
301: * <p>
302: * Defaults to <code>geoserver.getMimeType()</code>
303: * </p>
304: *
305: * @return
306: */
307: public String getMimeType(GeoServer geoserver) {
308: return geoserver.getMimeType();
309: }
310: }
|