001 /*---------------------------------------------------------------------------- 002 This file is part of deegree, http://deegree.org/ 003 Copyright (C) 2001-2009 by: 004 Department of Geography, University of Bonn 005 and 006 lat/lon GmbH 007 008 This library is free software; you can redistribute it and/or modify it under 009 the terms of the GNU Lesser General Public License as published by the Free 010 Software Foundation; either version 2.1 of the License, or (at your option) 011 any later version. 012 This library is distributed in the hope that it will be useful, but WITHOUT 013 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 014 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 015 details. 016 You should have received a copy of the GNU Lesser General Public License 017 along with this library; if not, write to the Free Software Foundation, Inc., 018 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 019 020 Contact information: 021 022 lat/lon GmbH 023 Aennchenstr. 19, 53177 Bonn 024 Germany 025 http://lat-lon.de/ 026 027 Department of Geography, University of Bonn 028 Prof. Dr. Klaus Greve 029 Postfach 1147, 53001 Bonn 030 Germany 031 http://www.geographie.uni-bonn.de/deegree/ 032 033 e-mail: info@deegree.org 034 ----------------------------------------------------------------------------*/ 035 036 package org.deegree.ogcwebservices.csw.iso_profile.ebrim; 037 038 import java.io.IOException; 039 import java.net.URI; 040 import java.net.URISyntaxException; 041 import java.security.InvalidParameterException; 042 import java.util.ArrayList; 043 import java.util.HashMap; 044 import java.util.List; 045 import java.util.Map; 046 import java.util.UUID; 047 048 import org.deegree.datatypes.QualifiedName; 049 import org.deegree.framework.log.ILogger; 050 import org.deegree.framework.log.LoggerFactory; 051 import org.deegree.framework.util.TimeTools; 052 import org.deegree.framework.xml.XMLParsingException; 053 import org.deegree.framework.xml.XMLTools; 054 import org.deegree.io.datastore.schema.MappedFeatureType; 055 import org.deegree.model.feature.Feature; 056 import org.deegree.model.feature.FeatureCollection; 057 import org.deegree.model.feature.FeatureFactory; 058 import org.deegree.model.feature.FeatureProperty; 059 import org.deegree.model.filterencoding.ComplexFilter; 060 import org.deegree.model.filterencoding.Expression; 061 import org.deegree.model.filterencoding.Literal; 062 import org.deegree.model.filterencoding.OperationDefines; 063 import org.deegree.model.filterencoding.PropertyIsCOMPOperation; 064 import org.deegree.model.filterencoding.PropertyName; 065 import org.deegree.ogcbase.CommonNamespaces; 066 import org.deegree.ogcbase.PropertyPath; 067 import org.deegree.ogcbase.PropertyPathFactory; 068 import org.deegree.ogcwebservices.OGCWebServiceException; 069 import org.deegree.ogcwebservices.csw.manager.Insert; 070 import org.deegree.ogcwebservices.csw.manager.Manager; 071 import org.deegree.ogcwebservices.csw.manager.Operation; 072 import org.deegree.ogcwebservices.csw.manager.Transaction; 073 import org.deegree.ogcwebservices.csw.manager.TransactionResult; 074 import org.deegree.ogcwebservices.wfs.WFService; 075 import org.deegree.ogcwebservices.wfs.XMLFactory; 076 import org.deegree.ogcwebservices.wfs.operation.FeatureResult; 077 import org.deegree.ogcwebservices.wfs.operation.GetFeatureDocument; 078 import org.deegree.ogcwebservices.wfs.operation.GetFeatureWithLock; 079 import org.deegree.ogcwebservices.wfs.operation.Query; 080 import org.deegree.ogcwebservices.wfs.operation.GetFeature.RESULT_TYPE; 081 import org.deegree.ogcwebservices.wfs.operation.LockFeature.ALL_SOME_TYPE; 082 import org.deegree.ogcwebservices.wfs.operation.transaction.TransactionDocument; 083 import org.deegree.ogcwebservices.wfs.operation.transaction.TransactionOperation; 084 import org.deegree.ogcwebservices.wfs.operation.transaction.TransactionResponse; 085 import org.deegree.ogcwebservices.wfs.operation.transaction.Insert.ID_GEN; 086 import org.w3c.dom.Document; 087 import org.w3c.dom.Element; 088 import org.w3c.dom.Node; 089 090 /** 091 * The <code>InsertTransactionHandler</code> class will cut an csw/wrs ebrim insert transaction into four differend 092 * transactions, some of which are handled as wfs transactions. For each record in an Insert Transaction the basic 093 * workflow is following: 094 * <ol> 095 * <li>find out if the to id of the to inserted record is allready in the wfs database</li> 096 * <li>if so, set it's app:status value to "invalid"</li> 097 * <li>insert / update the records</li> 098 * <li>create an audittrail, that is an app:AuditableEvent of the insertion</li> 099 * </ol> 100 * 101 * 102 * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a> 103 * 104 * @author last edited by: $Author: bezema $ 105 * 106 * @version $Revision: 1.7 $, $Date: 2007-06-21 13:54:33 $ 107 * 108 */ 109 110 public class InsertTransactionHandler { 111 112 private static ILogger LOG = LoggerFactory.getLogger( InsertTransactionHandler.class ); 113 114 private Transaction originalTransaction; 115 116 private Insert insert; 117 118 private URI appURI; 119 120 private String userName; 121 122 /** 123 * Creates an TransactionHandler which will be able to handle csw/ebrim inserts as defined in the wrs spec. 124 * 125 * @param originalTransaction 126 * parsed from the incoming HttpServletRequest. 127 * @param insert 128 * InsertOperation to be handled (as part of the original Transaction) may not be null; 129 * @param appURI 130 * defining a namespace in which the wfs RegistryObjects Recide. 131 * @param userName 132 * of the users which wants to insert registryObjects, if not set it will be set to anonymous. 133 */ 134 public InsertTransactionHandler( Transaction originalTransaction, Insert insert, URI appURI, String userName ) { 135 if ( originalTransaction == null ) { 136 throw new InvalidParameterException( "The transaction parameter may not be null" ); 137 } 138 if ( insert == null ) { 139 throw new InvalidParameterException( "The insert parameter may not be null" ); 140 } 141 this.originalTransaction = originalTransaction; 142 this.insert = insert; 143 if ( appURI == null ) { 144 try { 145 appURI = new URI( "http://www.deegree.org/app" ); 146 } catch ( URISyntaxException e ) { 147 // nothing to do here. 148 } 149 } else { 150 this.appURI = appURI; 151 } 152 if ( userName == null || "".equals( userName ) ) { 153 userName = "anonymous"; 154 } 155 this.userName = userName; 156 157 } 158 159 /** 160 * This method will handle the insert (given from 161 * 162 * @param transactionManager 163 * which can handle the csw transactions and allows the access to a localwfs, if null an 164 * InvalidParameterException will be thrown. 165 * @param resultValues 166 * an array[3] in which the number of insertions (resultValues[0]) and/or updates (resultValues[2]) will 167 * be saved. If resultValues.length != 3 an InvalidParameterException will be thrown. 168 * @return the brief representation of the inserted (not updated) elements. 169 * @throws OGCWebServiceException 170 */ 171 public List<Element> handleInsertTransaction( Manager transactionManager, int[] resultValues ) 172 throws OGCWebServiceException { 173 if ( transactionManager == null ) { 174 throw new InvalidParameterException( "The transactionManager may not be null" ); 175 } 176 if ( resultValues.length != 3 ) { 177 throw new InvalidParameterException( "The length of the resultValues array must be 3" ); 178 } 179 180 List<Element> records = insert.getRecords(); 181 182 // Some properterypaths which are used for the creation of a complex filter. 183 QualifiedName registryObject = new QualifiedName( "app", "RegistryObject", appURI ); 184 Expression iduriExpr = new PropertyName( new QualifiedName( "app", "liduri", appURI ) ); 185 186 Expression statusExpr = new PropertyName( new QualifiedName( "app", "status", appURI ) ); 187 PropertyIsCOMPOperation validOperator = new PropertyIsCOMPOperation( OperationDefines.PROPERTYISEQUALTO, 188 statusExpr, new Literal( "valid" ) ); 189 190 PropertyIsCOMPOperation emptyOperator = new PropertyIsCOMPOperation( OperationDefines.PROPERTYISEQUALTO, 191 statusExpr, new Literal( "" ) ); 192 ComplexFilter vFilter = new ComplexFilter( validOperator ); 193 ComplexFilter eFilter = new ComplexFilter( emptyOperator ); 194 ComplexFilter statusFilter = new ComplexFilter( vFilter, eFilter, OperationDefines.OR ); 195 196 FeatureCollection featureCollectionOnId = null; 197 WFService localWFS = transactionManager.getWFService(); 198 199 List<Element> briefRecords = new ArrayList<Element>( records.size() ); 200 201 /** 202 * Iterate over all records and for each record do the following, <code> 203 * 1) find out if the to id of the to inserted record is allready in the wfs database 204 * 2) if so, set it's app:status value to "invalid" 205 * 3) insert / update the records 206 * 4) create an audittrail, that is an app:AuditableEvent of the insertion 207 * </code> 208 */ 209 210 for ( int recordCount = 0; recordCount < records.size(); ++recordCount ) { 211 Element record = records.get( recordCount ); 212 String auditableEventType = "Created"; 213 String oldID = record.getAttribute( "id" ); 214 if ( oldID == null || "".equals( oldID ) ) { 215 throw new OGCWebServiceException( "You are trying to insert a(n) " + record.getNodeName() 216 + " which has no 'id' attribute set, this is a required attribute." ); 217 } 218 String predecessorID = oldID; 219 String logicalID = record.getAttribute( "lid" ); 220 if ( logicalID == null || "".equals( logicalID ) ) { 221 // throw new OGCWebServiceException( "You are trying to insert a(n) " + record.getNodeName() 222 // + " which has no 'lid' attribute set, for this registry, this is a required attribute." ); 223 LOG.logDebug( " no lid given, setting attribute to value of id" ); 224 logicalID = oldID; 225 record.setAttribute( "lid", oldID ); 226 } 227 228 String home = record.getAttribute( "home" ); 229 if ( home == null ) { 230 home = ""; 231 } 232 233 // Expression idLiteral = new Literal( oldID ); 234 Expression idLiteral = new Literal( logicalID ); 235 PropertyIsCOMPOperation idOperator = new PropertyIsCOMPOperation( OperationDefines.PROPERTYISEQUALTO, 236 iduriExpr, idLiteral ); 237 ComplexFilter idFilter = new ComplexFilter( idOperator ); 238 ComplexFilter idAndStatusFilter = new ComplexFilter( idFilter, statusFilter, OperationDefines.AND ); 239 try { 240 // FeatureResult fr = sendWFSGetFeature( localWFS, registryObject, idFilter ); 241 FeatureResult fr = sendWFSGetFeature( localWFS, registryObject, idAndStatusFilter ); 242 if ( fr != null ) { 243 featureCollectionOnId = (FeatureCollection) fr.getResponse(); 244 } 245 } catch ( OGCWebServiceException e ) { 246 throw new OGCWebServiceException( "The insertion of " + record.getNodeName() + " failed because: " 247 + e.getMessage() ); 248 } 249 if ( featureCollectionOnId == null || "".equals( featureCollectionOnId.getId() ) ) { 250 throw new OGCWebServiceException( "The insertion of " + record.getNodeName() + " failed." ); 251 } 252 253 String lockId = featureCollectionOnId.getAttribute( "lockId" ); 254 LOG.logDebug( " InsertHandler, the GetFeature lock is: " + lockId ); 255 if ( lockId == null || "".equals( lockId ) ) { 256 throw new OGCWebServiceException( "Couldn't get a lock for " + record.getNodeName() 257 + ". This object can therefore not be inserted." ); 258 } 259 String numbOfFeatures = featureCollectionOnId.getAttribute( "numberOfFeatures" ); 260 int featureCount = 0; 261 try { 262 featureCount = Integer.parseInt( numbOfFeatures ); 263 LOG.logDebug( " InsertHandler: the number of features in the GetFeatureWithLock was: " + featureCount ); 264 } catch ( NumberFormatException nfe ) { 265 // nottin 266 } 267 // Check the number of hits we've found, if the id allready exists it means we want to set the status of the 268 // object to invalid. 269 // String newID = id; 270 if ( featureCount > 1 ) { 271 throw new OGCWebServiceException( "The lid of this element: " + record.getNodeName() 272 + " is not unique. This object can therefore not be inserted." ); 273 } else if ( featureCount == 1 ) { 274 int totalUpdated = changeStatusOfObject( lockId, registryObject, idAndStatusFilter, 275 record.getNodeName(), localWFS ); 276 277 Feature f = featureCollectionOnId.getFeature( 0 ); 278 if ( f == null ) { 279 LOG.logError( "No feature found!!!!!" ); 280 } else { 281 FeatureProperty iduriProperty = f.getDefaultProperty( new QualifiedName( "app", "iduri", appURI ) ); 282 if ( iduriProperty == null ) { 283 LOG.logError( "The id of this element: " 284 + record.getNodeName() 285 + " is not found in the registry. No association of type 'predecessor' will be inserted!." ); 286 } else { 287 predecessorID = (String) iduriProperty.getValue(); 288 if ( predecessorID == null || "".equals( predecessorID.trim() ) ) { 289 LOG.logError( "The registry helds an id of this element: " 290 + record.getNodeName() 291 + " but it is empty. An association of type 'predecessor' will be inserted with to the oldID!." ); 292 predecessorID = oldID; 293 } else { 294 LOG.logDebug( " setting predecessorID to id of the registry (" + predecessorID + ")." ); 295 } 296 LOG.logDebug( " wcsFilter: total updated wfs:records (should be >= 1) = " + totalUpdated ); 297 if ( totalUpdated == 1 ) { 298 auditableEventType = "Versioned"; 299 } 300 } 301 } 302 } 303 304 // send the insertion to wcs and insert the auditable event 305 306 String newID = UUID.randomUUID().toString(); 307 308 if ( "Versioned".equals( auditableEventType ) ) { 309 record.setAttribute( "id", newID ); 310 } 311 312 List<Element> tmpRecords = new ArrayList<Element>( 1 ); 313 tmpRecords.add( record ); 314 315 Insert ins = new Insert( insert.getHandle(), tmpRecords ); 316 List<Operation> tmpOp = new ArrayList<Operation>( 1 ); 317 tmpOp.add( ins ); 318 Transaction transaction = new Transaction( originalTransaction.getVersion(), originalTransaction.getId(), 319 originalTransaction.getVendorSpecificParameters(), tmpOp, false ); 320 TransactionResult tmpInsertResult = null; 321 try { 322 tmpInsertResult = transactionManager.transaction( transaction ); 323 } catch ( OGCWebServiceException ogws ) { 324 throw new OGCWebServiceException( "CSW Insert Transaction: Error while inserting '" 325 + record.getNodeName() + "' with id='" + oldID + "' because: " 326 + ogws.getMessage() ); 327 } 328 329 if ( tmpInsertResult == null || tmpInsertResult.getTotalInserted() != 1 ) { 330 throw new OGCWebServiceException( 331 "The insertion of the element: " 332 + record.getNodeName() 333 + " failed, because the transactionresult is null or the number of inserted objects wasn't 1." ); 334 } 335 336 if ( featureCount == 1 ) { 337 // update 338 resultValues[2]++; 339 } else { 340 // insert 341 resultValues[0]++; 342 } 343 // First create the necessary Features 344 List<Feature> newObjectsInDB = new ArrayList<Feature>(); 345 newObjectsInDB.add( createAuditableEvent( localWFS, oldID, home, auditableEventType, userName ) ); 346 if ( "Versioned".equals( auditableEventType ) ) { 347 newObjectsInDB.add( createAssociation( localWFS, newID, predecessorID ) ); 348 349 // Now update all following associations which referenced the oldID. 350 for ( int i = ( recordCount + 1 ); i < records.size(); ++i ) { 351 Element tmpRec = records.get( i ); 352 if ( CommonNamespaces.OASIS_EBRIMNS.toASCIIString().equals( tmpRec.getNamespaceURI() ) 353 && "Association".equals( tmpRec.getLocalName() ) ) { 354 String sourceObject = tmpRec.getAttribute( "sourceObject" ); 355 String targetObject = tmpRec.getAttribute( "targetObject" ); 356 if ( oldID.equals( sourceObject ) ) { 357 LOG.logDebug( " Updating 'rim:Association/@sourceObject' Attribute to new id: " + newID 358 + " after an update of registryObject: " + record.getLocalName() ); 359 tmpRec.setAttribute( "sourceObject", newID ); 360 } 361 if ( oldID.equals( targetObject ) ) { 362 LOG.logDebug( " Updating 'rim:Association/@targetObject' Attribute to new id: " + newID 363 + " after an update of registryObject: " + record.getLocalName() ); 364 tmpRec.setAttribute( "targetObject", newID ); 365 } 366 } 367 } 368 } 369 insertFeatures( localWFS, newObjectsInDB, record.getNodeName() ); 370 // sendAuditableEvent( transactionManager.getWfsService(), record.getNodeName(), auditableEventType, 371 // username, 372 // id, home ); 373 // create a brief record description of the inserted record. 374 briefRecords.add( generateBriefRecord( record ) ); 375 } 376 return briefRecords; 377 } 378 379 /** 380 * 381 * @param localWFS 382 * @param registryObject 383 * @param filter 384 * @return the FeatureResult of the given filter or <code>null</code> if something went wrong. 385 * @throws OGCWebServiceException 386 */ 387 private FeatureResult sendWFSGetFeature( WFService localWFS, QualifiedName registryObject, ComplexFilter filter ) 388 throws OGCWebServiceException { 389 Query q = Query.create( registryObject, filter ); 390 GetFeatureWithLock gfwl = GetFeatureWithLock.create( "1.1.0", "0", "no_handle", RESULT_TYPE.RESULTS, 391 "text/xml; subtype=gml/3.1.1", -1, 0, -1, -1, 392 new Query[] { q }, null, 300000l, ALL_SOME_TYPE.ALL ); 393 if ( LOG.getLevel() == ILogger.LOG_DEBUG ) { 394 try { 395 GetFeatureDocument gd = XMLFactory.export( gfwl ); 396 LOG.logDebug( "The getFeature with lock request: " + gd.getAsPrettyString() ); 397 } catch ( IOException e ) { 398 LOG.logError( "InsertTransactionHandler: An error occurred while trying to get a debugging output for the generated GetFeatureDocument: " 399 + e.getMessage() ); 400 } catch ( XMLParsingException e ) { 401 LOG.logError( "InsertTransactionHandler: An error occurred while trying to get a debugging output for the generated GetFeatureDocument: " 402 + e.getMessage() ); 403 } 404 } 405 406 Object response = localWFS.doService( gfwl ); 407 if ( response instanceof FeatureResult ) { 408 LOG.logDebug( "InsertHandler tried to get A feature with Lock, with a valid response from the localwfs" ); 409 return (FeatureResult) response; 410 } 411 return null; 412 } 413 414 /** 415 * This method will create a WFSTransaction containing one update operation, which will set the app:status of the 416 * app:RegistryObject found using the complexFilter to superseded. 417 * 418 * @param newId 419 * of the registryObject 420 * @param lockId 421 * which was set while querying the app:RegistryObject for it's app:status 422 * @return the number of updated records, this value should only be 1 or 0. 423 * @throws OGCWebServiceException 424 * if something went wrong. 425 */ 426 private int changeStatusOfObject( String lockId, QualifiedName registryObject, ComplexFilter filter, 427 String originalRecordNodeName, WFService localWFS ) 428 throws OGCWebServiceException { 429 List<TransactionOperation> operations = new ArrayList<TransactionOperation>(); 430 Map<PropertyPath, FeatureProperty> properties = new HashMap<PropertyPath, FeatureProperty>(); 431 432 // the new status value, e.g. app:RegistryObject/app:status=invalid 433 QualifiedName status = new QualifiedName( "app", "status", appURI ); 434 PropertyPath statusPP = PropertyPathFactory.createPropertyPath( registryObject ); 435 statusPP.append( PropertyPathFactory.createPropertyPathStep( status ) ); 436 437 // // the new id value e.g app:RegistryObject/app:iduri=newId 438 // QualifiedName iduri = new QualifiedName( "app", "iduri", appURI ); 439 // PropertyPath iduriPP = PropertyPathFactory.createPropertyPath( registryObject ); 440 // iduriPP.append( PropertyPathFactory.createAttributePropertyPathStep( iduri ) ); 441 442 // Adding the properties (e.g. the status=ivalid and the iduri=newId) to the wfs:UpdateOperation. 443 properties.put( statusPP, FeatureFactory.createFeatureProperty( status, "superseded" ) ); 444 // properties.put( iduriPP, FeatureFactory.createFeatureProperty( iduri, newId ) ); 445 446 operations.add( new org.deegree.ogcwebservices.wfs.operation.transaction.Update( "no_handle", registryObject, 447 properties, filter ) ); 448 org.deegree.ogcwebservices.wfs.operation.transaction.Transaction wfsTransaction = new org.deegree.ogcwebservices.wfs.operation.transaction.Transaction( 449 "0", 450 "1.1.0", 451 null, 452 lockId, 453 operations, 454 true, 455 null ); 456 int totalUpdated = 0; 457 try { 458 Object response = localWFS.doService( wfsTransaction ); 459 if ( response instanceof TransactionResponse ) { 460 totalUpdated = ( (TransactionResponse) response ).getTotalUpdated(); 461 } 462 } catch ( OGCWebServiceException e ) { 463 throw new OGCWebServiceException( "The insertion of " + originalRecordNodeName + " failed: " 464 + e.getMessage() ); 465 } 466 return totalUpdated; 467 468 } 469 470 /** 471 * creates a brief representation of the given RegistryObject element, with following values (wrs spec): 472 * <ul> 473 * <li>rim:RegistryObject/@id</li> 474 * <li>rim:RegistryObject/@lid</li> 475 * <li>rim:RegistryObject/@objectType</li> 476 * <li>rim:RegistryObject/@status</li> 477 * <li>rim:RegistryObject/rim:VersionInfo</li> 478 * </ul> 479 * 480 * @return a brief record description of the given ebrim:RegistryObject 481 */ 482 private Element generateBriefRecord( Element record ) { 483 Document doc = XMLTools.create(); 484 Element resultElement = doc.createElement( "csw:result" ); 485 Element a = (Element) doc.importNode( record, false ); 486 resultElement.appendChild( a ); 487 List<Node> attribs = null; 488 try { 489 attribs = XMLTools.getNodes( a, "./@*", CommonNamespaces.getNamespaceContext() ); 490 } catch ( XMLParsingException e1 ) { 491 LOG.logError( 492 "InsertTransactionHandler: an error occurred while creating a briefrecord for registryObject: " 493 + record.getNodeName(), e1 ); 494 } 495 // NamedNodeMap attribs = a.getAttributes(); 496 if ( attribs != null ) { 497 for ( Node attribute : attribs ) { 498 // Attr attribute = (Attr) attribs.item( i ); 499 String localName = attribute.getLocalName(); 500 501 LOG.logDebug( "From: " + a.getNodeName() + " found attribute (localname): " + localName ); 502 if ( !( "id".equals( localName ) || "lid".equals( localName ) || "objectType".equals( localName ) || "status".equals( localName ) ) ) { 503 // resultElement.setAttributeNode( (Attr)attribs.item(i) ); 504 LOG.logDebug( " From: " + a.getNodeName() + " removing attribute (localname): " + localName ); 505 String namespace = attribute.getBaseURI(); 506 // a.removeChild( attribs.item(i) ); 507 a.removeAttributeNS( namespace, localName ); 508 } 509 } 510 } 511 Element versionInfo = null; 512 try { 513 versionInfo = XMLTools.getElement( record, "rim:VersionInfo", CommonNamespaces.getNamespaceContext() ); 514 if ( versionInfo != null ) { 515 Node vi = doc.importNode( versionInfo, true ); 516 a.appendChild( vi ); 517 } 518 } catch ( XMLParsingException e ) { 519 LOG.logError( 520 "InsertTransactionHandler: an error occurred while creating a briefrecord for registryObject: " 521 + record.getNodeName(), e ); 522 } 523 return a; 524 } 525 526 /** 527 * Creates an association of type "urn:adv:registry:AssociationType:predecessor" which associates an old 528 * (updated/superseded) registry object with a new registry object. 529 * 530 * @param localWFS 531 * which will be talked to directly (superseding the csw). 532 * @param newRegisterID 533 * the id of the new object inserted in the db 534 * @param oldRegisterID 535 * the id of the old updated object, superseded in the db 536 */ 537 private Feature createAssociation( WFService localWFS, String newRegisterID, String oldRegisterID ) { 538 539 QualifiedName registryObject = new QualifiedName( "app", "RegistryObject", appURI ); 540 QualifiedName associationType = new QualifiedName( "app", "Association", appURI ); 541 542 MappedFeatureType rootFT = localWFS.getMappedFeatureType( registryObject ); 543 MappedFeatureType associationFT = localWFS.getMappedFeatureType( associationType ); 544 545 List<FeatureProperty> featureProperties = new ArrayList<FeatureProperty>(); 546 547 // Generate the Auditable Event complex subfeature 548 549 QualifiedName associationTypeProp = new QualifiedName( "app", "associationType", appURI ); 550 featureProperties.add( FeatureFactory.createFeatureProperty( associationTypeProp, 551 "urn:adv:registry:AssociationType:predecessor" ) ); 552 553 QualifiedName sourceObject = new QualifiedName( "app", "sourceObject", appURI ); 554 featureProperties.add( FeatureFactory.createFeatureProperty( sourceObject, newRegisterID ) ); 555 556 QualifiedName targetObject = new QualifiedName( "app", "targetObject", appURI ); 557 featureProperties.add( FeatureFactory.createFeatureProperty( targetObject, oldRegisterID ) ); 558 559 Feature associationFeature = FeatureFactory.createFeature( null, associationFT, featureProperties ); 560 561 // Creation of the RegistryObject 562 featureProperties.clear(); 563 564 // type 565 QualifiedName type = new QualifiedName( "app", "type", appURI ); 566 featureProperties.add( FeatureFactory.createFeatureProperty( type, "Association" ) ); 567 568 QualifiedName iduri = new QualifiedName( "app", "iduri", appURI ); 569 featureProperties.add( FeatureFactory.createFeatureProperty( iduri, UUID.randomUUID().toString() ) ); 570 571 // objecttype 572 QualifiedName objectType = new QualifiedName( "app", "objectType", appURI ); 573 featureProperties.add( FeatureFactory.createFeatureProperty( objectType, 574 "urn:oasis:names:tc:ebxml-regrep:ObjectType:RegistryObject:Association" ) ); 575 576 // status 577 QualifiedName status = new QualifiedName( "app", "status", appURI ); 578 featureProperties.add( FeatureFactory.createFeatureProperty( status, "valid" ) ); 579 580 // create the auditable Event property with the feature 581 QualifiedName association = new QualifiedName( "app", "association", appURI ); 582 featureProperties.add( FeatureFactory.createFeatureProperty( association, associationFeature ) ); 583 584 Feature rootFeature = FeatureFactory.createFeature( null, rootFT, featureProperties ); 585 return rootFeature; 586 } 587 588 /** 589 * Creates an auditable event for the given objectid 590 * 591 * TODO shouldn't the slots of the original inserted Object not be handled? 592 * 593 * @param localWFS 594 * which will be talked to directly (superseding the csw). 595 * @param affectedObjectId 596 * of the object which has been inserted or updated 597 * @param affectedHome 598 * of the object which has been inserted or updated 599 * @param auditEventType 600 * should be one of 'Created' or 'Updated' (see the ebrim spec) 601 * @param username 602 * of the person doing the insertion 603 */ 604 private Feature createAuditableEvent( WFService localWFS, String affectedObjectId, String affectedHome, 605 String auditEventType, String username ) { 606 String requestId = originalTransaction.getId(); 607 608 QualifiedName registryObject = new QualifiedName( "app", "RegistryObject", appURI ); 609 QualifiedName auditableEventType = new QualifiedName( "app", "AuditableEvent", appURI ); 610 QualifiedName objectRefType = new QualifiedName( "app", "ObjectRef", appURI ); 611 612 MappedFeatureType rootFT = localWFS.getMappedFeatureType( registryObject ); 613 MappedFeatureType auditableEventFT = localWFS.getMappedFeatureType( auditableEventType ); 614 MappedFeatureType objectRefFT = localWFS.getMappedFeatureType( objectRefType ); 615 616 List<FeatureProperty> featureProperties = new ArrayList<FeatureProperty>(); 617 618 // Creating the Objectref 619 QualifiedName replacedURI = new QualifiedName( "app", "iduri", appURI ); 620 featureProperties.add( FeatureFactory.createFeatureProperty( replacedURI, affectedObjectId ) ); 621 622 QualifiedName replacedHome = new QualifiedName( "app", "home", appURI ); 623 featureProperties.add( FeatureFactory.createFeatureProperty( replacedHome, affectedHome ) ); 624 625 QualifiedName createReplica = new QualifiedName( "app", "createReplica", appURI ); 626 featureProperties.add( FeatureFactory.createFeatureProperty( createReplica, "false" ) ); 627 628 Feature objectRefFeature = FeatureFactory.createFeature( null, objectRefFT, featureProperties ); 629 630 // Generate the Auditable Event complex subfeature 631 featureProperties.clear(); 632 633 QualifiedName eventType = new QualifiedName( "app", "eventType", appURI ); 634 featureProperties.add( FeatureFactory.createFeatureProperty( eventType, auditEventType ) ); 635 636 QualifiedName timestamp = new QualifiedName( "app", "timestamp", appURI ); 637 featureProperties.add( FeatureFactory.createFeatureProperty( timestamp, TimeTools.getISOFormattedTime() ) ); 638 639 QualifiedName usernameQName = new QualifiedName( "app", "username", appURI ); 640 featureProperties.add( FeatureFactory.createFeatureProperty( usernameQName, username ) ); 641 642 QualifiedName requestIdQName = new QualifiedName( "app", "requestId", appURI ); 643 featureProperties.add( FeatureFactory.createFeatureProperty( requestIdQName, requestId ) ); 644 645 // add the affected ObjectsFeatureType to the affectedObjects property 646 QualifiedName affectedObjects = new QualifiedName( "app", "affectedObjects", appURI ); 647 featureProperties.add( FeatureFactory.createFeatureProperty( affectedObjects, objectRefFeature ) ); 648 649 Feature auditEventFeature = FeatureFactory.createFeature( null, auditableEventFT, featureProperties ); 650 651 // Creation of the RegistryObject 652 featureProperties.clear(); 653 654 // type 655 QualifiedName type = new QualifiedName( "app", "type", appURI ); 656 featureProperties.add( FeatureFactory.createFeatureProperty( type, "AuditableEvent" ) ); 657 658 QualifiedName iduri = new QualifiedName( "app", "iduri", appURI ); 659 featureProperties.add( FeatureFactory.createFeatureProperty( iduri, UUID.randomUUID().toString() ) ); 660 661 // objecttype 662 QualifiedName objectType = new QualifiedName( "app", "objectType", appURI ); 663 featureProperties.add( FeatureFactory.createFeatureProperty( objectType, 664 "urn:oasis:names:tc:ebxml-regrep:ObjectType:RegistryObject:AuditableEvent" ) ); 665 666 // status 667 QualifiedName status = new QualifiedName( "app", "status", appURI ); 668 featureProperties.add( FeatureFactory.createFeatureProperty( status, "valid" ) ); 669 670 // create the auditable Event property with the feature 671 QualifiedName auditableEvent = new QualifiedName( "app", "auditableEvent", appURI ); 672 featureProperties.add( FeatureFactory.createFeatureProperty( auditableEvent, auditEventFeature ) ); 673 674 Feature rootFeature = FeatureFactory.createFeature( null, rootFT, featureProperties ); 675 676 return rootFeature; 677 } 678 679 /** 680 * Puts an auditable event for the given objectid into the database, thus resulting in an AuditTrail for the 681 * inserted/updated Object. 682 * 683 * 684 * @param localWFS 685 * which will be talked to directly (superseding the csw). 686 * @param featuresToInsert 687 * an array of features (either an auditableEvent or an auditableEvent and an Association (if an update 688 * occurred) ). 689 * @param originalInsertObjectName 690 * the name of the object to be inserted (used for debug messages) 691 * @throws OGCWebServiceException 692 */ 693 private void insertFeatures( WFService localWFS, List<Feature> featuresToInsert, String originalInsertObjectName ) 694 throws OGCWebServiceException { 695 String requestId = originalTransaction.getId(); 696 if ( featuresToInsert.size() == 0 ) { 697 LOG.logError( "CSW (Ebrim) InsertTransactionHandler: there were no features to insert, this may not be (at least an auditableEvent feature should be inserted)!" ); 698 return; 699 } 700 Feature[] fA = new Feature[featuresToInsert.size()]; 701 for ( int i = 0; i < fA.length; ++i ) { 702 fA[i] = featuresToInsert.get( i ); 703 } 704 FeatureCollection fc = FeatureFactory.createFeatureCollection( requestId, fA ); 705 706 org.deegree.ogcwebservices.wfs.operation.transaction.Insert wfsInsert = new org.deegree.ogcwebservices.wfs.operation.transaction.Insert( 707 "no_handle", 708 ID_GEN.GENERATE_NEW, 709 null, 710 fc ); 711 List<TransactionOperation> ops = new ArrayList<TransactionOperation>( 1 ); 712 ops.add( wfsInsert ); 713 org.deegree.ogcwebservices.wfs.operation.transaction.Transaction transaction = new org.deegree.ogcwebservices.wfs.operation.transaction.Transaction( 714 originalTransaction.getId(), 715 "1.1.0", 716 null, 717 null, 718 ops, 719 true, 720 null ); 721 722 try { 723 localWFS.doService( transaction ); 724 } catch ( OGCWebServiceException e ) { 725 String features = "AuditableEvent "; 726 if ( fA.length > 1 ) 727 features += "and an Association "; 728 throw new OGCWebServiceException( "Could not insert an " + features 729 + "for the insertion/update of the RegistryObject: " 730 + originalInsertObjectName + " because: " + e.getMessage() ); 731 } 732 733 if ( LOG.getLevel() == ILogger.LOG_DEBUG ) { 734 try { 735 TransactionDocument doc = XMLFactory.export( transaction ); 736 LOG.logDebug( " The auditable event created for the insertion of '" + originalInsertObjectName 737 + "' is:\n" + doc.getAsPrettyString() ); 738 } catch ( IOException e ) { 739 LOG.logError( "InsertTransactionHandler: An error occurred while trying to create an auditable Event for insertion of the '" 740 + originalInsertObjectName + "'. Errormessage: " + e.getMessage() ); 741 } catch ( XMLParsingException e ) { 742 LOG.logError( "InsertTransactionHandler: An error occurred while trying to create an auditable Event for insertion of the '" 743 + originalInsertObjectName + "'. Errormessage: " + e.getMessage() ); 744 } 745 } 746 747 } 748 749 }