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