/* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
* This code is licensed under the GPL 2.0 license, availible at the root
* application directory.
*/
package org.geoserver.wms;
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextLayout;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.text.AttributedString;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import org.geoserver.config.GeoServer;
import org.geoserver.ows.LegacyServiceExceptionHandler;
import org.geoserver.ows.Request;
import org.geoserver.ows.util.OwsUtils;
import org.geoserver.platform.Service;
import org.geoserver.platform.ServiceException;
public class WmsExceptionHandler extends LegacyServiceExceptionHandler {
static final Set<String> FORMATS = new HashSet<String>(Arrays.asList("image/png", "image/png8",
"image/gif", "image/jpeg"));
static final Map<String, String> IMAGEIO_FORMATS = new HashMap<String, String>() {
{
put("image/png", "png");
put("image/png8", "png");
put("image/gif", "gif");
put("image/jpeg", "jpeg");
}
};
public WmsExceptionHandler(Service service, GeoServer geoServer) {
super(service, geoServer);
}
@Override
public void handleServiceException(ServiceException exception, Request request) {
// first of all check what kind of exception handling we must perform
String exceptions;
int width;
int height;
String format;
try {
exceptions = (String) request.getKvp().get("EXCEPTIONS");
width = (Integer) request.getKvp().get("WIDTH");
height = (Integer) request.getKvp().get("HEIGHT");
format = (String) request.getKvp().get("FORMAT");
} catch (Exception e) {
// width and height might be missing
super.handleServiceException(exception, request);
return;
}
if (exceptions == null || !"application/vnd.ogc.se_inimage".equals(exceptions)
|| width <= 0 || height <= 0 || !FORMATS.contains(format)) {
super.handleServiceException(exception, request);
return;
}
// ok, it's image, then we have to build a text representing the
// exception and lay it out in the image
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D g = (Graphics2D) img.getGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, img.getWidth(), img.getHeight());
g.setColor(Color.BLACK);
// draw the exception text (give it a good offset so that it can be read
// properly in the OL preview as well)
paintLines(g, buildExceptionText(exception), width - 2, 35, 5);
// encode
g.dispose();
try {
final HttpServletResponse response = request.getHttpResponse();
response.setContentType(format);
final ServletOutputStream os = response.getOutputStream();
ImageIO.write(img, IMAGEIO_FORMATS.get(format), os);
os.flush();
} catch (IOException e) {
LOGGER.log(Level.INFO, "Problem writing exception information back to calling client:",
e);
}
}
String buildExceptionText(ServiceException exception) {
StringBuffer sb = new StringBuffer();
// exception code and locator
if ((exception.getCode() != null) && !exception.getCode().equals("")) {
sb.append("code=\"" + exception.getCode() + "\"");
}
// exception locator
if ((exception.getLocator() != null) && !exception.getLocator().equals("")) {
sb.append(" locator=\"" + exception.getLocator() + "\"");
}
// message
if ((exception.getMessage() != null)) {
OwsUtils.dumpExceptionMessages(exception, sb, false);
if (geoServer.getGlobal().isVerboseExceptions()) {
ByteArrayOutputStream stackTrace = new ByteArrayOutputStream();
exception.printStackTrace(new PrintStream(stackTrace));
sb.append("\nDetails:\n");
sb.append(new String(stackTrace.toByteArray()));
}
}
return sb.toString();
}
/**
* Paint the provided text onto the graphics wrapping words at the specified
* lineWidth
*
* @param lineWidth
* @param g
* @param text
*/
void paintLines(Graphics2D g, String text, int lineWidth, int startX, int startY) {
// split the text into lines, LineBreakMeasurer only lays out the single
// line
String[] lines = text.split("\\n");
// setup the cursor
Point cursor = new Point(startX, startY);
// grab the line height to skip empty lines
final FontMetrics metrics = g.getFontMetrics();
int lineHeight = metrics.getAscent() + metrics.getDescent() + metrics.getLeading();
FontRenderContext frc = g.getFontRenderContext();
// scan over the
for (int i = 0; i < lines.length; i++) {
final String line = lines[i];
if ("".equals(line)) {
cursor.y += lineHeight;
} else {
AttributedString styledText = new AttributedString(line);
LineBreakMeasurer measurer = new LineBreakMeasurer(styledText.getIterator(), frc);
while (measurer.getPosition() < line.length()) {
TextLayout layout = measurer.nextLayout(lineWidth);
cursor.y += (layout.getAscent());
float dx = layout.isLeftToRight() ? 0 : (lineWidth - layout.getAdvance());
layout.draw(g, cursor.x + dx, cursor.y);
cursor.y += layout.getDescent() + layout.getLeading();
}
}
}
}
}