001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/ogcwebservices/wms/configuration/WMSConfigurationDocument.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.ogcwebservices.wms.configuration;
037    
038    import static org.deegree.framework.xml.XMLTools.getElements;
039    import static org.deegree.framework.xml.XMLTools.getNodeAsBoolean;
040    
041    import java.awt.Color;
042    import java.io.IOException;
043    import java.net.MalformedURLException;
044    import java.net.URI;
045    import java.net.URL;
046    import java.util.ArrayList;
047    import java.util.Arrays;
048    import java.util.HashMap;
049    import java.util.List;
050    import java.util.Map;
051    
052    import javax.xml.transform.TransformerException;
053    
054    import org.deegree.datatypes.QualifiedName;
055    import org.deegree.enterprise.Proxy;
056    import org.deegree.framework.log.ILogger;
057    import org.deegree.framework.log.LoggerFactory;
058    import org.deegree.framework.util.BootLogger;
059    import org.deegree.framework.util.IDGenerator;
060    import org.deegree.framework.util.KVP2Map;
061    import org.deegree.framework.util.StringTools;
062    import org.deegree.framework.xml.InvalidConfigurationException;
063    import org.deegree.framework.xml.XMLFragment;
064    import org.deegree.framework.xml.XMLParsingException;
065    import org.deegree.framework.xml.XMLTools;
066    import org.deegree.framework.xml.XSLTDocument;
067    import org.deegree.i18n.Messages;
068    import org.deegree.io.IODocument;
069    import org.deegree.io.JDBCConnection;
070    import org.deegree.model.crs.CRSFactory;
071    import org.deegree.model.crs.CoordinateSystem;
072    import org.deegree.model.crs.UnknownCRSException;
073    import org.deegree.model.metadata.iso19115.OnlineResource;
074    import org.deegree.model.spatialschema.Envelope;
075    import org.deegree.model.spatialschema.GMLGeometryAdapter;
076    import org.deegree.model.spatialschema.Geometry;
077    import org.deegree.model.spatialschema.GeometryException;
078    import org.deegree.ogcbase.CommonNamespaces;
079    import org.deegree.ogcwebservices.OGCWebService;
080    import org.deegree.ogcwebservices.getcapabilities.InvalidCapabilitiesException;
081    import org.deegree.ogcwebservices.getcapabilities.MetadataURL;
082    import org.deegree.ogcwebservices.getcapabilities.OGCCapabilities;
083    import org.deegree.ogcwebservices.wcs.RemoteWCService;
084    import org.deegree.ogcwebservices.wcs.WCService;
085    import org.deegree.ogcwebservices.wcs.configuration.WCSConfiguration;
086    import org.deegree.ogcwebservices.wcs.getcapabilities.WCSCapabilities;
087    import org.deegree.ogcwebservices.wcs.getcapabilities.WCSCapabilitiesDocument;
088    import org.deegree.ogcwebservices.wcs.getcoverage.GetCoverage;
089    import org.deegree.ogcwebservices.wfs.RemoteWFService;
090    import org.deegree.ogcwebservices.wfs.WFServiceFactory;
091    import org.deegree.ogcwebservices.wfs.capabilities.WFSCapabilities;
092    import org.deegree.ogcwebservices.wfs.capabilities.WFSCapabilitiesDocument;
093    import org.deegree.ogcwebservices.wfs.configuration.WFSConfiguration;
094    import org.deegree.ogcwebservices.wfs.configuration.WFSConfigurationDocument;
095    import org.deegree.ogcwebservices.wfs.operation.Query;
096    import org.deegree.ogcwebservices.wms.RemoteWMService;
097    import org.deegree.ogcwebservices.wms.capabilities.Attribution;
098    import org.deegree.ogcwebservices.wms.capabilities.AuthorityURL;
099    import org.deegree.ogcwebservices.wms.capabilities.DataURL;
100    import org.deegree.ogcwebservices.wms.capabilities.Dimension;
101    import org.deegree.ogcwebservices.wms.capabilities.Extent;
102    import org.deegree.ogcwebservices.wms.capabilities.FeatureListURL;
103    import org.deegree.ogcwebservices.wms.capabilities.Identifier;
104    import org.deegree.ogcwebservices.wms.capabilities.Layer;
105    import org.deegree.ogcwebservices.wms.capabilities.LayerBoundingBox;
106    import org.deegree.ogcwebservices.wms.capabilities.LegendURL;
107    import org.deegree.ogcwebservices.wms.capabilities.ScaleHint;
108    import org.deegree.ogcwebservices.wms.capabilities.Style;
109    import org.deegree.ogcwebservices.wms.capabilities.StyleSheetURL;
110    import org.deegree.ogcwebservices.wms.capabilities.StyleURL;
111    import org.deegree.ogcwebservices.wms.capabilities.UserDefinedSymbolization;
112    import org.deegree.ogcwebservices.wms.capabilities.WMSCapabilities;
113    import org.deegree.ogcwebservices.wms.capabilities.WMSCapabilitiesDocument;
114    import org.deegree.ogcwebservices.wms.capabilities.WMSCapabilitiesDocumentFactory;
115    import org.deegree.ogcwebservices.wms.dataaccess.ExternalDataAccess;
116    import org.deegree.ogcwebservices.wms.operation.GetMap;
117    import org.deegree.owscommon_new.OperationsMetadata;
118    import org.deegree.owscommon_new.ServiceIdentification;
119    import org.deegree.owscommon_new.ServiceProvider;
120    import org.w3c.dom.Element;
121    import org.w3c.dom.Node;
122    import org.xml.sax.SAXException;
123    
124    /**
125     * <code>WMSConfigurationDocument</code> is the parser class for a standard 1.1.1 WMS configuration document, ie, a
126     * capabilities document enriched by deegree parameters.
127     * 
128     * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider </a>
129     * @author last edited by: $Author: aschmitz $
130     * 
131     * @version 2.0, $Revision: 18525 $, $Date: 2009-07-20 10:26:25 +0200 (Mo, 20. Jul 2009) $
132     * 
133     * @since 2.0
134     */
135    public class WMSConfigurationDocument extends WMSCapabilitiesDocument {
136    
137        private static final long serialVersionUID = 2320990982989322325L;
138    
139        protected static final URI DEEGREEWMSNS = CommonNamespaces.DEEGREEWMS;
140    
141        private static final String XML_TEMPLATE = "WMSConfigurationTemplate.xml";
142    
143        private static final String XSLT_TEMPLATE_NAME = "WMSConfigurationTransform.xsl";
144    
145        private static XSLTDocument XSLT_TEMPLATE;
146    
147        private static Map<URL, OGCCapabilities> capaCache = new HashMap<URL, OGCCapabilities>();
148    
149        private static final ILogger LOG = LoggerFactory.getLogger( WMSConfigurationDocument.class );
150    
151        private static final QualifiedName DEFAULT_GEO_PROP = new QualifiedName(
152                                                                                 "app",
153                                                                                 "GEOM",
154                                                                                 CommonNamespaces.buildNSURI( "http://www.deegree.org/app" ) );
155    
156        static {
157            XSLT_TEMPLATE = new XSLTDocument();
158            try {
159                XSLT_TEMPLATE.load( WMSConfigurationDocument.class.getResource( XSLT_TEMPLATE_NAME ) );
160            } catch ( Exception e ) {
161                BootLogger.logError( "Error loading XSLT sheet in WMSConfigurationDocument.", e );
162            }
163        }
164    
165        /**
166         *
167         */
168        public static void resetCapabilitiesCache() {
169            capaCache.clear();
170        }
171    
172        /**
173         * Creates a skeleton capabilities document that contains the mandatory elements only.
174         * 
175         * @throws IOException
176         * @throws SAXException
177         */
178        @Override
179        public void createEmptyDocument()
180                                throws IOException, SAXException {
181            URL url = WMSConfigurationDocument.class.getResource( XML_TEMPLATE );
182            if ( url == null ) {
183                throw new IOException( "The resource '" + XML_TEMPLATE + " could not be found." );
184            }
185            load( url );
186        }
187    
188        /**
189         * Creates a class representation of the document.
190         * 
191         * @return class representation of the configuration document
192         * @throws InvalidConfigurationException
193         */
194        public WMSConfiguration parseConfiguration()
195                                throws InvalidConfigurationException {
196    
197            try {
198                // transform document to fill missing elements and attributes with
199                // default values
200                XMLFragment frag = XSLT_TEMPLATE.transform( this );
201                this.setRootElement( frag.getRootElement() );
202                if ( LOG.isDebug() ) {
203                    LOG.logDebug( "Transformed WMS configuration document", getAsPrettyString() );
204                }
205            } catch ( TransformerException e ) {
206                String msg = Messages.getMessage( "WMS_CONFIGURATION_TRANSFORM" );
207                LOG.logError( msg, e );
208                throw new InvalidConfigurationException( msg, e );
209            }
210    
211            ServiceIdentification serviceIdentification = null;
212            ServiceProvider serviceProvider = null;
213            OperationsMetadata metadata = null;
214            Layer layer = null;
215            UserDefinedSymbolization uds = null;
216            WMSDeegreeParams params = null;
217            Element root = getRootElement();
218            String version = root.getAttribute( "version" );
219            String updateSeq = root.getAttribute( "updateSequence" );
220            List<String> exceptions;
221    
222            try {
223                Node node = XMLTools.getRequiredNode( getRootElement(), "./deegreewms:DeegreeParam", nsContext );
224                params = parseDeegreeParams( node );
225    
226                serviceIdentification = parseServiceIdentification();
227                serviceProvider = parseServiceProvider();
228                metadata = parseOperationsMetadata();
229    
230                Element exceptionElement = XMLTools.getRequiredElement( getRootElement(), "Capability/Exception", nsContext );
231                exceptions = parseExceptionFormats( exceptionElement );
232    
233                uds = parseUserDefinedSymbolization();
234                Element layerElem = (Element) XMLTools.getRequiredNode( getRootElement(), "./Capability/Layer", nsContext );
235                layer = parseLayers( layerElem, null, null );
236    
237            } catch ( XMLParsingException e ) {
238                e.printStackTrace();
239                throw new InvalidConfigurationException( e.getMessage() + StringTools.stackTraceToString( e ) );
240            } catch ( MalformedURLException e ) {
241                throw new InvalidConfigurationException( e.getMessage() + " - " + StringTools.stackTraceToString( e ) );
242            } catch ( UnknownCRSException e ) {
243                throw new InvalidConfigurationException( e.getMessage() + " - " + StringTools.stackTraceToString( e ) );
244            }
245    
246            WMSConfiguration wmsConfiguration = new WMSConfiguration( version, updateSeq, serviceIdentification,
247                                                                      serviceProvider, uds, metadata, layer, params,
248                                                                      getSystemId(), exceptions );
249    
250            return wmsConfiguration;
251        }
252    
253        /**
254         * Creates a class representation of the <code>deegreeParams</code>- section.
255         * 
256         * @param root
257         * 
258         * @return the deegree params
259         * @throws XMLParsingException
260         * @throws MalformedURLException
261         */
262        public WMSDeegreeParams parseDeegreeParams( Node root )
263                                throws XMLParsingException, MalformedURLException {
264    
265            Element elem = (Element) XMLTools.getRequiredNode( root, "./deegreewms:DefaultOnlineResource", nsContext );
266            OnlineResource ol = parseOnLineResource( elem );
267            int cache = XMLTools.getNodeAsInt( root, "./deegreewms:CacheSize", nsContext, 100 );
268            int maxLifeTime = XMLTools.getNodeAsInt( root, "./deegreewms:MaxLifeTime", nsContext, 3600 );
269            int reqTimeLimit = XMLTools.getNodeAsInt( root, "./deegreewms:RequestTimeLimit", nsContext, 15 );
270            reqTimeLimit *= 1000;
271            double mapQuality = XMLTools.getNodeAsDouble( root, "./deegreewms:MapQuality", nsContext, 0.95 );
272            int maxMapWidth = XMLTools.getNodeAsInt( root, "./deegreewms:MaxMapWidth", nsContext, 1000 );
273            int maxMapHeight = XMLTools.getNodeAsInt( root, "./deegreewms:MaxMapHeight", nsContext, 1000 );
274            int featureInfoRadius = XMLTools.getNodeAsInt( root, "./deegreewms:FeatureInfoRadius", nsContext, 5 );
275            String copyright = XMLTools.getNodeAsString( root, "./deegreewms:Copyright", nsContext, "" );
276            String defaultPNGFormat = XMLTools.getNodeAsString( root, "deegreewms:DefaultPNGFormat", nsContext,
277                                                                "image/png; mode=24bit" );
278    
279            URL dtdLocation = null;
280            if ( XMLTools.getNode( root, "deegreewms:DTDLocation", nsContext ) != null ) {
281                elem = (Element) XMLTools.getRequiredNode( root, "./deegreewms:DTDLocation/deegreewms:OnlineResource",
282                                                           nsContext );
283                OnlineResource olr = parseOnLineResource( elem );
284                dtdLocation = olr.getLinkage().getHref();
285            } else {
286                dtdLocation = new URL( "http://schemas.opengis.net/wms/1.1.1/WMS_MS_Capabilities.dtd" );
287            }
288    
289            URL featureSchemaLocation = null;
290            String featureSchemaNamespace = null;
291            if ( XMLTools.getNode( root, "deegreewms:FeatureInfoSchema", nsContext ) != null ) {
292                featureSchemaNamespace = XMLTools.getRequiredNodeAsString(
293                                                                           root,
294                                                                           "deegreewms:FeatureInfoSchema/deegreewms:Namespace",
295                                                                           nsContext );
296                elem = (Element) XMLTools.getRequiredNode( root, "deegreewms:FeatureInfoSchema/deegreewms:OnlineResource",
297                                                           nsContext );
298                OnlineResource link = parseOnLineResource( elem );
299                featureSchemaLocation = link.getLinkage().getHref();
300            }
301    
302            boolean antiAliased = XMLTools.getNodeAsBoolean( root, "./deegreewms:AntiAliased", nsContext, true );
303    
304            boolean filtersAllowed = getNodeAsBoolean( root, "./deegreewms:FiltersAllowed", nsContext, false );
305    
306            Proxy proxy = parseProxy( root );
307    
308            List<String> supportedVersions = parseSupportedVersions( root );
309    
310            WMSDeegreeParams deegreeParams = new WMSDeegreeParams( cache, maxLifeTime, reqTimeLimit, (float) mapQuality,
311                                                                   ol, maxMapWidth, maxMapHeight, antiAliased,
312                                                                   featureInfoRadius, copyright, null, dtdLocation, proxy,
313                                                                   supportedVersions, featureSchemaLocation,
314                                                                   featureSchemaNamespace, filtersAllowed, defaultPNGFormat );
315    
316            return deegreeParams;
317        }
318    
319        // returns the list of supported versions
320        private List<String> parseSupportedVersions( Node root )
321                                throws XMLParsingException {
322    
323            String[] versions = XMLTools.getNodesAsStrings( root, "./deegreewms:SupportedVersion", nsContext );
324    
325            if ( versions != null )
326                return Arrays.asList( versions );
327    
328            return new ArrayList<String>();
329    
330        }
331    
332        /**
333         * @param root
334         * @return the proxy
335         * @throws XMLParsingException
336         */
337        private Proxy parseProxy( Node root )
338                                throws XMLParsingException {
339    
340            Proxy proxy = null;
341            Node pro = XMLTools.getNode( root, "./deegreewms:Proxy", nsContext );
342            if ( pro != null ) {
343                String proxyHost = XMLTools.getRequiredNodeAsString( pro, "./@proxyHost", nsContext );
344                String proxyPort = XMLTools.getRequiredNodeAsString( pro, "./@proxyPort", nsContext );
345                proxy = new Proxy( proxyHost, proxyPort );
346            }
347    
348            return proxy;
349        }
350    
351        /**
352         * returns the layers offered by the WMS
353         * 
354         * @throws XMLParsingException
355         * @throws UnknownCRSException
356         */
357        @Override
358        protected Layer parseLayers( Element layerElem, Layer parent, ScaleHint scaleHint )
359                                throws XMLParsingException, UnknownCRSException {
360    
361            boolean queryable = XMLTools.getNodeAsBoolean( layerElem, "./@queryable", nsContext, false );
362            int cascaded = XMLTools.getNodeAsInt( layerElem, "./@cascaded", nsContext, 0 );
363            boolean opaque = XMLTools.getNodeAsBoolean( layerElem, "./@opaque", nsContext, false );
364            boolean noSubsets = XMLTools.getNodeAsBoolean( layerElem, "./@noSubsets", nsContext, false );
365            int fixedWidth = XMLTools.getNodeAsInt( layerElem, "./@fixedWidth", nsContext, 0 );
366            int fixedHeight = XMLTools.getNodeAsInt( layerElem, "./@fixedHeight", nsContext, 0 );
367            String name = XMLTools.getNodeAsString( layerElem, "./Name", nsContext, null );
368            String title = XMLTools.getRequiredNodeAsString( layerElem, "./Title", nsContext );
369            String layerAbstract = XMLTools.getNodeAsString( layerElem, "./Abstract", nsContext, null );
370            String[] keywords = XMLTools.getNodesAsStrings( layerElem, "./KeywordList/Keyword", nsContext );
371            String[] srs = XMLTools.getNodesAsStrings( layerElem, "./SRS", nsContext );
372    
373            List<Element> nl = XMLTools.getElements( layerElem, "./BoundingBox", nsContext );
374            // TODO
375            // substitue with Envelope
376            LayerBoundingBox[] bboxes = null;
377            if ( nl.size() == 0 && parent != null ) {
378                // inherit BoundingBoxes from parent layer
379                bboxes = parent.getBoundingBoxes();
380            } else {
381                bboxes = parseLayerBoundingBoxes( nl );
382            }
383    
384            Element llBox = (Element) XMLTools.getNode( layerElem, "./LatLonBoundingBox", nsContext );
385            Envelope llBoundingBox = null;
386            if ( llBox == null && parent != null ) {
387                // inherit LatLonBoundingBox parent layer
388                llBoundingBox = parent.getLatLonBoundingBox();
389            } else {
390                llBoundingBox = parseLatLonBoundingBox( llBox );
391            }
392    
393            Dimension[] dimensions = parseDimensions( layerElem );
394            Extent[] extents = parseExtents( layerElem );
395    
396            Attribution attribution = parseAttribution( layerElem );
397    
398            AuthorityURL[] authorityURLs = parseAuthorityURLs( layerElem );
399    
400            MetadataURL[] metadataURLs = parseMetadataURLs( layerElem );
401    
402            DataURL[] dataURLs = parseDataURL( layerElem );
403    
404            Identifier[] identifiers = parseIdentifiers( layerElem );
405    
406            FeatureListURL[] featureListURLs = parseFeatureListURL( layerElem );
407    
408            Style[] styles = parseStyles( layerElem );
409    
410            scaleHint = parseScaleHint( layerElem, scaleHint );
411    
412            AbstractDataSource[] ds = parseDataSources( layerElem, name, scaleHint );
413    
414            Layer layer = new Layer( queryable, cascaded, opaque, noSubsets, fixedWidth, fixedHeight, name, title,
415                                     layerAbstract, llBoundingBox, attribution, scaleHint, keywords, srs, bboxes,
416                                     dimensions, extents, authorityURLs, identifiers, metadataURLs, dataURLs,
417                                     featureListURLs, styles, null, ds, parent );
418    
419            // get Child layers
420            nl = XMLTools.getElements( layerElem, "./Layer", nsContext );
421            Layer[] layers = new Layer[nl.size()];
422            for ( int i = 0; i < layers.length; i++ ) {
423                layers[i] = parseLayers( nl.get( i ), layer, scaleHint );
424            }
425    
426            // set child layers
427            layer.setLayer( layers );
428    
429            return layer;
430        }
431    
432        @Override
433        protected Dimension[] parseDimensions( Element layerElem )
434                                throws XMLParsingException {
435            // it's a config document and uses extended 1.3.0 style dimension configuration
436            List<Element> nl = getElements( layerElem, "Dimension", nsContext );
437            Dimension[] dimensions = new Dimension[nl.size()];
438    
439            for ( int i = 0; i < dimensions.length; i++ ) {
440                dimensions[i] = new Dimension( nl.get( i ) );
441            }
442    
443            return dimensions;
444        }
445    
446        /**
447         * 
448         * @param layerElem
449         * @return the data sources
450         * @throws XMLParsingException
451         */
452        protected AbstractDataSource[] parseDataSources( Element layerElem, String layerName, ScaleHint scaleHint )
453                                throws XMLParsingException {
454    
455            List<Node> nl = XMLTools.getNodes( layerElem, "./deegreewms:DataSource", nsContext );
456    
457            AbstractDataSource[] ds = new AbstractDataSource[nl.size()];
458            for ( int i = 0; i < ds.length; i++ ) {
459                boolean failOnEx = XMLTools.getNodeAsBoolean( nl.get( i ), "./@failOnException", nsContext, true );
460                boolean queryable = XMLTools.getNodeAsBoolean( nl.get( i ), "./@queryable", nsContext, false );
461                QualifiedName name = XMLTools.getNodeAsQualifiedName( nl.get( i ), "./deegreewms:Name/text()", nsContext,
462                                                                      new QualifiedName( layerName ) );
463                String stype = XMLTools.getRequiredNodeAsString( nl.get( i ), "./deegreewms:Type", nsContext );
464    
465                int reqTimeLimit = XMLTools.getNodeAsInt( nl.get( i ), "./deegreewms:RequestTimeLimit/text()", nsContext,
466                                                          30 );
467    
468                scaleHint = parseDSScaleHint( (Element) nl.get( i ), scaleHint );
469    
470                String s = "./deegreewms:OWSCapabilities/deegreewms:OnlineResource";
471                Node node = XMLTools.getRequiredNode( nl.get( i ), s, nsContext );
472    
473                URL url = parseOnLineResource( (Element) node ).getLinkage().getHref();
474    
475                Geometry validArea = parseValidArea( nl.get( i ) );
476                try {
477                    if ( "LOCALWFS".equals( stype ) ) {
478                        ds[i] = createLocalWFSDataSource( nl.get( i ), failOnEx, queryable, name, url, scaleHint,
479                                                          validArea, reqTimeLimit );
480                    } else if ( "LOCALWCS".equals( stype ) ) {
481                        ds[i] = createLocalWCSDataSource( nl.get( i ), failOnEx, queryable, name, url, scaleHint,
482                                                          validArea, reqTimeLimit );
483                    } else if ( "REMOTEWFS".equals( stype ) ) {
484                        ds[i] = createRemoteWFSDataSource( nl.get( i ), failOnEx, queryable, name, url, scaleHint,
485                                                           validArea, reqTimeLimit );
486                    } else if ( "REMOTEWCS".equals( stype ) ) {
487                        ds[i] = createRemoteWCSDataSource( nl.get( i ), failOnEx, queryable, name, url, scaleHint,
488                                                           validArea, reqTimeLimit );
489                    } else if ( "REMOTEWMS".equals( stype ) ) {
490                        ds[i] = createRemoteWMSDataSource( nl.get( i ), failOnEx, queryable, name, url, scaleHint,
491                                                           validArea, reqTimeLimit );
492                    } else if ( "DATABASE".equals( stype ) ) {
493                        ds[i] = createDatabaseSource( nl.get( i ), failOnEx, queryable, name, scaleHint, validArea,
494                                                      reqTimeLimit );
495                    } else if ( "EXTERNALDATAACCESS".equals( stype ) ) {
496                        ds[i] = createExternalDataAccess( nl.get( i ), failOnEx, queryable, name, scaleHint, validArea,
497                                                          reqTimeLimit );
498                    } else {
499                        throw new XMLParsingException( "invalid DataSource type: " + stype + " defined "
500                                                       + "in deegree WMS configuration for DataSource: " + name );
501                    }
502                } catch ( Exception e ) {
503                    LOG.logError( e.getMessage(), e );
504                    throw new XMLParsingException( "could not create service instance for WMS " + "datasource: " + name, e );
505                }
506            }
507    
508            return ds;
509        }
510    
511        /**
512         * @param node
513         * @param failOnEx
514         * @param queryable
515         * @param name
516         * @param scaleHint
517         * @param validArea
518         * @param reqTimeLimit
519         */
520        private AbstractDataSource createExternalDataAccess( Node node, boolean failOnEx, boolean queryable,
521                                                             QualifiedName name, ScaleHint scaleHint, Geometry validArea,
522                                                             int reqTimeLimit )
523                                throws Exception {
524            String className = XMLTools.getRequiredNodeAsString( node, "./deegreewms:ClassName/text()", nsContext );
525            String confPath = XMLTools.getNodeAsString( node, "./deegreewms:ConfigurationFile/text()", nsContext, null );
526            ExternalDataAccess externalDataAccess = (ExternalDataAccess) Class.forName( className ).newInstance();
527            URL confURL = null;
528            if ( confPath != null ) {
529                confURL = resolve( confPath );
530            }
531            externalDataAccess.setConfigurationFile( confURL );
532    
533            return new ExternalDataAccessDataSource( queryable, failOnEx, name, scaleHint, validArea, reqTimeLimit,
534                                                     externalDataAccess );
535        }
536    
537        /**
538         * parses the ScaleHint for a data source
539         * 
540         * @param layerElem
541         * @param scaleHint
542         * @return the scale hint for the data source
543         * @throws XMLParsingException
544         */
545        protected ScaleHint parseDSScaleHint( Element layerElem, ScaleHint scaleHint )
546                                throws XMLParsingException {
547    
548            Node scNode = XMLTools.getNode( layerElem, "./deegreewms:ScaleHint", nsContext );
549            if ( scNode != null ) {
550                double mn = XMLTools.getNodeAsDouble( scNode, "./@min", nsContext, 0 );
551                double mx = XMLTools.getNodeAsDouble( scNode, "./@max", nsContext, Double.MAX_VALUE );
552                scaleHint = new ScaleHint( mn, mx );
553            }
554    
555            if ( scaleHint == null ) {
556                // set default value to avoid NullPointerException
557                // when accessing a layers scalehint
558                scaleHint = new ScaleHint( 0, Double.MAX_VALUE );
559            }
560    
561            return scaleHint;
562        }
563    
564        /**
565         * returns the area a data source is valid. If the optional element <ValidArea>is not defined in the configuration
566         * <code>null</code>.
567         * 
568         * @param node
569         * @return the geometry
570         */
571        private Geometry parseValidArea( Node node )
572                                throws XMLParsingException {
573    
574            Geometry geom = null;
575    
576            List<Node> nl = XMLTools.getNodes( node, "./deegreewms:ValidArea/*", nsContext );
577            if ( node != null ) {
578    
579                try {
580                    for ( int i = 0; i < nl.size(); i++ ) {
581    
582                        if ( nl.get( 0 ).getNamespaceURI().equals( GMLNS.toString() ) ) {
583    
584                            geom = GMLGeometryAdapter.wrap( (Element) nl.get( 0 ), null );
585                            break;
586                        }
587                    }
588                } catch ( GeometryException e ) {
589                    e.printStackTrace();
590                    throw new XMLParsingException( "couldn't parse/create valid aera of a datasource", e );
591                }
592            }
593    
594            return geom;
595        }
596    
597        /**
598         * @param vendor
599         * @return the parsed list
600         * @throws XMLParsingException
601         */
602        static List<String> parsePassedParameters( Node vendor )
603                                throws XMLParsingException {
604            List<String> passedParameters = new ArrayList<String>( 10 );
605    
606            if ( vendor == null ) {
607                return passedParameters;
608            }
609    
610            List<Node> nl = XMLTools.getNodes( vendor, "deegreewms:PassedVendorspecificParameter/deegreewms:Name",
611                                               nsContext );
612    
613            for ( Object obj : nl ) {
614                passedParameters.add( ( (Node) obj ).getTextContent().toUpperCase() );
615            }
616    
617            return passedParameters;
618        }
619    
620        /**
621         * @param vendor
622         * @return the parsed map
623         * @throws XMLParsingException
624         */
625        static Map<String, String> parseAddedParameters( Node vendor )
626                                throws XMLParsingException {
627            Map<String, String> addedParameters = new HashMap<String, String>( 10 );
628    
629            if ( vendor == null ) {
630                return addedParameters;
631            }
632    
633            List<Node> nl = XMLTools.getNodes(
634                                               vendor,
635                                               "deegreewms:AddedVendorspecificParameter/deegreewms:VendorspecificParameter",
636                                               nsContext );
637    
638            for ( Object obj : nl ) {
639                String pName = XMLTools.getRequiredNodeAsString( (Node) obj, "deegreewms:Name", nsContext );
640                String pValue = XMLTools.getRequiredNodeAsString( (Node) obj, "deegreewms:Value", nsContext );
641    
642                addedParameters.put( pName, pValue );
643            }
644            return addedParameters;
645        }
646    
647        /**
648         * 
649         * @param node
650         * @param failOnEx
651         * @param queryable
652         * @param name
653         * @param scaleHint
654         * @param validArea
655         * @param reqTimeLimit
656         * @return {@link DatabaseDataSource}
657         * @throws Exception
658         */
659        public static DatabaseDataSource createDatabaseSource( Node node, boolean failOnEx, boolean queryable,
660                                                               QualifiedName name, ScaleHint scaleHint, Geometry validArea,
661                                                               int reqTimeLimit )
662                                throws Exception {
663    
664            String s = "./dgjdbc:JDBCConnection";
665            Node jdbcNode = XMLTools.getRequiredNode( node, s, nsContext );
666            IODocument io = new IODocument( (Element) jdbcNode );
667            JDBCConnection jdbc = io.parseJDBCConnection();
668            String sqlTemplate = XMLTools.getRequiredNodeAsString( node, "./deegreewms:SQLTemplate/text()", nsContext );
669            String geometryField = XMLTools.getRequiredNodeAsString( node, "./deegreewms:GeometryField/text()", nsContext );
670            String tmp = XMLTools.getRequiredNodeAsString( node, "./deegreewms:NativeCRS/text()", nsContext );
671            boolean customSQL = getNodeAsBoolean( node, "deegreewms:CustomSQLAllowed", nsContext, false );
672            CoordinateSystem nativeCRS = CRSFactory.create( tmp );
673    
674            Map<String, String> dimProps = new HashMap<String, String>();
675    
676            for ( Element e : getElements( node, "deegreewms:DimensionProperty", nsContext ) ) {
677                dimProps.put( e.getAttribute( "name" ), e.getTextContent() );
678            }
679    
680            return new DatabaseDataSource( queryable, failOnEx, name, scaleHint, validArea, reqTimeLimit, jdbc,
681                                           sqlTemplate, geometryField, nativeCRS, customSQL, dimProps );
682        }
683    
684        /**
685         * @param node
686         * @param failOnEx
687         * @param queryable
688         * @param name
689         * @param url
690         * @param scaleHint
691         * @throws Exception
692         */
693        private RemoteWMSDataSource createRemoteWMSDataSource( Node node, boolean failOnEx, boolean queryable,
694                                                               QualifiedName name, URL url, ScaleHint scaleHint,
695                                                               Geometry validArea, int reqTimeLimit )
696                                throws Exception {
697            int type = AbstractDataSource.REMOTEWMS;
698    
699            String s = "./deegreewms:FeatureInfoTransformation/deegreewms:OnlineResource";
700            Node fitNode = XMLTools.getNode( node, s, nsContext );
701            URL fitURL = null;
702            if ( fitNode != null ) {
703                fitURL = parseOnLineResource( (Element) fitNode ).getLinkage().getHref();
704            }
705    
706            GetMap getMap = parseWMSFilterCondition( node );
707            Color[] colors = parseTransparentColors( node );
708            WMSCapabilities wCapa = null;
709            if ( capaCache.get( url ) != null ) {
710                wCapa = (WMSCapabilities) capaCache.get( url );
711            } else {
712                LOG.logDebug( "Fetching remote WMS capabilities from URL " + url );
713                WMSCapabilitiesDocument doc = WMSCapabilitiesDocumentFactory.getWMSCapabilitiesDocument( url );
714                wCapa = (WMSCapabilities) doc.parseCapabilities();
715                capaCache.put( url, wCapa );
716            }
717            OGCWebService ows = new RemoteWMService( wCapa );
718    
719            // parse added/passed parameter map/list
720            Node vendor = XMLTools.getNode( node,
721                                            "deegreewms:FilterCondition/deegreewms:VendorspecificParameterDefinition",
722                                            nsContext );
723    
724            List<String> passedParameters = parsePassedParameters( vendor );
725            Map<String, String> addedParameters = parseAddedParameters( vendor );
726    
727            return new RemoteWMSDataSource( queryable, failOnEx, name, type, ows, url, scaleHint, validArea, getMap,
728                                            colors, fitURL, reqTimeLimit, passedParameters, addedParameters );
729        }
730    
731        /**
732         * @param node
733         * @param failOnEx
734         * @param queryable
735         * @param name
736         * @param url
737         * @param scaleHint
738         * @throws Exception
739         */
740        private RemoteWFSDataSource createRemoteWFSDataSource( Node node, boolean failOnEx, boolean queryable,
741                                                               QualifiedName name, URL url, ScaleHint scaleHint,
742                                                               Geometry validArea, int reqTimeLimit )
743                                throws Exception {
744            int type = AbstractDataSource.REMOTEWFS;
745            String s = "./deegreewms:FeatureInfoTransformation/deegreewms:OnlineResource";
746            Node fitNode = XMLTools.getNode( node, s, nsContext );
747            URL fitURL = null;
748            if ( fitNode != null ) {
749                fitURL = parseOnLineResource( (Element) fitNode ).getLinkage().getHref();
750            }
751            Query query = parseWFSFilterCondition( node );
752    
753            WFSCapabilities wfsCapa = null;
754            if ( capaCache.get( url ) != null ) {
755                wfsCapa = (WFSCapabilities) capaCache.get( url );
756            } else {
757                WFSCapabilitiesDocument wfsDoc = new WFSCapabilitiesDocument();
758                LOG.logDebug( "Fetching remote WFS capabilities from URL " + url );
759                wfsDoc.load( url );
760                wfsCapa = (WFSCapabilities) wfsDoc.parseCapabilities();
761                capaCache.put( url, wfsCapa );
762            }
763            OGCWebService ows = new RemoteWFService( wfsCapa );
764            // OGCWebService ows = null;
765    
766            Node geoPropNode = XMLTools.getNode( node, "deegreewms:GeometryProperty/text()", nsContext );
767            QualifiedName geoProp = DEFAULT_GEO_PROP;
768            if ( geoPropNode != null ) {
769                geoProp = parseQualifiedName( geoPropNode );
770            }
771    
772            return new RemoteWFSDataSource( queryable, failOnEx, name, type, geoProp, ows, url, scaleHint, validArea,
773                                            query, fitURL, reqTimeLimit );
774    
775        }
776    
777        /**
778         * @param node
779         * @param failOnEx
780         * @param queryable
781         * @param name
782         * @param url
783         * @param scaleHint
784         * @throws Exception
785         */
786        private LocalWCSDataSource createLocalWCSDataSource( Node node, boolean failOnEx, boolean queryable,
787                                                             QualifiedName name, URL url, ScaleHint scaleHint,
788                                                             Geometry validArea, int reqTimeLimit )
789                                throws Exception {
790            int type = AbstractDataSource.LOCALWCS;
791            GetCoverage getCoverage = parseWCSFilterCondition( node );
792            Color[] colors = parseTransparentColors( node );
793            WCSConfiguration configuration = null;
794            if ( capaCache.get( url ) != null ) {
795                configuration = (WCSConfiguration) capaCache.get( url );
796            } else {
797                configuration = WCSConfiguration.create( url );
798                capaCache.put( url, configuration );
799            }
800    
801            OGCWebService ows = new WCService( configuration );
802    
803            return new LocalWCSDataSource( queryable, failOnEx, name, type, ows, url, scaleHint, validArea, getCoverage,
804                                           colors, reqTimeLimit );
805        }
806    
807        /**
808         * 
809         * @param node
810         * @param failOnEx
811         * @param queryable
812         * @param name
813         * @param url
814         * @param scaleHint
815         * @param validArea
816         * @param reqTimeLimit
817         * @return {@link RemoteWCSDataSource}
818         * @throws XMLParsingException
819         * @throws SAXException
820         * @throws IOException
821         * @throws InvalidCapabilitiesException
822         */
823        private AbstractDataSource createRemoteWCSDataSource( Node node, boolean failOnEx, boolean queryable,
824                                                              QualifiedName name, URL url, ScaleHint scaleHint,
825                                                              Geometry validArea, int reqTimeLimit )
826                                throws XMLParsingException, InvalidCapabilitiesException, IOException, SAXException {
827            int type = AbstractDataSource.REMOTEWCS;
828    
829            GetCoverage getCoverage = parseWCSFilterCondition( node );
830            Color[] colors = parseTransparentColors( node );
831            WCSCapabilities capabilities = null;
832            if ( capaCache.get( url ) != null ) {
833                capabilities = (WCSCapabilities) capaCache.get( url );
834            } else {
835                WCSCapabilitiesDocument doc = new WCSCapabilitiesDocument();
836                doc.load( url );
837                capabilities = (WCSCapabilities) doc.parseCapabilities();
838                capaCache.put( url, capabilities );
839            }
840    
841            OGCWebService ows = new RemoteWCService( capabilities );
842    
843            return new RemoteWCSDataSource( queryable, failOnEx, name, type, ows, url, scaleHint, validArea, getCoverage,
844                                            colors, reqTimeLimit );
845    
846        }
847    
848        /**
849         * @param node
850         * @param failOnEx
851         * @param queryable
852         * @param name
853         * @param url
854         * @param scaleHint
855         * @throws Exception
856         */
857        private LocalWFSDataSource createLocalWFSDataSource( Node node, boolean failOnEx, boolean queryable,
858                                                             QualifiedName name, URL url, ScaleHint scaleHint,
859                                                             Geometry validArea, int reqTimeLimit )
860                                throws Exception {
861            int type = AbstractDataSource.LOCALWFS;
862            String s = "./deegreewms:FeatureInfoTransformation/deegreewms:OnlineResource";
863            Node fitNode = XMLTools.getNode( node, s, nsContext );
864            URL fitURL = null;
865            if ( fitNode != null ) {
866                fitURL = parseOnLineResource( (Element) fitNode ).getLinkage().getHref();
867            }
868            Query query = parseWFSFilterCondition( node );
869            WFSConfiguration wfsCapa = null;
870            if ( capaCache.get( url ) != null ) {
871                wfsCapa = (WFSConfiguration) capaCache.get( url );
872            } else {
873                WFSConfigurationDocument wfsDoc = new WFSConfigurationDocument();
874                wfsDoc.load( url );
875                wfsCapa = wfsDoc.getConfiguration();
876                // wfsCapa = new WFSCapabilitiesDocument( url ).createCapabilities();
877                capaCache.put( url, wfsCapa );
878            }
879            // OGCWebService ows = WFServiceFactory.getUncachedService( wfsCapa );
880            OGCWebService ows = WFServiceFactory.createInstance( wfsCapa );
881    
882            Node geoPropNode = XMLTools.getNode( node, "deegreewms:GeometryProperty/text()", nsContext );
883            QualifiedName geoProp = DEFAULT_GEO_PROP;
884            if ( geoPropNode != null ) {
885                geoProp = parseQualifiedName( geoPropNode );
886            }
887    
888            Map<String, String> dimProps = new HashMap<String, String>();
889    
890            for ( Element e : getElements( node, "deegreewms:DimensionProperty", nsContext ) ) {
891                dimProps.put( e.getAttribute( "name" ), e.getTextContent() );
892            }
893    
894            LOG.logDebug( "geometry property", geoProp );
895    
896            return new LocalWFSDataSource( queryable, failOnEx, name, type, geoProp, ows, url, scaleHint, validArea, query,
897                                           fitURL, reqTimeLimit, dimProps );
898        }
899    
900        private Color[] parseTransparentColors( Node node )
901                                throws XMLParsingException {
902    
903            String s = "./deegreewms:TransparentColors/deegreewms:Color";
904            List<Node> clnl = XMLTools.getNodes( node, s, nsContext );
905            Color[] colors = new Color[clnl.size()];
906            for ( int j = 0; j < colors.length; j++ ) {
907                colors[j] = Color.decode( XMLTools.getStringValue( clnl.get( j ) ) );
908            }
909    
910            return colors;
911        }
912    
913        /**
914         * 
915         * @param node
916         * @return the query
917         * @throws XMLParsingException
918         */
919        private Query parseWFSFilterCondition( Node node )
920                                throws XMLParsingException {
921    
922            Query o = null;
923    
924            Node queryNode = XMLTools.getNode( node, "./deegreewms:FilterCondition/wfs:Query", nsContext );
925            if ( queryNode != null ) {
926                try {
927                    o = Query.create( (Element) queryNode );
928                } catch ( Exception e ) {
929                    throw new XMLParsingException( StringTools.stackTraceToString( e ) );
930                }
931            }
932    
933            return o;
934        }
935    
936        /**
937         * 
938         * @param node
939         * @return the request
940         * @throws XMLParsingException
941         */
942        private GetCoverage parseWCSFilterCondition( Node node )
943                                throws XMLParsingException {
944    
945            GetCoverage o = null;
946    
947            String id = "" + IDGenerator.getInstance().generateUniqueID();
948    
949            StringBuffer sd = new StringBuffer( 1000 );
950            sd.append( "version=1.0.0&Coverage=%default%&" );
951            sd.append( "CRS=EPSG:4326&BBOX=0,0,1,1&Width=1" );
952            sd.append( "&Height=1&Format=%default%&" );
953            String s = XMLTools.getNodeAsString( node, "./deegreewms:FilterCondition/deegreewms:WCSRequest", nsContext, "" );
954            sd.append( s );
955            try {
956                o = GetCoverage.create( id, sd.toString() );
957            } catch ( Exception e ) {
958                throw new XMLParsingException( "could not create GetCoverage from layer FilterCondition", e );
959            }
960    
961            return o;
962        }
963    
964        /**
965         * 
966         * @param node
967         * @return the request
968         * @throws XMLParsingException
969         */
970        private GetMap parseWMSFilterCondition( Node node )
971                                throws XMLParsingException {
972    
973            GetMap o = null;
974    
975            String id = "" + IDGenerator.getInstance().generateUniqueID();
976    
977            StringBuffer sd = new StringBuffer( 1000 );
978            sd.append( "REQUEST=GetMap&LAYERS=%default%&" );
979            sd.append( "STYLES=&SRS=EPSG:4326&BBOX=0,0,1,1&WIDTH=1&" );
980            sd.append( "HEIGHT=1&FORMAT=%default%" );
981            Map<String, String> map1 = KVP2Map.toMap( sd.toString() );
982            String s = XMLTools.getRequiredNodeAsString( node, "./deegreewms:FilterCondition/deegreewms:WMSRequest",
983                                                         nsContext );
984            Map<String, String> map2 = KVP2Map.toMap( s );
985            if ( map2.get( "VERSION" ) == null && map2.get( "WMTVER" ) == null ) {
986                map2.put( "VERSION", "1.1.1" );
987            }
988            // if no service is set use WMS as default
989            if ( map2.get( "SERVICE" ) == null ) {
990                map2.put( "SERVICE", "WMS" );
991            }
992            map1.putAll( map2 );
993            try {
994                map1.put( "ID", id );
995                o = GetMap.create( map1 );
996            } catch ( Exception e ) {
997                throw new XMLParsingException( "could not create GetMap from layer FilterCondition", e );
998            }
999    
1000            return o;
1001        }
1002    
1003        /**
1004         * 
1005         * @param layerElem
1006         * @throws XMLParsingException
1007         */
1008        @Override
1009        protected Style[] parseStyles( Element layerElem )
1010                                throws XMLParsingException {
1011    
1012            List<Node> nl = XMLTools.getNodes( layerElem, "./Style", nsContext );
1013            Style[] styles = new Style[nl.size()];
1014            for ( int i = 0; i < styles.length; i++ ) {
1015                String name = XMLTools.getRequiredNodeAsString( nl.get( i ), "./Name", nsContext );
1016                String title = XMLTools.getNodeAsString( nl.get( i ), "./Title", nsContext, null );
1017                String styleAbstract = XMLTools.getNodeAsString( nl.get( i ), "./Abstract", nsContext, null );
1018                LegendURL[] legendURLs = parseLegendURL( nl.get( i ) );
1019                StyleURL styleURL = parseStyleURL( nl.get( i ) );
1020                StyleSheetURL styleSheetURL = parseStyleSheetURL( nl.get( i ) );
1021                String styleResource = XMLTools.getNodeAsString( nl.get( i ), "deegreewms:StyleResource", nsContext,
1022                                                                 "styles.xml" );
1023                URL sr = null;
1024                try {
1025                    sr = resolve( styleResource );
1026                } catch ( MalformedURLException e ) {
1027                    throw new XMLParsingException( "could not parse style resource of style: " + name, e );
1028                }
1029    
1030                String layerName = XMLTools.getRequiredNodeAsString( layerElem, "Name", nsContext );
1031                if ( name == null || name.length() == 0 ) {
1032                    name = "default:" + layerName;
1033                }
1034                if ( name.equals( "default" ) ) {
1035                    name += ":" + layerName;
1036                }
1037                if ( name.equals( "default:" ) ) {
1038                    name += layerName;
1039                }
1040    
1041                styles[i] = new Style( name, title, styleAbstract, legendURLs, styleSheetURL, styleURL, sr );
1042            }
1043    
1044            return styles;
1045        }
1046    
1047    }