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 }