001    // $HeadURL:  
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     and 
009       grit GmbH
010       http://www.grit.de
011    
012     This library is free software; you can redistribute it and/or modify it under
013     the terms of the GNU Lesser General Public License as published by the Free
014     Software Foundation; either version 2.1 of the License, or (at your option)
015     any later version.
016     This library is distributed in the hope that it will be useful, but WITHOUT
017     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
018     FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
019     details.
020     You should have received a copy of the GNU Lesser General Public License
021     along with this library; if not, write to the Free Software Foundation, Inc.,
022     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
023    
024     Contact information:
025    
026     lat/lon GmbH
027     Aennchenstr. 19, 53177 Bonn
028     Germany
029     http://lat-lon.de/
030    
031     Department of Geography, University of Bonn
032     Prof. Dr. Klaus Greve
033     Postfach 1147, 53001 Bonn
034     Germany
035     http://www.geographie.uni-bonn.de/deegree/
036    
037     e-mail: info@deegree.org
038    ----------------------------------------------------------------------------*/
039    package org.deegree.ogcwebservices.wcs.getcapabilities;
040    
041    import java.net.URI;
042    import java.net.URL;
043    
044    import org.deegree.crs.exceptions.CRSException;
045    import org.deegree.datatypes.Code;
046    import org.deegree.datatypes.CodeList;
047    import org.deegree.framework.log.ILogger;
048    import org.deegree.framework.log.LoggerFactory;
049    import org.deegree.model.crs.CRSFactory;
050    import org.deegree.model.crs.CRSTransformationException;
051    import org.deegree.model.crs.GeoTransformer;
052    import org.deegree.model.crs.UnknownCRSException;
053    import org.deegree.model.spatialschema.Envelope;
054    import org.deegree.model.spatialschema.GeometryException;
055    import org.deegree.model.spatialschema.GeometryFactory;
056    import org.deegree.model.spatialschema.Surface;
057    import org.deegree.ogcbase.ExceptionCode;
058    import org.deegree.ogcwebservices.CurrentUpdateSequenceException;
059    import org.deegree.ogcwebservices.InvalidParameterValueException;
060    import org.deegree.ogcwebservices.InvalidUpdateSequenceException;
061    import org.deegree.ogcwebservices.LonLatEnvelope;
062    import org.deegree.ogcwebservices.OGCWebServiceException;
063    import org.deegree.ogcwebservices.OGCWebServiceRequest;
064    import org.deegree.ogcwebservices.SupportedFormats;
065    import org.deegree.ogcwebservices.wcs.CoverageOfferingBrief;
066    import org.deegree.ogcwebservices.wcs.describecoverage.CoverageDescription;
067    import org.deegree.ogcwebservices.wcs.describecoverage.CoverageOffering;
068    import org.deegree.ogcwebservices.wcs.describecoverage.DescribeCoverage;
069    import org.deegree.ogcwebservices.wcs.getcoverage.GetCoverage;
070    
071    /**
072     * @version $Revision: 20437 $
073     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth </a>
074     * @author <a href="mailto:reichhelm@grit.de">Stephan Reichhelm</a>
075     * @author last edited by: $Author: apoth $
076     * 
077     * @version 1.0. $Revision: 20437 $, $Date: 2009-10-29 09:49:03 +0100 (Do, 29. Okt 2009) $
078     * 
079     * @since 2.0
080     */
081    public class WCSRequestValidator {
082    
083        private static final ILogger LOG = LoggerFactory.getLogger( WCSRequestValidator.class );
084    
085        /**
086         * validates the passed <tt>AbstractOGCWebServiceRequest</tt> which must be a request that is known by a WCS against
087         * the passed <tt>WCSCapabilities</tt>
088         * 
089         * @param capabilities
090         * @param request
091         * @throws CurrentUpdateSequenceException
092         * @throws InvalidUpdateSequenceException
093         * @throws OGCWebServiceException
094         */
095        public static void validate( WCSCapabilities capabilities, OGCWebServiceRequest request )
096                                throws CurrentUpdateSequenceException, InvalidUpdateSequenceException,
097                                OGCWebServiceException {
098            // schmitz: since we only support one version, we can actually just ignore
099            // the attribute (at least for GetCapabilities requests). I do not think
100            // it does any harm to remove it completely.
101    
102            // if ( !request.getVersion().equals(capabilities.getVersion() )) {
103            // throw new InvalidParameterValueException(request.getVersion() + " is not " +
104            // "a valid version for requesting this WCS");
105            // }
106    
107            if ( request instanceof WCSGetCapabilities ) {
108                validate( capabilities, (WCSGetCapabilities) request );
109            } else if ( request instanceof GetCoverage ) {
110                validate( capabilities, (GetCoverage) request );
111            } else if ( request instanceof DescribeCoverage ) {
112                validate( capabilities, (DescribeCoverage) request );
113            } else {
114                throw new OGCWebServiceException( "Invalid request type: " + request );
115            }
116        }
117    
118        /**
119         * validates the passed <tt>WCSGetCapabilities</tt> against the passed <tt>WCSCapabilities</tt>
120         * 
121         * @param capabilities
122         * @param request
123         * @throws CurrentUpdateSequenceException
124         * @throws InvalidUpdateSequenceException
125         */
126        private static void validate( WCSCapabilities capabilities, WCSGetCapabilities request )
127                                throws CurrentUpdateSequenceException, InvalidUpdateSequenceException {
128            String rUp = request.getUpdateSequence();
129            String cUp = capabilities.getUpdateSequence();
130    
131            if ( ( rUp != null ) && ( cUp != null ) && ( rUp.compareTo( cUp ) == 0 ) ) {
132                ExceptionCode code = ExceptionCode.CURRENT_UPDATE_SEQUENCE;
133                throw new CurrentUpdateSequenceException( "WCS GetCapabilities", "request update sequence: " + rUp
134                                                                                 + "is equal to capabilities"
135                                                                                 + " update sequence " + cUp, code );
136            }
137    
138            if ( ( rUp != null ) && ( cUp != null ) && ( rUp.compareTo( cUp ) > 0 ) ) {
139                ExceptionCode code = ExceptionCode.INVALID_UPDATESEQUENCE;
140                throw new InvalidUpdateSequenceException( "WCS GetCapabilities", "request update sequence: " + rUp
141                                                                                 + " is higher then the "
142                                                                                 + "capabilities update sequence " + cUp,
143                                                          code );
144            }
145        }
146    
147        /**
148         * validates the passed <tt>DescribeCoverage</tt> against the passed <tt>WCSCapabilities</tt>
149         * 
150         * @param capabilities
151         * @param request
152         * @throws InvalidParameterValueException
153         */
154        private static void validate( WCSCapabilities capabilities, DescribeCoverage request )
155                                throws InvalidParameterValueException {
156            String[] coverages = request.getCoverages();
157            if ( coverages != null ) {
158                ContentMetadata cm = capabilities.getContentMetadata();
159                for ( int i = 0; i < coverages.length; i++ ) {
160                    if ( cm.getCoverageOfferingBrief( coverages[i] ) == null ) {
161                        throw new InvalidParameterValueException( "Coverage: " + coverages[i] + "is not known by the WCS" );
162                    }
163                }
164            }
165        }
166    
167        /**
168         * validates the passed <tt>GetCoverage</tt> against the passed <tt>WCSCapabilities</tt>
169         * 
170         * @param capabilities
171         * @param request
172         * @throws InvalidParameterValueException
173         */
174        private static void validate( WCSCapabilities capabilities, GetCoverage request )
175                                throws InvalidParameterValueException {
176            String coverage = request.getSourceCoverage();
177            ContentMetadata cm = capabilities.getContentMetadata();
178            // is coverage known by the WCS?
179            CoverageOfferingBrief cob = cm.getCoverageOfferingBrief( coverage );
180            if ( cob == null ) {
181                throw new InvalidParameterValueException( "Coverage: " + coverage + " is not known by the WCS" );
182            }
183    
184            URL url = cob.getConfiguration();
185            CoverageDescription cd = null;
186            try {
187                cd = CoverageDescription.createCoverageDescription( url );
188            } catch ( Exception e ) {
189                LOG.logError( e.getMessage(), e );
190                throw new InvalidParameterValueException( e.getMessage() );
191            }
192            CoverageOffering co = cd.getCoverageOffering( coverage );
193            if ( co == null ) {
194                throw new InvalidParameterValueException( "no coverage descrition " + "available for requested coverage: "
195                                                          + coverage );
196            }
197            // validate requested format
198            String format = request.getOutput().getFormat().getCode();
199            SupportedFormats sf = co.getSupportedFormats();
200            CodeList[] codeList = sf.getFormats();
201            if ( !validate( codeList, null, format ) ) {
202                throw new InvalidParameterValueException( "requested format: " + format
203                                                          + " is not known by the WCS for coverage:" + coverage );
204            }
205            // validate requested response CRS
206            String crs = request.getOutput().getCrs().getCode();
207            URI codeSpace = request.getOutput().getCrs().getCodeSpace();
208            String space = null;
209            if ( codeSpace != null ) {
210                space = codeSpace.toString();
211            }
212    
213            CodeList[] rrcrs = co.getSupportedCRSs().getRequestResponseSRSs();
214            CodeList[] rescrs = co.getSupportedCRSs().getResponseSRSs();
215            if ( !validate( rrcrs, space, crs ) && !validate( rescrs, space, crs ) ) {
216                throw new InvalidParameterValueException( "requested response CRS: " + crs + " is not known by the WCS "
217                                                          + "for coverage:" + coverage );
218            }
219            // validate requested CRS
220            crs = request.getDomainSubset().getRequestSRS().getCode();
221            codeSpace = request.getDomainSubset().getRequestSRS().getCodeSpace();
222            if ( codeSpace != null ) {
223                space = codeSpace.toString();
224            }
225            CodeList[] reqcrs = co.getSupportedCRSs().getRequestSRSs();
226    
227            if ( !validate( rrcrs, space, crs ) && !validate( reqcrs, space, crs ) ) {
228                throw new InvalidParameterValueException( "requested request CRS: " + crs
229                                                          + " is not known by the WCS for coverage:" + coverage );
230            }
231            // validate requested envelope
232            Envelope envelope = request.getDomainSubset().getSpatialSubset().getEnvelope();
233            LonLatEnvelope llEnv = cob.getLonLatEnvelope();
234            Envelope[] domEnvs = co.getDomainSet().getSpatialDomain().getEnvelops();
235    
236            try {
237                if ( !intersects( envelope, request.getDomainSubset().getRequestSRS(), domEnvs, llEnv ) ) {
238                    throw new InvalidParameterValueException( "requested BBOX: doesn't intersect "
239                                                              + " the area of the requested coverage: " + coverage );
240                }
241            } catch ( UnknownCRSException e ) {
242                throw new InvalidParameterValueException( e );
243            }
244    
245        }
246    
247        /**
248         * @return true if the passed <tt>CodeList</tt> s contains the also passed codeSpace-value combination. Otherwise
249         *         false will be returned
250         * 
251         * @param codeList
252         * @param codeSpace
253         * @param value
254         */
255        private static boolean validate( CodeList[] codeList, String codeSpace, String value ) {
256            for ( int i = 0; i < codeList.length; i++ ) {
257                if ( codeList[i].validate( codeSpace, value ) ) {
258                    return true;
259                }
260            }
261            return false;
262        }
263    
264        private static boolean intersects( Envelope envelope, Code reqCRS, Envelope[] envs, LonLatEnvelope llEnv )
265                                throws UnknownCRSException {
266    
267            boolean res = false;
268            String reqCRSCode = reqCRS.getCode();
269    
270            try {
271                if ( envs == null || envs.length == 0 ) {
272                    Envelope latlonEnv = GeometryFactory.createEnvelope( llEnv.getMin().getX(), llEnv.getMin().getY(),
273                                                                         llEnv.getMax().getX(), llEnv.getMax().getY(),
274                                                                         CRSFactory.create( "EPSG:4326" ) );
275    
276                    if ( !"EPSG:4326".equals( reqCRSCode ) ) {
277                        res = intersects( envelope, reqCRSCode, latlonEnv, "EPSG:4326" );
278                    } else {
279                        res = envelope.intersects( latlonEnv );
280                    }
281                } else {
282                    for ( int i = 0; i < envs.length && !res; i++ ) {
283                        if ( intersects( envelope, reqCRSCode, envs[i], envs[i].getCoordinateSystem().getPrefixedName() ) ) {
284                            res = true;
285                            break;
286                        }
287                    }
288                }
289            } catch ( GeometryException ex ) {
290                LOG.logWarning( "intersection test; translation into surface failed", ex );
291            } catch ( CRSException ex ) {
292                LOG.logWarning( "intersection test; transformation of reqeust envelope/valid area impossible", ex );
293            } catch ( CRSTransformationException ex ) {
294                LOG.logWarning( "intersection test; transformation of reqeust envelope/valid area failed", ex );
295            }
296            return res;
297        }
298    
299        private static boolean intersects( Envelope requestEnv, String requestCrs, Envelope regionEnv, String regionCrs )
300                                throws CRSException, GeometryException, CRSTransformationException, UnknownCRSException {
301            Surface request = GeometryFactory.createSurface( requestEnv, CRSFactory.create( requestCrs ) );
302            Surface region = GeometryFactory.createSurface( regionEnv, CRSFactory.create( regionCrs ) );
303    
304            if ( !requestCrs.equalsIgnoreCase( regionCrs ) ) {
305                GeoTransformer gt = new GeoTransformer( requestCrs );
306                region = (Surface) gt.transform( region );
307            }
308    
309            return request.intersects( region );
310        }
311    
312    }