001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/ogcwebservices/wfs/operation/GetFeatureWithLock.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.wfs.operation;
037    
038    import java.util.Map;
039    
040    import org.deegree.datatypes.QualifiedName;
041    import org.deegree.framework.log.ILogger;
042    import org.deegree.framework.log.LoggerFactory;
043    import org.deegree.framework.util.KVP2Map;
044    import org.deegree.i18n.Messages;
045    import org.deegree.model.filterencoding.Filter;
046    import org.deegree.ogcbase.PropertyPath;
047    import org.deegree.ogcbase.SortProperty;
048    import org.deegree.ogcwebservices.InconsistentRequestException;
049    import org.deegree.ogcwebservices.InvalidParameterValueException;
050    import org.deegree.ogcwebservices.MissingParameterValueException;
051    import org.deegree.ogcwebservices.OGCWebServiceException;
052    import org.deegree.ogcwebservices.wfs.operation.LockFeature.ALL_SOME_TYPE;
053    import org.w3c.dom.Element;
054    
055    /**
056     * Represents a <code>GetFeatureWithLock</code> request to a web feature service.
057     * <p>
058     * This is identical to a {@link GetFeature} request, except that the features matching the request will also be locked.
059     *
060     * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider</a>
061     * @author last edited by: $Author: mschneider $
062     *
063     * @version $Revision: 18195 $
064     */
065    public class GetFeatureWithLock extends GetFeature {
066    
067        private static final ILogger LOG = LoggerFactory.getLogger( GetFeatureWithLock.class );
068    
069        private static final long serialVersionUID = 8885456550385437651L;
070    
071        /** Duration until timeout (in milliseconds). */
072        private long expiry;
073    
074        private ALL_SOME_TYPE lockAction;
075    
076        /**
077         * Creates a new <code>GetFeatureWithLock</code> instance.
078         *
079         * @param version
080         *            request version
081         * @param id
082         *            id of the request
083         * @param handle
084         * @param resultType
085         *            desired result type (results | hits)
086         * @param outputFormat
087         *            requested result format
088         * @param maxFeatures
089         * @param startPosition
090         *            deegree specific parameter defining where to start considering features
091         * @param traverseXLinkDepth
092         * @param traverseXLinkExpiry
093         * @param queries
094         * @param vendorSpecificParam
095         * @param expiry
096         *            the limit on how long the web feature service keeps the lock (in milliseconds)
097         * @param lockAction
098         *            method for lock acquisition
099         */
100        GetFeatureWithLock( String version, String id, String handle, RESULT_TYPE resultType, String outputFormat,
101                            int maxFeatures, int startPosition, int traverseXLinkDepth, int traverseXLinkExpiry,
102                            Query[] queries, Map<String, String> vendorSpecificParam, long expiry, ALL_SOME_TYPE lockAction ) {
103            super( version, id, handle, resultType, outputFormat, maxFeatures, startPosition, traverseXLinkDepth,
104                   traverseXLinkExpiry, queries, vendorSpecificParam );
105            this.expiry = expiry;
106            this.lockAction = lockAction;
107        }
108    
109        /**
110         * Creates a new <code>GetFeatureWithLock</code> instance from the given parameters.
111         *
112         * @param version
113         *            request version
114         * @param id
115         *            id of the request
116         * @param handle
117         * @param resultType
118         *            desired result type (results | hits)
119         * @param outputFormat
120         *            requested result format
121         * @param maxFeatures
122         * @param startPosition
123         *            deegree specific parameter defining where to start considering features
124         * @param traverseXLinkDepth
125         * @param traverseXLinkExpiry
126         * @param queries
127         * @param vendorSpecificParam
128         * @param expiry
129         *            the limit on how long the web feature service keeps the lock (in milliseconds)
130         * @param lockAction
131         *            method for lock acquisition
132         * @return new <code>GetFeatureWithLock</code> request
133         */
134        public static GetFeatureWithLock create( String version, String id, String handle, RESULT_TYPE resultType,
135                                                 String outputFormat, int maxFeatures, int startPosition,
136                                                 int traverseXLinkDepth, int traverseXLinkExpiry, Query[] queries,
137                                                 Map<String, String> vendorSpecificParam, long expiry,
138                                                 ALL_SOME_TYPE lockAction ) {
139            return new GetFeatureWithLock( version, id, handle, resultType, outputFormat, maxFeatures, startPosition,
140                                           traverseXLinkDepth, traverseXLinkExpiry, queries, vendorSpecificParam, expiry,
141                                           lockAction );
142        }
143    
144        /**
145         * Creates a new <code>GetFeatureWithLock</code> instance from a document that contains the DOM representation of
146         * the request.
147         *
148         * @param id
149         *            of the request
150         * @param root
151         *            element that contains the DOM representation of the request
152         * @return new <code>GetFeatureWithLock</code> request
153         * @throws OGCWebServiceException
154         */
155        public static GetFeatureWithLock create( String id, Element root )
156                                throws OGCWebServiceException {
157            GetFeatureWithLockDocument doc = new GetFeatureWithLockDocument();
158            doc.setRootElement( root );
159            GetFeatureWithLock request;
160            try {
161                request = doc.parse( id );
162            } catch ( Exception e ) {
163                LOG.logError( e.getMessage(), e );
164                throw new OGCWebServiceException( "GetFeatureWithLock", e.getMessage() );
165            }
166            return request;
167        }
168    
169        /**
170         * Creates a new <code>GetFeatureWithLock</code> instance from the given key-value pair encoded request.
171         *
172         * @param id
173         *            request identifier
174         * @param request
175         * @return new <code>GetFeatureWithLock</code> request
176         * @throws InvalidParameterValueException
177         * @throws InconsistentRequestException
178         * @throws MissingParameterValueException
179         */
180        public static GetFeatureWithLock create( String id, String request )
181                                throws InconsistentRequestException, InvalidParameterValueException,
182                                MissingParameterValueException {
183            Map<String, String> map = KVP2Map.toMap( request );
184            map.put( "ID", id );
185            return create( map );
186        }
187    
188        /**
189         * Creates a new <code>GetFeatureWithLock</code> request from the given map.
190         *
191         * @param kvp
192         *            key-value pairs, keys have to be uppercase
193         * @return new <code>GetFeatureWithLock</code> request
194         * @throws InconsistentRequestException
195         * @throws InvalidParameterValueException
196         * @throws MissingParameterValueException
197         */
198        public static GetFeatureWithLock create( Map<String, String> kvp )
199                                throws InconsistentRequestException, InvalidParameterValueException,
200                                MissingParameterValueException {
201    
202            // SERVICE
203            checkServiceParameter( kvp );
204    
205            // ID (deegree specific)
206            String id = kvp.get( "ID" );
207    
208            // VERSION
209            String version = checkVersionParameter( kvp );
210    
211            // OUTPUTFORMAT
212            String outputFormat = getParam( "OUTPUTFORMAT", kvp, version.equals( "1.0.0" ) ? FORMAT_GML2_WFS100
213                                                                                          : FORMAT_GML3 );
214    
215            // RESULTTYPE
216            RESULT_TYPE resultType = RESULT_TYPE.RESULTS;
217            String resultTypeString = kvp.get( "RESULTTYPE" );
218            if ( "hits".equals( resultTypeString ) ) {
219                resultType = RESULT_TYPE.HITS;
220            }
221    
222            // FEATUREVERSION
223            String featureVersion = kvp.get( "FEATUREVERSION" );
224    
225            // MAXFEATURES
226            String maxFeaturesString = kvp.get( "MAXFEATURES" );
227            // -1: fetch all features
228            int maxFeatures = -1;
229            if ( maxFeaturesString != null ) {
230                try {
231                    maxFeatures = Integer.parseInt( maxFeaturesString );
232                    if ( maxFeatures < 1 ) {
233                        throw new NumberFormatException();
234                    }
235                } catch ( NumberFormatException e ) {
236                    LOG.logError( e.getMessage(), e );
237                    String msg = Messages.getMessage( "WFS_PARAMETER_INVALID_INT", maxFeaturesString, "MAXFEATURES" );
238                    throw new InvalidParameterValueException( msg );
239                }
240            }
241    
242            // STARTPOSITION (deegree specific)
243            String startPosString = getParam( "STARTPOSITION", kvp, "1" );
244            int startPosition = 1;
245            try {
246                startPosition = Integer.parseInt( startPosString );
247                if ( startPosition < 1 ) {
248                    throw new NumberFormatException();
249                }
250            } catch ( NumberFormatException e ) {
251                LOG.logError( e.getMessage(), e );
252                String msg = Messages.getMessage( "WFS_PARAMETER_INVALID_INT", startPosString, "STARTPOSITION" );
253                throw new InvalidParameterValueException( msg );
254            }
255    
256            // SRSNAME
257            String srsName = kvp.get( "SRSNAME" );
258    
259            // TYPENAME
260            QualifiedName[] typeNames = extractTypeNames( kvp );
261            if ( typeNames == null ) {
262                // no TYPENAME parameter -> FEATUREID must be present
263                String featureId = kvp.get( "FEATUREID" );
264                if ( featureId != null ) {
265                    String msg = Messages.getMessage( "WFS_FEATUREID_PARAM_UNSUPPORTED" );
266                    throw new InvalidParameterValueException( msg );
267                }
268                String msg = Messages.getMessage( "WFS_TYPENAME+FID_PARAMS_MISSING" );
269                throw new InvalidParameterValueException( msg );
270            }
271    
272            // BBOX
273            Filter bboxFilter = extractBBOXFilter( kvp );
274    
275            // FILTER (prequisite: TYPENAME)
276            Map<QualifiedName, Filter> filterMap = extractFilters( kvp, typeNames );
277            if ( bboxFilter != null && filterMap.size() > 0 ) {
278                String msg = Messages.getMessage( "WFS_BBOX_FILTER_INVALID" );
279                throw new InvalidParameterValueException( msg );
280            }
281    
282            // PROPERTYNAME
283            Map<QualifiedName, PropertyPath[]> propertyNameMap = extractPropNames( kvp, typeNames );
284    
285            // SORTBY
286            SortProperty[] sortProperties = null;
287    
288            // TRAVERSEXLINKDEPTH
289            int traverseXLinkDepth = -1;
290    
291            // TRAVERSEXLINKEXPIRY
292            int traverseXLinkExpiry = -1;
293    
294            // build a Query instance for each requested feature type (later also for each featureid...)
295            Query[] queries = new Query[typeNames.length];
296            for ( int i = 0; i < queries.length; i++ ) {
297                QualifiedName ftName = typeNames[i];
298                PropertyPath[] properties = propertyNameMap.get( ftName );
299                Filter filter;
300                if ( bboxFilter != null ) {
301                    filter = bboxFilter;
302                } else {
303                    filter = filterMap.get( ftName );
304                }
305                QualifiedName[] ftNames = new QualifiedName[] { ftName };
306                queries[i] = new Query( properties, null, sortProperties, null, featureVersion, ftNames, null, srsName,
307                                        filter, resultType, maxFeatures, startPosition );
308            }
309    
310            // EXPIRY
311            String expiryString = getParam( "EXPIRY", kvp, LockFeature.DEFAULT_EXPIRY );
312            int expiry = 0;
313            try {
314                expiry = Integer.parseInt( expiryString );
315                if ( expiry < 1 ) {
316                    throw new NumberFormatException();
317                }
318            } catch ( NumberFormatException e ) {
319                String msg = Messages.getMessage( "WFS_PARAMETER_INVALID_INT", expiryString, "EXPIRY" );
320                throw new InvalidParameterValueException( msg );
321            }
322    
323            // LOCKACTION
324            String lockActionString = getParam( "LOCKACTION", kvp, "ALL" );
325            ALL_SOME_TYPE lockAction = LockFeature.validateLockAction( lockActionString );
326    
327            // build a GetFeatureLock request that contains all queries
328            GetFeatureWithLock request = new GetFeatureWithLock( version, id, null, resultType, outputFormat, maxFeatures,
329                                                                 startPosition, traverseXLinkDepth, traverseXLinkExpiry,
330                                                                 queries, kvp, expiry, lockAction );
331            return request;
332        }
333    
334        /**
335         * Returns the limit on how long the web feature service holds the lock in the event that a transaction is never
336         * issued that would release the lock. The expiry limit is specified in milliseconds.
337         *
338         * @return the limit on how long the web feature service holds the lock (in milliseconds)
339         */
340        public long getExpiry() {
341            return this.expiry;
342        }
343    
344        /**
345         * Returns the mode for lock acquisition.
346         *
347         * @see ALL_SOME_TYPE
348         *
349         * @return the mode for lock acquisition
350         */
351        public ALL_SOME_TYPE getLockAction() {
352            return this.lockAction;
353        }
354    }