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