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    }