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 }