001 //$Header: /deegreerepository/deegree/src/org/deegree/ogcwebservices/wfs/operation/transaction/Transaction.java,v 1.11 2007/02/07 15:01:51 poth Exp $ 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 Aennchenstraße 19 030 53177 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 package org.deegree.ogcwebservices.wfs.operation.transaction; 044 045 import java.util.ArrayList; 046 import java.util.HashSet; 047 import java.util.Iterator; 048 import java.util.List; 049 import java.util.Map; 050 import java.util.Set; 051 052 import org.deegree.datatypes.QualifiedName; 053 import org.deegree.framework.log.ILogger; 054 import org.deegree.framework.log.LoggerFactory; 055 import org.deegree.framework.util.KVP2Map; 056 import org.deegree.ogcwebservices.InconsistentRequestException; 057 import org.deegree.ogcwebservices.InvalidParameterValueException; 058 import org.deegree.ogcwebservices.OGCWebServiceException; 059 import org.deegree.ogcwebservices.wfs.operation.AbstractWFSRequest; 060 import org.w3c.dom.Element; 061 062 /** 063 * Represents a <code>Transaction</code> request to a web feature service. 064 * <p> 065 * A <code>Transaction</code> consists of a sequence of {@link Insert}, {@link Update}, 066 * {@link Delete} and {@link Native} operations. 067 * <p> 068 * From the WFS Specification 1.1.0 OGC 04-094 (#12, Pg.63): 069 * <p> 070 * A <code>Transaction</code> request is used to describe data transformation operations that are 071 * to be applied to web accessible feature instances. When the transaction has been completed, a web 072 * feature service will generate an XML response document indicating the completion status of the 073 * transaction. 074 * 075 * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider </a> 076 * @author last edited by: $Author: apoth $ 077 * 078 * @version $Revision: 9348 $, $Date: 2007-12-27 17:59:14 +0100 (Do, 27 Dez 2007) $ 079 */ 080 public class Transaction extends AbstractWFSRequest { 081 082 private static final long serialVersionUID = 6904739857311368390L; 083 084 private static final ILogger LOG = LoggerFactory.getLogger( Transaction.class ); 085 086 private List<TransactionOperation> operations; 087 088 // request version 089 private String version; 090 091 // transaction ID 092 private String id; 093 094 // LockID associated with the request 095 private String lockId; 096 097 /** 098 * Specifies if ALL records should be released or if SOME records, indicating only those records 099 * which have been modified will be released. The default is ALL. 100 */ 101 private RELEASE_ACTION releaseAction = RELEASE_ACTION.ALL; 102 103 private TransactionDocument sourceDocument; 104 105 /** Controls how locked features are treated when a transaction request is completed. */ 106 public static enum RELEASE_ACTION { 107 108 /** 109 * Indicates that the locks on all feature instances locked using the associated lockId 110 * should be released when the transaction completes, regardless of whether or not a 111 * particular feature instance in the locked set was actually operated upon. 112 */ 113 ALL, 114 115 /** 116 * Indicates that only the locks on feature instances modified by the transaction should be 117 * released. The other, unmodified, feature instances should remain locked using the same 118 * lockId so that subsequent transactions can operate on those feature instances. If an 119 * expiry period was specified, the expiry counter must be reset to zero after each 120 * transaction unless all feature instances in the locked set have been operated upon. 121 */ 122 SOME 123 } 124 125 /** 126 * Creates a new <code>Transaction</code> instance. 127 * 128 * @param version 129 * WFS version 130 * @param id 131 * Transaction id 132 * @param versionSpecificParameter 133 * @param lockID 134 * Lock Id 135 * @param operations 136 * List of operations to be carried out 137 * @param releaseAllFeatures 138 * @param sourceDocument 139 */ 140 public Transaction( String id, String version, Map<String, String> versionSpecificParameter, String lockID, 141 List<TransactionOperation> operations, boolean releaseAllFeatures, 142 TransactionDocument sourceDocument ) { 143 super( version, id, null, versionSpecificParameter ); 144 this.id = id; 145 this.version = version; 146 this.lockId = lockID; 147 this.operations = operations; 148 if ( !releaseAllFeatures ) { 149 this.releaseAction = RELEASE_ACTION.SOME; 150 } 151 this.sourceDocument = sourceDocument; 152 } 153 154 /** 155 * Returns the source document that was used to create this <code>Transaction</code> instance. 156 * 157 * @return the source document 158 */ 159 public TransactionDocument getSourceDocument() { 160 return this.sourceDocument; 161 } 162 163 /** 164 * Returns the {@link TransactionOperation}s that are contained in the transaction. 165 * 166 * @return the contained operations 167 */ 168 public List<TransactionOperation> getOperations() { 169 return this.operations; 170 } 171 172 /** 173 * Returns the lock identifier associated with this transaction. 174 * 175 * @return the lock identifier associated with this transaction if it exists, null otherwise 176 */ 177 public String getLockId() { 178 return this.lockId; 179 } 180 181 /** 182 * Returns the release action mode to be applied after the transaction finished successfully. 183 * 184 * @see RELEASE_ACTION 185 * @return the release action mode to be applied after the transaction finished successfully 186 */ 187 public RELEASE_ACTION getReleaseAction() { 188 return this.releaseAction; 189 } 190 191 /** 192 * Returns the names of the feature types that are affected by the transaction. 193 * 194 * @return the names of the affected feature types 195 */ 196 public Set<QualifiedName> getAffectedFeatureTypes() { 197 Set<QualifiedName> featureTypeSet = new HashSet<QualifiedName>(); 198 199 Iterator<TransactionOperation> iter = this.operations.iterator(); 200 while ( iter.hasNext() ) { 201 TransactionOperation operation = iter.next(); 202 featureTypeSet.addAll( operation.getAffectedFeatureTypes() ); 203 } 204 return featureTypeSet; 205 } 206 207 /** 208 * Creates a <code>Transaction</code> request from a key-value-pair encoding of the parameters 209 * contained in the passed variable 'request'. 210 * 211 * @param id 212 * id of the request 213 * @param request 214 * key-value-pair encoded GetFeature request 215 * @return new created Transaction instance 216 * @throws InconsistentRequestException 217 * @throws InvalidParameterValueException 218 */ 219 public static Transaction create( String id, String request ) 220 throws InconsistentRequestException, InvalidParameterValueException { 221 222 Map<String, String> model = KVP2Map.toMap( request ); 223 model.put( "ID", id ); 224 225 return create( model ); 226 } 227 228 /** 229 * Creates a <code>Transaction</code> request from a key-value-pair encoding of the parameters 230 * contained in the given Map. 231 * 232 * @param model 233 * key-value-pair encoded Transaction request 234 * @return new Transaction instance 235 * @throws InconsistentRequestException 236 * @throws InvalidParameterValueException 237 */ 238 public static Transaction create( Map<String, String> model ) 239 throws InconsistentRequestException, InvalidParameterValueException { 240 241 Map<String, String> versionSpecificParameter = null; 242 243 String id = model.get( "ID" ); 244 245 String version = checkVersionParameter( model ); 246 247 checkServiceParameter( model ); 248 249 String request = model.remove( "REQUEST" ); 250 if ( request == null ) { 251 throw new InconsistentRequestException( "Request parameter for a transaction request must be set." ); 252 } 253 254 String lockID = model.remove( "LOCKID" ); 255 256 String releaseAction = model.remove( "RELEASEACTION" ); 257 boolean releaseAllFeatures = true; 258 if ( releaseAction != null ) { 259 if ( "SOME".equals( releaseAction ) ) { 260 releaseAllFeatures = false; 261 } else if ( "ALL".equals( releaseAction ) ) { 262 releaseAllFeatures = true; 263 } else { 264 throw new InvalidParameterValueException( "releaseAction", releaseAction ); 265 } 266 } 267 268 QualifiedName[] typeNames = extractTypeNames( model ); 269 270 String featureIdParameter = model.remove( "FEATUREID" ); 271 if ( typeNames == null && featureIdParameter == null ) { 272 throw new InconsistentRequestException( "TypeName OR FeatureId parameter must be set." ); 273 } 274 275 // String[] featureIds = null; 276 // if ( featureIdParameter != null ) { 277 // // FEATUREID specified. Looking for featureId 278 // // declaration TYPENAME contained in featureId declaration (eg. 279 // // FEATUREID=InWaterA_1M.1013) 280 // featureIds = StringTools.toArray( featureIdParameter, ",", false ); 281 // //typeNameSet = extractTypeNameFromFeatureId( featureIds, context, (HashSet) typeNameSet 282 // ); 283 // } 284 285 // Filters 286 // Map typeFilter = buildFilterMap( model, typeNames, featureIds, context ); 287 288 // // BBOX 289 // typeFilter = extractBBOXParameter( model, typeNames, typeFilter ); 290 // 291 // if ( typeFilter == null || typeFilter.size() == 0 ) { 292 // for ( int i = 0; i < typeNames.length; i++ ) { 293 // typeFilter.put( typeNames[i], null ); 294 // } 295 // } 296 297 List<TransactionOperation> operations = extractOperations( model, null ); 298 299 return new Transaction( id, version, versionSpecificParameter, lockID, operations, releaseAllFeatures, null ); 300 } 301 302 /** 303 * Extracts the {@link TransactionOperation}s contained in the given kvp request. 304 * 305 * @param model 306 * @param typeFilter 307 * @param typeNames 308 * @return List 309 * @throws InconsistentRequestException 310 */ 311 private static List<TransactionOperation> extractOperations( Map model, Map typeFilter ) 312 throws InconsistentRequestException { 313 List<TransactionOperation> operation = new ArrayList<TransactionOperation>(); 314 String op = (String) model.remove( "OPERATION" ); 315 if ( op == null ) { 316 throw new InconsistentRequestException( "Operation parameter must be set" ); 317 } 318 if ( op.equals( "Delete" ) ) { 319 List<Delete> deletes = Delete.create( typeFilter ); 320 operation.addAll( deletes ); 321 } else { 322 String msg = "Invalid OPERATION parameter '" + op 323 + "'. KVP Transactions only support the 'Delete' operation."; 324 throw new InconsistentRequestException( msg ); 325 } 326 return operation; 327 } 328 329 /** 330 * Creates a <code>Transaction</code> instance from a document that contains the DOM 331 * representation of the request. 332 * 333 * @param id 334 * @param root 335 * element that contains the DOM representation of the request 336 * @return transaction instance 337 * @throws OGCWebServiceException 338 */ 339 public static Transaction create( String id, Element root ) 340 throws OGCWebServiceException { 341 TransactionDocument doc = new TransactionDocument(); 342 doc.setRootElement( root ); 343 Transaction request; 344 try { 345 request = doc.parse( id ); 346 } catch ( Exception e ) { 347 LOG.logError( e.getMessage(), e ); 348 throw new OGCWebServiceException( "Transaction", e.getMessage() ); 349 } 350 return request; 351 } 352 353 @Override 354 public String toString() { 355 String ret = this.getClass().getName(); 356 ret += "version: " + this.version + "\n"; 357 ret += "id: " + this.id + "\n"; 358 ret += "lockID: " + this.lockId + "\n"; 359 ret += "operations: \n"; 360 for ( int i = 0; i < operations.size(); i++ ) { 361 ret += ( i + ": " + operations.get( i ) + "\n " ); 362 } 363 ret += "releaseAllFeatures: " + this.releaseAction; 364 return ret; 365 } 366 }