001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/ogcwebservices/wfs/operation/GetFeatureWithLock.java $
002    /*----------------    FILE HEADER  ------------------------------------------
003    
004     This file is part of deegree.
005     Copyright (C) 2001-2008 by:
006     EXSE, Department of Geography, University of Bonn
007     http://www.giub.uni-bonn.de/deegree/
008     lat/lon GmbH
009     http://www.lat-lon.de
010    
011     This library is free software; you can redistribute it and/or
012     modify it under the terms of the GNU Lesser General Public
013     License as published by the Free Software Foundation; either
014     version 2.1 of the License, or (at your option) any later version.
015    
016     This library is distributed in the hope that it will be useful,
017     but WITHOUT ANY WARRANTY; without even the implied warranty of
018     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
019     Lesser General Public License for more details.
020    
021     You should have received a copy of the GNU Lesser General Public
022     License along with this library; if not, write to the Free Software
023     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
024    
025     Contact:
026    
027     Andreas Poth
028     lat/lon GmbH
029     Aennchenstr. 19
030     53115 Bonn
031     Germany
032     E-Mail: poth@lat-lon.de
033    
034     Prof. Dr. Klaus Greve
035     Department of Geography
036     University of Bonn
037     Meckenheimer Allee 166
038     53115 Bonn
039     Germany
040     E-Mail: greve@giub.uni-bonn.de
041    
042     
043     ---------------------------------------------------------------------------*/
044    package org.deegree.ogcwebservices.wfs.operation;
045    
046    import java.util.Map;
047    
048    import org.deegree.datatypes.QualifiedName;
049    import org.deegree.framework.log.ILogger;
050    import org.deegree.framework.log.LoggerFactory;
051    import org.deegree.framework.util.KVP2Map;
052    import org.deegree.i18n.Messages;
053    import org.deegree.model.filterencoding.Filter;
054    import org.deegree.ogcbase.PropertyPath;
055    import org.deegree.ogcbase.SortProperty;
056    import org.deegree.ogcwebservices.InconsistentRequestException;
057    import org.deegree.ogcwebservices.InvalidParameterValueException;
058    import org.deegree.ogcwebservices.OGCWebServiceException;
059    import org.deegree.ogcwebservices.wfs.operation.LockFeature.ALL_SOME_TYPE;
060    import org.w3c.dom.Element;
061    
062    /**
063     * Represents a <code>GetFeatureWithLock</code> request to a web feature service.
064     * <p>
065     * This is identical to a {@link GetFeature} request, except that the features matching the request
066     * will also be locked.
067     * 
068     * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider</a>
069     * @author last edited by: $Author: apoth $
070     * 
071     * @version $Revision: 9345 $
072     */
073    public class GetFeatureWithLock extends GetFeature {
074    
075        private static final ILogger LOG = LoggerFactory.getLogger( GetFeatureWithLock.class );
076    
077        private static final long serialVersionUID = 8885456550385437651L;
078    
079        /** Duration until timeout (in milliseconds). */
080        private long expiry;
081    
082        private ALL_SOME_TYPE lockAction;
083    
084        /**
085         * Creates a new <code>GetFeatureWithLock</code> instance.
086         * 
087         * @param version
088         *            request version
089         * @param id
090         *            id of the request
091         * @param handle
092         * @param resultType
093         *            desired result type (results | hits)
094         * @param outputFormat
095         *            requested result format
096         * @param maxFeatures
097         * @param startPosition
098         *            deegree specific parameter defining where to start considering features
099         * @param traverseXLinkDepth
100         * @param traverseXLinkExpiry
101         * @param queries
102         * @param vendorSpecificParam
103         * @param expiry
104         *            the limit on how long the web feature service keeps the lock (in milliseconds)
105         * @param lockAction
106         *            method for lock acquisition
107         */
108        GetFeatureWithLock( String version, String id, String handle, RESULT_TYPE resultType, String outputFormat,
109                            int maxFeatures, int startPosition, int traverseXLinkDepth, int traverseXLinkExpiry,
110                            Query[] queries, Map<String, String> vendorSpecificParam, long expiry, ALL_SOME_TYPE lockAction ) {
111            super( version, id, handle, resultType, outputFormat, maxFeatures, startPosition, traverseXLinkDepth,
112                   traverseXLinkExpiry, queries, vendorSpecificParam );
113            this.expiry = expiry;
114            this.lockAction = lockAction;
115        }
116    
117        /**
118         * Creates a new <code>GetFeatureWithLock</code> instance from the given parameters.
119         * 
120         * @param version
121         *            request version
122         * @param id
123         *            id of the request
124         * @param handle
125         * @param resultType
126         *            desired result type (results | hits)
127         * @param outputFormat
128         *            requested result format
129         * @param maxFeatures
130         * @param startPosition
131         *            deegree specific parameter defining where to start considering features
132         * @param traverseXLinkDepth
133         * @param traverseXLinkExpiry
134         * @param queries
135         * @param vendorSpecificParam
136         * @param expiry
137         *            the limit on how long the web feature service keeps the lock (in milliseconds)
138         * @param lockAction
139         *            method for lock acquisition
140         * @return new <code>GetFeatureWithLock</code> request
141         */
142        public static GetFeatureWithLock create( String version, String id, String handle, RESULT_TYPE resultType,
143                                                 String outputFormat, int maxFeatures, int startPosition,
144                                                 int traverseXLinkDepth, int traverseXLinkExpiry, Query[] queries,
145                                                 Map<String, String> vendorSpecificParam, long expiry,
146                                                 ALL_SOME_TYPE lockAction ) {
147            return new GetFeatureWithLock( version, id, handle, resultType, outputFormat, maxFeatures, startPosition,
148                                           traverseXLinkDepth, traverseXLinkExpiry, queries, vendorSpecificParam, expiry,
149                                           lockAction );
150        }
151    
152        /**
153         * Creates a new <code>GetFeatureWithLock</code> instance from a document that contains the
154         * DOM representation of the request.
155         * 
156         * @param id
157         *            of the request
158         * @param root
159         *            element that contains the DOM representation of the request
160         * @return new <code>GetFeatureWithLock</code> request
161         * @throws OGCWebServiceException
162         */
163        public static GetFeatureWithLock create( String id, Element root )
164                                throws OGCWebServiceException {
165            GetFeatureWithLockDocument doc = new GetFeatureWithLockDocument();
166            doc.setRootElement( root );
167            GetFeatureWithLock request;
168            try {
169                request = doc.parse( id );
170            } catch ( Exception e ) {
171                LOG.logError( e.getMessage(), e );
172                throw new OGCWebServiceException( "GetFeatureWithLock", e.getMessage() );
173            }
174            return request;
175        }
176    
177        /**
178         * Creates a new <code>GetFeatureWithLock</code> instance from the given key-value pair
179         * encoded request.
180         * 
181         * @param id
182         *            request identifier
183         * @param request
184         * @return new <code>GetFeatureWithLock</code> request
185         * @throws InvalidParameterValueException
186         * @throws InconsistentRequestException
187         */
188        public static GetFeatureWithLock create( String id, String request )
189                                throws InconsistentRequestException, InvalidParameterValueException {
190            Map<String, String> map = KVP2Map.toMap( request );
191            map.put( "ID", id );
192            return create( map );
193        }
194    
195        /**
196         * Creates a new <code>GetFeatureWithLock</code> request from the given map.
197         * 
198         * @param kvp
199         *            key-value pairs, keys have to be uppercase
200         * @return new <code>GetFeatureWithLock</code> request
201         * @throws InconsistentRequestException
202         * @throws InvalidParameterValueException
203         */
204        public static GetFeatureWithLock create( Map<String, String> kvp )
205                                throws InconsistentRequestException, InvalidParameterValueException {
206    
207            // SERVICE
208            checkServiceParameter( kvp );
209    
210            // ID (deegree specific)
211            String id = kvp.get( "ID" );
212    
213            // VERSION
214            String version = checkVersionParameter( kvp );
215    
216            // OUTPUTFORMAT
217            String outputFormat = getParam( "OUTPUTFORMAT", kvp, FORMAT_GML3 );
218    
219            // RESULTTYPE
220            RESULT_TYPE resultType = RESULT_TYPE.RESULTS;
221            String resultTypeString = kvp.get( "RESULTTYPE" );
222            if ( "hits".equals( resultTypeString ) ) {
223                resultType = RESULT_TYPE.HITS;
224            }
225    
226            // FEATUREVERSION
227            String featureVersion = kvp.get( "FEATUREVERSION" );
228    
229            // MAXFEATURES
230            String maxFeaturesString = kvp.get( "MAXFEATURES" );
231            // -1: fetch all features
232            int maxFeatures = -1;
233            if ( maxFeaturesString != null ) {
234                try {
235                    maxFeatures = Integer.parseInt( maxFeaturesString );
236                    if ( maxFeatures < 1 ) {
237                        throw new NumberFormatException();
238                    }
239                } catch ( NumberFormatException e ) {
240                    LOG.logError( e.getMessage(), e );
241                    String msg = Messages.getMessage( "WFS_PARAMETER_INVALID_INT", maxFeaturesString, "MAXFEATURES" );
242                    throw new InvalidParameterValueException( msg );
243                }
244            }
245    
246            // STARTPOSITION (deegree specific)
247            String startPosString = getParam( "STARTPOSITION", kvp, "1" );
248            int startPosition = 1;
249            try {
250                startPosition = Integer.parseInt( startPosString );
251                if ( startPosition < 1 ) {
252                    throw new NumberFormatException();
253                }
254            } catch ( NumberFormatException e ) {
255                LOG.logError( e.getMessage(), e );
256                String msg = Messages.getMessage( "WFS_PARAMETER_INVALID_INT", startPosString, "STARTPOSITION" );
257                throw new InvalidParameterValueException( msg );
258            }
259    
260            // SRSNAME
261            String srsName = kvp.get( "SRSNAME" );
262    
263            // TYPENAME
264            QualifiedName[] typeNames = extractTypeNames( kvp );
265            if ( typeNames == null ) {
266                // no TYPENAME parameter -> FEATUREID must be present
267                String featureId = kvp.get( "FEATUREID" );
268                if ( featureId != null ) {
269                    String msg = Messages.getMessage( "WFS_FEATUREID_PARAM_UNSUPPORTED" );
270                    throw new InvalidParameterValueException( msg );
271                }
272                String msg = Messages.getMessage( "WFS_TYPENAME+FID_PARAMS_MISSING" );
273                throw new InvalidParameterValueException( msg );
274            }
275    
276            // BBOX
277            Filter bboxFilter = extractBBOXFilter( kvp );
278    
279            // FILTER (prequisite: TYPENAME)
280            Map<QualifiedName, Filter> filterMap = extractFilters( kvp, typeNames );
281            if ( bboxFilter != null && filterMap.size() > 0 ) {
282                String msg = Messages.getMessage( "WFS_BBOX_FILTER_INVALID" );
283                throw new InvalidParameterValueException( msg );
284            }
285    
286            // PROPERTYNAME
287            Map<QualifiedName, PropertyPath[]> propertyNameMap = extractPropNames( kvp, typeNames );
288    
289            // SORTBY
290            SortProperty[] sortProperties = null;
291    
292            // TRAVERSEXLINKDEPTH
293            int traverseXLinkDepth = -1;
294    
295            // TRAVERSEXLINKEXPIRY
296            int traverseXLinkExpiry = -1;
297    
298            // build a Query instance for each requested feature type (later also for each featureid...)
299            Query[] queries = new Query[typeNames.length];
300            for ( int i = 0; i < queries.length; i++ ) {
301                QualifiedName ftName = typeNames[i];
302                PropertyPath[] properties = propertyNameMap.get( ftName );
303                Filter filter;
304                if ( bboxFilter != null ) {
305                    filter = bboxFilter;
306                } else {
307                    filter = filterMap.get( ftName );
308                }
309                QualifiedName[] ftNames = new QualifiedName[] { ftName };
310                queries[i] = new Query( properties, null, sortProperties, null, featureVersion, ftNames, null, srsName,
311                                        filter, resultType, maxFeatures, startPosition );
312            }
313    
314            // EXPIRY
315            String expiryString = getParam( "EXPIRY", kvp, LockFeature.DEFAULT_EXPIRY );
316            int expiry = 0;
317            try {
318                expiry = Integer.parseInt( expiryString );
319                if ( expiry < 1 ) {
320                    throw new NumberFormatException();
321                }
322            } catch ( NumberFormatException e ) {
323                String msg = Messages.getMessage( "WFS_PARAMETER_INVALID_INT", expiryString, "EXPIRY" );
324                throw new InvalidParameterValueException( msg );
325            }
326    
327            // LOCKACTION
328            String lockActionString = getParam( "LOCKACTION", kvp, "ALL" );
329            ALL_SOME_TYPE lockAction = LockFeature.validateLockAction( lockActionString );
330    
331            // build a GetFeatureLock request that contains all queries
332            GetFeatureWithLock request = new GetFeatureWithLock( version, id, null, resultType, outputFormat, maxFeatures,
333                                                                 startPosition, traverseXLinkDepth, traverseXLinkExpiry,
334                                                                 queries, kvp, expiry, lockAction );
335            return request;
336        }
337    
338        /**
339         * Returns the limit on how long the web feature service holds the lock in the event that a
340         * transaction is never issued that would release the lock. The expiry limit is specified in
341         * milliseconds.
342         * 
343         * @return the limit on how long the web feature service holds the lock (in milliseconds)
344         */
345        public long getExpiry() {
346            return this.expiry;
347        }
348    
349        /**
350         * Returns the mode for lock acquisition.
351         * 
352         * @see ALL_SOME_TYPE
353         * 
354         * @return the mode for lock acquisition
355         */
356        public ALL_SOME_TYPE getLockAction() {
357            return this.lockAction;
358        }
359    }