001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/ogcwebservices/wfs/configuration/WFSConfiguration.java $ 002 /*---------------- FILE HEADER ------------------------------------------ 003 004 This file is part of deegree. 005 Copyright (C) 2001-2006 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 Aennchenstraße 19 030 53177 Bonn 031 E-Mail: poth@lat-lon.de 032 033 Prof. Dr. Klaus Greve 034 Department of Geography 035 University of Bonn 036 Meckenheimer Allee 166 037 53115 Bonn 038 Germany 039 E-Mail: greve@giub.uni-bonn.de 040 041 ---------------------------------------------------------------------------*/ 042 package org.deegree.ogcwebservices.wfs.configuration; 043 044 import java.io.File; 045 import java.io.FilenameFilter; 046 import java.io.IOException; 047 import java.net.URI; 048 import java.net.URL; 049 import java.util.ArrayList; 050 import java.util.HashMap; 051 import java.util.Iterator; 052 import java.util.List; 053 import java.util.Map; 054 055 import org.deegree.datatypes.QualifiedName; 056 import org.deegree.framework.log.ILogger; 057 import org.deegree.framework.log.LoggerFactory; 058 import org.deegree.framework.xml.InvalidConfigurationException; 059 import org.deegree.i18n.Messages; 060 import org.deegree.io.datastore.schema.MappedFeatureType; 061 import org.deegree.io.datastore.schema.MappedGMLSchema; 062 import org.deegree.io.datastore.schema.MappedGMLSchemaDocument; 063 import org.deegree.model.crs.CRSFactory; 064 import org.deegree.model.crs.CoordinateSystem; 065 import org.deegree.model.crs.UnknownCRSException; 066 import org.deegree.model.feature.schema.FeatureType; 067 import org.deegree.model.filterencoding.capabilities.FilterCapabilities; 068 import org.deegree.model.spatialschema.Envelope; 069 import org.deegree.model.spatialschema.GeometryFactory; 070 import org.deegree.ogcwebservices.getcapabilities.Contents; 071 import org.deegree.ogcwebservices.getcapabilities.OperationsMetadata; 072 import org.deegree.ogcwebservices.getcapabilities.ServiceIdentification; 073 import org.deegree.ogcwebservices.getcapabilities.ServiceProvider; 074 import org.deegree.ogcwebservices.wfs.WFService; 075 import org.deegree.ogcwebservices.wfs.capabilities.FeatureTypeList; 076 import org.deegree.ogcwebservices.wfs.capabilities.FormatType; 077 import org.deegree.ogcwebservices.wfs.capabilities.GMLObject; 078 import org.deegree.ogcwebservices.wfs.capabilities.Operation; 079 import org.deegree.ogcwebservices.wfs.capabilities.WFSCapabilities; 080 import org.deegree.ogcwebservices.wfs.capabilities.WFSFeatureType; 081 082 /** 083 * Represents the configuration for a deegree {@link WFService} instance. 084 * 085 * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider </a> 086 * @author last edited by: $Author: mschneider $ 087 * 088 * @version $Revision: 6588 $, $Date: 2007-04-11 17:31:29 +0200 (Mi, 11 Apr 2007) $ 089 */ 090 public class WFSConfiguration extends WFSCapabilities { 091 092 private static final long serialVersionUID = -8929822028461025018L; 093 094 protected static final ILogger LOG = LoggerFactory.getLogger( WFSConfiguration.class ); 095 096 private WFSDeegreeParams deegreeParams; 097 098 private Map<QualifiedName, MappedFeatureType> ftMap = new HashMap<QualifiedName, MappedFeatureType>(); 099 100 /** 101 * Generates a new <code>WFSConfiguration</code> instance from the given parameters. 102 * 103 * @param version 104 * @param updateSequence 105 * @param serviceIdentification 106 * @param serviceProvider 107 * @param operationsMetadata 108 * @param featureTypeList 109 * @param servesGMLObjectTypeList 110 * @param supportsGMLObjectTypeList 111 * @param contents 112 * TODO field not verified! Check spec. 113 * @param filterCapabilities 114 * @param deegreeParams 115 * @throws InvalidConfigurationException 116 */ 117 public WFSConfiguration( String version, String updateSequence, 118 ServiceIdentification serviceIdentification, 119 ServiceProvider serviceProvider, OperationsMetadata operationsMetadata, 120 FeatureTypeList featureTypeList, GMLObject[] servesGMLObjectTypeList, 121 GMLObject[] supportsGMLObjectTypeList, Contents contents, 122 FilterCapabilities filterCapabilities, WFSDeegreeParams deegreeParams ) 123 throws InvalidConfigurationException { 124 super( version, updateSequence, serviceIdentification, serviceProvider, operationsMetadata, 125 featureTypeList, servesGMLObjectTypeList, supportsGMLObjectTypeList, contents, 126 filterCapabilities ); 127 this.deegreeParams = deegreeParams; 128 try { 129 validateFeatureTypeDefinitions(); 130 } catch ( InvalidConfigurationException e ) { 131 LOG.logError( e.getMessage() ); 132 throw e; 133 } 134 } 135 136 /** 137 * Returns the deegreeParams. 138 * 139 * @return the deegreeParams 140 */ 141 public WFSDeegreeParams getDeegreeParams() { 142 return deegreeParams; 143 } 144 145 /** 146 * The deegreeParams to set. 147 * 148 * @param deegreeParams 149 */ 150 public void setDeegreeParams( WFSDeegreeParams deegreeParams ) { 151 this.deegreeParams = deegreeParams; 152 } 153 154 /** 155 * Returns a <code>Map</code> of the feature types that this configuration defines. 156 * 157 * @return keys: feature type names, values: mapped feature types 158 */ 159 public Map<QualifiedName, MappedFeatureType> getMappedFeatureTypes() { 160 return this.ftMap; 161 } 162 163 /** 164 * The <code>WFSConfiguration</code> is processed as follows: 165 * <ul> 166 * <li>The data directories (as specified in the configuration) are scanned for 167 * {@link MappedGMLSchemaDocument}s</li> 168 * <li>All {@link MappedFeatureType}s defined in any of the found 169 * {@link MappedGMLSchemaDocument}s are extracted, if duplicate feature type definitions occur, 170 * an <code>InvalidConfigurationException</code> is thrown.</li> 171 * <li>All feature types defined in the FeatureTypeList section of the WFS configuration are 172 * checked to have a corresponding {@link MappedFeatureType} definition. If this is not the case 173 * (or if the DefaultSRS is not equal to the CRS in the respective datastore definition), an 174 * <code>InvalidConfigurationException</code> is thrown.</li> 175 * <li>NOTE: the above is not necessary for FeatureTypes that are processed by XSLT-scripts 176 * (because they are usually mapped to a different FeatureTypes by XSLT)</li> 177 * <li>All feature types that are not defined in the FeatureTypeList section, but have been 178 * defined in one of the {@link MappedGMLSchemaDocument}s are added as feature types to the WFS 179 * configuration.</li> 180 * </ul> 181 * </p> 182 * 183 * @throws InvalidConfigurationException 184 */ 185 private void validateFeatureTypeDefinitions() 186 throws InvalidConfigurationException { 187 188 // extract the mapped feature types from the given data directory 189 this.ftMap = scanForMappedFeatureTypes(); 190 Map tempFeatureTypeMap = (Map) ( (HashMap) this.ftMap ).clone(); 191 192 // check that for each configuration feature type a mapped feature type exists and that 193 // their DefaultSRS are identical 194 WFSFeatureType[] wfsFTs = getFeatureTypeList().getFeatureTypes(); 195 for ( int i = 0; i < wfsFTs.length; i++ ) { 196 197 if ( !wfsFTs[i].isVirtual() ) { 198 MappedFeatureType mappedFT = this.ftMap.get( wfsFTs[i].getName() ); 199 if ( mappedFT == null ) { 200 String msg = Messages.getMessage( "WFS_CONF_FT_APP_SCHEMA_MISSING", 201 wfsFTs[i].getName() ); 202 throw new InvalidConfigurationException( msg ); 203 } 204 if ( !mappedFT.isVisible() ) { 205 String msg = Messages.getMessage( "WFS_CONF_FT_APP_SCHEMA_INVISIBLE", 206 wfsFTs[i].getName() ); 207 throw new InvalidConfigurationException( msg ); 208 } 209 URI defaultSRS = mappedFT.getGMLSchema().getDefaultSRS(); 210 if ( !defaultSRS.equals( wfsFTs[i].getDefaultSRS() ) ) { 211 String msg = Messages.getMessage( "WFS_CONF_FT_APP_SCHEMA_WRONG_SRS", 212 wfsFTs[i].getName(), 213 wfsFTs[i].getDefaultSRS(), defaultSRS ); 214 throw new InvalidConfigurationException( msg ); 215 } 216 } 217 218 // remove datastore feature type 219 tempFeatureTypeMap.remove( wfsFTs[i].getName() ); 220 } 221 222 // add all remaining mapped feature types to the WFS configuration 223 Iterator it = tempFeatureTypeMap.keySet().iterator(); 224 while ( it.hasNext() ) { 225 QualifiedName featureTypeName = (QualifiedName) it.next(); 226 MappedFeatureType mappedFT = (MappedFeatureType) tempFeatureTypeMap.get( featureTypeName ); 227 if ( mappedFT.isVisible() ) { 228 try { 229 getFeatureTypeList().addFeatureType( createWFSFeatureType( mappedFT ) ); 230 } catch ( UnknownCRSException e ) { 231 throw new InvalidConfigurationException( e ); 232 } 233 } 234 } 235 } 236 237 /** 238 * Scans for and extracts the <code>MappedFeatureType</code> s that are located in the data 239 * directories of the current WFS configuration. 240 * 241 * @return keys: featureTypeNames, values: mapped feature types 242 * @throws InvalidConfigurationException 243 */ 244 private Map<QualifiedName, MappedFeatureType> scanForMappedFeatureTypes() 245 throws InvalidConfigurationException { 246 List<String> fileNameList = new ArrayList<String>(); 247 String[] dataDirectories = getDeegreeParams().getDataDirectories(); 248 249 for ( int i = 0; i < dataDirectories.length; i++ ) { 250 File file = new File( dataDirectories[i] ); 251 String[] list = file.list( new XSDFileFilter() ); 252 if ( list != null ) { 253 if ( list.length == 0 ) { 254 LOG.logInfo( "Specified datastore directory '" + dataDirectories[i] 255 + "' does not contain any '.xsd' files." ); 256 } 257 for ( int j = 0; j < list.length; j++ ) { 258 fileNameList.add( dataDirectories[i] + '/' + list[j] ); 259 } 260 } else { 261 LOG.logInfo( "Specified datastore directory '" + dataDirectories[i] 262 + "' does not denote a directory." ); 263 } 264 } 265 String[] fileNames = fileNameList.toArray( new String[fileNameList.size()] ); 266 return extractMappedFeatureTypes( fileNames ); 267 } 268 269 /** 270 * Extracts the {@link MappedFeatureType} s which are defined in the given array of mapped 271 * application schema filenames. Files that do not conform to mapped GML Application Schemas 272 * definition are omitted, so are multiple definitions of the same feature types. 273 * 274 * @param fileNames 275 * fileNames to be scanned 276 * @return keys: feature type names, values: mapped feature types 277 * @throws InvalidConfigurationException 278 */ 279 private Map<QualifiedName, MappedFeatureType> extractMappedFeatureTypes( String[] fileNames ) 280 throws InvalidConfigurationException { 281 282 Map<QualifiedName, MappedFeatureType> featureTypeMap = new HashMap<QualifiedName, MappedFeatureType>( 283 fileNames.length ); 284 285 for ( int i = 0; i < fileNames.length; i++ ) { 286 try { 287 URL fileURL = new File( fileNames[i] ).toURL(); 288 LOG.logInfo( "Reading annotated GML application schema from URL '" + fileURL + "'." ); 289 MappedGMLSchemaDocument doc = new MappedGMLSchemaDocument(); 290 doc.load( fileURL ); 291 MappedGMLSchema gmlSchema = doc.parseMappedGMLSchema(); 292 293 FeatureType[] featureTypes = gmlSchema.getFeatureTypes(); 294 for ( int j = 0; j < featureTypes.length; j++ ) { 295 MappedFeatureType ft = featureTypeMap.get( featureTypes[j].getName() ); 296 if ( ft != null ) { 297 String msg = Messages.getMessage( "WFS_CONF_MULTIPLE_FEATURE_TYPE_DEF", 298 featureTypes[j].getName(), fileNames[i] ); 299 throw new InvalidConfigurationException( msg ); 300 } 301 featureTypeMap.put( featureTypes[j].getName(), 302 (MappedFeatureType) featureTypes[j] ); 303 } 304 } catch ( IOException e ) { 305 LOG.logError( e.getMessage(), e ); 306 String msg = "Error loading '" + fileNames[i] + "': " + e.getMessage(); 307 throw new InvalidConfigurationException( msg, e ); 308 } catch ( Exception e ) { 309 LOG.logError( e.getMessage(), e ); 310 String msg = "Error parsing '" + fileNames[i] + "': " + e.getMessage(); 311 throw new InvalidConfigurationException( msg, e ); 312 } 313 } 314 return featureTypeMap; 315 } 316 317 /** 318 * Creates a (minimal) <code>WFSFeatureType</code> from the given 319 * <code>MappedFeatureType</code>. 320 * 321 * @param rootfeatureType 322 * @throws UnknownCRSException 323 */ 324 private WFSFeatureType createWFSFeatureType( MappedFeatureType mappedFeatureType ) 325 throws UnknownCRSException { 326 Operation[] operations = new Operation[1]; 327 operations[0] = new Operation( Operation.QUERY ); 328 FormatType[] outputFormats = new FormatType[1]; 329 // according to WFS 1.1.0 spec text/xml; subtype=gml/3.1.1 330 // is the only mandatory format 331 outputFormats[0] = new FormatType( null, null, null, "text/xml; subtype=gml/3.1.1" ); 332 CoordinateSystem crs = CRSFactory.create( "EPSG:4326" ); 333 Envelope[] wgs84BoundingBoxes = new Envelope[1]; 334 wgs84BoundingBoxes[0] = GeometryFactory.createEnvelope( -180, -90, 180, 90, crs ); 335 URI defaultSRS = mappedFeatureType.getGMLSchema().getDefaultSRS(); 336 WFSFeatureType featureType = new WFSFeatureType( mappedFeatureType.getName(), null, null, 337 null, defaultSRS, null, operations, 338 outputFormats, wgs84BoundingBoxes, null ); 339 return featureType; 340 } 341 342 static class XSDFileFilter implements FilenameFilter { 343 344 /** 345 * Tests if a specified file should be included in a file list. 346 * 347 * @param dir 348 * the directory in which the file was found 349 * @param name 350 * the name of the file 351 * @return <code>true</code> if and only if the name should be included in the file list; 352 * <code>false</code> otherwise 353 */ 354 public boolean accept( File dir, String name ) { 355 int pos = name.lastIndexOf( "." ); 356 String ext = name.substring( pos + 1 ); 357 return ext.toUpperCase().equals( "XSD" ); 358 } 359 } 360 }