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