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