//$Header: /home/deegree/jail/deegreerepository/deegree/src/org/deegree/ogcwebservices/wfs/configuration/WFSConfiguration.java,v 1.22 2006/11/27 09:07:53 poth Exp $ /*---------------- FILE HEADER ------------------------------------------ This file is part of deegree. Copyright (C) 2001-2006 by: EXSE, Department of Geography, University of Bonn http://www.giub.uni-bonn.de/deegree/ lat/lon GmbH http://www.lat-lon.de This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Contact: Andreas Poth lat/lon GmbH Aennchenstraße 19 53177 Bonn E-Mail: poth@lat-lon.de Prof. Dr. Klaus Greve Department of Geography University of Bonn Meckenheimer Allee 166 53115 Bonn Germany E-Mail: greve@giub.uni-bonn.de ---------------------------------------------------------------------------*/ package org.deegree.ogcwebservices.wfs.configuration; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.net.URI; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.deegree.datatypes.QualifiedName; import org.deegree.framework.log.ILogger; import org.deegree.framework.log.LoggerFactory; import org.deegree.framework.xml.InvalidConfigurationException; import org.deegree.i18n.Messages; import org.deegree.io.datastore.schema.MappedFeatureType; import org.deegree.io.datastore.schema.MappedGMLSchema; import org.deegree.io.datastore.schema.MappedGMLSchemaDocument; import org.deegree.model.crs.CRSFactory; import org.deegree.model.crs.CoordinateSystem; import org.deegree.model.crs.UnknownCRSException; import org.deegree.model.feature.schema.FeatureType; import org.deegree.model.filterencoding.capabilities.FilterCapabilities; import org.deegree.model.spatialschema.Envelope; import org.deegree.model.spatialschema.GeometryFactory; import org.deegree.ogcwebservices.getcapabilities.Contents; import org.deegree.ogcwebservices.getcapabilities.OperationsMetadata; import org.deegree.ogcwebservices.getcapabilities.ServiceIdentification; import org.deegree.ogcwebservices.getcapabilities.ServiceProvider; import org.deegree.ogcwebservices.wfs.WFService; import org.deegree.ogcwebservices.wfs.capabilities.FeatureTypeList; import org.deegree.ogcwebservices.wfs.capabilities.FormatType; import org.deegree.ogcwebservices.wfs.capabilities.GMLObject; import org.deegree.ogcwebservices.wfs.capabilities.Operation; import org.deegree.ogcwebservices.wfs.capabilities.WFSCapabilities; import org.deegree.ogcwebservices.wfs.capabilities.WFSFeatureType; /** * Represents the configuration for a deegree {@link WFService} instance. * * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider </a> * @author last edited by: $Author: poth $ * * @version $Revision: 1.22 $, $Date: 2006/11/27 09:07:53 $ */ public class WFSConfiguration extends WFSCapabilities { private static final long serialVersionUID = -8929822028461025018L; protected static final ILogger LOG = LoggerFactory.getLogger( WFSConfiguration.class ); private WFSDeegreeParams deegreeParams; private Map<QualifiedName, MappedFeatureType> featureTypeMap = new HashMap<QualifiedName, MappedFeatureType>(); /** * Generates a new <code>WFSConfiguration</code> instance from the given parameters. * * @param version * @param updateSequence * @param serviceIdentification * @param serviceProvider * @param operationsMetadata * @param featureTypeList * @param servesGMLObjectTypeList * @param supportsGMLObjectTypeList * @param contents * TODO field not verified! Check spec. * @param filterCapabilities * @param deegreeParams * @throws InvalidConfigurationException */ public WFSConfiguration( String version, String updateSequence, ServiceIdentification serviceIdentification, ServiceProvider serviceProvider, OperationsMetadata operationsMetadata, FeatureTypeList featureTypeList, GMLObject[] servesGMLObjectTypeList, GMLObject[] supportsGMLObjectTypeList, Contents contents, FilterCapabilities filterCapabilities, WFSDeegreeParams deegreeParams ) throws InvalidConfigurationException { super( version, updateSequence, serviceIdentification, serviceProvider, operationsMetadata, featureTypeList, servesGMLObjectTypeList, supportsGMLObjectTypeList, contents, filterCapabilities ); this.deegreeParams = deegreeParams; try { validateFeatureTypeDefinitions(); } catch ( InvalidConfigurationException e ) { LOG.logError( e.getMessage() ); throw e; } } /** * Returns the deegreeParams. * * @return the deegreeParams */ public WFSDeegreeParams getDeegreeParams() { return deegreeParams; } /** * The deegreeParams to set. * * @param deegreeParams */ public void setDeegreeParams( WFSDeegreeParams deegreeParams ) { this.deegreeParams = deegreeParams; } /** * Returns a <code>Map</code> of the feature types that this configuration defines. * * @return keys: feature type names, values: mapped feature types */ public Map<QualifiedName, MappedFeatureType> getMappedFeatureTypes() { return this.featureTypeMap; } /** * The <code>WFSConfiguration</code> is processed as follows: * <ul> * <li>The data directories (as specified in the configuration) are scanned for * {@link MappedGMLSchemaDocument}s</li> * <li>All {@link MappedFeatureType}s defined in any of the found * {@link MappedGMLSchemaDocument}s are extracted, if duplicate feature type definitions * occur, an <code>InvalidConfigurationException</code> is thrown.</li> * <li>All feature types defined in the FeatureTypeList section of the WFS configuration are * checked to have a corresponding {@link MappedFeatureType} definition. If this is not the * case (or if the DefaultSRS is not equal to the CRS in the respective datastore definition), * an <code>InvalidConfigurationException</code> is thrown.</li> * <li>NOTE: the above is not necessary for FeatureTypes that are processed by XSLT-scripts * (because they are usually mapped to a different FeatureTypes by XSLT)</li> * <li>All feature types that are not defined in the FeatureTypeList section, but have been * defined in one of the {@link MappedGMLSchemaDocument}s are added as feature types to * the WFS configuration.</li> * </ul> * </p> * * @throws InvalidConfigurationException */ private void validateFeatureTypeDefinitions() throws InvalidConfigurationException { // extract the mapped feature types from the given data directory this.featureTypeMap = scanForMappedFeatureTypes(); Map tempFeatureTypeMap = (Map) ( (HashMap) this.featureTypeMap ).clone(); // check that for each configuration feature type a mapped feature type exists and that // their DefaultSRS are identical WFSFeatureType[] wfsFTs = getFeatureTypeList().getFeatureTypes(); for ( int i = 0; i < wfsFTs.length; i++ ) { if ( !wfsFTs[i].isVirtual() ) { MappedFeatureType mappedFT = this.featureTypeMap.get( wfsFTs[i].getName() ); if ( mappedFT == null ) { String msg = Messages.getMessage( "WFS_CONF_FT_APP_SCHEMA_MISSING", wfsFTs[i].getName() ); throw new InvalidConfigurationException( msg ); } if ( !mappedFT.isVisible() ) { String msg = Messages.getMessage( "WFS_CONF_FT_APP_SCHEMA_INVISIBLE", wfsFTs[i].getName() ); throw new InvalidConfigurationException( msg ); } URI defaultSRS = mappedFT.getGMLSchema().getDefaultSRS(); if ( !defaultSRS.equals( wfsFTs[i].getDefaultSRS() ) ) { String msg = Messages.getMessage( "WFS_CONF_FT_APP_SCHEMA_WRONG_SRS", wfsFTs[i].getName(), wfsFTs[i].getDefaultSRS(), defaultSRS ); throw new InvalidConfigurationException( msg ); } } // remove datastore feature type tempFeatureTypeMap.remove( wfsFTs[i].getName() ); } // add all remaining mapped feature types to the WFS configuration Iterator it = tempFeatureTypeMap.keySet().iterator(); while ( it.hasNext() ) { QualifiedName featureTypeName = (QualifiedName) it.next(); MappedFeatureType mappedFT = (MappedFeatureType) tempFeatureTypeMap.get( featureTypeName ); if ( mappedFT.isVisible() ) { try { getFeatureTypeList().addFeatureType( createWFSFeatureType( mappedFT ) ); } catch ( UnknownCRSException e ) { throw new InvalidConfigurationException( e ); } } } } /** * Scans for and extracts the <code>MappedFeatureType</code> s that are located in the data * directories of the current WFS configuration. * * @return keys: featureTypeNames, values: mapped feature types * @throws InvalidConfigurationException */ private Map<QualifiedName, MappedFeatureType> scanForMappedFeatureTypes() throws InvalidConfigurationException { List<String> fileNameList = new ArrayList<String>(); String[] dataDirectories = getDeegreeParams().getDataDirectories(); for ( int i = 0; i < dataDirectories.length; i++ ) { File file = new File( dataDirectories[i] ); String[] list = file.list( new XSDFileFilter() ); if ( list != null ) { if ( list.length == 0 ) { LOG.logInfo( "Specified datastore directory '" + dataDirectories[i] + "' does not contain any '.xsd' files." ); } for ( int j = 0; j < list.length; j++ ) { fileNameList.add( dataDirectories[i] + '/' + list[j] ); } } else { LOG.logInfo( "Specified datastore directory '" + dataDirectories[i] + "' does not denote a directory." ); } } String[] fileNames = fileNameList.toArray( new String[fileNameList.size()] ); return extractMappedFeatureTypes( fileNames ); } /** * Extracts the {@link MappedFeatureType} s which are defined in the given array of * mapped application schema filenames. Files that do not conform to mapped GML Application * Schemas definition are omitted, so are multiple definitions of the same feature types. * * @param fileNames * fileNames to be scanned * @return keys: feature type names, values: mapped feature types * @throws InvalidConfigurationException */ private Map<QualifiedName, MappedFeatureType> extractMappedFeatureTypes( String[] fileNames ) throws InvalidConfigurationException { Map<QualifiedName, MappedFeatureType> featureTypeMap = new HashMap<QualifiedName, MappedFeatureType>( fileNames.length ); for ( int i = 0; i < fileNames.length; i++ ) { LOG.logInfo( "Parsing (mapped) GML application schema file '" + fileNames[i] + "'." ); try { URL fileURL = new File( fileNames[i] ).toURL(); MappedGMLSchemaDocument doc = new MappedGMLSchemaDocument(); doc.load( fileURL ); MappedGMLSchema gmlSchema = doc.parseMappedGMLSchema(); FeatureType[] featureTypes = gmlSchema.getFeatureTypes(); for ( int j = 0; j < featureTypes.length; j++ ) { MappedFeatureType ft = featureTypeMap.get( featureTypes[j].getName() ); if ( ft != null ) { String msg = Messages.getMessage( "WFS_CONF_MULTIPLE_FEATURE_TYPE_DEF", featureTypes[j].getName(), fileNames[i] ); throw new InvalidConfigurationException( msg ); } featureTypeMap.put( featureTypes[j].getName(), (MappedFeatureType) featureTypes[j] ); } } catch ( IOException e ) { String msg = "Error loading '" + fileNames[i] + "': " + e.getMessage(); throw new InvalidConfigurationException( msg, e ); } catch ( Exception e ) { String msg = "Error parsing '" + fileNames[i] + "': " + e.getMessage(); throw new InvalidConfigurationException( msg, e ); } } return featureTypeMap; } /** * Creates a (minimal) <code>WFSFeatureType</code> from the given * <code>MappedFeatureType</code>. * * @param rootfeatureType * @throws UnknownCRSException */ private WFSFeatureType createWFSFeatureType( MappedFeatureType mappedFeatureType ) throws UnknownCRSException { Operation[] operations = new Operation[1]; operations[0] = new Operation( Operation.QUERY ); FormatType[] outputFormats = new FormatType[1]; // according to WFS 1.1.0 spec text/xml; subtype=gml/3.1.1 // is the only mandatory format outputFormats[0] = new FormatType( null, null, null, "text/xml; subtype=gml/3.1.1" ); CoordinateSystem crs = CRSFactory.create( "EPSG:4326" ); Envelope[] wgs84BoundingBoxes = new Envelope[1]; wgs84BoundingBoxes[0] = GeometryFactory.createEnvelope( -180, -90, 180, 90, crs ); URI defaultSRS = mappedFeatureType.getGMLSchema().getDefaultSRS(); WFSFeatureType featureType = new WFSFeatureType( mappedFeatureType.getName(), null, null, null, defaultSRS, null, operations, outputFormats, wgs84BoundingBoxes, null ); return featureType; } static class XSDFileFilter implements FilenameFilter { /** * Tests if a specified file should be included in a file list. * * @param dir * the directory in which the file was found * @param name * the name of the file * @return <code>true</code> if and only if the name should be included in the file list; * <code>false</code> otherwise */ public boolean accept( File dir, String name ) { int pos = name.lastIndexOf( "." ); String ext = name.substring( pos + 1 ); return ext.toUpperCase().equals( "XSD" ); } } } /* ******************************************************************** Changes to this class. What the people have been up to: $Log: WFSConfiguration.java,v $ Revision 1.22 2006/11/27 09:07:53 poth JNI integration of proj4 has been removed. The CRS functionality now will be done by native deegree code. Revision 1.21 2006/11/15 18:39:16 mschneider Fixed reference to Message. Revision 1.20 2006/11/02 12:34:10 mschneider Fixed NPE in #postprocess() that occured when a feature type definition in the WFS configuration had no DefaultSRS element. Revision 1.19 2006/10/12 16:24:00 mschneider Javadoc + compiler warning fixes. Revision 1.18 2006/10/02 16:54:47 mschneider Added handling of virtual feature type, i.e. feature types that are handled via XSL-scripts. Revision 1.17 2006/08/24 06:42:17 poth File header corrected Revision 1.16 2006/07/12 14:46:18 poth comment footer added ********************************************************************** */