001/* 002 * Copyright 2015 DuraSpace, Inc. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.fcrepo.migration.handlers; 017 018import com.hp.hpl.jena.datatypes.xsd.XSDDatatype; 019import com.hp.hpl.jena.graph.Node; 020import com.hp.hpl.jena.graph.NodeFactory; 021import com.hp.hpl.jena.graph.Triple; 022import com.hp.hpl.jena.rdf.model.Model; 023import com.hp.hpl.jena.rdf.model.ModelFactory; 024import com.hp.hpl.jena.rdf.model.Statement; 025import com.hp.hpl.jena.rdf.model.StmtIterator; 026import com.hp.hpl.jena.sparql.modify.request.QuadAcc; 027import com.hp.hpl.jena.sparql.modify.request.QuadDataAcc; 028import com.hp.hpl.jena.sparql.modify.request.UpdateDataInsert; 029import com.hp.hpl.jena.sparql.modify.request.UpdateDeleteWhere; 030import com.hp.hpl.jena.update.UpdateFactory; 031import com.hp.hpl.jena.update.UpdateRequest; 032import org.apache.jena.atlas.io.IndentedWriter; 033import org.apache.jena.riot.system.ErrorHandlerFactory; 034import org.fcrepo.migration.DatastreamVersion; 035import org.fcrepo.migration.ExternalContentURLMapper; 036import org.fcrepo.migration.Fedora4Client; 037import org.fcrepo.migration.FedoraObjectVersionHandler; 038import org.fcrepo.migration.MigrationIDMapper; 039import org.fcrepo.migration.ObjectProperty; 040import org.fcrepo.migration.ObjectReference; 041import org.fcrepo.migration.ObjectVersionReference; 042import org.fcrepo.migration.foxml.DC; 043import org.fcrepo.migration.foxml.NamespacePrefixMapper; 044import org.fcrepo.migration.urlmappers.SelfReferencingURLMapper; 045import org.slf4j.Logger; 046 047import javax.xml.bind.JAXBException; 048import javax.xml.datatype.DatatypeConfigurationException; 049import javax.xml.datatype.DatatypeFactory; 050import javax.xml.datatype.XMLGregorianCalendar; 051import java.io.ByteArrayOutputStream; 052import java.io.File; 053import java.io.FileInputStream; 054import java.io.FileNotFoundException; 055import java.io.IOException; 056import java.util.ArrayList; 057import java.util.Date; 058import java.util.GregorianCalendar; 059import java.util.List; 060import java.util.Properties; 061 062import static org.slf4j.LoggerFactory.getLogger; 063 064/** 065 * 066 * @author mdurbin 067 * 068 */ 069public class BasicObjectVersionHandler implements FedoraObjectVersionHandler { 070 071 private static Logger LOGGER = getLogger(BasicObjectVersionHandler.class); 072 073 private static int suffix = 0; 074 075 private Fedora4Client f4client; 076 077 private MigrationIDMapper idMapper; 078 079 private boolean importExternal; 080 081 private boolean importRedirect; 082 083 private ExternalContentURLMapper externalContentUrlMapper; 084 085 private NamespacePrefixMapper namespacePrefixMapper; 086 087 private boolean skipDisseminators = false; 088 089 private Properties customPropertyMapping; 090 091 /** 092 * Basic object version handler. 093 * @param client a fedora4 client 094 * @param idMapper the id mapper 095 * @param localFedoraServer uri to fedora server 096 * @param namespacePrefixMapper a namespace prefix mapper. 097 */ 098 public BasicObjectVersionHandler(final Fedora4Client client, 099 final MigrationIDMapper idMapper, 100 final String localFedoraServer, 101 final NamespacePrefixMapper namespacePrefixMapper) { 102 this.f4client = client; 103 this.idMapper = idMapper; 104 this.externalContentUrlMapper = new SelfReferencingURLMapper(localFedoraServer, idMapper); 105 this.namespacePrefixMapper = namespacePrefixMapper; 106 107 // Do not throw exception for invalid RDF 108 ErrorHandlerFactory.setDefaultErrorHandler(ErrorHandlerFactory.errorHandlerWarn); 109 } 110 111 /** 112 * A property setter for a property that determines the handling of External (X) 113 * fedora 3 datastreams. If true, the content of the URL to which those datastreams 114 * redirect is fetched and ingested as a fedora 4-managed non-RDF resource. If false 115 * (default), a non-RDF resource is created in fedora 4 that when fetched results in 116 * an HTTP redirect to the external url. 117 * 118 * @param value indicating if content is external 119 */ 120 public void setImportExternal(final boolean value) { 121 this.importExternal = value; 122 } 123 124 /** 125 * A property setter for a property that determines the handling of Redirect (R) 126 * fedora 3 datastreams. If true, the content of the URL to which those datastreams 127 * redirect is fetched and ingested as a fedora 4-managed non-RDF resource. If false 128 * (default), a non-RDF resource is created in fedora 4 that when fetched results in 129 * an HTTP redirect to the external url. 130 * 131 * @param value indicating if content is imported 132 */ 133 public void setImportRedirect(final boolean value) { 134 this.importRedirect = value; 135 } 136 137 /** 138 * A property setter for the optional propertly that indicates a Properties file whose 139 * key value pairs represent custom mappings from fedora 3 properties to fedora 4 140 * properties. 141 * @param propertiesFile a properties file containing mappings from foxml to fedoar 4 properties 142 */ 143 public void setCustomPropertyMapping(final File propertiesFile) { 144 this.customPropertyMapping = new Properties(); 145 try { 146 final FileInputStream fis = new FileInputStream(propertiesFile); 147 try { 148 this.customPropertyMapping.load(fis); 149 } finally { 150 fis.close(); 151 } 152 } catch (FileNotFoundException e) { 153 throw new RuntimeException("The file, \"" + propertiesFile.getAbsolutePath() 154 + "\" , specified in the 'customPropertyMapping' property was not found!", e); 155 } catch (IOException e) { 156 throw new RuntimeException("Error access the file, \"" + propertiesFile.getAbsolutePath() 157 + "\" , specified in the 'customPropertyMapping' property!", e); 158 } 159 } 160 161 /** 162 * Sets a property that indicates whether fedora 2 disseminators will be skipped or 163 * not. 164 * @param skip indicates whether it is ok to skip fedora 2 disseminators. 165 */ 166 public void setSkipDisseminators(final boolean skip) { 167 this.skipDisseminators = skip; 168 } 169 170 @Override 171 public void processObjectVersions(final Iterable<ObjectVersionReference> versions) { 172 String objectPath = null; 173 try { 174 for (final ObjectVersionReference version : versions) { 175 176 LOGGER.debug("Considering object " 177 + version.getObjectInfo().getPid() 178 + " version at " + version.getVersionDate() + "."); 179 180 if (objectPath == null) { 181 if (!skipDisseminators && version.getObject().hadFedora2Disseminators()) { 182 throw new RuntimeException("Fedora 2 disseminators are not supported, set" + 183 " \"skipDisseminators\" to true to migration this object without" + 184 " the disseminators!"); 185 } 186 objectPath = idMapper.mapObjectPath(version.getObjectInfo().getPid()); 187 if (!f4client.exists(objectPath)) { 188 f4client.createPlaceholder(objectPath); 189 } else if (!f4client.isPlaceholder(objectPath)) { 190 LOGGER.warn(objectPath + " already exists, skipping migration of " 191 + version.getObject().getObjectInfo().getPid() + "!"); 192 return; 193 } 194 } 195 196 final QuadDataAcc triplesToInsert = new QuadDataAcc(); 197 final QuadAcc triplesToRemove = new QuadAcc(); 198 199 for (final DatastreamVersion v : withRELSINTLast(version.listChangedDatastreams())) { 200 LOGGER.debug("Considering changed datastream version " + v.getVersionId()); 201 final String dsPath = idMapper.mapDatastreamPath(v.getDatastreamInfo().getObjectInfo().getPid(), 202 v.getDatastreamInfo().getDatastreamId()); 203 if (v.getDatastreamInfo().getDatastreamId().equals("DC")) { 204 migrateDc(v, triplesToRemove, triplesToInsert); 205 } else if (v.getDatastreamInfo().getDatastreamId().equals("RELS-EXT")) { 206 migrateRelsExt(v, triplesToRemove, triplesToInsert); 207 } else if (v.getDatastreamInfo().getDatastreamId().equals("RELS-INT")) { 208 migrateRelsInt(v); 209 } else if ((v.getDatastreamInfo().getControlGroup().equals("E") && !importExternal) 210 || (v.getDatastreamInfo().getControlGroup().equals("R") && !importRedirect)) { 211 f4client.createOrUpdateRedirectNonRDFResource(dsPath, 212 externalContentUrlMapper.mapURL(v.getExternalOrRedirectURL())); 213 updateDatastreamProperties(version.getObject(), v, dsPath); 214 } else { 215 f4client.createOrUpdateNonRDFResource(dsPath, v.getContent(), v.getMimeType()); 216 updateDatastreamProperties(version.getObject(), v, dsPath); 217 } 218 } 219 220 updateObjectProperties(version, objectPath, triplesToRemove, triplesToInsert); 221 222 f4client.createVersionSnapshot(objectPath, 223 "imported-version-" + String.valueOf(version.getVersionIndex())); 224 } 225 } catch (final IOException e) { 226 throw new RuntimeException(e); 227 } 228 } 229 230 private List<DatastreamVersion> withRELSINTLast(final List<DatastreamVersion> orig) { 231 final List<DatastreamVersion> versionsWithRELSINTLast = new ArrayList<DatastreamVersion>(orig); 232 for (int i = 0; i < versionsWithRELSINTLast.size(); i ++) { 233 if (versionsWithRELSINTLast.get(i).getDatastreamInfo().getDatastreamId().equals("RELS-INT")) { 234 versionsWithRELSINTLast.add(versionsWithRELSINTLast.remove(i)); 235 break; 236 } 237 } 238 return versionsWithRELSINTLast; 239 } 240 241 /** 242 * Evaluates if an object/datastream property is a date. 243 * 244 * @param uri The predicate in question. 245 * @return True if the property is a date. False otherwise. 246 */ 247 protected boolean isDateProperty(final String uri) { 248 return uri.equals("info:fedora/fedora-system:def/model#createdDate") || 249 uri.equals("info:fedora/fedora-system:def/view#lastModifiedDate") || 250 uri.equals("http://www.loc.gov/premis/rdf/v1#hasDateCreatedByApplication") || 251 uri.equals("http://www.loc.gov/premis/rdf/v1#hasEventDateTime"); 252 } 253 254 /** 255 * Updates object properties after mapping them from 3 to 4. 256 * 257 * @param version Object version to reference 258 * @param objectPath Destination path (in f4) for the object being migrated 259 * @param triplesToRemove List of triples to remove from resource. 260 * @param triplesToInsert List of triples to add to resource. 261 262 */ 263 protected void updateObjectProperties(final ObjectVersionReference version, 264 final String objectPath, 265 final QuadAcc triplesToRemove, 266 final QuadDataAcc triplesToInsert) { 267 if (version.isFirstVersion()) { 268 // Migration event (current time) 269 final String now = getCurrentTimeInXSDDateTime(); 270 if (now != null) { 271 addDateEvent(triplesToInsert, 272 "http://id.loc.gov/vocabulary/preservation/eventType/mig", 273 getCurrentTimeInXSDDateTime()); 274 } 275 276 mapProperty("info:fedora/fedora-system:def/model#PID", version.getObject().getObjectInfo().getPid(), 277 triplesToRemove, triplesToInsert, true); 278 } 279 280 if (version.isLastVersion()) { 281 for (final ObjectProperty p : version.getObjectProperties().listProperties()) { 282 mapProperty(p.getName(), p.getValue(), triplesToRemove, triplesToInsert, true); 283 } 284 } 285 286 // Update if there's triples to remove / add. 287 // Some may come from other datastreams like RELS-EXT and DC, not just 288 // in this function. 289 if (!triplesToInsert.getQuads().isEmpty() && !triplesToRemove.getQuads().isEmpty()) { 290 updateResourceProperties(objectPath, triplesToRemove, triplesToInsert, false); 291 } 292 } 293 294 /** 295 * WIP function to map properties from 3 to 4. 296 * Feel free to override this to suit your needs. 297 * 298 * @param origPred Predicate of property to map from 3 to 4. 299 * @param obj Object of property to map from 3 to 4. 300 * @param triplesToRemove List of triples to remove from resource. 301 * @param triplesToInsert List of triples to add to resource. 302 * @param isLiteral TRUE if obj is a literal triple, FALSE if a URI 303 */ 304 protected void mapProperty(final String origPred, 305 final String obj, 306 final QuadAcc triplesToRemove, 307 final QuadDataAcc triplesToInsert, 308 final Boolean isLiteral) { 309 String pred = origPred; 310 // Map dates and object state 311 if (customPropertyMapping != null && customPropertyMapping.containsKey(pred)) { 312 pred = customPropertyMapping.getProperty(pred); 313 } else if (pred.equals("info:fedora/fedora-system:def/model#createdDate")) { 314 // Handle created date seperately and exit early. 315 updateDateTriple(triplesToRemove, triplesToInsert, 316 "http://fedora.info/definitions/v4/repository#created", 317 obj); 318 return; 319 } else if (pred.equals("info:fedora/fedora-system:def/view#lastModifiedDate")) { 320 // Handle modified date seperately and exit early. 321 updateDateTriple(triplesToRemove, triplesToInsert, 322 "http://fedora.info/definitions/v4/repository#lastModified", 323 obj); 324 return; 325 } 326 327 if (isDateProperty(origPred)) { 328 updateDateTriple(triplesToRemove, 329 triplesToInsert, 330 pred, 331 obj); 332 } else { 333 if (isLiteral) { 334 updateLiteralTriple(triplesToRemove, 335 triplesToInsert, 336 pred, 337 obj); 338 } else { 339 updateUriTriple(triplesToRemove, 340 triplesToInsert, 341 pred, 342 obj); 343 } 344 } 345 } 346 347 /** 348 * WIP utility function to update datastream properties. 349 * Feel free to override this to suit your needs. 350 * 351 * @param obj Object to operate upon 352 * @param v Version of the datasream to update. 353 * @param dsPath resolved path (in f4) for the datastream 354 */ 355 protected void updateDatastreamProperties(final ObjectReference obj, 356 final DatastreamVersion v, final String dsPath) { 357 final QuadDataAcc triplesToInsert = new QuadDataAcc(); 358 final QuadAcc triplesToRemove = new QuadAcc(); 359 360 final String createdDate = v.getCreated(); 361 362 // Get some initial properties 363 if (v.isFirstVersionIn(obj)) { 364 // Migration event (current time) 365 final String now = getCurrentTimeInXSDDateTime(); 366 if (now != null) { 367 addDateEvent(triplesToInsert, 368 "http://id.loc.gov/vocabulary/preservation/eventType/mig", 369 getCurrentTimeInXSDDateTime()); 370 } 371 372 // Created date 373 if (createdDate != null) { 374 LOGGER.debug("Setting created date to " + createdDate + "..."); 375 updateDateTriple(triplesToRemove, 376 triplesToInsert, 377 "http://fedora.info/definitions/v4/repository#created", 378 createdDate); 379 } 380 } 381 382 // Get the rest of the properties from the last version. 383 if (v.isLastVersionIn(obj)) { 384 // DSID 385 final String dsid = v.getDatastreamInfo().getDatastreamId(); 386 if (dsid != null) { 387 updateLiteralTriple(triplesToRemove, 388 triplesToInsert, 389 "http://purl.org/dc/terms/identifier", 390 dsid); 391 } 392 393 // The created date of the last version is the last modified date. 394 if (createdDate != null) { 395 updateDateTriple(triplesToRemove, 396 triplesToInsert, 397 "http://fedora.info/definitions/v4/repository#lastModified", 398 createdDate); 399 } 400 401 // Label 402 final String label = v.getLabel(); 403 if (label != null) { 404 updateLiteralTriple(triplesToRemove, 405 triplesToInsert, 406 "http://purl.org/dc/terms/title", 407 label); 408 } 409 410 // Object State 411 final String state = v.getDatastreamInfo().getState(); 412 if (state != null) { 413 updateLiteralTriple(triplesToRemove, 414 triplesToInsert, 415 "http://fedora.info/definitions/1/0/access/objState", 416 state); 417 } 418 419 // Format URI 420 final String formatUri = v.getFormatUri(); 421 if (formatUri != null) { 422 updateLiteralTriple(triplesToRemove, 423 triplesToInsert, 424 "http://www.loc.gov/premis/rdf/v1#formatDesignation", 425 formatUri); 426 } 427 } 428 429 // Only do the update if you've got stuff to change. 430 if (!triplesToInsert.getQuads().isEmpty() && !triplesToRemove.getQuads().isEmpty()) { 431 updateResourceProperties(dsPath, triplesToRemove, triplesToInsert, true); 432 } 433 } 434 435 /** 436 * Migrates a RELS-EXT datastream by splitting it apart into triples to 437 * update on the object it describes. 438 * 439 * @param v Version of the datasream to migrate. 440 * @param triplesToRemove List of triples to remove from resource. 441 * @param triplesToInsert List of triples to add to resource. 442 * 443 * @throws IOException on error 444 */ 445 protected void migrateRelsExt(final DatastreamVersion v, final QuadAcc triplesToRemove, 446 final QuadDataAcc triplesToInsert) throws IOException { 447 // Get the identifier for the object this describes 448 final String objectUri = "info:fedora/" + v.getDatastreamInfo().getObjectInfo().getPid(); 449 450 // Read the RDF 451 final Model m = ModelFactory.createDefaultModel(); 452 m.read(v.getContent(), null); 453 final StmtIterator statementIt = m.listStatements(); 454 while (statementIt.hasNext()) { 455 final Statement s = statementIt.nextStatement(); 456 if (s.getSubject().getURI().equals(objectUri)) { 457 final String predicateUri = s.getPredicate().getURI(); 458 if (s.getObject().isLiteral()) { 459 mapProperty(predicateUri, 460 s.getObject().asLiteral().getString(), 461 triplesToRemove, 462 triplesToInsert, 463 true); 464 } else if (s.getObject().isURIResource()) { 465 mapProperty(predicateUri, 466 s.getObject().asResource().getURI(), 467 triplesToRemove, 468 triplesToInsert, 469 false); 470 } else { 471 throw new RuntimeException("No current handling for non-URI," + 472 " non-Literal subjects in Fedora RELS-INT."); 473 } 474 } else { 475 throw new RuntimeException("Non-resource subject found: " + s.getSubject().getURI()); 476 } 477 } 478 } 479 480 /** 481 * Migrates a RELS-INT datastream by splitting it apart and updating the 482 * other datastreams it describes. 483 * 484 * @param v Version of the datasream to migrate. 485 * 486 * @throws java.io.IOException on error 487 * @throws java.lang.RuntimeException on error 488 */ 489 protected void migrateRelsInt(final DatastreamVersion v) throws IOException, RuntimeException { 490 // Read the RDF. 491 final Model m = ModelFactory.createDefaultModel(); 492 m.read(v.getContent(), null); 493 final StmtIterator statementIt = m.listStatements(); 494 while (statementIt.hasNext()) { 495 // Get the datastream this triple describes. 496 final Statement s = statementIt.nextStatement(); 497 final String dsUri = s.getSubject().getURI(); 498 final String[] splitUri = dsUri.split("/"); 499 final String dsId = splitUri[splitUri.length - 1]; 500 final String dsPath = idMapper.mapDatastreamPath(v.getDatastreamInfo().getObjectInfo().getPid(), dsId); 501 if (!f4client.exists(dsPath)) { 502 f4client.createNonRDFPlaceholder(dsPath); 503 LOGGER.warn("The datastream \"" + dsId 504 + "\" referenced in the RDF datastream \"" + v.getDatastreamInfo().getDatastreamId() + "\" on " 505 + v.getDatastreamInfo().getObjectInfo().getPid() + " did not exist at " 506 + v.getCreated() + ", making a placeholder!"); 507 } 508 509 // Update this datastream with the RELS-INT RDF. 510 final QuadDataAcc triplesToInsert = new QuadDataAcc(); 511 final QuadAcc triplesToRemove = new QuadAcc(); 512 513 final String pred = s.getPredicate().getURI(); 514 515 if (s.getObject().isLiteral()) { 516 mapProperty(pred, 517 s.getObject().asLiteral().getString(), 518 triplesToRemove, 519 triplesToInsert, 520 true); 521 } else if (s.getObject().isURIResource()) { 522 mapProperty(pred, 523 s.getObject().asResource().getURI(), 524 triplesToRemove, 525 triplesToInsert, 526 false); 527 } else { 528 throw new RuntimeException("No current handling for non-URI, non-Literal subjects in Fedora RELS-INT."); 529 } 530 531 updateResourceProperties(dsPath, triplesToRemove, triplesToInsert, true); 532 } 533 } 534 535 /** 536 * Migrates a DC datastream by shredding it into RDF properties and 537 * applying them directly to the object. 538 * 539 * @param v Version of the datasream to migrate. 540 * @param triplesToRemove List of triples to remove from resource. 541 * @param triplesToInsert List of triples to add to resource. 542 * 543 * @throws java.io.IOException on error 544 * @throws java.lang.RuntimeException on error 545 */ 546 protected void migrateDc(final DatastreamVersion v, final QuadAcc triplesToRemove, 547 final QuadDataAcc triplesToInsert) throws IOException, RuntimeException { 548 try { 549 final DC dc = DC.parseDC(v.getContent()); 550 for (String uri : dc.getRepresentedElementURIs()) { 551 triplesToRemove.addTriple(new Triple(NodeFactory.createURI(""), NodeFactory.createURI(uri), 552 NodeFactory.createVariable("o"))); 553 for (String value : dc.getValuesForURI(uri)) { 554 triplesToInsert.addTriple(new Triple(NodeFactory.createURI(""), NodeFactory.createURI(uri), 555 NodeFactory.createLiteral(value))); 556 LOGGER.debug("Adding " + uri + " value " + value); 557 } 558 } 559 } catch (JAXBException e) { 560 throw new RuntimeException("Error parsing DC datastream " + v.getVersionId()); 561 } 562 } 563 564 /** 565 * Utility function for updating a FedoraResource's properties. 566 * 567 * @param path Path to the fedora resource to update. 568 * @param triplesToRemove List of triples to remove from resource. 569 * @param triplesToInsert List of triples to add to resource. 570 * @param isNonRDF true if the resource is a non-RDF resource. 571 * 572 * @throws RuntimeException Possible FedoraExcpetions and IOExceptions 573 */ 574 protected void updateResourceProperties(final String path, 575 final QuadAcc triplesToRemove, 576 final QuadDataAcc triplesToInsert, 577 final boolean isNonRDF) throws RuntimeException { 578 try { 579 final UpdateRequest updateRequest = UpdateFactory.create(); 580 namespacePrefixMapper.setPrefixes(updateRequest); 581 updateRequest.add(new UpdateDeleteWhere(triplesToRemove)); 582 updateRequest.add(new UpdateDataInsert(triplesToInsert)); 583 final ByteArrayOutputStream sparqlUpdate = new ByteArrayOutputStream(); 584 updateRequest.output(new IndentedWriter(sparqlUpdate)); 585 LOGGER.trace("SPARQL: " + sparqlUpdate.toString("UTF-8")); 586 if (isNonRDF) { 587 f4client.updateNonRDFResourceProperties(path, sparqlUpdate.toString("UTF-8")); 588 } else { 589 f4client.updateResourceProperties(path, sparqlUpdate.toString("UTF-8")); 590 } 591 suffix = 0; 592 } catch (final IOException e) { 593 throw new RuntimeException(e); 594 } 595 } 596 597 /** 598 * Utility function for updating a literal triple. 599 * 600 * @param triplesToRemove List of triples to remove from resource. 601 * @param triplesToInsert List of triples to add to resource. 602 * @param predicate Predicate of relationship (assumed to be URI). 603 * @param object Object of relationship (assumed to be literal). 604 */ 605 protected void updateLiteralTriple(final QuadAcc triplesToRemove, 606 final QuadDataAcc triplesToInsert, 607 final String predicate, 608 final String object) { 609 triplesToRemove.addTriple(new Triple(NodeFactory.createURI(""), 610 NodeFactory.createURI(predicate), 611 NodeFactory.createVariable("o" + String.valueOf(suffix)))); 612 triplesToInsert.addTriple(new Triple(NodeFactory.createURI(""), 613 NodeFactory.createURI(predicate), 614 NodeFactory.createLiteral(object))); 615 suffix++; 616 } 617 618 /** 619 * Utility function for updating a uri triple. 620 * 621 * @param triplesToRemove List of triples to remove from resource. 622 * @param triplesToInsert List of triples to add to resource. 623 * @param predicate Predicate of relationship (assumed to be URI). 624 * @param object Object of relationship (assumed to URI). 625 */ 626 protected void updateUriTriple(final QuadAcc triplesToRemove, 627 final QuadDataAcc triplesToInsert, 628 final String predicate, 629 final String object) { 630 final String newObjectUri = resolveInternalURI(object); 631 triplesToRemove.addTriple(new Triple(NodeFactory.createURI(""), 632 NodeFactory.createURI(predicate), 633 NodeFactory.createVariable("o" + String.valueOf(suffix)))); 634 triplesToInsert.addTriple(new Triple(NodeFactory.createURI(""), 635 NodeFactory.createURI(predicate), 636 NodeFactory.createURI(newObjectUri))); 637 suffix++; 638 } 639 640 /** 641 * Takes a URI (String) and if it appears to be an internal Fedora URI ("info:fedora/pid") 642 * the migrated URI for that resource is returned (and a placeholder is created in the 643 * repository if it doesn't already exist). Otherwise the value is returned unmodified. 644 * 645 * @param uri to be resolved 646 * 647 * @return string which is either the migrated URI or the unmodified URI 648 */ 649 protected String resolveInternalURI(final String uri) { 650 if (uri.startsWith("info:fedora/")) { 651 final String path = idMapper.mapObjectPath(uri.substring("info:fedora/".length())); 652 f4client.createPlaceholder(path); 653 return f4client.getRepositoryUrl() + path; 654 } 655 return uri; 656 } 657 658 /** 659 * Utility function for updating a date triple. 660 * 661 * @param triplesToRemove List of triples to remove from resource. 662 * @param triplesToInsert List of triples to add to resource. 663 * @param predicate Predicate of relationship (assumed to be URI). 664 * @param object Object of relationship (assumed to be literal). 665 */ 666 protected void updateDateTriple(final QuadAcc triplesToRemove, 667 final QuadDataAcc triplesToInsert, 668 final String predicate, 669 final String object) { 670 triplesToRemove.addTriple(new Triple(NodeFactory.createURI(""), 671 NodeFactory.createURI(predicate), 672 NodeFactory.createVariable("o" + String.valueOf(suffix)))); 673 triplesToInsert.addTriple(new Triple(NodeFactory.createURI(""), 674 NodeFactory.createURI(predicate), 675 NodeFactory.createLiteral(object, XSDDatatype.XSDdateTime))); 676 suffix++; 677 } 678 679 /** 680 * Utility function for adding a premis date event. Current 681 * implementation utilizes a blank node. 682 * 683 * @param triplesToInsert List of triples to add to resource. 684 * @param eventTypeURI Type of premis event. 685 * @param object Object of relationship (e.g. the date. Assumed to be literal). 686 */ 687 protected void addDateEvent(final QuadDataAcc triplesToInsert, 688 final String eventTypeURI, 689 final String object) { 690 final String eventPred = "http://www.loc.gov/premis/rdf/v1#hasEvent"; 691 final String eventTypePred = "http://www.loc.gov/premis/rdf/v1#hasEventType"; 692 final String eventDatePred = "http://www.loc.gov/premis/rdf/v1#hasEventDateTime"; 693 final Node bnode = NodeFactory.createAnon(); 694 695 triplesToInsert.addTriple(new Triple(NodeFactory.createURI(""), 696 NodeFactory.createURI(eventPred), 697 bnode)); 698 triplesToInsert.addTriple(new Triple(bnode, 699 NodeFactory.createURI(eventTypePred), 700 NodeFactory.createURI(eventTypeURI))); 701 triplesToInsert.addTriple(new Triple(bnode, 702 NodeFactory.createURI(eventDatePred), 703 NodeFactory.createLiteral(object, XSDDatatype.XSDdateTime))); 704 } 705 706 /** 707 * Utility function to get the current time properly formatted for SPARQL 708 * or XML. 709 * 710 * @return String representing current time in XSDdateTime format (null if error). 711 */ 712 protected String getCurrentTimeInXSDDateTime() { 713 try { 714 final GregorianCalendar cal = new GregorianCalendar(); 715 cal.setTime(new Date()); 716 final XMLGregorianCalendar now = DatatypeFactory.newInstance().newXMLGregorianCalendar(cal); 717 return now.toString(); 718 } catch (final DatatypeConfigurationException e) { 719 LOGGER.error("Error converting date object to proper format!", e); 720 return null; 721 } 722 } 723}