001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/ogcwebservices/wms/RemoteWMService.java $
002    /*----------------    FILE HEADER  ------------------------------------------
003    
004     This file is part of deegree.
005     Copyright (C) 2001-2008 by:
006     EXSE, Department of Geography, University of Bonn
007     http://www.giub.uni-bonn.de/deegree/
008     lat/lon GmbH
009     http://www.lat-lon.de
010    
011     This library is free software; you can redistribute it and/or
012     modify it under the terms of the GNU Lesser General Public
013     License as published by the Free Software Foundation; either
014     version 2.1 of the License, or (at your option) any later version.
015    
016     This library is distributed in the hope that it will be useful,
017     but WITHOUT ANY WARRANTY; without even the implied warranty of
018     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
019     Lesser General Public License for more details.
020    
021     You should have received a copy of the GNU Lesser General Public
022     License along with this library; if not, write to the Free Software
023     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
024    
025     Contact:
026    
027     Andreas Poth
028     lat/lon GmbH
029     Aennchenstr. 19
030     53115 Bonn
031     Germany
032     E-Mail: poth@lat-lon.de
033    
034     Prof. Dr. Klaus Greve
035     Department of Geography
036     University of Bonn
037     Meckenheimer Allee 166
038     53115 Bonn
039     Germany
040     E-Mail: greve@giub.uni-bonn.de
041    
042     
043     ---------------------------------------------------------------------------*/
044    package org.deegree.ogcwebservices.wms;
045    
046    import static org.deegree.enterprise.WebUtils.enableProxyUsage;
047    import static org.deegree.ogcwebservices.OWSUtils.validateHTTPGetBaseURL;
048    
049    import java.io.IOException;
050    import java.io.InputStream;
051    import java.io.StringReader;
052    import java.net.URL;
053    import java.util.HashMap;
054    import java.util.List;
055    import java.util.Properties;
056    
057    import javax.media.jai.JAI;
058    import javax.media.jai.RenderedOp;
059    
060    import org.apache.commons.httpclient.Header;
061    import org.apache.commons.httpclient.HttpClient;
062    import org.apache.commons.httpclient.methods.GetMethod;
063    import org.deegree.datatypes.QualifiedName;
064    import org.deegree.framework.log.ILogger;
065    import org.deegree.framework.log.LoggerFactory;
066    import org.deegree.framework.util.BootLogger;
067    import org.deegree.framework.util.CharsetUtils;
068    import org.deegree.framework.util.MimeTypeMapper;
069    import org.deegree.framework.util.NetWorker;
070    import org.deegree.framework.util.StringTools;
071    import org.deegree.framework.xml.XMLFragment;
072    import org.deegree.i18n.Messages;
073    import org.deegree.ogcwebservices.OGCWebService;
074    import org.deegree.ogcwebservices.OGCWebServiceException;
075    import org.deegree.ogcwebservices.OGCWebServiceRequest;
076    import org.deegree.ogcwebservices.getcapabilities.OGCCapabilities;
077    import org.deegree.ogcwebservices.wms.capabilities.WMSCapabilities;
078    import org.deegree.ogcwebservices.wms.capabilities.WMSCapabilitiesDocument;
079    import org.deegree.ogcwebservices.wms.capabilities.WMSCapabilitiesDocumentFactory;
080    import org.deegree.ogcwebservices.wms.operation.DescribeLayer;
081    import org.deegree.ogcwebservices.wms.operation.GetFeatureInfo;
082    import org.deegree.ogcwebservices.wms.operation.GetLegendGraphic;
083    import org.deegree.ogcwebservices.wms.operation.GetMap;
084    import org.deegree.ogcwebservices.wms.operation.GetStyles;
085    import org.deegree.ogcwebservices.wms.operation.PutStyles;
086    import org.deegree.ogcwebservices.wms.operation.WMSGetCapabilities;
087    import org.deegree.ogcwebservices.wms.operation.WMSProtocolFactory;
088    import org.deegree.owscommon_new.DCP;
089    import org.deegree.owscommon_new.HTTP;
090    import org.deegree.owscommon_new.Operation;
091    import org.deegree.owscommon_new.OperationsMetadata;
092    
093    import com.sun.media.jai.codec.MemoryCacheSeekableStream;
094    
095    /**
096     * An instance of the class acts as a wrapper to a remote WMS.
097     * 
098     * @version $Revision: 11920 $
099     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
100     */
101    public class RemoteWMService implements OGCWebService {
102    
103        private static ILogger LOG = LoggerFactory.getLogger( RemoteWMService.class );
104    
105        private static final String GETCAPABILITIES_NAME = "GetCapabilities";
106    
107        private static final String CAPABILITIES_NAME = "Capabilities";
108    
109        private static final String GETMAP_NAME = "GetMap";
110    
111        private static final String MAP_NAME = "Map";
112    
113        private static final String GETFEATUREINFO_NAME = "GetFeatureInfo";
114    
115        private static final String FEATUREINFO_NAME = "FeatureInfo";
116    
117        private static final String DESCRIBELAYER_NAME = "DescribeLayer";
118    
119        private static final String GETLEGENDGRAPHIC_NAME = "GetLegendGraphic";
120    
121        private static final String GETSTYLES_NAME = "GetStyles";
122    
123        private static final String PUTSTYLES_NAME = "PutStyles";
124    
125        // private static final String UNKNOWN_NAME = "Unknown";
126    
127        protected HashMap<String, URL> addresses = null;
128    
129        protected WMSCapabilities capabilities = null;
130    
131        private static Properties properties;
132        static {
133            if ( properties == null ) {
134                try {
135                    properties = new Properties();
136                    InputStream is = RemoteWMService.class.getResourceAsStream( "remotewmservice.properties" );
137                    properties.load( is );
138                    is.close();
139                } catch ( Exception e ) {
140                    BootLogger.logError( e.getMessage(), e );
141                }
142            }
143        }
144    
145        /**
146         * Creates a new instance of RemoteWMService
147         * 
148         * @param capabilities
149         */
150        public RemoteWMService( WMSCapabilities capabilities ) {
151            this.capabilities = capabilities;
152            addresses = new HashMap<String, URL>();
153    
154            // get GetCapabilities operation address
155            List<DCP> dcps = null;
156            HTTP http = null;
157    
158            OperationsMetadata om = capabilities.getOperationMetadata();
159    
160            if ( capabilities.getVersion().equals( "1.0.0" ) ) {
161                dcps = om.getOperation( new QualifiedName( CAPABILITIES_NAME ) ).getDCP();
162                for ( DCP dcp : dcps )
163                    if ( dcp instanceof HTTP )
164                        http = (HTTP) dcp;
165                addresses.put( CAPABILITIES_NAME, http.getLinks().get( 0 ).getLinkage().getHref() );
166            } else {
167                dcps = om.getOperation( new QualifiedName( GETCAPABILITIES_NAME ) ).getDCP();
168                for ( DCP dcp : dcps )
169                    if ( dcp instanceof HTTP )
170                        http = (HTTP) dcp;
171                addresses.put( GETCAPABILITIES_NAME, http.getLinks().get( 0 ).getLinkage().getHref() );
172            }
173    
174            // get GetMap operation address
175            if ( capabilities.getVersion().equals( "1.0.0" ) ) {
176                dcps = om.getOperation( new QualifiedName( MAP_NAME ) ).getDCP();
177                for ( DCP dcp : dcps )
178                    if ( dcp instanceof HTTP )
179                        http = (HTTP) dcp;
180                addresses.put( MAP_NAME, http.getLinks().get( 0 ).getLinkage().getHref() );
181            } else {
182                dcps = om.getOperation( new QualifiedName( GETMAP_NAME ) ).getDCP();
183                for ( DCP dcp : dcps )
184                    if ( dcp instanceof HTTP )
185                        http = (HTTP) dcp;
186                addresses.put( GETMAP_NAME, http.getLinks().get( 0 ).getLinkage().getHref() );
187            }
188    
189            // get GetFeatureInfo operation address
190            if ( capabilities.getVersion().equals( "1.0.0" ) ) {
191                Operation operation = om.getOperation( new QualifiedName( FEATUREINFO_NAME ) );
192    
193                if ( operation != null ) {
194                    dcps = operation.getDCP();
195                    for ( DCP dcp : dcps )
196                        if ( dcp instanceof HTTP )
197                            http = (HTTP) dcp;
198                    addresses.put( FEATUREINFO_NAME, http.getLinks().get( 0 ).getLinkage().getHref() );
199                }
200            } else {
201                Operation operation = om.getOperation( new QualifiedName( GETFEATUREINFO_NAME ) );
202    
203                if ( operation != null ) {
204                    dcps = operation.getDCP();
205                    for ( DCP dcp : dcps )
206                        if ( dcp instanceof HTTP )
207                            http = (HTTP) dcp;
208                    addresses.put( GETFEATUREINFO_NAME, http.getLinks().get( 0 ).getLinkage().getHref() );
209                }
210            }
211    
212            // get GetLegendGraphic operation address
213            Operation operation = om.getOperation( new QualifiedName( GETLEGENDGRAPHIC_NAME ) );
214    
215            if ( operation != null ) {
216                dcps = operation.getDCP();
217                for ( DCP dcp : dcps )
218                    if ( dcp instanceof HTTP )
219                        http = (HTTP) dcp;
220                addresses.put( GETLEGENDGRAPHIC_NAME, http.getLinks().get( 0 ).getLinkage().getHref() );
221            }
222    
223            // get GetStyles operation address
224            operation = om.getOperation( new QualifiedName( GETSTYLES_NAME ) );
225    
226            if ( operation != null ) {
227                dcps = operation.getDCP();
228                for ( DCP dcp : dcps )
229                    if ( dcp instanceof HTTP )
230                        http = (HTTP) dcp;
231                addresses.put( GETSTYLES_NAME, http.getLinks().get( 0 ).getLinkage().getHref() );
232            }
233    
234            // get PutStyles operation address
235            operation = om.getOperation( new QualifiedName( PUTSTYLES_NAME ) );
236    
237            if ( operation != null ) {
238                dcps = operation.getDCP();
239                for ( DCP dcp : dcps )
240                    if ( dcp instanceof HTTP )
241                        http = (HTTP) dcp;
242                addresses.put( PUTSTYLES_NAME, http.getLinks().get( 0 ).getLinkage().getHref() );
243            }
244    
245            // get DescribeLayer operation address
246            operation = om.getOperation( new QualifiedName( DESCRIBELAYER_NAME ) );
247    
248            if ( operation != null ) {
249                dcps = operation.getDCP();
250                for ( DCP dcp : dcps )
251                    if ( dcp instanceof HTTP )
252                        http = (HTTP) dcp;
253                addresses.put( DESCRIBELAYER_NAME, http.getLinks().get( 0 ).getLinkage().getHref() );
254            }
255    
256        }
257    
258        public OGCCapabilities getCapabilities() {
259            return capabilities;
260        }
261    
262        /**
263         * the method performs the handling of the passed OGCWebServiceEvent directly and returns the
264         * result to the calling class/method
265         * 
266         * @param request
267         *            request (WMS, WCS, WFS, WCAS, WCTS, WTS, Gazetter) to perform
268         * 
269         * @throws OGCWebServiceException
270         */
271        public Object doService( OGCWebServiceRequest request )
272                                throws OGCWebServiceException {
273            Object o = null;
274            if ( request instanceof GetMap ) {
275                o = handleGetMap( (GetMap) request );
276                o = WMSProtocolFactory.createGetMapResponse( request, null, o );
277            } else if ( request instanceof GetFeatureInfo ) {
278                o = handleFeatureInfo( (GetFeatureInfo) request );
279                o = WMSProtocolFactory.createGetFeatureInfoResponse( request, null, (String) o );
280            }
281            /*
282             * else if ( request instanceof WMSGetCapabilities) { handleGetCapabilities(
283             * (WMSGetCapabilities)request, client ); } else if ( request instanceof GetStyles ) {
284             * handleGetStyles( (GetStyles)request, client ); } else if ( request instanceof PutStyles ) {
285             * handlePutStyles( (PutStyles)request, client ); } else if ( request instanceof
286             * DescribeLayer ) { handleDescribeLayer( (DescribeLayer)request, client ); } else if (
287             * request instanceof GetLegendGraphic ) { handleGetLegendGraphic(
288             * (GetLegendGraphic)request, client ); }
289             */
290    
291            return o;
292    
293        }
294    
295        // checks for excessive &
296        private static String constructRequestURL( String params, String url ) {
297            if ( url.endsWith( "?" ) && params.startsWith( "&" ) ) {
298                return url + params.substring( 1 );
299            }
300    
301            return url + params;
302        }
303    
304        /**
305         * performs a GetMap request against the remote service. The result contains the map decoded in
306         * the desired format as a byte array.
307         * 
308         * @param request
309         *            GetMap request
310         */
311        protected Object handleGetMap( GetMap request )
312                                throws OGCWebServiceException {
313    
314            URL url = null;
315    
316            if ( request.getVersion().equals( "1.0.0" ) ) {
317                url = addresses.get( MAP_NAME );
318            } else {
319                url = addresses.get( GETMAP_NAME );
320            }
321    
322            String us = constructRequestURL( request.getRequestParameter(), validateHTTPGetBaseURL( url.toExternalForm() ) );
323    
324            LOG.logDebug( "remote wms getmap", us );
325    
326            if ( capabilities.getVersion().compareTo( "1.0.0" ) <= 0 ) {
327                us = StringTools.replace( us, "TRANSPARENCY", "TRANSPARENT", false );
328                us = StringTools.replace( us, "GetMap", "map", false );
329                us = StringTools.replace( us, "image/", "", false );
330            }
331    
332            Object result = null;
333            try {
334                HttpClient client = new HttpClient();
335                enableProxyUsage( client, new URL( us ) );
336                int timeout = 25000;
337                if ( properties != null && properties.getProperty( "timeout" ) != null ) {
338                    timeout = Integer.parseInt( properties.getProperty( "timeout" ) );
339                }
340                LOG.logDebug( "timeout is:", timeout );
341                client.getHttpConnectionManager().getParams().setSoTimeout( timeout );
342                GetMethod get = new GetMethod( us );
343                client.executeMethod( get );
344                InputStream is = get.getResponseBodyAsStream();
345                Header header = get.getResponseHeader( "Content-type" );
346    
347                String contentType = header.getValue();
348                String[] tmp = StringTools.toArray( contentType, ";", true );
349                for ( int i = 0; i < tmp.length; i++ ) {
350                    if ( tmp[i].indexOf( "image" ) > -1 ) {
351                        contentType = tmp[i];
352                        break;
353                    }
354                    contentType = tmp[0];
355                }
356    
357                if ( MimeTypeMapper.isImageType( contentType ) && MimeTypeMapper.isKnownImageType( contentType ) ) {
358                    MemoryCacheSeekableStream mcss = new MemoryCacheSeekableStream( is );
359                    RenderedOp rop = JAI.create( "stream", mcss );
360                    result = rop.getAsBufferedImage();
361                    mcss.close();
362                } else {
363                    // extract remote (error) message if the response
364                    // contains a known mime type
365                    String res = "";
366                    if ( MimeTypeMapper.isKnownMimeType( contentType ) ) {
367                        res = "; remote message: ";
368                        res += getInputStreamContent( is );
369                    }
370                    String msg = Messages.getMessage( "REMOTEWMS_GETMAP_INVALID_RESULT", contentType, us );
371                    throw new OGCWebServiceException( "RemoteWMS:handleGetMap", msg );
372                }
373            } catch ( Exception e ) {
374                LOG.logError( e.getMessage(), e );
375                String msg = Messages.getMessage( "REMOTEWMS_GETMAP_GENERAL_ERROR",
376                                                  capabilities.getServiceIdentification().getTitle(), us );
377                throw new OGCWebServiceException( "RemoteWMS:handleGetMap", msg );
378            }
379    
380            return result;
381        }
382    
383        /**
384         * reads feature infos from the remote WMS by performing a FeatureInfo request against it. As
385         * long the result of a FeatureInfo request is generic (for usual it is som HTML) it isn't easy
386         * to combine the result with that of other WMS's
387         * 
388         * @param request
389         *            feature info request
390         */
391        protected Object handleFeatureInfo( GetFeatureInfo request )
392                                throws OGCWebServiceException {
393    
394            URL url = null;
395    
396            if ( request.getVersion().equals( "1.0.0" ) ) {
397                url = addresses.get( FEATUREINFO_NAME );
398            } else {
399                url = addresses.get( GETFEATUREINFO_NAME );
400            }
401    
402            if ( url == null ) {
403                String msg = Messages.getMessage( "REMOTEWMS_GFI_NOT_SUPPORTED",
404                                                  capabilities.getServiceIdentification().getTitle() );
405                throw new OGCWebServiceException( msg );
406            }
407    
408            String us = constructRequestURL( request.getRequestParameter(), validateHTTPGetBaseURL( url.toExternalForm() ) );
409    
410            String result = null;
411            try {
412                LOG.logDebug( "GetFeatureInfo: ", us );
413                URL ur = new URL( us );
414                // get map from the remote service
415                NetWorker nw = new NetWorker( ur );
416                byte[] b = nw.getDataAsByteArr( 20000 );
417                String contentType = nw.getContentType();
418    
419                // extract content charset if available; otherwise use configured system charset
420                String charset = null;
421                LOG.logDebug( "content type: ", contentType );
422                if ( contentType != null ) {
423                    String[] tmp = StringTools.toArray( contentType, ";", false );
424                    if ( tmp.length == 2 ) {
425                        charset = tmp[1].substring( tmp[1].indexOf( '=' ) + 1, tmp[1].length() );
426                    } else {
427                        charset = CharsetUtils.getSystemCharset();
428                    }
429                } else {
430                    charset = CharsetUtils.getSystemCharset();
431                }
432    
433                if ( contentType.toLowerCase().startsWith( "application/vnd.ogc.gml" ) ) {
434                    result = new String( b, charset );
435                } else {
436                    throw new OGCWebServiceException( "RemoteWMS:handleFeatureInfo" );
437                }
438            } catch ( Exception e ) {
439                LOG.logError( e.getMessage(), e );
440                String msg = Messages.getMessage( "REMOTEWMS_GFI_GENERAL_ERROR",
441                                                  capabilities.getServiceIdentification().getTitle(), us );
442                throw new OGCWebServiceException( "RemoteWMS:handleFeatureInfo", msg );
443            }
444    
445            return result;
446        }
447    
448        /**
449         * reads the capabilities from the remote WMS by performing a GetCapabilities request against
450         * it.
451         * 
452         * @param request
453         *            capabilities request
454         */
455        protected WMSCapabilities handleGetCapabilities( WMSGetCapabilities request )
456                                throws OGCWebServiceException {
457    
458            URL url = null;
459    
460            if ( request.getVersion().equals( "1.0.0" ) ) {
461                url = addresses.get( CAPABILITIES_NAME );
462            } else {
463                url = addresses.get( GETCAPABILITIES_NAME );
464            }
465    
466            String us = constructRequestURL( request.getRequestParameter(), validateHTTPGetBaseURL( url.toExternalForm() ) );
467    
468            WMSCapabilities result = null;
469    
470            try {
471                URL ur = new URL( us );
472                // get map from the remote service
473                NetWorker nw = new NetWorker( ur );
474                byte[] b = nw.getDataAsByteArr( 20000 );
475                String contentType = nw.getContentType();
476    
477                if ( MimeTypeMapper.isKnownMimeType( contentType ) ) {
478                    // create a WMSCapabilitiesTEMP instance from the result
479                    StringReader reader = new StringReader( new String( b ) );
480                    WMSCapabilitiesDocument doc = new WMSCapabilitiesDocument();
481                    doc.load( reader, XMLFragment.DEFAULT_URL );
482                    doc = WMSCapabilitiesDocumentFactory.getWMSCapabilitiesDocument( doc.getRootElement() );
483                    result = (WMSCapabilities) doc.parseCapabilities();
484                } else {
485                    String msg = Messages.getMessage( "REMOTEWMS_GETCAPS_INVALID_CONTENTTYPE", contentType, us );
486                    throw new OGCWebServiceException( "RemoteWMS:handleGetCapabilities", msg );
487                }
488            } catch ( Exception e ) {
489                LOG.logError( e.getMessage(), e );
490                String msg = Messages.getMessage( "REMOTEWMS_GETCAPS_GENERAL_ERROR",
491                                                  capabilities.getServiceIdentification().getTitle(), us );
492                throw new OGCWebServiceException( "RemoteWMS:handleGetCapabilities", msg );
493            }
494    
495            return result;
496        }
497    
498        /**
499         * 
500         * 
501         * @param request
502         *            get styles request (WMS 1.1.1 - SLD)
503         */
504        protected Object handleGetStyles( GetStyles request )
505                                throws OGCWebServiceException {
506    
507            URL url = addresses.get( GETSTYLES_NAME );
508    
509            if ( url == null ) {
510                throw new OGCWebServiceException( "GetStyles is not supported by the RemoteWMS: "
511                                                  + capabilities.getServiceIdentification().getTitle() );
512            }
513    
514            constructRequestURL( request.getRequestParameter(), validateHTTPGetBaseURL( url.toExternalForm() ) );
515    
516            // FIXME
517            // TODO
518            return null;
519        }
520    
521        /**
522         * 
523         * 
524         * @param request
525         *            put styles request (WMS 1.1.1 - SLD)
526         */
527        protected Object handlePutStyles( PutStyles request )
528                                throws OGCWebServiceException {
529    
530            URL url = addresses.get( PUTSTYLES_NAME );
531    
532            if ( url == null ) {
533                throw new OGCWebServiceException( "PUTSTYLES is not supported by the RemoteWMS: "
534                                                  + capabilities.getServiceIdentification().getTitle() );
535            }
536    
537            constructRequestURL( request.getRequestParameter(), validateHTTPGetBaseURL( url.toExternalForm() ) );
538    
539            // FIXME
540            // TODO
541    
542            return null;
543        }
544    
545        /**
546         * 
547         * 
548         * @param request
549         *            describe layer request (WMS 1.1.1 - SLD)
550         */
551        protected Object handleDescribeLayer( DescribeLayer request )
552                                throws OGCWebServiceException {
553    
554            URL url = addresses.get( DESCRIBELAYER_NAME );
555    
556            if ( url == null ) {
557                throw new OGCWebServiceException( "DESCRIBELAYER is not supported by the RemoteWMS: "
558                                                  + capabilities.getServiceIdentification().getTitle() );
559            }
560    
561            constructRequestURL( request.getRequestParameter(), validateHTTPGetBaseURL( url.toExternalForm() ) );
562    
563            // FIXME
564            // TODO
565    
566            return null;
567        }
568    
569        /**
570         * 
571         * 
572         * @param request
573         *            describe layer request (WMS 1.1.1 - SLD)
574         */
575        protected Object handleGetLegendGraphic( GetLegendGraphic request )
576                                throws OGCWebServiceException {
577    
578            URL url = addresses.get( GETLEGENDGRAPHIC_NAME );
579    
580            if ( url == null ) {
581                throw new OGCWebServiceException( "GETLEGENDGRAPHIC is not supported by the RemoteWMS: "
582                                                  + capabilities.getServiceIdentification().getTitle() );
583            }
584    
585            constructRequestURL( request.getRequestParameter(), validateHTTPGetBaseURL( url.toExternalForm() ) );
586    
587            // FIXME
588            // TODO
589    
590            return null;
591        }
592    
593        /**
594         * 
595         * 
596         * @param is
597         * 
598         * @return thr content as String
599         * 
600         * @throws IOException
601         */
602        protected String getInputStreamContent( InputStream is )
603                                throws IOException {
604            StringBuffer sb = new StringBuffer( 1000 );
605            int c = 0;
606    
607            while ( ( c = is.read() ) >= 0 ) {
608                sb.append( (char) c );
609            }
610    
611            is.close();
612            return sb.toString();
613        }
614    
615    }