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 }