001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/security/owsrequestvalidator/wms/GetMapRequestValidator.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 53177 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 package org.deegree.security.owsrequestvalidator.wms; 044 045 import java.net.URL; 046 import java.util.ArrayList; 047 import java.util.HashMap; 048 import java.util.List; 049 import java.util.Map; 050 051 import org.deegree.datatypes.QualifiedName; 052 import org.deegree.datatypes.Types; 053 import org.deegree.framework.log.ILogger; 054 import org.deegree.framework.log.LoggerFactory; 055 import org.deegree.framework.util.ColorUtils; 056 import org.deegree.framework.util.MapUtils; 057 import org.deegree.framework.util.StringTools; 058 import org.deegree.framework.xml.XMLParsingException; 059 import org.deegree.graphics.sld.AbstractStyle; 060 import org.deegree.graphics.sld.NamedLayer; 061 import org.deegree.graphics.sld.NamedStyle; 062 import org.deegree.graphics.sld.SLDFactory; 063 import org.deegree.graphics.sld.StyledLayerDescriptor; 064 import org.deegree.model.crs.CRSFactory; 065 import org.deegree.model.crs.CoordinateSystem; 066 import org.deegree.model.crs.GeoTransformer; 067 import org.deegree.model.feature.Feature; 068 import org.deegree.model.feature.FeatureFactory; 069 import org.deegree.model.feature.FeatureProperty; 070 import org.deegree.model.feature.schema.FeatureType; 071 import org.deegree.model.feature.schema.PropertyType; 072 import org.deegree.model.spatialschema.Envelope; 073 import org.deegree.model.spatialschema.GeometryFactory; 074 import org.deegree.ogcwebservices.InvalidParameterValueException; 075 import org.deegree.ogcwebservices.OGCWebServiceRequest; 076 import org.deegree.ogcwebservices.wms.operation.GetMap; 077 import org.deegree.security.UnauthorizedException; 078 import org.deegree.security.drm.model.RightType; 079 import org.deegree.security.drm.model.User; 080 import org.deegree.security.owsproxy.Condition; 081 import org.deegree.security.owsproxy.OperationParameter; 082 import org.deegree.security.owsproxy.Request; 083 import org.deegree.security.owsrequestvalidator.Messages; 084 import org.deegree.security.owsrequestvalidator.Policy; 085 086 /** 087 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth </a> 088 * @author last edited by: $Author: rbezema $ 089 * 090 * @version 1.1, $Revision: 9459 $, $Date: 2008-01-08 12:05:05 +0100 (Di, 08 Jan 2008) $ 091 * 092 * @since 1.1 093 */ 094 095 public class GetMapRequestValidator extends AbstractWMSRequestValidator { 096 097 private static final ILogger LOG = LoggerFactory.getLogger( GetMapRequestValidator.class ); 098 099 private static double DEFAULT_PIXEL_SIZE = 0.00028; 100 101 // known condition parameter 102 private static final String BBOX = "bbox"; 103 104 private static final String LAYERS = "layers"; 105 106 private static final String BGCOLOR = "bgcolor"; 107 108 private static final String TRANSPARENCY = "transparency"; 109 110 private static final String RESOLUTION = "resolution"; 111 112 private static final String SLD = "sld"; 113 114 private static final String SLD_BODY = "sld_body"; 115 116 private static final String INVALIDBBOX = Messages.getString( "GetMapRequestValidator.INVALIDBBOX" ); 117 118 private static final String INVALIDLAYER = Messages.getString( "GetMapRequestValidator.INVALIDLAYER" ); 119 120 private static final String INVALIDSTYLE = Messages.getString( "GetMapRequestValidator.INVALIDSTYLE" ); 121 122 private static final String INVALIDBGCOLOR = Messages.getString( "GetMapRequestValidator.INVALIDBGCOLOR" ); 123 124 private static final String INVALIDTRANSPARENCY = Messages.getString( "GetMapRequestValidator.INVALIDTRANSPARENCY" ); 125 126 private static final String INVALIDRESOLUTION = Messages.getString( "GetMapRequestValidator.INVALIDRESOLUTION" ); 127 128 private static final String INVALIDSLD = Messages.getString( "GetMapRequestValidator.INVALIDSLD" ); 129 130 private static final String INVALIDSLD_BODY = Messages.getString( "GetMapRequestValidator.INVALIDSLD_BODY" ); 131 132 private static final String MISSINGCRS = Messages.getString( "GetMapRequestValidator.MISSINGCRS" ); 133 134 private List<String> accessdRes = new ArrayList<String>(); 135 136 private static FeatureType mapFT = null; 137 138 private GeoTransformer gt = null; 139 140 static { 141 if ( mapFT == null ) { 142 mapFT = GetMapRequestValidator.createFeatureType(); 143 } 144 } 145 146 /** 147 * @param policy 148 */ 149 public GetMapRequestValidator( Policy policy ) { 150 super( policy ); 151 try { 152 gt = new GeoTransformer( "EPSG:4326" ); 153 } catch ( Exception e ) { 154 e.printStackTrace(); 155 } 156 } 157 158 /** 159 * validates the incomming GetMap request against the policy assigend to a validator 160 * 161 * @param request 162 * request to validate 163 * @param user 164 * name of the user who likes to perform the request (can be null) 165 */ 166 public void validateRequest( OGCWebServiceRequest request, User user ) 167 throws InvalidParameterValueException, UnauthorizedException { 168 169 accessdRes.clear(); 170 userCoupled = false; 171 Request req = policy.getRequest( "WMS", "GetMap" ); 172 // request is valid because no restrictions are made 173 if ( req.isAny() ) 174 return; 175 Condition condition = req.getPreConditions(); 176 177 GetMap wmsreq = (GetMap) request; 178 179 validateVersion( condition, wmsreq.getVersion() ); 180 Envelope env = wmsreq.getBoundingBox(); 181 try { 182 env = gt.transform( env, wmsreq.getSrs() ); 183 } catch ( Exception e ) { 184 throw new InvalidParameterValueException( "condition envelope isn't in the right CRS ", e ); 185 } 186 validateBBOX( condition, env ); 187 validateLayers( condition, wmsreq.getLayers() ); 188 validateBGColor( condition, ColorUtils.toHexCode( "0x", wmsreq.getBGColor() ) ); 189 validateTransparency( condition, wmsreq.getTransparency() ); 190 validateExceptions( condition, wmsreq.getExceptions() ); 191 validateFormat( condition, wmsreq.getFormat() ); 192 validateMaxWidth( condition, wmsreq.getWidth() ); 193 validateMaxHeight( condition, wmsreq.getHeight() ); 194 validateResolution( condition, wmsreq ); 195 validateSLD( condition, wmsreq.getSLD_URL() ); 196 validateSLD_Body( condition, wmsreq.getStyledLayerDescriptor() ); 197 198 if ( userCoupled ) { 199 validateAgainstRightsDB( wmsreq, user ); 200 } 201 202 } 203 204 /** 205 * checks if the passed envelope is valid against the maximum bounding box defined in the 206 * policy. If <tt>user</ff> != <tt>null</tt> the 207 * maximu valid BBOX will be read from the user/rights repository 208 * @param condition condition containing the definition of the valid BBOX 209 * @param envelope 210 * @throws InvalidParameterValueException 211 */ 212 private void validateBBOX( Condition condition, Envelope envelope ) 213 throws InvalidParameterValueException { 214 215 OperationParameter op = condition.getOperationParameter( BBOX ); 216 217 // version is valid because no restrictions are made 218 if ( op.isAny() ) 219 return; 220 221 String v = op.getFirstAsString(); 222 String[] d = StringTools.toArray( v, ",", false ); 223 Envelope env = GeometryFactory.createEnvelope( Double.parseDouble( d[0] ), Double.parseDouble( d[1] ), 224 Double.parseDouble( d[2] ), Double.parseDouble( d[3] ), null ); 225 226 try { 227 env = gt.transform( env, d[4] ); 228 } catch ( Exception e ) { 229 throw new InvalidParameterValueException( MISSINGCRS, e ); 230 } 231 232 if ( !env.contains( envelope ) ) { 233 if ( !op.isUserCoupled() ) { 234 // if not user coupled the validation has failed 235 throw new InvalidParameterValueException( INVALIDBBOX + op.getFirstAsString() ); 236 } 237 userCoupled = true; 238 accessdRes.add( "BBOX: " + v ); 239 } 240 } 241 242 /** 243 * checks if the passed layres/styles are valid against the layers/styles list defined in the 244 * policy. If <tt>user</ff> != <tt>null</tt> the 245 * valid layers/styles will be read from the user/rights repository 246 * @param condition condition containing the definition of the valid layers/styles 247 * @param layers 248 * @throws InvalidParameterValueException 249 */ 250 private void validateLayers( Condition condition, GetMap.Layer[] layers ) 251 throws InvalidParameterValueException { 252 253 OperationParameter op = condition.getOperationParameter( LAYERS ); 254 255 // version is valid because no restrictions are made 256 if ( op.isAny() ) { 257 return; 258 } 259 260 List<String> v = op.getValues(); 261 // seperate layers from assigned styles 262 Map<String, String> map = new HashMap<String, String>(); 263 for ( int i = 0; i < v.size(); i++ ) { 264 String[] tmp = StringTools.toArray( v.get( i ), "|", false ); 265 map.put( tmp[0], tmp[1] ); 266 } 267 268 for ( int i = 0; i < layers.length; i++ ) { 269 String style = layers[i].getStyleName(); 270 String vs = map.get( layers[i].getName() ); 271 if ( vs == null ) { 272 if ( !op.isUserCoupled() ) { 273 throw new InvalidParameterValueException( INVALIDLAYER + layers[i].getName() ); 274 } 275 accessdRes.add( "Layers: " + layers[i].getName() ); 276 userCoupled = true; 277 } else if ( !style.equalsIgnoreCase( "default" ) && vs.indexOf( "$any$" ) < 0 && vs.indexOf( style ) < 0 ) { 278 // a style is valid for a layer if it's the default style 279 // or the layer accepts any style or a style is explicit defined 280 // to be valid 281 if ( !op.isUserCoupled() ) { 282 throw new InvalidParameterValueException( INVALIDSTYLE + layers[i].getName() + ':' + style ); 283 } 284 userCoupled = true; 285 accessdRes.add( "Styles: " + style ); 286 } 287 } 288 289 } 290 291 /** 292 * checks if the passed bgcolor is valid against the bgcolor(s) defined in the policy. If 293 * <tt>user</ff> != <tt>null</tt> the valid bgcolors will be read from 294 * the user/rights repository 295 * @param condition condition containing the definition of the valid bgcolors 296 * @param bgcolor 297 * @throws InvalidParameterValueException 298 */ 299 private void validateBGColor( Condition condition, String bgcolor ) 300 throws InvalidParameterValueException { 301 302 OperationParameter op = condition.getOperationParameter( BGCOLOR ); 303 304 // version is valid because no restrictions are made 305 if ( op.isAny() ) 306 return; 307 308 List list = op.getValues(); 309 310 if ( !list.contains( bgcolor ) ) { 311 if ( !op.isUserCoupled() ) { 312 throw new InvalidParameterValueException( INVALIDBGCOLOR + bgcolor ); 313 } 314 accessdRes.add( "BGCOLOR" + bgcolor ); 315 userCoupled = true; 316 } 317 318 } 319 320 /** 321 * checks if the passed transparency is valid against the transparency defined in the policy. If 322 * <tt>user</ff> != <tt>null</tt> the valid transparency will be 323 * read from the user/rights repository 324 * @param condition condition containing the definition of the valid transparency 325 * @param transparency 326 * @throws InvalidParameterValueException 327 */ 328 private void validateTransparency( Condition condition, boolean transparency ) 329 throws InvalidParameterValueException { 330 331 OperationParameter op = condition.getOperationParameter( TRANSPARENCY ); 332 333 // version is valid because no restrictions are made 334 if ( op.isAny() ) 335 return; 336 337 List<String> v = op.getValues(); 338 String s = "" + transparency; 339 if ( !v.get( 0 ).equals( s ) && !v.get( v.size() - 1 ).equals( s ) ) { 340 if ( !op.isUserCoupled() ) { 341 throw new InvalidParameterValueException( INVALIDTRANSPARENCY + transparency ); 342 } 343 userCoupled = true; 344 accessdRes.add( "Transparency: " + transparency ); 345 } 346 347 } 348 349 /** 350 * checks if the requested map area/size is valid against the minimum resolution defined in the 351 * policy. If <tt>user</ff> != <tt>null</tt> the valid 352 * resolution will be read from the user/rights repository 353 * @param condition condition containing the definition of the valid resolution 354 * @param resolution 355 * @throws InvalidParameterValueException 356 */ 357 private void validateResolution( Condition condition, GetMap gmr ) 358 throws InvalidParameterValueException { 359 360 OperationParameter op = condition.getOperationParameter( RESOLUTION ); 361 362 // version is valid because no restrictions are made 363 if ( op.isAny() ) 364 return; 365 366 double scale = 0; 367 try { 368 scale = calcScale( gmr ); 369 } catch ( Exception e ) { 370 throw new InvalidParameterValueException( StringTools.stackTraceToString( e ) ); 371 } 372 double compareRes = 0; 373 compareRes = op.getFirstAsDouble(); 374 if ( scale < compareRes ) { 375 if ( !op.isUserCoupled() ) { 376 throw new InvalidParameterValueException( INVALIDRESOLUTION + scale ); 377 } 378 userCoupled = true; 379 accessdRes.add( "resolution: " + scale ); 380 } 381 } 382 383 /** 384 * checks if the passed reference to a SLD document is valid against the defined in the policy. 385 * If <tt>user</ff> != <tt>null</tt> the valid 386 * sld reference addresses will be read from the user/rights repository 387 * @param condition condition containing the definition of the valid sldRef 388 * @param sldRef 389 * @throws InvalidParameterValueException 390 */ 391 private void validateSLD( Condition condition, URL sldRef ) 392 throws InvalidParameterValueException { 393 394 OperationParameter op = condition.getOperationParameter( SLD ); 395 OperationParameter gmop = condition.getOperationParameter( LAYERS ); 396 397 if ( op == null && sldRef != null ) { 398 throw new InvalidParameterValueException( INVALIDSLD + sldRef ); 399 } 400 // sldRef is valid because no restrictions are made 401 if ( sldRef == null || op.isAny() ) { 402 return; 403 } 404 405 // validate reference base of the SLD 406 List<String> list = op.getValues(); 407 String port = null; 408 if ( sldRef.getPort() != -1 ) { 409 port = ":" + sldRef.getPort(); 410 } else { 411 port = ":80"; 412 } 413 String addr = sldRef.getProtocol() + "://" + sldRef.getHost() + port; 414 if ( !list.contains( addr ) ) { 415 if ( !op.isUserCoupled() ) { 416 throw new InvalidParameterValueException( INVALIDSLD + sldRef ); 417 } 418 userCoupled = true; 419 } 420 421 // validate referenced dacument to be a valid SLD 422 StyledLayerDescriptor sld = null; 423 try { 424 sld = SLDFactory.createSLD( sldRef ); 425 } catch ( XMLParsingException e ) { 426 String s = org.deegree.i18n.Messages.getMessage( "WMS_SLD_IS_NOT_VALID", sldRef ); 427 throw new InvalidParameterValueException( s ); 428 } 429 430 // validate NamedLayers referenced by the SLD 431 NamedLayer[] nl = sld.getNamedLayers(); 432 List<String> v = gmop.getValues(); 433 // seperate layers from assigned styles 434 Map<String, String> map = new HashMap<String, String>(); 435 for ( int i = 0; i < v.size(); i++ ) { 436 String[] tmp = StringTools.toArray( v.get( i ), "|", false ); 437 map.put( tmp[0], tmp[1] ); 438 } 439 if ( !userCoupled ) { 440 for ( int i = 0; i < nl.length; i++ ) { 441 AbstractStyle st = nl[i].getStyles()[0]; 442 String style = null; 443 if ( st instanceof NamedStyle ) { 444 style = ( (NamedStyle) st ).getName(); 445 } else { 446 // use default as name if a UserStyle is defined 447 // to ensure that the style will be accepted by 448 // the validator 449 style = "default"; 450 } 451 String vs = map.get( nl[i].getName() ); 452 if ( vs == null ) { 453 if ( !op.isUserCoupled() ) { 454 throw new InvalidParameterValueException( INVALIDLAYER + nl[i].getName() ); 455 } 456 accessdRes.add( "Layers: " + nl[i].getName() ); 457 userCoupled = true; 458 } else if ( !style.equalsIgnoreCase( "default" ) && vs.indexOf( "$any$" ) < 0 459 && vs.indexOf( style ) < 0 ) { 460 // a style is valid for a layer if it's the default style 461 // or the layer accepts any style or a style is explicit defined 462 // to be valid 463 if ( !op.isUserCoupled() ) { 464 throw new InvalidParameterValueException( INVALIDSTYLE + nl[i].getName() + ':' + style ); 465 } 466 userCoupled = true; 467 accessdRes.add( "Styles: " + style ); 468 } 469 } 470 } 471 472 } 473 474 /** 475 * checks if the passed user is allowed to perform a GetMap request containing a SLD_BODY 476 * parameter. 477 * 478 * @param condition 479 * condition containing when SLD_BODY is valid or nots 480 * @param sld_body 481 * @throws InvalidParameterValueException 482 */ 483 private void validateSLD_Body( Condition condition, StyledLayerDescriptor sld_body ) 484 throws InvalidParameterValueException { 485 486 /* 487 * 488 * OperationParameter op = condition.getOperationParameter( SLD_BODY ); // version is valid 489 * because no restrictions are made if ( sld_body == null ||op.isAny() ) return; // at the 490 * moment it is just evaluated if the user is allowed // to perform a SLD request or not. no 491 * content validation will // be made boolean isAllowed = false; if ( op.isUserCoupled() ) { 492 * //TODO // get comparator list from security registry } if (!isAllowed ) { throw new 493 * InvalidParameterValueException( INVALIDSLD_BODY ); } 494 */ 495 } 496 497 /** 498 * validates the passed WMS GetMap request against a User- and Rights-Management DB. 499 * 500 * @param wmsreq 501 * @param user 502 * @throws InvalidParameterValueException 503 */ 504 private void validateAgainstRightsDB( GetMap wmsreq, User user ) 505 throws InvalidParameterValueException, UnauthorizedException { 506 507 if ( user == null ) { 508 StringBuffer sb = new StringBuffer( 1000 ); 509 sb.append( ' ' ); 510 for ( int i = 0; i < accessdRes.size(); i++ ) { 511 sb.append( accessdRes.get( i ) ).append( "; " ); 512 } 513 throw new UnauthorizedException( Messages.format( "RequestValidator.NOACCESS", sb ) ); 514 } 515 516 Double scale = null; 517 try { 518 scale = new Double( calcScale( wmsreq ) ); 519 } catch ( Exception e ) { 520 throw new InvalidParameterValueException( e ); 521 } 522 523 // create feature that describes the map request 524 FeatureProperty[] fps = new FeatureProperty[11]; 525 fps[0] = FeatureFactory.createFeatureProperty( new QualifiedName( "version" ), wmsreq.getVersion() ); 526 fps[1] = FeatureFactory.createFeatureProperty( new QualifiedName( "width" ), new Integer( wmsreq.getWidth() ) ); 527 fps[2] = FeatureFactory.createFeatureProperty( new QualifiedName( "height" ), new Integer( wmsreq.getHeight() ) ); 528 Envelope env = wmsreq.getBoundingBox(); 529 try { 530 env = gt.transform( env, wmsreq.getSrs() ); 531 } catch ( Exception e ) { 532 throw new InvalidParameterValueException( "A:condition envelope isn't in " + "the right CRS ", e ); 533 } 534 Object geom = null; 535 try { 536 geom = GeometryFactory.createSurface( env, null ); 537 } catch ( Exception e1 ) { 538 e1.printStackTrace(); 539 } 540 fps[3] = FeatureFactory.createFeatureProperty( new QualifiedName( "GEOM" ), geom ); 541 fps[4] = FeatureFactory.createFeatureProperty( new QualifiedName( "format" ), wmsreq.getFormat() ); 542 fps[5] = FeatureFactory.createFeatureProperty( new QualifiedName( "bgcolor" ), 543 ColorUtils.toHexCode( "0x", wmsreq.getBGColor() ) ); 544 fps[6] = FeatureFactory.createFeatureProperty( new QualifiedName( "transparent" ), "" 545 + wmsreq.getTransparency() ); 546 fps[7] = FeatureFactory.createFeatureProperty( new QualifiedName( "exceptions" ), wmsreq.getExceptions() ); 547 fps[8] = FeatureFactory.createFeatureProperty( new QualifiedName( "resolution" ), scale ); 548 fps[9] = FeatureFactory.createFeatureProperty( new QualifiedName( "sld" ), wmsreq.getSLD_URL() ); 549 550 GetMap.Layer[] layers = wmsreq.getLayers(); 551 for ( int i = 0; i < layers.length; i++ ) { 552 fps[10] = FeatureFactory.createFeatureProperty( new QualifiedName( "style" ), layers[i].getStyleName() ); 553 Feature feature = FeatureFactory.createFeature( "id", mapFT, fps ); 554 555 if ( LOG.getLevel() == ILogger.LOG_DEBUG ) { 556 for ( int ii = 0; ii < fps.length; ii++ ) { 557 LOG.logDebug( "compared property: ", fps[ii] ); 558 } 559 } 560 561 handleUserCoupledRules( user, feature, layers[i].getName(), "Layer", RightType.GETMAP ); 562 } 563 564 565 } 566 567 /** 568 * calculates the map scale as defined in the OGC WMS 1.1.1 specifications 569 * 570 * @return scale of the map 571 */ 572 private double calcScale( GetMap request ) 573 throws Exception { 574 575 Envelope bbox = request.getBoundingBox(); 576 577 CoordinateSystem crs = CRSFactory.create( request.getSrs() ); 578 return MapUtils.calcScale( request.getWidth(), request.getHeight(), bbox, crs, 1 ); 579 // would return scale denominator 580 //return MapUtils.calcScale( request.getWidth(), request.getHeight(), bbox, crs, DEFAULT_PIXEL_SIZE ); 581 } 582 583 private static FeatureType createFeatureType() { 584 PropertyType[] ftps = new PropertyType[11]; 585 ftps[0] = FeatureFactory.createSimplePropertyType( new QualifiedName( "version" ), Types.VARCHAR, false ); 586 ftps[1] = FeatureFactory.createSimplePropertyType( new QualifiedName( "width" ), Types.INTEGER, false ); 587 ftps[2] = FeatureFactory.createSimplePropertyType( new QualifiedName( "height" ), Types.INTEGER, false ); 588 ftps[3] = FeatureFactory.createSimplePropertyType( new QualifiedName( "GEOM" ), Types.GEOMETRY, false ); 589 ftps[4] = FeatureFactory.createSimplePropertyType( new QualifiedName( "format" ), Types.VARCHAR, false ); 590 ftps[5] = FeatureFactory.createSimplePropertyType( new QualifiedName( "bgcolor" ), Types.VARCHAR, false ); 591 ftps[6] = FeatureFactory.createSimplePropertyType( new QualifiedName( "transparent" ), Types.VARCHAR, false ); 592 ftps[7] = FeatureFactory.createSimplePropertyType( new QualifiedName( "exceptions" ), Types.VARCHAR, false ); 593 ftps[8] = FeatureFactory.createSimplePropertyType( new QualifiedName( "resolution" ), Types.DOUBLE, false ); 594 ftps[9] = FeatureFactory.createSimplePropertyType( new QualifiedName( "sld" ), Types.VARCHAR, false ); 595 ftps[10] = FeatureFactory.createSimplePropertyType( new QualifiedName( "style" ), Types.VARCHAR, false ); 596 597 return FeatureFactory.createFeatureType( "GetMap", false, ftps ); 598 } 599 600 }