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 }