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 }