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 }