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 }