001 //$Header: /deegreerepository/deegree/src/org/deegree/ogcwebservices/wfs/TransactionHandler.java,v 1.69 2007/03/14 14:41:43 mschneider 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 037 package org.deegree.ogcwebservices.wfs; 038 039 import static java.util.Collections.singletonList; 040 import static org.deegree.ogcwebservices.wfs.operation.transaction.Transaction.RELEASE_ACTION.ALL; 041 import static org.deegree.ogcwebservices.wfs.operation.transaction.Transaction.RELEASE_ACTION.SOME; 042 043 import java.util.ArrayList; 044 import java.util.HashMap; 045 import java.util.Iterator; 046 import java.util.List; 047 import java.util.Map; 048 import java.util.Set; 049 import java.util.TreeSet; 050 051 import org.deegree.datatypes.QualifiedName; 052 import org.deegree.framework.log.ILogger; 053 import org.deegree.framework.log.LoggerFactory; 054 import org.deegree.i18n.Messages; 055 import org.deegree.io.datastore.Datastore; 056 import org.deegree.io.datastore.DatastoreException; 057 import org.deegree.io.datastore.DatastoreTransaction; 058 import org.deegree.io.datastore.FeatureId; 059 import org.deegree.io.datastore.LockManager; 060 import org.deegree.io.datastore.MissingLockIdException; 061 import org.deegree.io.datastore.PropertyPathResolver; 062 import org.deegree.io.datastore.idgenerator.FeatureIdAssigner; 063 import org.deegree.io.datastore.schema.MappedFeaturePropertyType; 064 import org.deegree.io.datastore.schema.MappedFeatureType; 065 import org.deegree.io.datastore.schema.MappedGMLSchema; 066 import org.deegree.io.datastore.sql.transaction.SQLTransaction; 067 import org.deegree.model.feature.Feature; 068 import org.deegree.model.feature.FeatureCollection; 069 import org.deegree.model.feature.FeatureProperty; 070 import org.deegree.model.feature.GMLFeatureAdapter; 071 import org.deegree.model.feature.GMLFeatureCollectionDocument; 072 import org.deegree.model.feature.Validator; 073 import org.deegree.model.feature.schema.FeatureType; 074 import org.deegree.model.feature.schema.PropertyType; 075 import org.deegree.ogcbase.PropertyPath; 076 import org.deegree.ogcbase.PropertyPathStep; 077 import org.deegree.ogcwebservices.InvalidParameterValueException; 078 import org.deegree.ogcwebservices.MissingParameterValueException; 079 import org.deegree.ogcwebservices.OGCWebServiceException; 080 import org.deegree.ogcwebservices.wfs.operation.transaction.Delete; 081 import org.deegree.ogcwebservices.wfs.operation.transaction.Insert; 082 import org.deegree.ogcwebservices.wfs.operation.transaction.InsertResults; 083 import org.deegree.ogcwebservices.wfs.operation.transaction.Native; 084 import org.deegree.ogcwebservices.wfs.operation.transaction.Transaction; 085 import org.deegree.ogcwebservices.wfs.operation.transaction.TransactionOperation; 086 import org.deegree.ogcwebservices.wfs.operation.transaction.TransactionResponse; 087 import org.deegree.ogcwebservices.wfs.operation.transaction.Update; 088 import org.deegree.ogcwebservices.wfs.operation.transaction.Insert.ID_GEN; 089 090 /** 091 * Handler for transaction requests to the {@link WFService}. 092 * <p> 093 * If the used backend does not support atomic transactions, it is possible that one part fails while another works 094 * well. Depending on definitions made in the OGC WFS 1.1.0 specification in this case it is possible that even if a sub 095 * part of the request fails no exception will be thrown. In this case the result objects contains informations on the 096 * parts of the request that worked and that did not. 097 * 098 * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider </a> 099 * @author <a href="mailto:deshmukh@lat-lon.de">Anup Deshmukh </a> 100 * @author last edited by: $Author: aschmitz $ 101 * 102 * @version $Revision: 24604 $, $Date: 2010-05-27 11:59:30 +0200 (Do, 27 Mai 2010) $ 103 */ 104 class TransactionHandler { 105 106 private static final ILogger LOG = LoggerFactory.getLogger( TransactionHandler.class ); 107 108 private WFService service; 109 110 private Transaction request; 111 112 private Map<QualifiedName, MappedFeatureType> ftMap; 113 114 // filled by #acquireDSTransactions() 115 private Map<QualifiedName, DatastoreTransaction> taMap = new HashMap<QualifiedName, DatastoreTransaction>(); 116 117 // filled by #acquireDSTransactions() 118 private Map<Datastore, DatastoreTransaction> dsToTaMap = new HashMap<Datastore, DatastoreTransaction>(); 119 120 private Set<FeatureId> changedFIDs = new TreeSet<FeatureId>(); 121 122 /** 123 * Creates a new <code>TransactionHandler</code> instance. 124 * 125 * @param service 126 * @param request 127 */ 128 TransactionHandler( WFService service, Transaction request ) { 129 this.service = service; 130 this.request = request; 131 this.ftMap = service.getMappedFeatureTypes(); 132 } 133 134 /** 135 * Performs the associated transaction. 136 * 137 * @return transaction response 138 * @throws OGCWebServiceException 139 * if an error occurred 140 */ 141 synchronized TransactionResponse handleRequest() 142 throws OGCWebServiceException { 143 144 changedFIDs.clear(); 145 146 validate( this.request ); 147 148 TransactionResponse response = null; 149 150 acquireDSTransactions(); 151 152 try { 153 try { 154 response = performOperations(); 155 } catch ( OGCWebServiceException e ) { 156 abortDSTransactions(); 157 throw e; 158 } 159 commitDSTransactions(); 160 if ( request.getLockId() != null && request.getReleaseAction() == ALL ) { 161 try { 162 LockManager.getInstance().releaseLock( request.getLockId() ); 163 } catch ( DatastoreException e ) { 164 throw new InvalidParameterValueException( e.getMessage() ); 165 } 166 } 167 if ( request.getLockId() != null && request.getReleaseAction() == SOME ) { 168 try { 169 LockManager.getInstance().releaseLockPartly( request.getLockId(), changedFIDs ); 170 } catch ( DatastoreException e ) { 171 throw new InvalidParameterValueException( e.getMessage() ); 172 } 173 } 174 } finally { 175 releaseDSTransactions(); 176 } 177 178 return response; 179 } 180 181 /** 182 * Validates the feature instances in the given transaction against the WFS' application schemas. 183 * <p> 184 * The feature instances are assigned the corresponding <code>MappedFeatureType</code> in the process. 185 * 186 * @param request 187 * @throws OGCWebServiceException 188 */ 189 private void validate( Transaction request ) 190 throws OGCWebServiceException { 191 192 List<TransactionOperation> operations = request.getOperations(); 193 194 Iterator<TransactionOperation> iter = operations.iterator(); 195 while ( iter.hasNext() ) { 196 TransactionOperation operation = iter.next(); 197 if ( operation instanceof Insert ) { 198 try { 199 validateInsert( (Insert) operation ); 200 } catch ( DatastoreException e ) { 201 throw new OGCWebServiceException( this.getClass().getName(), e.getMessage() ); 202 } 203 } else if ( operation instanceof Delete ) { 204 validateDelete( (Delete) operation ); 205 } else if ( operation instanceof Update ) { 206 validateUpdate( (Update) operation ); 207 } else if ( operation instanceof Native ) { 208 // nothing to do 209 } else { 210 String msg = "Internal error. Unhandled transaction operation type '" + operation.getClass().getName() 211 + "'."; 212 throw new OGCWebServiceException( this.getClass().getName(), msg ); 213 } 214 } 215 } 216 217 /** 218 * Validates all feature instances in the given insert operation against the WFS' application schemas. 219 * <p> 220 * The feature instances are assigned the corresponding <code>MappedFeatureType</code> in the process and are 221 * disambiguated, so every feature has a unique feature id afterwards. 222 * 223 * @param operation 224 * @throws OGCWebServiceException 225 * @throws DatastoreException 226 */ 227 @SuppressWarnings("unchecked") 228 private void validateInsert( Insert operation ) 229 throws OGCWebServiceException, DatastoreException { 230 FeatureCollection fc = operation.getFeatures(); 231 if ( LOG.getLevel() == ILogger.LOG_DEBUG ) { 232 try { 233 GMLFeatureAdapter ada = new GMLFeatureAdapter( false ); 234 GMLFeatureCollectionDocument doc = ada.export( fc ); 235 LOG.logDebugXMLFile( "TransactionHandler_insert_incoming", doc ); 236 } catch ( Exception e ) { 237 LOG.logError( e.getMessage(), e ); 238 } 239 } 240 Validator validator = new Validator( (Map) this.service.getMappedFeatureTypes() ); 241 for ( int i = 0; i < fc.size(); i++ ) { 242 validator.validate( fc.getFeature( i ) ); 243 } 244 245 // merge all equal and anonymous features (without fid) 246 FeatureDisambiguator merger = new FeatureDisambiguator( fc ); 247 if ( operation.getIdGen() == ID_GEN.USE_EXISTING ) { 248 if ( merger.checkForAnonymousFeatures() ) { 249 String msg = Messages.getMessage( "WFS_INSERT_USE_EXISTING_AND_NO_FID" ); 250 throw new DatastoreException( msg ); 251 } 252 } 253 fc = merger.mergeFeatures(); 254 } 255 256 /** 257 * Validates all feature instances in the given insert operation against the WFS' application schemas. 258 * <p> 259 * The feature instances are assigned the corresponding <code>MappedFeatureType</code> in the process. 260 * 261 * @param operation 262 * @throws OGCWebServiceException 263 */ 264 private void validateDelete( Delete operation ) 265 throws OGCWebServiceException { 266 QualifiedName ftName = operation.getTypeName(); 267 MappedFeatureType ft = this.ftMap.get( ftName ); 268 if ( ft == null ) { 269 String msg = Messages.getMessage( "WFS_DELETE_FEATURE_TYPE_UNKNOWN", ftName ); 270 throw new OGCWebServiceException( this.getClass().getName(), msg ); 271 } 272 if ( ft.isAbstract() ) { 273 String msg = Messages.getMessage( "WFS_DELETE_FEATURE_TYPE_ABSTRACT", ftName ); 274 throw new OGCWebServiceException( this.getClass().getName(), msg ); 275 } 276 } 277 278 /** 279 * Validates any feature instance in the given update operation against the WFS' application schemas. 280 * <p> 281 * Feature instances are assigned the corresponding <code>MappedFeatureType</code> in the process, property names 282 * are normalized and their values are parsed into the respective objects. 283 * 284 * @param operation 285 * update operation 286 * @throws OGCWebServiceException 287 */ 288 @SuppressWarnings("unchecked") 289 private void validateUpdate( Update operation ) 290 throws OGCWebServiceException { 291 292 QualifiedName ftName = operation.getTypeName(); 293 MappedFeatureType ft = this.ftMap.get( ftName ); 294 if ( ft == null ) { 295 String msg = Messages.getMessage( "WFS_UPDATE_FEATURE_TYPE_UNKNOWN", ftName ); 296 throw new OGCWebServiceException( this.getClass().getName(), msg ); 297 } 298 299 Feature feature = operation.getFeature(); 300 if ( feature != null ) { 301 Validator validator = new Validator( (Map) this.service.getMappedFeatureTypes() ); 302 validator.validate( feature ); 303 } else { 304 validateProperties( ft, operation ); 305 } 306 } 307 308 /** 309 * Validates the properties and their replacement values that are specified in the given <code>Update</code> 310 * operation. 311 * <p> 312 * Property names are normalized and their values are parsed into the respective objects. 313 * 314 * @param ft 315 * feature type 316 * @param operation 317 * update operation 318 * @throws OGCWebServiceException 319 */ 320 private void validateProperties( MappedFeatureType ft, Update operation ) 321 throws OGCWebServiceException { 322 323 Map<PropertyPath, FeatureProperty> replacementProps = operation.getReplacementProperties(); 324 Map<PropertyPath, FeatureProperty> normalizedProps = new HashMap<PropertyPath, FeatureProperty>(); 325 326 for ( PropertyPath path : replacementProps.keySet() ) { 327 FeatureProperty property = replacementProps.get( path ); 328 path = PropertyPathResolver.normalizePropertyPath( ft, null, path ); 329 validateProperty( ft, path, property ); 330 normalizedProps.put( path, property ); 331 } 332 333 // remove all mappings and add normalized ones 334 replacementProps.clear(); 335 for ( PropertyPath path : normalizedProps.keySet() ) { 336 replacementProps.put( path, normalizedProps.get( path ) ); 337 } 338 } 339 340 /** 341 * Validates the property name and it's replacement value. 342 * <p> 343 * Values are parsed into the respective objects. 344 * 345 * @param ft 346 * feature type 347 * @param path 348 * property name 349 * @param replacementProperty 350 * replacement property value (as XML node) 351 * @throws OGCWebServiceException 352 */ 353 private void validateProperty( MappedFeatureType ft, PropertyPath path, FeatureProperty replacementProperty ) 354 throws OGCWebServiceException { 355 356 for ( int i = 0; i < path.getSteps(); i += 2 ) { 357 // check if feature step is valid 358 PropertyPathStep ftStep = path.getStep( i ); 359 FeatureType stepFt = this.ftMap.get( ftStep.getPropertyName() ); 360 if ( stepFt == null ) { 361 String msg = Messages.getMessage( "WFS_UPDATE_FEATURE_STEP_UNKNOWN", path, "unknown" ); 362 throw new OGCWebServiceException( this.getClass().getName(), msg ); 363 } 364 MappedGMLSchema schema = ft.getGMLSchema(); 365 if ( !schema.isValidSubstitution( ft, stepFt ) ) { 366 String msg = Messages.getMessage( "WFS_UPDATE_FEATURE_STEP_INVALID", path, stepFt.getName(), 367 ft.getName() ); 368 throw new OGCWebServiceException( this.getClass().getName(), msg ); 369 } 370 371 // check if property step is valid 372 PropertyPathStep propertyStep = path.getStep( i + 1 ); 373 QualifiedName propertyName = propertyStep.getPropertyName(); 374 PropertyType pt = ft.getProperty( propertyName ); 375 if ( pt == null ) { 376 String msg = Messages.getMessage( "WFS_UPDATE_PROPERTY_STEP_UNKNOWN", path, propertyName, ft.getName() ); 377 throw new OGCWebServiceException( this.getClass().getName(), msg ); 378 } 379 if ( i + 2 == path.getSteps() ) { 380 if ( replacementProperty.getValue() == null && pt.getMinOccurs() > 0 ) { 381 String msg = Messages.getMessage( "WFS_UPDATE_PROPERTY_NULL_INVALID", path, pt.getMinOccurs() ); 382 throw new InvalidParameterValueException( this.getClass().getName(), msg ); 383 } 384 if ( replacementProperty.getValue() instanceof Feature ) { 385 Validator validator = new Validator( (Map) this.service.getMappedFeatureTypes() ); 386 validator.validate( (Feature) replacementProperty.getValue() ); 387 } 388 } else { 389 if ( !( pt instanceof MappedFeaturePropertyType ) ) { 390 String msg = Messages.getMessage( "WFS_UPDATE_NOT_FEATURE_PROPERTY", path, propertyName ); 391 throw new OGCWebServiceException( this.getClass().getName(), msg ); 392 } 393 MappedFeaturePropertyType fpt = (MappedFeaturePropertyType) pt; 394 ft = fpt.getFeatureTypeReference().getFeatureType(); 395 } 396 } 397 } 398 399 /** 400 * Performs the operations contained in the transaction. 401 * 402 * @throws OGCWebServiceException 403 */ 404 private TransactionResponse performOperations() 405 throws OGCWebServiceException { 406 407 int inserts = 0; 408 int deletes = 0; 409 int updates = 0; 410 411 List<InsertResults> insertResults = new ArrayList<InsertResults>(); 412 List<TransactionOperation> operations = request.getOperations(); 413 List<Exception> exceptions = new ArrayList<Exception>(); 414 415 Iterator<TransactionOperation> iter = operations.iterator(); 416 while ( iter.hasNext() ) { 417 TransactionOperation operation = iter.next(); 418 String handle = operation.getHandle(); 419 try { 420 if ( operation instanceof Insert ) { 421 List<FeatureId> insertedFIDs = performInsert( (Insert) operation ); 422 inserts += insertedFIDs.size(); 423 424 for ( FeatureId id : insertedFIDs ) { 425 InsertResults results = new InsertResults( handle, singletonList( id ) ); 426 insertResults.add( results ); 427 } 428 } else if ( operation instanceof Delete ) { 429 // the intended behavior is different between versions here, so in #performDelete the exception 430 // will be thrown now, and caught here if 1.1.0 431 if ( request.getVersion().equals( "1.0.0" ) ) { 432 deletes += performDelete( (Delete) operation ); 433 } else { 434 try { 435 deletes += performDelete( (Delete) operation ); 436 } catch ( MissingLockIdException e ) { 437 throw new MissingParameterValueException( "LockId", e.getMessage() ); 438 } 439 } 440 } else if ( operation instanceof Update ) { 441 if ( request.getVersion().equals( "1.0.0" ) ) { 442 try { 443 updates += performUpdate( (Update) operation ); 444 } catch ( MissingLockIdException e ) { 445 exceptions.add( e ); 446 } 447 } else { 448 updates += performUpdate( (Update) operation ); 449 } 450 } else if ( operation instanceof Native ) { 451 String msg = Messages.getMessage( "WFS_NATIVE_OPERATIONS_UNSUPPORTED" ); 452 throw new OGCWebServiceException( this.getClass().getName(), msg ); 453 } else { 454 String opType = operation.getClass().getName(); 455 String msg = Messages.getMessage( "WFS_UNHANDLED_OPERATION_TYPE", opType ); 456 throw new OGCWebServiceException( this.getClass().getName(), msg ); 457 } 458 } catch ( DatastoreException e ) { 459 LOG.logError( e.getMessage(), e ); 460 String msg = "A datastore exception occured during the processing of operation with handle '" + handle 461 + "': " + e.getMessage(); 462 throw new InvalidParameterValueException( this.getClass().getName(), msg ); 463 } 464 } 465 TransactionResponse response = new TransactionResponse( request, inserts, updates, deletes, insertResults, 466 exceptions ); 467 return response; 468 } 469 470 /** 471 * Performs the given insert operation. 472 * 473 * @param insert 474 * insert operation to be performed 475 * @throws DatastoreException 476 */ 477 private List<FeatureId> performInsert( Insert insert ) 478 throws DatastoreException { 479 480 List<FeatureId> fids = new ArrayList<FeatureId>(); 481 FeatureCollection fc = insert.getFeatures(); 482 483 // clear reference to feature collection 484 // insert.setFeatureCollection( null ); 485 486 if ( LOG.getLevel() == ILogger.LOG_DEBUG ) { 487 try { 488 GMLFeatureAdapter ada = new GMLFeatureAdapter( false ); 489 GMLFeatureCollectionDocument doc = ada.export( fc ); 490 LOG.logDebugXMLFile( "TransactionHandler_insert_merged", doc ); 491 } catch ( Exception e ) { 492 LOG.logError( e.getMessage(), e ); 493 } 494 } 495 496 Map<DatastoreTransaction, List<Feature>> taFeaturesMap = new HashMap<DatastoreTransaction, List<Feature>>(); 497 FeatureIdAssigner fidAssigner = new FeatureIdAssigner( insert.getIdGen() ); 498 499 // assign features to corresponding datastore transactions 500 for ( int i = 0; i < fc.size(); i++ ) { 501 Feature feature = fc.getFeature( i ); 502 QualifiedName ftName = feature.getName(); 503 DatastoreTransaction dsTa = this.taMap.get( ftName ); 504 // reassign feature ids (if necessary) 505 fidAssigner.assignFID( feature, dsTa ); 506 List<Feature> features = taFeaturesMap.get( dsTa ); 507 if ( features == null ) { 508 features = new ArrayList<Feature>(); 509 taFeaturesMap.put( dsTa, features ); 510 } 511 features.add( feature ); 512 } 513 514 // TODO remove this hack 515 fidAssigner.markStoredFeatures(); 516 517 // clear reference to fidAssigner (implicitly to feature collection) 518 fidAssigner = null; 519 520 if ( LOG.getLevel() == ILogger.LOG_DEBUG ) { 521 try { 522 GMLFeatureAdapter ada = new GMLFeatureAdapter( false ); 523 GMLFeatureCollectionDocument doc = ada.export( fc ); 524 LOG.logDebugXMLFile( "TransactionHandler_insert_marked", doc ); 525 } catch ( Exception e ) { 526 LOG.logError( e.getMessage(), e ); 527 } 528 } 529 530 // clear reference to feature collection 531 fc = null; 532 533 for ( DatastoreTransaction ta : taFeaturesMap.keySet() ) { 534 List<Feature> features = taFeaturesMap.get( ta ); 535 fids.addAll( ta.performInsert( features ) ); 536 } 537 return fids; 538 } 539 540 /** 541 * Performs the given delete operation. 542 * 543 * @param delete 544 * delete operation to be performed 545 * @throws DatastoreException 546 */ 547 private int performDelete( Delete delete ) 548 throws DatastoreException { 549 QualifiedName ftName = delete.getTypeName(); 550 MappedFeatureType ft = this.ftMap.get( ftName ); 551 DatastoreTransaction dsTa = this.taMap.get( ftName ); 552 int deleted = dsTa.performDelete( ft, delete.getFilter(), this.request.getLockId() ); 553 return deleted; 554 } 555 556 /** 557 * Performs the given update operation. 558 * <p> 559 * Assigning of FIDs to replacement features is performed in the {@link DatastoreTransaction}. 560 * 561 * @param update 562 * update operation to be perform 563 * @throws DatastoreException 564 */ 565 private int performUpdate( Update update ) 566 throws DatastoreException { 567 568 QualifiedName ftName = update.getTypeName(); 569 MappedFeatureType ft = this.ftMap.get( ftName ); 570 DatastoreTransaction dsTa = this.taMap.get( ftName ); 571 int updated = 0; 572 if ( update.getFeature() == null ) { 573 updated = dsTa.performUpdate( ft, update.getReplacementProperties(), update.getFilter(), 574 this.request.getLockId() ); 575 } else { 576 updated = dsTa.performUpdate( ft, update.getFeature(), update.getFilter(), this.request.getLockId() ); 577 } 578 579 // to work around API changes... 580 if ( dsTa instanceof SQLTransaction ) { 581 changedFIDs.addAll( ( (SQLTransaction) dsTa ).determineAffectedFIDs( ft, update.getFilter() ) ); 582 } 583 584 return updated; 585 } 586 587 /** 588 * Acquires the necessary <code>DatastoreTransaction</code>s. For each participating <code>Datastore</code>, one 589 * transaction is needed. 590 * <p> 591 * Fills the <code>taMap</code> and <code>dsToTaMap</code> members of this class. 592 * 593 * @throws OGCWebServiceException 594 * if a feature type is unknown or a DatastoreTransaction could not be acquired 595 */ 596 private void acquireDSTransactions() 597 throws OGCWebServiceException { 598 Set<QualifiedName> ftNames = this.request.getAffectedFeatureTypes(); 599 for ( QualifiedName ftName : ftNames ) { 600 MappedFeatureType ft = this.ftMap.get( ftName ); 601 if ( ft == null ) { 602 String msg = "FeatureType '" + ftName + "' is not known to the WFS."; 603 throw new OGCWebServiceException( this.getClass().getName(), msg ); 604 } 605 Datastore ds = ft.getGMLSchema().getDatastore(); 606 DatastoreTransaction dsTa = this.dsToTaMap.get( ds ); 607 if ( dsTa == null ) { 608 try { 609 dsTa = ds.acquireTransaction(); 610 } catch ( DatastoreException e ) { 611 LOG.logError( e.getMessage(), e ); 612 String msg = "Could not acquire transaction for FeatureType '" + ftName + "'."; 613 throw new OGCWebServiceException( this.getClass().getName(), msg ); 614 } 615 this.dsToTaMap.put( ds, dsTa ); 616 } 617 this.taMap.put( ftName, dsTa ); 618 } 619 } 620 621 /** 622 * Releases all acquired <code>DatastoreTransaction</code>s. 623 * 624 * @throws OGCWebServiceException 625 * if a DatastoreTransaction could not be released 626 */ 627 private void releaseDSTransactions() 628 throws OGCWebServiceException { 629 String msg = ""; 630 for ( DatastoreTransaction dsTa : this.dsToTaMap.values() ) { 631 LOG.logDebug( "Releasing DatastoreTransaction " + dsTa ); 632 try { 633 dsTa.release(); 634 } catch ( DatastoreException e ) { 635 LOG.logError( "Error releasing DatastoreTransaction: " + e.getMessage(), e ); 636 msg += e.getMessage() + "\n"; 637 } 638 } 639 if ( msg.length() != 0 ) { 640 msg = "Could not release one or more DatastoreTransactions: " + msg; 641 throw new OGCWebServiceException( this.getClass().getName(), msg ); 642 } 643 } 644 645 /** 646 * Commits all pending <code>DatastoreTransaction</code>s. 647 * 648 * @throws OGCWebServiceException 649 * if a DatastoreException could not be committed 650 */ 651 private void commitDSTransactions() 652 throws OGCWebServiceException { 653 String msg = ""; 654 for ( DatastoreTransaction dsTa : this.dsToTaMap.values() ) { 655 LOG.logDebug( "Committing DatastoreTransaction " + dsTa ); 656 try { 657 dsTa.commit(); 658 } catch ( DatastoreException e ) { 659 LOG.logError( "Error committing DatastoreTransaction: " + e.getMessage(), e ); 660 msg += e.getMessage() + "\n"; 661 } 662 } 663 if ( msg.length() != 0 ) { 664 msg = "Could not commit one or more DatastoreTransactions: " + msg; 665 throw new OGCWebServiceException( this.getClass().getName(), msg ); 666 } 667 } 668 669 /** 670 * Aborts all pending <code>DatastoreTransaction</code>s. 671 * 672 * @throws OGCWebServiceException 673 * if a DatastoreException could not be aborted 674 */ 675 private void abortDSTransactions() 676 throws OGCWebServiceException { 677 String msg = ""; 678 for ( DatastoreTransaction dsTa : this.dsToTaMap.values() ) { 679 LOG.logDebug( "Aborting DatastoreTransaction " + dsTa ); 680 try { 681 dsTa.rollback(); 682 } catch ( DatastoreException e ) { 683 LOG.logError( "Error aborting DatastoreTransaction: " + e.getMessage(), e ); 684 msg += e.getMessage() + "\n"; 685 } 686 } 687 if ( msg.length() != 0 ) { 688 msg = "Could not abort one or more DatastoreTransactions: " + msg; 689 throw new OGCWebServiceException( this.getClass().getName(), msg ); 690 } 691 } 692 }