    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
    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
    037    package org.deegree.ogcwebservices.wcts;
    038
    039    import static org.deegree.framework.xml.XMLTools.appendElement;
    040    import static org.deegree.framework.xml.XMLTools.create;
    041    import static org.deegree.framework.xml.XMLTools.getElement;
    042    import static org.deegree.ogcbase.CommonNamespaces.GMLNS;
    043    import static org.deegree.ogcbase.CommonNamespaces.GML_PREFIX;
    044    import static org.deegree.ogcbase.CommonNamespaces.OWS_1_1_0PREFIX;
    045    import static org.deegree.ogcbase.CommonNamespaces.WCTSNS;
    046    import static org.deegree.ogcbase.CommonNamespaces.WCTS_PREFIX;
    047    import static org.deegree.ogcbase.CommonNamespaces.getNamespaceContext;
    048
    049    import java.util.ArrayList;
    050    import java.util.List;
    051    import java.util.Map;
    052
    053    import org.deegree.crs.configuration.CRSConfiguration;
    054    import org.deegree.crs.configuration.CRSProvider;
    055    import org.deegree.crs.transformations.Transformation;
    056    import org.deegree.framework.log.ILogger;
    057    import org.deegree.framework.log.LoggerFactory;
    058    import org.deegree.framework.util.Pair;
    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.i18n.Messages;
    063    import org.deegree.model.crs.CRSFactory;
    064    import org.deegree.model.crs.CoordinateSystem;
    065    import org.deegree.model.spatialschema.GMLGeometryAdapter;
    066    import org.deegree.ogcbase.ExceptionCode;
    067    import org.deegree.ogcwebservices.OGCWebService;
    068    import org.deegree.ogcwebservices.OGCWebServiceException;
    069    import org.deegree.ogcwebservices.OGCWebServiceRequest;
    070    import org.deegree.ogcwebservices.wcts.capabilities.Content;
    071    import org.deegree.ogcwebservices.wcts.capabilities.FeatureAbilities;
    072    import org.deegree.ogcwebservices.wcts.capabilities.WCTSCapabilities;
    073    import org.deegree.ogcwebservices.wcts.capabilities.mdprofiles.MetadataProfile;
    074    import org.deegree.ogcwebservices.wcts.capabilities.mdprofiles.TransformationMetadata;
    075    import org.deegree.ogcwebservices.wcts.configuration.WCTSConfiguration;
    076    import org.deegree.ogcwebservices.wcts.configuration.WCTSDeegreeParams;
    077    import org.deegree.ogcwebservices.wcts.data.TransformableData;
    078    import org.deegree.ogcwebservices.wcts.operation.GetResourceByID;
    079    import org.deegree.ogcwebservices.wcts.operation.GetTransformation;
    080    import org.deegree.ogcwebservices.wcts.operation.IsTransformable;
    081    import org.deegree.ogcwebservices.wcts.operation.Transform;
    082    import org.deegree.ogcwebservices.wcts.operation.TransformResponse;
    083    import org.deegree.ogcwebservices.wcts.operation.TransformationReference;
    084    import org.deegree.ogcwebservices.wcts.operation.WCTSGetCapabilities;
    085    import org.w3c.dom.Document;
    086    import org.w3c.dom.Element;
    087
    088    /**
    089     * The <code>WCTService</code> class is the interface between the actual handling of an incoming request and the
    090     * configuration, key method is the {@link #doService(OGCWebServiceRequest)} implementation.
    091     *
    092     * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
    093     *
    094     * @author last edited by: $Author:$
    095     *
    096     * @version February 7th 2008
    097     *
    098     */
    099    public class WCTService implements OGCWebService {
    100        private static ILogger LOG = LoggerFactory.getLogger( WCTService.class );
    101
    102        private final WCTSConfiguration config;
    103
    104        /**
    105         * The version of this wcts, will be read from the configuration.
    106         */
    107        public static String version = "0.4.0";
    108
    109        /**
    110         * The configured crs provider to use.
    111         */
    112        public static String CRS_PROVIDER = null;
    113
    114        /**
    115         * @param config
    116         */
    117        public WCTService( final WCTSConfiguration config ) {
    118            this.config = config;
    119            synchronized ( LOG ) {
    120                version = config.getVersion();
    121                LOG.notifyAll();
    122            }
    123            CRS_PROVIDER = config.getDeegreeParams().getConfiguredCRSProvider();
    124        }
    125
    126        /*
    127         * (non-Javadoc)
    128         *
    129         * @see org.deegree.ogcwebservices.OGCWebService#doService(org.deegree.ogcwebservices.OGCWebServiceRequest)
    130         */
    131        public Object doService( OGCWebServiceRequest request )
    132                                throws OGCWebServiceException {
    133            Object result = null;
    134            if ( request != null ) {
    135                long time = System.currentTimeMillis();
    136                LOG.logDebug( "Incoming request with id: " + request.getId() );
    137                if ( request instanceof GetResourceByID ) {
    138                    result = handleGetResourceByID( (GetResourceByID) request );
    139                } else if ( request instanceof IsTransformable ) {
    140                    result = handleIsTransformable( (IsTransformable) request );
    141                } else if ( request instanceof GetTransformation ) {
    142                    throw new OGCWebServiceException( Messages.getMessage( "WCTS_OPERATION_NOT_SUPPORTED",
    143                                                                           "GetTransformation" ),
    144                                                      ExceptionCode.OPERATIONNOTSUPPORTED );
    145                } else if ( request instanceof WCTSGetCapabilities ) {
    146                    result = handleCapabilities( (WCTSGetCapabilities) request );
    147                } else if ( request instanceof Transform ) {
    148                    result = handleTransform( (Transform) request );
    149                } else {
    150                    throw new OGCWebServiceException( request.toString(), Messages.getMessage( "WCTS_UNKNOWN_REQUEST" ),
    151                                                      ExceptionCode.OPERATIONNOTSUPPORTED );
    152                }
    153                LOG.logDebug( "The handling of request with id: " + request.getId() + " took: "
    154                              + ( System.currentTimeMillis() - time ) / 1000. + " seconds" );
    155            }
    156            if ( result == null ) {
    157                LOG.logError( Messages.getMessage( "WCTS_ILLEGAL_STATE" ) + " incoming request is: " + request );
    158                throw new OGCWebServiceException( Messages.getMessage( "WCTS_ILLEGAL_STATE" ),
    159                                                  ExceptionCode.NOAPPLICABLECODE );
    160            }
    161
    162            return result;
    163        }
    164
    165        /**
    166         * @param request
    167         *            to be handled.
    168         * @return the response to a GetResourceByID request (i.e. a gml:Dictionary with the description of the resource in
    169         *         gml3 ).
    170         */
    171        private XMLFragment handleGetResourceByID( GetResourceByID request ) {
    172            Document doc = create();
    173            Element root = doc.createElementNS( GMLNS.toASCIIString(), GML_PREFIX + ":Dictionary" );
    174            XMLFragment response = new XMLFragment( root );
    175            List<String> ids = request.getResourceIDs();
    176            // for a crs this might be the way, maybe enhance the CRSProvider api to get an
    177            // Identifiable?
    178            CRSProvider provider = CRSConfiguration.getCRSConfiguration().getProvider();
    179            List<org.deegree.crs.coordinatesystems.CoordinateSystem> requestedCRSs = new ArrayList<org.deegree.crs.coordinatesystems.CoordinateSystem>(
    180                                                                                                                                                        ids.size() );
    181            for ( String id : ids ) {
    182                org.deegree.crs.coordinatesystems.CoordinateSystem tmp = provider.getCRSByID( id );
    183                if ( tmp != null ) {
    184                    requestedCRSs.add( tmp );
    185                } else {
    186                    // what to do here? maybe an exception or try something else?.
    187                }
    188            }
    189
    190            return response;
    191        }
    192
    193        /**
    194         * @param request
    195         *            to be handled.
    196         * @return a IsTransformable response, never <code>null</code>.
    197         */
    198        private XMLFragment handleIsTransformable( IsTransformable request ) {
    199            Document doc = create();
    200            Element root = doc.createElementNS( WCTSNS.toASCIIString(), WCTS_PREFIX + ":IsTransformableResponse" );
    201            XMLFragment response = new XMLFragment( root );
    202
    203            Content content = config.getContents();
    204            List<String> problems = new ArrayList<String>();
    205            if ( request.getSourceCRS() == null ) {
    206                problems.add( "SourceCRS" );
    207            } else {
    208                if ( content != null ) {
    209                    List<CoordinateSystem> sourceCRSs = content.getSourceCRSs();
    210                    if ( sourceCRSs == null || !sourceCRSs.contains( request.getSourceCRS() ) ) {
    211                        problems.add( "SourceCRS" );
    212                    }
    213                }
    214            }
    215            if ( request.getTargetCRS() == null ) {
    216                problems.add( "TargetCRS" );
    217            } else {
    218                if ( content != null ) {
    219                    List<CoordinateSystem> targetCRSs = content.getTargetCRSs();
    220                    if ( targetCRSs == null || !targetCRSs.contains( request.getTargetCRS() ) ) {
    221                        problems.add( "TargetCRS" );
    222                    }
    223                }
    224            }
    225
    226            // currently not supported operations.
    227            if ( request.getCoverageTypes() != null && request.getCoverageTypes().size() > 0 ) {
    228                problems.add( "CoverageType" );
    229            }
    230            if ( request.getInterpolationTypes() != null && request.getInterpolationTypes().size() > 0 ) {
    231                problems.add( "InterpolationMethod" );
    232            }
    233
    234            // check for geometry types.
    235            List<Pair<String, String>> requestedGeoms = request.getGeometryTypes();
    236            boolean geometriesFitConfigured = true;
    237            if ( requestedGeoms != null && requestedGeoms.size() > 0 ) {
    238                if ( content != null ) {
    239                    FeatureAbilities featureAbilities = content.getFeatureAbilities();
    240                    if ( featureAbilities != null ) {
    241                        List<Pair<String, String>> configuredGeomTypes = featureAbilities.getGeometryTypes();
    242                        if ( configuredGeomTypes != null && configuredGeomTypes.size() > 0 ) {
    243                            for ( Pair<String, String> requestedGeometry : requestedGeoms ) {
    244                                if ( geometriesFitConfigured && !configuredGeomTypes.contains( requestedGeometry ) ) {
    245                                    problems.add( "GeometryType" );
    246                                    geometriesFitConfigured = false;
    247                                }
    248                            }
    249                        } else {
    250                            problems.add( "GeometryType" );
    251                            geometriesFitConfigured = false;
    252                        }
    253                    } else {
    254                        problems.add( "GeometryType" );
    255                        geometriesFitConfigured = false;
    256                    }
    257                } else {
    258                    problems.add( "GeometryType" );
    259                    geometriesFitConfigured = false;
    260                }
    261            }
    262            if ( geometriesFitConfigured ) {
    263                // the requested geometries did fit the configured geometries, but does deegree support them too?
    264                if ( requestedGeoms != null ) {
    265                    boolean deegreeSupported = true;
    266                    for ( Pair<String, String> requestedGeometry : requestedGeoms ) {
    267                        if ( deegreeSupported && requestedGeometry != null ) {
    268                            String value = requestedGeometry.first;
    269                            if ( !GMLGeometryAdapter.isGeometrieSupported( value ) ) {
    270                                problems.add( "GeometryType" );
    271                                deegreeSupported = false;
    272                            }
    273                        }
    274                    }
    275                }
    276            }
    277            root.setAttribute( "transformable", problems.size() == 0 ? "true" : "false" );
    278            if ( problems.size() != 0 ) {
    279                for ( String problem : problems ) {
    280                    if ( problem != null && !"".equals( problem.trim() ) ) {
    281                        Element problemo = appendElement( root, WCTSNS, WCTS_PREFIX + ":problem", problem );
    282                        problemo.setAttribute( "codeSpace", "http://schemas.opengis.net/wcts/0.0.0/problemType.xml" );
    283                    }
    284                }
    285            }
    286            return response;
    287        }
    288
    289        /**
    290         * @return the capabilities according to the request.
    291         */
    292        private XMLFragment handleCapabilities( WCTSGetCapabilities request ) {
    293            XMLFragment result = XMLFactory.create( getCapabilities() );
    294            Element oldRoot = result.getRootElement();
    295            Document doc = XMLTools.create();
    296            Element root = (Element) doc.importNode( oldRoot, true );
    297            doc.appendChild( root );
    298            List<String> sections = request.getSections();
    299            if ( sections.size() > 0 ) {
    300                try {
    301                    // sections
    302                    if ( !sections.contains( "all" ) ) {
    303                        if ( !sections.contains( "serviceidentification" ) ) {
    304                            Element remove = getElement( root, OWS_1_1_0PREFIX + ":ServiceIdentification",
    305                                                         getNamespaceContext() );
    306                            if ( remove != null ) {
    307                                root.removeChild( remove );
    308                            }
    309                        }
    310                        if ( !sections.contains( "serviceprovider" ) ) {
    311                            Element remove = getElement( root, OWS_1_1_0PREFIX + ":ServiceProvider", getNamespaceContext() );
    312                            if ( remove != null ) {
    313                                root.removeChild( remove );
    314                            }
    315                        }
    316                        if ( !sections.contains( "operationsmetadata" ) ) {
    317                            Element remove = getElement( root, OWS_1_1_0PREFIX + ":OperationsMetadata",
    318                                                         getNamespaceContext() );
    319                            if ( remove != null ) {
    320                                root.removeChild( remove );
    321                            }
    322                        }
    323                        if ( !sections.contains( "contents" ) ) {
    324                            Element remove = getElement( root, WCTS_PREFIX + ":Contents", getNamespaceContext() );
    325                            if ( remove != null ) {
    326                                root.removeChild( remove );
    327                            }
    328                        }
    329                    }
    330                } catch ( XMLParsingException e ) {
    331                    LOG.logError( "Could not handle requested 'sections' parameter because: " + e.getMessage(), e );
    332                }
    333            }
    334            return new XMLFragment( root );
    335        }
    336
    337        /**
    338         * @param request
    339         *            to be handled
    340         * @return the response or <code>null</code> if the request was <code>null</code>.
    341         * @throws OGCWebServiceException
    342         *             if the inputdata could not be found, or the given crs are not configured to be supported.
    343         */
    344        public TransformResponse handleTransform( Transform request )
    345                                throws OGCWebServiceException {
    346            if ( request == null ) {
    347                return null;
    348            }
    349            // get the data and call transform.
    350            TransformableData<?> transformableData = request.getTransformableData();
    351            if ( transformableData == null ) {
    352                throw new OGCWebServiceException( Messages.getMessage( "WCTS_TRANSFORM_MISSING_DATA" ),
    353                                                  WCTSExceptionCode.NO_INPUT_DATA );
    354            }
    355            Content content = config.getContents();
    356            CoordinateSystem sourceCRS = request.getSourceCRS();
    357            CoordinateSystem targetCRS = request.getTargetCRS();
    358            Transformation transform = null;
    359            if ( content != null ) {
    360                if ( sourceCRS != null && targetCRS != null ) {
    361                    List<CoordinateSystem> sourceCRSs = content.getSourceCRSs();
    362                    if ( sourceCRSs == null || !sourceCRSs.contains( request.getSourceCRS() ) ) {
    363                        throw new OGCWebServiceException( Messages.getMessage( "CRS_UNKNOWNCRS",
    364                                                                               request.getSourceCRS().getIdentifier() ),
    365                                                          ExceptionCode.INVALID_SRS );
    366                    }
    367                    List<CoordinateSystem> targetCRSs = content.getTargetCRSs();
    368                    if ( targetCRSs == null || !targetCRSs.contains( request.getTargetCRS() ) ) {
    369                        throw new OGCWebServiceException( Messages.getMessage( "CRS_UNKNOWNCRS",
    370                                                                               request.getTargetCRS().getIdentifier() ),
    371                                                          ExceptionCode.INVALID_SRS );
    372                    }
    373                } else {
    374                    // if no source and target crs were given, lets find the transformation.
    375                    TransformationReference transRef = request.getTransformationReference();
    376                    if ( transRef != null ) {
    377                        Map<String, Transformation> transformations = content.getTransformations();
    378                        String transformID = transRef.gettransformationId();
    379                        if ( transformID == null ) {
    380                            throw new OGCWebServiceException( Messages.getMessage( "WCTS_NO_TRANSFORM_ID" ),
    381                                                              ExceptionCode.MISSINGPARAMETERVALUE );
    382                        }
    383                        transform = transformations.get( transformID );
    384                        if ( transform == null ) {
    385                            List<MetadataProfile<?>> transformMetadata = content.getTransformMetadata();
    386                            if ( transformMetadata.size() > 0 ) {
    387                                for ( int i = 0; i < transformMetadata.size() && sourceCRS == null; ++i ) {
    388                                    MetadataProfile<?> mp = transformMetadata.get( i );
    389                                    if ( mp != null && ( mp instanceof TransformationMetadata ) ) {
    390                                        if ( transformID.equals( ( (TransformationMetadata) mp ).getTransformID() ) ) {
    391                                            sourceCRS = ( (TransformationMetadata) mp ).getSourceCRS();
    392                                            targetCRS = ( (TransformationMetadata) mp ).getTargetCRS();
    393                                        }
    394                                    }
    395                                }
    396                            } else {
    397                                throw new OGCWebServiceException( Messages.getMessage( "WCTS_INVALID_TRANSFORM",
    398                                                                                       transformID ),
    399                                                                  ExceptionCode.MISSINGPARAMETERVALUE );
    400                            }
    401                        }
    402                    } else {
    403                        throw new OGCWebServiceException( Messages.getMessage( "WCTS_NOT_VALID_XML_CHOICE",
    404                                                                               "SourceCRS/TargetCRS and Transformation" ),
    405                                                          ExceptionCode.MISSINGPARAMETERVALUE );
    406                    }
    407
    408                }
    409            }
    410            if ( transform != null ) {
    411                transformableData.doTransform( transform, request.extensiveLogging() );
    412                sourceCRS = CRSFactory.create( transform.getSourceCRS() );
    413                targetCRS = CRSFactory.create( transform.getTargetCRS() );
    414            } else {
    415                if ( sourceCRS == null || targetCRS == null ) {
    416                    throw new OGCWebServiceException( Messages.getMessage( "WCTS_NO_TRANSFORM_ID",
    417                                                                           ExceptionCode.MISSINGPARAMETERVALUE ) );
    418                }
    419                transformableData.doTransform( sourceCRS, targetCRS, request.extensiveLogging() );
    420            }
    421
    422            return new TransformResponse( sourceCRS, targetCRS, request.getDataPresentation(), request.mustStore(),
    423                                          request.getInputData(), transformableData );
    424        }
    425
    426        /*
    427         * (non-Javadoc)
    428         *
    429         * @see org.deegree.ogcwebservices.OGCWebService#getCapabilities()
    430         */
    431        public WCTSCapabilities getCapabilities() {
    432            return config;
    433        }
    434
    435        /**
    436         * The configuration of this wcts.
    437         *
    438         * @return the configuration of this wcts.
    439         */
    440        public final WCTSConfiguration getConfiguration() {
    441            return config;
    442        }
    443
    444        /**
    445         * @return the deegree specific parameters for the wcts.
    446         */
    447        public final WCTSDeegreeParams getDeegreeParams() {
    448            return config.getDeegreeParams();
    449        }
    450
    451    }