    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    }