001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/enterprise/servlet/WMSHandler.java $
002    /*----------------------------------------------------------------------------
003     This file is part of deegree, http://deegree.org/
004     Copyright (C) 2001-2009 by:
005     Department of Geography, University of Bonn
006     and
007     lat/lon GmbH
008    
009     This library is free software; you can redistribute it and/or modify it under
010     the terms of the GNU Lesser General Public License as published by the Free
011     Software Foundation; either version 2.1 of the License, or (at your option)
012     any later version.
013     This library is distributed in the hope that it will be useful, but WITHOUT
014     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
015     FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
016     details.
017     You should have received a copy of the GNU Lesser General Public License
018     along with this library; if not, write to the Free Software Foundation, Inc.,
019     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020    
021     Contact information:
022    
023     lat/lon GmbH
024     Aennchenstr. 19, 53177 Bonn
025     Germany
026     http://lat-lon.de/
027    
028     Department of Geography, University of Bonn
029     Prof. Dr. Klaus Greve
030     Postfach 1147, 53001 Bonn
031     Germany
032     http://www.geographie.uni-bonn.de/deegree/
033    
034     e-mail: info@deegree.org
035     ----------------------------------------------------------------------------*/
036    
037    package org.deegree.enterprise.servlet;
038    
039    import static javax.imageio.ImageIO.write;
040    import static org.deegree.framework.util.CharsetUtils.getSystemCharset;
041    
042    import java.awt.Color;
043    import java.awt.Graphics;
044    import java.awt.image.BufferedImage;
045    import java.io.ByteArrayOutputStream;
046    import java.io.IOException;
047    import java.io.OutputStream;
048    import java.io.OutputStreamWriter;
049    import java.io.PrintWriter;
050    import java.io.StringReader;
051    import java.io.Writer;
052    import java.net.MalformedURLException;
053    import java.net.URISyntaxException;
054    import java.net.URL;
055    import java.util.LinkedList;
056    import java.util.List;
057    
058    import javax.imageio.ImageIO;
059    import javax.servlet.http.HttpServletResponse;
060    import javax.xml.transform.Source;
061    import javax.xml.transform.TransformerException;
062    import javax.xml.transform.stream.StreamResult;
063    import javax.xml.transform.stream.StreamSource;
064    
065    import org.deegree.datatypes.QualifiedName;
066    import org.deegree.datatypes.values.TypedLiteral;
067    import org.deegree.enterprise.ServiceException;
068    import org.deegree.enterprise.servlet.GetMapFilter.DummyRequest;
069    import org.deegree.framework.log.ILogger;
070    import org.deegree.framework.log.LoggerFactory;
071    import org.deegree.framework.util.CharsetUtils;
072    import org.deegree.framework.util.ImageUtils;
073    import org.deegree.framework.util.MimeTypeMapper;
074    import org.deegree.framework.util.NetWorker;
075    import org.deegree.framework.util.StringTools;
076    import org.deegree.framework.xml.DOMPrinter;
077    import org.deegree.framework.xml.Marshallable;
078    import org.deegree.framework.xml.XMLFragment;
079    import org.deegree.framework.xml.XSLTDocument;
080    import org.deegree.ogcwebservices.ExceptionReport;
081    import org.deegree.ogcwebservices.OGCWebService;
082    import org.deegree.ogcwebservices.OGCWebServiceException;
083    import org.deegree.ogcwebservices.OGCWebServiceRequest;
084    import org.deegree.ogcwebservices.OGCWebServiceResponse;
085    import org.deegree.ogcwebservices.wms.InvalidFormatException;
086    import org.deegree.ogcwebservices.wms.WMService;
087    import org.deegree.ogcwebservices.wms.WMServiceFactory;
088    import org.deegree.ogcwebservices.wms.capabilities.WMSCapabilities;
089    import org.deegree.ogcwebservices.wms.capabilities.WMSCapabilities_1_3_0;
090    import org.deegree.ogcwebservices.wms.configuration.WMSConfigurationType;
091    import org.deegree.ogcwebservices.wms.configuration.WMSDeegreeParams;
092    import org.deegree.ogcwebservices.wms.operation.DescribeLayerResult;
093    import org.deegree.ogcwebservices.wms.operation.GetFeatureInfo;
094    import org.deegree.ogcwebservices.wms.operation.GetFeatureInfoResult;
095    import org.deegree.ogcwebservices.wms.operation.GetLegendGraphic;
096    import org.deegree.ogcwebservices.wms.operation.GetLegendGraphicResult;
097    import org.deegree.ogcwebservices.wms.operation.GetMap;
098    import org.deegree.ogcwebservices.wms.operation.GetMapResult;
099    import org.deegree.ogcwebservices.wms.operation.GetStylesResult;
100    import org.deegree.ogcwebservices.wms.operation.PutStylesResult;
101    import org.deegree.ogcwebservices.wms.operation.WMSGetCapabilitiesResult;
102    import org.deegree.owscommon.XMLFactory;
103    import org.deegree.owscommon_new.DomainType;
104    import org.deegree.owscommon_new.Operation;
105    import org.deegree.owscommon_new.OperationsMetadata;
106    import org.w3c.dom.Node;
107    import org.xml.sax.SAXException;
108    
109    /**
110     * <code>WMSHandler</code> is the handler class for WMS requests and their results.
111     * 
112     * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
113     * @author last edited by: $Author: aschmitz $
114     * 
115     * @version 2.0, $Revision: 20537 $, $Date: 2009-11-03 17:29:02 +0100 (Di, 03. Nov 2009) $
116     * 
117     * @since 2.0
118     */
119    public class WMSHandler extends AbstractOWServiceHandler {
120    
121        private static ILogger LOG = LoggerFactory.getLogger( WMSHandler.class );
122    
123        private Color bgColor = Color.WHITE;
124    
125        private HttpServletResponse resp = null;
126    
127        private OGCWebServiceRequest request = null;
128    
129        private String exceptionFormat;
130    
131        private String format = null, version = null;
132    
133        private boolean transparent = false;
134    
135        private int height = 400;
136    
137        private int width = 600;
138    
139        private WMSConfigurationType configuration = null;
140    
141        /**
142         *
143         */
144        public WMSHandler() {
145            LOG.logDebug( "New WMSHandler instance created: " + this.getClass().getName() );
146        }
147    
148        private String checkExceptionFormat( String exceptionFormat, List<String> availableExceptionFormats ) {
149            boolean found = false;
150            for ( String f : availableExceptionFormats ) {
151                if ( f.equalsIgnoreCase( exceptionFormat ) ) {
152                    found = true;
153                }
154            }
155            if ( !found ) {
156                return availableExceptionFormats.get( 0 );
157            }
158    
159            return exceptionFormat;
160        }
161    
162        /**
163         * performs the passed OGCWebServiceRequest by accessing service from the pool and passing the request to it
164         */
165        public void perform( OGCWebServiceRequest request, HttpServletResponse response )
166                                throws ServiceException {
167            this.request = request;
168            resp = response;
169            version = request.getVersion();
170    
171            try {
172    
173                OGCWebService service = WMServiceFactory.getService();
174                configuration = (WMSConfigurationType) ( (WMService) service ).getCapabilities();
175    
176                List<String> availableExceptionFormats = new LinkedList<String>();
177    
178                // add 1.1.1 names if 1.3.0 capable
179                for ( String f : configuration.getExceptions() ) {
180                    if ( f.equalsIgnoreCase( "XML" ) ) {
181                        availableExceptionFormats.add( "application/vnd.ogc.se_xml" );
182                    }
183                    if ( f.equalsIgnoreCase( "INIMAGE" ) ) {
184                        availableExceptionFormats.add( "application/vnd.ogc.se_inimage" );
185                    }
186                    if ( f.equalsIgnoreCase( "BLANK" ) ) {
187                        availableExceptionFormats.add( "application/vnd.ogc.se_blank" );
188                    }
189    
190                    availableExceptionFormats.add( f );
191                }
192    
193                // EXCEPTION HANDLING NOTES:
194                // currently, the exceptions are handled differently for each request type,
195                // change the behaviour here
196                if ( request instanceof GetMap ) {
197                    GetMap req = (GetMap) request;
198                    exceptionFormat = req.getExceptions();
199                    exceptionFormat = checkExceptionFormat( exceptionFormat, availableExceptionFormats );
200                    format = req.getFormat();
201                    bgColor = req.getBGColor();
202                    transparent = req.getTransparency();
203                    height = req.getHeight();
204                    width = req.getWidth();
205                }
206    
207                if ( request instanceof GetLegendGraphic ) {
208                    GetLegendGraphic req = (GetLegendGraphic) request;
209                    exceptionFormat = req.getExceptions();
210                    exceptionFormat = checkExceptionFormat( exceptionFormat, availableExceptionFormats );
211                    format = req.getFormat();
212                    height = req.getHeight();
213                    width = req.getWidth();
214                }
215    
216                if ( request instanceof GetFeatureInfo ) {
217                    GetFeatureInfo req = (GetFeatureInfo) request;
218                    exceptionFormat = req.getExceptions();
219                    exceptionFormat = checkExceptionFormat( exceptionFormat, availableExceptionFormats );
220                }
221    
222                fixupExceptionFormat();
223    
224                if ( request instanceof DummyRequest ) {
225                    // the dummy request is used to prevent using the response object so the handler can be
226                    // used externally. One could also rewrite the handler...
227                } else {
228    
229                    Object o = service.doService( request );
230                    if ( request instanceof GetMap ) {
231                        for ( String header : ( (GetMap) request ).warningHeaders ) {
232                            response.setHeader( "Warning", header );
233                        }
234                    }
235                    handleResponse( o );
236                }
237    
238            } catch ( OGCWebServiceException e ) {
239                LOG.logError( e.getMessage(), e );
240                writeServiceExceptionReport( e );
241            }
242        }
243    
244        private void fixupExceptionFormat() {
245            if ( exceptionFormat == null || exceptionFormat.equals( "" ) ) {
246                if ( "1.1.1".equals( version ) ) {
247                    exceptionFormat = "application/vnd.ogc.se_xml";
248                } else {
249                    exceptionFormat = "XML";
250                }
251            }
252    
253            // fixup the exception formats, 1.3.0 has it different
254            // note that XML/....se_xml are not the same format!
255            if ( "INIMAGE".equalsIgnoreCase( exceptionFormat ) ) {
256                exceptionFormat = "application/vnd.ogc.se_inimage";
257            }
258            if ( "BLANK".equalsIgnoreCase( exceptionFormat ) ) {
259                exceptionFormat = "application/vnd.ogc.se_blank";
260            }
261    
262        }
263    
264        /**
265         * 
266         * 
267         * @param result
268         */
269        private void handleResponse( Object result ) {
270            // this method may need restructuring
271    
272            // handle exception case
273            if ( result instanceof OGCWebServiceException ) {
274                writeServiceExceptionReport( (OGCWebServiceException) result );
275                return;
276            }
277    
278            try {
279                OGCWebServiceResponse response = (OGCWebServiceResponse) result;
280    
281                if ( response.getException() != null ) {
282                    // handle the case that an exception occurred during the
283                    // request performance
284                    writeServiceExceptionReport( response.getException() );
285                } else {
286                    if ( response instanceof OGCWebServiceException ) {
287                        writeServiceExceptionReport( (OGCWebServiceException) response );
288                    } else if ( response instanceof Exception ) {
289                        sendException( resp, (Exception) response );
290                    } else if ( response instanceof WMSGetCapabilitiesResult ) {
291                        handleGetCapabilitiesResponse( (WMSGetCapabilitiesResult) response );
292                    } else if ( response instanceof GetMapResult ) {
293                        handleGetMapResponse( (GetMapResult) response );
294                    } else if ( response instanceof GetFeatureInfoResult ) {
295                        handleFeatureInfoResponse( (GetFeatureInfoResult) response );
296                    } else if ( response instanceof GetStylesResult ) {
297                        handleGetStylesResponse( (GetStylesResult) response );
298                    } else if ( response instanceof PutStylesResult ) {
299                        handlePutStylesResponse( (PutStylesResult) response );
300                    } else if ( response instanceof DescribeLayerResult ) {
301                        handleDescribeLayerResponse( (DescribeLayerResult) response );
302                    } else if ( response instanceof GetLegendGraphicResult ) {
303                        handleGetLegendGraphicResponse( (GetLegendGraphicResult) response );
304                    }
305                }
306            } catch ( InvalidFormatException ife ) {
307                LOG.logError( ife.getMessage(), ife );
308                writeServiceExceptionReport( ife );
309            } catch ( Exception e ) {
310                LOG.logError( e.getMessage(), e );
311                writeServiceExceptionReport( new OGCWebServiceException( "WMS:write", e.getLocalizedMessage() ) );
312            }
313        }
314    
315        /**
316         * handles the response to a get capabilities request
317         * 
318         * @param response
319         * @throws IOException
320         * @throws TransformerException
321         */
322        private void handleGetCapabilitiesResponse( WMSGetCapabilitiesResult response )
323                                throws IOException, TransformerException {
324            WMSConfigurationType capa = response.getCapabilities();
325    
326            WMSDeegreeParams params = capa.getDeegreeParams();
327    
328            // version war follows
329    
330            boolean version130 = "1.3.0".equals( capa.calculateVersion( request.getVersion() ) );
331    
332            // version not set -> use highest supported version
333            // use request's version otherwise
334    
335            boolean support111 = false;
336            boolean support130 = false;
337            for ( String version : params.getSupportedVersions() ) {
338                if ( "1.1.1".equals( version ) )
339                    support111 = true;
340                if ( "1.3.0".equals( version ) )
341                    support130 = true;
342            }
343    
344            if ( ( !support130 ) && ( !support111 ) ) {
345                support111 = true;
346            }
347    
348            if ( version130 && support130 ) {
349                resp.setContentType( "text/xml" );
350            } else {
351                resp.setContentType( "application/vnd.ogc.wms_xml" );
352            }
353    
354            resp.setCharacterEncoding( getSystemCharset() );
355    
356            XMLFragment doc = null;
357    
358            if ( ( ( ( !version130 ) && support111 ) || ( !support130 ) ) && ( capa instanceof WMSCapabilities_1_3_0 ) ) {
359                doc = org.deegree.ogcwebservices.wms.XMLFactory.exportAs_1_1_1( (WMSCapabilities_1_3_0) capa );
360            } else {
361                doc = org.deegree.ogcwebservices.wms.XMLFactory.export( (WMSCapabilities) capa );
362            }
363    
364            if ( ( version130 && support130 ) || ( !support111 ) ) {
365                doc.getRootElement().setAttribute( "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance" );
366                doc.getRootElement().setAttribute(
367                                                   "xsi:schemaLocation",
368                                                   "http://www.opengis.net/wms "
369                                                                           + "http://schemas.opengis.net/wms/1.3.0/capabilities_1_3_0.xsd"
370                                                                           + " http://www.opengis.net/sld "
371                                                                           + "http://schemas.opengis.net/sld/1.1.0/sld_capabilities.xsd" );
372    
373                doc.prettyPrint( resp.getWriter() );
374            } else {
375                String xml = new XMLFragment( doc.getRootElement() ).getAsString();
376                xml = doc.getAsString();
377                String dtd = NetWorker.url2String( configuration.getDeegreeParams().getDTDLocation() );
378                StringBuffer sb = new StringBuffer();
379                sb.append( "<!DOCTYPE WMT_MS_Capabilities SYSTEM " );
380                sb.append( "'" + dtd + "' \n" );
381                sb.append( "[\n<!ELEMENT VendorSpecificCapabilities EMPTY>\n]>" );
382    
383                int p = xml.indexOf( "?>" );
384                if ( p > -1 ) {
385                    xml = xml.substring( p + 2, xml.length() );
386                }
387    
388                xml = StringTools.concat( 50000, "<?xml version=\"1.0\" encoding=\"", CharsetUtils.getSystemCharset(),
389                                          "\"?>", "\n", sb.toString(), xml );
390    
391                xml = StringTools.replace( xml, "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"", "", false );
392    
393                try {
394                    PrintWriter pw = resp.getWriter();
395                    pw.print( xml );
396                    pw.close();
397                } catch ( Exception e ) {
398                    LOG.logError( "-", e );
399                }
400    
401            }
402        }
403    
404        /**
405         * handles the response to a get map request
406         * 
407         * @param response
408         * @throws InvalidFormatException
409         */
410        public void handleGetMapResponse( GetMapResult response )
411                                throws InvalidFormatException {
412            // schmitz: added the toLowerCase to avoid errors
413            String mime = MimeTypeMapper.toMimeType( ( (GetMap) request ).getFormat().toLowerCase() );
414    
415            if ( !MimeTypeMapper.isImageType( mime ) ) {
416                throw new InvalidFormatException( mime + " is not a known image format" );
417            }
418    
419            writeImage( response.getMap(), mime );
420        }
421    
422        /**
423         * handles the response to a get featureinfo request
424         * 
425         * @param response
426         */
427        private void handleFeatureInfoResponse( GetFeatureInfoResult response )
428                                throws Exception {
429            GetFeatureInfo req = (GetFeatureInfo) request;
430    
431            String s = req.getInfoFormat();
432    
433            // check if GML is actually the correct one
434            // THIS IS A HACK
435            if ( req.isInfoFormatDefault() ) {
436                OperationsMetadata om = configuration.getOperationMetadata();
437                Operation op = om.getOperation( new QualifiedName( "GetFeatureInfo" ) );
438                DomainType dt = (DomainType) op.getParameter( new QualifiedName( "Format" ) );
439                List<TypedLiteral> vals = dt.getValues();
440                s = vals.get( 0 ).getValue();
441            }
442    
443            String mime = MimeTypeMapper.toMimeType( s );
444            resp.setContentType( mime + "; charset=" + CharsetUtils.getSystemCharset() );
445    
446            String fir = response.getFeatureInfo();
447    
448            String filter = FeatureInfoFilterDef.getString( s );
449    
450            if ( filter != null ) {
451                handleFilteredFeatureInfoResponse( fir, filter );
452            } else {
453                OutputStreamWriter os = null;
454                try {
455                    os = new OutputStreamWriter( resp.getOutputStream(), CharsetUtils.getSystemCharset() );
456                    os.write( fir );
457                } catch ( Exception e ) {
458                    LOG.logError( "could not write to outputstream", e );
459                } finally {
460                    if ( os != null ) {
461                        os.close();
462                    }
463                }
464            }
465        }
466    
467        /**
468         * @param fir
469         * @param filter
470         * @throws MalformedURLException
471         * @throws SAXException
472         * @throws IOException
473         * @throws URISyntaxException
474         * @throws TransformerException
475         */
476        private void handleFilteredFeatureInfoResponse( String fir, String filter )
477                                throws Exception {
478            URL url = new URL( configuration.getBaseURL(), filter );
479            LOG.logDebug( "used XSLT for transformation: ", url );
480            LOG.logDebug( "GML document to transform", fir );
481            Source xmlSource = new StreamSource( new StringReader( fir ) );
482            Source xslSource;
483            try {
484                xslSource = new StreamSource( url.openStream() );
485            } catch ( IOException ioe ) {
486                LOG.logError( "Unknown error", ioe );
487                throw new InvalidFormatException( "Unknown feature info format (missing XSL script in configuration?)." );
488            }
489            OutputStream os = null;
490            try {
491                os = resp.getOutputStream();
492                StreamResult result = new StreamResult( os );
493                XSLTDocument.transform( xmlSource, xslSource, result, null, null );
494            } catch ( IOException e ) {
495                LOG.logError( "could not write to outputstream", e );
496            } finally {
497                if ( os != null ) {
498                    os.close();
499                }
500            }
501        }
502    
503        /**
504         * handles the response to a get styles request
505         * 
506         * @param response
507         */
508        private void handleGetStylesResponse( GetStylesResult response ) {
509            throw new RuntimeException( "method: handleGetStylesResponse not implemented yet" );
510        }
511    
512        /**
513         * handles the response to a put styles request
514         * 
515         * @param response
516         */
517        private void handlePutStylesResponse( PutStylesResult response ) {
518            throw new RuntimeException( "method: handlePutStylesResponse not implemented yet" );
519        }
520    
521        /**
522         * handles the response to a describe layer request
523         * 
524         * @param response
525         * @throws IOException
526         * @throws TransformerException
527         */
528        private void handleDescribeLayerResponse( DescribeLayerResult response )
529                                throws TransformerException, IOException {
530            resp.setCharacterEncoding( "UTF-8" );
531            resp.setContentType( "text/xml" );
532            Writer out = resp.getWriter();
533            response.getResult().prettyPrint( out );
534            out.close();
535        }
536    
537        /**
538         * handles the response to a get legend graphic request
539         * 
540         * @param response
541         */
542        private void handleGetLegendGraphicResponse( GetLegendGraphicResult response )
543                                throws Exception {
544            String mime = MimeTypeMapper.toMimeType( ( (GetLegendGraphic) request ).getFormat() );
545    
546            if ( !MimeTypeMapper.isImageType( mime ) ) {
547                throw new InvalidFormatException( mime + " is not a known image format" );
548            }
549    
550            writeImage( response.getLegendGraphic(), mime );
551        }
552    
553        private void writeServiceExceptionReport( OGCWebServiceException exception, OutputStream out ) {
554            LOG.logInfo( "Sending exception in XML format." );
555            if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
556                ExceptionReport report = new ExceptionReport( new OGCWebServiceException[] { exception } );
557                String msg;
558                if ( exceptionFormat.equals( "XML" ) ) {
559                    msg = XMLFactory.exportNS( report ).getAsPrettyString();
560                } else {
561                    msg = XMLFactory.export( report ).getAsPrettyString();
562                }
563    
564                LOG.logDebug( "Exception being sent: " + msg );
565            }
566    
567            ExceptionReport report = new ExceptionReport( new OGCWebServiceException[] { exception } );
568            try {
569                XMLFragment doc;
570    
571                if ( exceptionFormat.equals( "XML" ) ) {
572                    resp.setContentType( "text/xml" );
573                    doc = XMLFactory.exportNS( report );
574                } else {
575                    resp.setContentType( "application/vnd.ogc.se_xml" );
576                    doc = XMLFactory.export( report );
577                }
578    
579                doc.write( out );
580                out.close();
581            } catch ( Exception ex ) {
582                LOG.logError( "ERROR: " + ex.getMessage(), ex );
583            }
584        }
585    
586        /**
587         * @param eFormat
588         * @param format
589         * @param version
590         * @param response
591         */
592        public void determineExceptionFormat( String eFormat, String format, String version, HttpServletResponse response ) {
593            exceptionFormat = eFormat;
594            this.format = format;
595            this.version = version;
596            resp = response;
597    
598            fixupExceptionFormat();
599        }
600    
601        /**
602         * writes an service exception report into the <tt>OutputStream</tt> back to the client. the method considers the
603         * format an exception shall be returned to the client as defined in the request.
604         * 
605         * @param exception
606         *            the exception object containing the code and message
607         */
608        public void writeServiceExceptionReport( OGCWebServiceException exception ) {
609            String code = "none";
610            if ( exception.getCode() != null ) {
611                code = exception.getCode().value;
612            }
613            String message = exception.getMessage();
614    
615            LOG.logInfo( "sending exception in format " + exceptionFormat );
616            if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
617                ExceptionReport report = new ExceptionReport( new OGCWebServiceException[] { exception } );
618                String msg;
619                if ( exceptionFormat.equals( "XML" ) ) {
620                    msg = XMLFactory.exportNS( report ).getAsPrettyString();
621                } else {
622                    msg = XMLFactory.export( report ).getAsPrettyString();
623                }
624    
625                LOG.logDebug( "Exception being sent: " + msg );
626            }
627    
628            if ( exceptionFormat.equals( "application/vnd.ogc.se_inimage" ) ) {
629                BufferedImage bi = new BufferedImage( width, height, BufferedImage.TYPE_INT_ARGB );
630                Graphics g = bi.getGraphics();
631    
632                if ( !transparent ) {
633                    g.setColor( bgColor );
634                    g.fillRect( 0, 0, bi.getWidth(), bi.getHeight() );
635                }
636    
637                g.setColor( Color.BLUE );
638                g.drawString( code, 5, 20 );
639                int pos1 = message.indexOf( ':' );
640                g.drawString( message.substring( 0, pos1 + 1 ), 5, 50 );
641                g.drawString( message.substring( pos1 + 1, message.length() ), 5, 80 );
642                String mime = MimeTypeMapper.toMimeType( format );
643                writeImage( bi, mime );
644            } else if ( exceptionFormat.equals( "application/vnd.ogc.se_blank" ) ) {
645                BufferedImage bi = new BufferedImage( width, height, BufferedImage.TYPE_INT_ARGB );
646                Graphics g = bi.getGraphics();
647    
648                if ( !transparent ) {
649                    g.setColor( bgColor );
650                    g.fillRect( 0, 0, bi.getWidth(), bi.getHeight() );
651                }
652    
653                g.dispose();
654                String mime = MimeTypeMapper.toMimeType( format );
655                writeImage( bi, mime );
656            } else {
657                LOG.logInfo( "Sending OGCWebServiceException to client." );
658                ExceptionReport report = new ExceptionReport( new OGCWebServiceException[] { exception } );
659                try {
660                    XMLFragment doc;
661    
662                    if ( exceptionFormat.equals( "XML" ) ) {
663                        resp.setContentType( "text/xml" );
664                        doc = XMLFactory.exportNS( report );
665                    } else {
666                        resp.setContentType( "application/vnd.ogc.se_xml" );
667                        doc = XMLFactory.export( report );
668                    }
669    
670                    OutputStream os = resp.getOutputStream();
671                    doc.write( os );
672                    os.close();
673                } catch ( Exception ex ) {
674                    LOG.logError( "ERROR: " + ex.getMessage(), ex );
675                }
676            }
677        }
678    
679        /**
680         * writes the passed image to the response output stream.
681         * 
682         * @param output
683         * @param mime
684         */
685        private void writeImage( Object output, String mime ) {
686            OutputStream os = null;
687            try {
688                resp.setContentType( mime );
689                if ( mime.equalsIgnoreCase( "image/gif" ) ) {
690                    BufferedImage img = (BufferedImage) output;
691                    ByteArrayOutputStream out = new ByteArrayOutputStream( img.getWidth() * img.getHeight() * 4 );
692                    try {
693                        ImageUtils.encodeGif( out, img );
694                    } catch ( IOException e ) {
695                        LOG.logWarning( "ACME failed to write GIF image, trying ImageIO." );
696                        LOG.logDebug( "Stack trace", e );
697                        // use imageio, it can transform the colors correctly starting from Java 1.6
698                        if ( !write( img, "gif", out ) ) {
699                            os = resp.getOutputStream();
700                            writeServiceExceptionReport( new OGCWebServiceException( e.getLocalizedMessage() ), os );
701                            os.close();
702                            return;
703                        }
704                    }
705    
706                    resp.setContentType( mime );
707                    os = resp.getOutputStream();
708    
709                    out.close();
710                    byte[] bs = out.toByteArray();
711                    out = null;
712    
713                    os.write( bs );
714                } else if ( mime.equalsIgnoreCase( "image/jpg" ) || mime.equalsIgnoreCase( "image/jpeg" ) ) {
715                    os = resp.getOutputStream();
716                    ImageUtils.saveImage( (BufferedImage) output, os, "jpeg",
717                                          configuration.getDeegreeParams().getMapQuality() );
718                } else if ( mime.equalsIgnoreCase( "image/png" ) || mime.equalsIgnoreCase( "image/png; mode=24bit" ) ) {
719                    os = resp.getOutputStream();
720                    ImageUtils.saveImage( (BufferedImage) output, os, "png", 1 );
721                } else if ( mime.equalsIgnoreCase( "image/png; mode=8bit" ) ) {
722                    os = resp.getOutputStream();
723                    ImageIO.write( (BufferedImage) output, "png", os );
724                    // ImageUtils.saveImage( (BufferedImage) output, os, "png", 1 ); // ImageUtils produces double size
725                    // images...
726                } else if ( mime.equalsIgnoreCase( "image/tif" ) || mime.equalsIgnoreCase( "image/tiff" ) ) {
727                    os = resp.getOutputStream();
728                    ImageUtils.saveImage( (BufferedImage) output, os, "tif", 1 );
729                } else if ( mime.equalsIgnoreCase( "image/bmp" ) ) {
730                    os = resp.getOutputStream();
731                    ImageUtils.saveImage( (BufferedImage) output, os, "bmp", 1 );
732                } else if ( mime.equalsIgnoreCase( "image/svg+xml" ) ) {
733                    os = resp.getOutputStream();
734                    resp.setContentType( "text/xml; charset=" + CharsetUtils.getSystemCharset() );
735                    PrintWriter pw = new PrintWriter( os );
736                    DOMPrinter.printNode( pw, (Node) output );
737                    pw.close();
738                } else {
739                    resp.setContentType( "text/xml; charset=" + CharsetUtils.getSystemCharset() );
740                    os = resp.getOutputStream();
741                    OGCWebServiceException exce = new OGCWebServiceException( "WMS:writeImage",
742                                                                              "unsupported image format: " + mime );
743                    os.write( ( (Marshallable) exce ).exportAsXML().getBytes() );
744                }
745    
746                os.close();
747            } catch ( Exception e ) {
748                LOG.logError( resp.isCommitted() ? "Response is already committed!" : "Response is not committed yet." );
749                LOG.logError( "Error while writing image: ", e );
750                writeServiceExceptionReport( new OGCWebServiceException( e.getLocalizedMessage() ), os );
751            }
752        }
753    
754        /**
755         * It's a workaround to make the 'API' more usable.
756         * 
757         * @param request
758         */
759        public void setRequest( OGCWebServiceRequest request ) {
760            this.request = request;
761        }
762    
763    }