001 //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_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 }