001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/ogcwebservices/wfs/operation/LockFeature.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.ArrayList;
047    import java.util.List;
048    import java.util.Map;
049    
050    import org.deegree.datatypes.QualifiedName;
051    import org.deegree.framework.log.ILogger;
052    import org.deegree.framework.log.LoggerFactory;
053    import org.deegree.i18n.Messages;
054    import org.deegree.model.filterencoding.Filter;
055    import org.deegree.ogcwebservices.InconsistentRequestException;
056    import org.deegree.ogcwebservices.InvalidParameterValueException;
057    import org.deegree.ogcwebservices.OGCWebServiceException;
058    import org.w3c.dom.Element;
059    
060    /**
061     * Represents a <code>LockFeature</code> request to a web feature service.
062     * <p>
063     * Web connections are inherently stateless. Unfortunately, this means that the semantics of
064     * serializable transactions are not preserved. To understand the issue consider an UPDATE
065     * operation.
066     * <p>
067     * The client fetches a feature instance. The feature is then modified on the client side, and
068     * submitted back to the database, via a Transaction request for update. Serializability is lost
069     * since there is nothing to guarantee that while the feature was being modified on the client side,
070     * another client did not come along and update that same feature in the database.
071     * <p>
072     * One way to ensure serializability is to require that access to data be done in a mutually
073     * exclusive manner; that is while one transaction accesses a data item, no other transaction can
074     * modify the same data item. This can be accomplished by using locks that control access to the
075     * data.
076     * <p>
077     * The purpose of the LockFeature interface is to expose a long term feature locking mechanism to
078     * ensure consistency. The lock is considered long term because network latency would make feature
079     * locks last relatively longer than native commercial database locks.
080     * <p>
081     * The LockFeature interface is optional and need only be implemented if the underlying datastore
082     * supports (or can be made to support) data locking. In addition, the implementation of locking is
083     * completely opaque to the client.
084     * 
085     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth </a>
086     * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider</a>
087     * @author last edited by: $Author: apoth $
088     * 
089     * @version $Revision: 9345 $
090     */
091    public class LockFeature extends AbstractWFSRequest {
092    
093        private static final ILogger LOG = LoggerFactory.getLogger( LockFeature.class );
094    
095        private static final long serialVersionUID = 1407310243527517490L;
096    
097        /** Default value for expiry (in minutes). */
098        public static String DEFAULT_EXPIRY = "5";
099    
100        /** Duration until timeout (in milliseconds). */
101        private long expiry;
102    
103        private ALL_SOME_TYPE lockAction;
104    
105        private List<Lock> locks;
106    
107        /**
108         * Known lock actions.
109         */
110        public static enum ALL_SOME_TYPE {
111    
112            /**
113             * Acquire a lock on all requested feature instances. If some feature instances cannot be
114             * locked, the operation should fail, and no feature instances should remain locked.
115             */
116            ALL,
117    
118            /**
119             * Lock as many of the requested feature instances as possible.
120             */
121            SOME
122        }
123    
124        /**
125         * String value for {@link ALL_SOME_TYPE ALL_SOME_TYPE.ALL}.
126         */
127        public static String LOCK_ACTION_ALL = "ALL";
128    
129        /**
130         * String value for {@link ALL_SOME_TYPE ALL_SOME_TYPE.SOME}.
131         */
132        public static String LOCK_ACTION_SOME = "SOME";
133    
134        /**
135         * Creates a new <code>LockFeature</code> instance from the given parameters.
136         * 
137         * @param version
138         *            request version
139         * @param id
140         *            id of the request
141         * @param handle
142         *            handle of the request
143         * @param expiry
144         *            the limit on how long the web feature service keeps the lock (in milliseconds)
145         * @param lockAction
146         *            method for lock acquisition
147         * @param locks
148         *            contained lock operations
149         */
150        LockFeature( String version, String id, String handle, long expiry, ALL_SOME_TYPE lockAction,
151                     List<Lock> locks ) {
152            super( version, id, handle, null );
153            this.expiry = expiry;
154            this.lockAction = lockAction;
155            this.locks = locks;
156        }
157    
158        /**
159         * Creates a new <code>LockFeature</code> instance from the given parameters.
160         * 
161         * @param version
162         *            request version
163         * @param id
164         *            id of the request
165         * @param handle
166         *            handle of the request
167         * @param expiry
168         *            the limit on how long the web feature service holds the lock (in milliseconds)
169         * @param lockAction
170         *            method for lock acquisition
171         * @param locks
172         *            contained lock operations
173         * @return new <code>LockFeature</code> request
174         */
175        public static LockFeature create( String version, String id, String handle, long expiry,
176                                          ALL_SOME_TYPE lockAction, List<Lock> locks ) {
177            return new LockFeature( version, id, handle, expiry, lockAction, locks );
178        }
179    
180        /**
181         * Creates a new <code>LockFeature</code> instance from a document that contains the DOM
182         * representation of the request.
183         * 
184         * @param id
185         *            of the request
186         * @param root
187         *            element that contains the DOM representation of the request
188         * @return new <code>LockFeature</code> request
189         * @throws OGCWebServiceException
190         */
191        public static LockFeature create( String id, Element root )
192                                throws OGCWebServiceException {
193            LockFeatureDocument doc = new LockFeatureDocument();
194            doc.setRootElement( root );
195            LockFeature request;
196            try {
197                request = doc.parse( id );
198            } catch ( Exception e ) {
199                LOG.logError( e.getMessage(), e );
200                throw new OGCWebServiceException( "LockFeature", e.getMessage() );
201            }
202            return request;
203        }
204    
205        /**
206         * Creates a new <code>LockFeature</code> request from the given parameter map.
207         * 
208         * @param kvp
209         *            key-value pairs, keys have to be uppercase
210         * @return new <code>LockFeature</code> request
211         * @throws InconsistentRequestException
212         * @throws InvalidParameterValueException
213         */
214        public static LockFeature create( Map<String, String> kvp )
215                                throws InconsistentRequestException, InvalidParameterValueException {
216    
217            // SERVICE
218            checkServiceParameter( kvp );
219    
220            // ID (deegree specific)
221            String id = kvp.get( "ID" );
222    
223            // VERSION
224            String version = checkVersionParameter( kvp );
225    
226            // TYPENAME
227            QualifiedName[] typeNames = extractTypeNames( kvp );
228            if ( typeNames == null ) {
229                // no TYPENAME parameter -> FEATUREID must be present
230                String featureId = kvp.get( "FEATUREID" );
231                if ( featureId != null ) {
232                    String msg = Messages.getMessage( "WFS_FEATUREID_PARAM_UNSUPPORTED" );
233                    throw new InvalidParameterValueException( msg );
234                }
235                String msg = Messages.getMessage( "WFS_TYPENAME+FID_PARAMS_MISSING" );
236                throw new InvalidParameterValueException( msg );
237            }
238    
239            // EXPIRY
240            String expiryString = getParam( "EXPIRY", kvp, DEFAULT_EXPIRY );
241            int expiry = 0;
242            try {
243                expiry = Integer.parseInt( expiryString );
244                if ( expiry < 1 ) {
245                    throw new NumberFormatException();
246                }
247            } catch ( NumberFormatException e ) {
248                String msg = Messages.getMessage( "WFS_PARAMETER_INVALID_INT", expiryString, "EXPIRY" );
249                throw new InvalidParameterValueException( msg );
250            }
251    
252            // LOCKACTION
253            String lockActionString = getParam( "LOCKACTION", kvp, "ALL" );
254            ALL_SOME_TYPE lockAction = validateLockAction( lockActionString );
255    
256            // BBOX
257            Filter bboxFilter = extractBBOXFilter( kvp );
258    
259            // FILTER (prequisite: TYPENAME)
260            Map<QualifiedName, Filter> filterMap = extractFilters( kvp, typeNames );
261            if ( bboxFilter != null && filterMap.size() > 0 ) {
262                String msg = Messages.getMessage( "WFS_BBOX_FILTER_INVALID" );
263                throw new InvalidParameterValueException( msg );
264            }
265    
266            // build a Lock instance for each requested feature type (later also for each featureid...)
267            List<Lock> locks = new ArrayList<Lock>( typeNames.length );
268            for ( QualifiedName ftName : typeNames ) {
269                Filter filter;
270                if ( bboxFilter != null ) {
271                    filter = bboxFilter;
272                } else {
273                    filter = filterMap.get( ftName );
274                }
275                locks.add( new Lock( null, ftName, filter ) );
276            }
277            return new LockFeature( version, id, null, expiry, lockAction, locks );
278        }
279    
280        /**
281         * Returns the limit on how long the web feature service holds the lock in the event that a
282         * transaction is never issued that would release the lock. The expiry limit is specified in
283         * milliseconds.
284         * 
285         * @return the limit on how long the web feature service holds the lock (in milliseconds)
286         */
287        public long getExpiry() {
288            return this.expiry;
289        }
290    
291        /**
292         * Returns the mode for lock acquisition.
293         * 
294         * @see ALL_SOME_TYPE
295         * 
296         * @return the mode for lock acquisition
297         */
298        public ALL_SOME_TYPE getLockAction() {
299            return this.lockAction;
300        }
301    
302        /**
303         * Returns whether this request requires that all features have to be lockable in order to be
304         * performed succesfully.
305         * 
306         * @see ALL_SOME_TYPE
307         * 
308         * @return true, if all features have to be lockable, false otherwise
309         */
310        public boolean lockAllFeatures() {
311            return this.lockAction == ALL_SOME_TYPE.ALL;
312        }
313    
314        /**
315         * Returns the contained lock operations.
316         * 
317         * @return the contained lock operations
318         */
319        public List<Lock> getLocks() {
320            return this.locks;
321        }
322    
323        static ALL_SOME_TYPE validateLockAction( String lockActionString )
324                                throws InvalidParameterValueException {
325            ALL_SOME_TYPE lockAction = ALL_SOME_TYPE.ALL;
326            if ( LOCK_ACTION_ALL.equals( lockActionString ) ) {
327                // nothing to do
328            } else if ( LOCK_ACTION_SOME.equals( lockActionString ) ) {
329                lockAction = ALL_SOME_TYPE.SOME;
330            } else {
331                String msg = Messages.getMessage( "WFS_LOCKACTION_INVALID", lockActionString,
332                                                  LOCK_ACTION_ALL, LOCK_ACTION_SOME );
333                throw new InvalidParameterValueException( "LOCKACTION", msg );
334            }
335            return lockAction;
336        }
337    }