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