You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@taverna.apache.org by st...@apache.org on 2016/04/28 02:15:35 UTC
[03/26] incubator-taverna-engine git commit: org.apache.taverna.prov
(and ASF header)
org.apache.taverna.prov (and ASF header)
Project: http://git-wip-us.apache.org/repos/asf/incubator-taverna-engine/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-taverna-engine/commit/c1608c61
Tree: http://git-wip-us.apache.org/repos/asf/incubator-taverna-engine/tree/c1608c61
Diff: http://git-wip-us.apache.org/repos/asf/incubator-taverna-engine/diff/c1608c61
Branch: refs/heads/master
Commit: c1608c61fa433b531232ce523ae72b34e0d05726
Parents: c0b4d17
Author: Stian Soiland-Reyes <st...@apache.org>
Authored: Thu Apr 28 00:02:34 2016 +0100
Committer: Stian Soiland-Reyes <st...@apache.org>
Committed: Thu Apr 28 00:02:34 2016 +0100
----------------------------------------------------------------------
.travis.yml | 15 +
.../taverna/prov/SafeFileOutputStream.java | 137 +++
.../java/org/apache/taverna/prov/Saver.java | 406 +++++++
.../apache/taverna/prov/W3ProvenanceExport.java | 1026 ++++++++++++++++++
.../export/SafeFileOutputStream.java | 119 --
.../purl/wf4ever/provtaverna/export/Saver.java | 388 -------
.../provtaverna/export/W3ProvenanceExport.java | 1008 -----------------
7 files changed, 1584 insertions(+), 1515 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-taverna-engine/blob/c1608c61/.travis.yml
----------------------------------------------------------------------
diff --git a/.travis.yml b/.travis.yml
index dff5f3a..0b741c3 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1 +1,16 @@
+## Licensed to the Apache Software Foundation (ASF) under one or more
+## contributor license agreements. See the NOTICE file distributed with
+## this work for additional information regarding copyright ownership.
+## The ASF licenses this file to You under the Apache License, Version 2.0
+## (the "License"); you may not use this file except in compliance with
+## the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+
language: java
http://git-wip-us.apache.org/repos/asf/incubator-taverna-engine/blob/c1608c61/taverna-prov/src/main/java/org/apache/taverna/prov/SafeFileOutputStream.java
----------------------------------------------------------------------
diff --git a/taverna-prov/src/main/java/org/apache/taverna/prov/SafeFileOutputStream.java b/taverna-prov/src/main/java/org/apache/taverna/prov/SafeFileOutputStream.java
new file mode 100644
index 0000000..237c1da
--- /dev/null
+++ b/taverna-prov/src/main/java/org/apache/taverna/prov/SafeFileOutputStream.java
@@ -0,0 +1,137 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+package org.apache.taverna.prov;
+
+/**
+ * Copied out from net.sf.taverna.t2.workbench.file.impl.SafeFileOutputStream
+ * to avoid dependency on file-impl.
+ *
+ * TODO: Move to taverna utils!
+ *
+ */
+
+import java.io.FileNotFoundException;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+
+public class SafeFileOutputStream extends FilterOutputStream {
+
+ private final Path desiredFile;
+ private Path tempFile;
+ boolean desiredAlreadyExisted;
+
+ public SafeFileOutputStream(Path file) throws IOException {
+ this(file, tempFile(file));
+ }
+
+ public SafeFileOutputStream(Path desiredFile, Path tempFile)
+ throws IOException {
+
+ super(Files.newOutputStream(tempFile));
+ this.desiredFile = desiredFile;
+ this.tempFile = tempFile;
+ // Some useful things to check that we preferably don't want to fail on
+ // close()
+ desiredAlreadyExisted = Files.exists(desiredFile);
+ Path desiredFolder = this.desiredFile.getParent();
+ if (desiredAlreadyExisted) {
+ if (!Files.isWritable(desiredFile)) {
+ throw new FileNotFoundException("Can't write to " + desiredFile);
+ }
+ } else {
+ if (!Files.exists(desiredFolder)) {
+ throw new FileNotFoundException("Folder does not exist: "
+ + desiredFolder);
+ }
+ if (!Files.isDirectory(desiredFolder)) {
+ throw new FileNotFoundException("Not a directory: "
+ + desiredFolder);
+ }
+ }
+ if (!Files.isWritable(desiredFolder)) {
+ throw new FileNotFoundException("Can't modify folder "
+ + desiredFolder);
+ }
+ }
+
+ private static Path tempFile(Path file) throws IOException {
+ return Files.createTempFile(file.getParent(), file.getFileName()
+ .toString(), ".tmp");
+ }
+
+ @Override
+ public void close() throws IOException {
+ // If super.close fails - we leave the tempfiles behind
+ super.close();
+ if (!Files.exists(tempFile)) {
+ // Probably something went wrong before close called,
+ // like rollback()
+ return;
+ }
+ Path beforeDeletion = null;
+ try {
+ if (desiredAlreadyExisted) {
+ // In case renaming goes wrong, we don't want to have already
+ // deleted the
+ // desired file. So we'll rename it to another temporary file
+ // instead which
+ // we can delete on successful rename.
+ beforeDeletion = tempFile(desiredFile);
+ if (!Files.deleteIfExists(beforeDeletion)) {
+ // Should not happen, we just made it!
+ throw new IOException("Can't delete temporary file "
+ + beforeDeletion);
+ }
+ Files.move(desiredFile, beforeDeletion,
+ StandardCopyOption.ATOMIC_MOVE);
+ }
+ try {
+ Files.move(tempFile, desiredFile,
+ StandardCopyOption.ATOMIC_MOVE);
+ } catch (IOException e) {
+ // We'll leave our tempFiles for recovery.
+ tempFile = null;
+ beforeDeletion = null;
+
+ if (Files.exists(desiredFile)) {
+ // Someone else added or replaced the file afterwards,
+ // kind-a OK
+ return;
+ }
+ throw e;
+ }
+ } finally {
+ if (beforeDeletion != null) {
+ Files.deleteIfExists(beforeDeletion);
+ }
+ if (tempFile != null) {
+ Files.deleteIfExists(tempFile);
+ }
+ }
+ }
+
+ public void rollback() throws IOException {
+ super.close();
+ Files.deleteIfExists(tempFile);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-engine/blob/c1608c61/taverna-prov/src/main/java/org/apache/taverna/prov/Saver.java
----------------------------------------------------------------------
diff --git a/taverna-prov/src/main/java/org/apache/taverna/prov/Saver.java b/taverna-prov/src/main/java/org/apache/taverna/prov/Saver.java
new file mode 100644
index 0000000..fce5520
--- /dev/null
+++ b/taverna-prov/src/main/java/org/apache/taverna/prov/Saver.java
@@ -0,0 +1,406 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+package org.apache.taverna.prov;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.ProtocolException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.DigestOutputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.taverna.invocation.InvocationContext;
+import org.apache.taverna.provenance.api.ProvenanceAccess;
+import org.apache.taverna.reference.ExternalReferenceSPI;
+import org.apache.taverna.reference.ReferenceService;
+import org.apache.taverna.reference.ReferenceSet;
+import org.apache.taverna.reference.ReferenceSetService;
+import org.apache.taverna.reference.T2Reference;
+import org.apache.taverna.reference.ValueCarryingExternalReference;
+import org.apache.taverna.spi.SPIRegistry;
+import org.apache.taverna.reference.config.DataManagementConfiguration;
+
+import org.apache.commons.beanutils.PropertyUtils;
+import org.apache.commons.codec.binary.Hex;
+import org.apache.commons.io.IOUtils;
+import org.apache.log4j.Logger;
+import org.apache.tika.Tika;
+import org.apache.tika.mime.MimeTypeException;
+import org.apache.tika.mime.MimeTypes;
+import org.openrdf.rio.RDFParserFactory;
+import org.openrdf.rio.RDFParserRegistry;
+import org.openrdf.rio.RDFWriterFactory;
+import org.openrdf.rio.RDFWriterRegistry;
+import org.apache.taverna.robundle.Bundle;
+
+import org.apache.taverna.databundle.DataBundles;
+
+public class Saver {
+
+ private static final String APPLICATION_OCTET_STREAM = "application/octet-stream";
+
+ private static Logger logger = Logger.getLogger(Saver.class);
+
+ /**
+ * @param saveProvAction
+ */
+ public Saver(ReferenceService referenceService, InvocationContext context,
+ String runId, Map<String, T2Reference> chosenReferences) {
+ this.setReferenceService(referenceService);
+ this.setContext(context);
+ this.setRunId(runId);
+ this.setChosenReferences(chosenReferences);
+ prepareSesame();
+ }
+
+ /**
+ * Load
+ */
+ protected void prepareSesame() {
+ RDFParserRegistry parserReg = RDFParserRegistry.getInstance();
+ SPIRegistry<RDFParserFactory> parserSPI = new SPIRegistry<>(
+ RDFParserFactory.class);
+ for (RDFParserFactory service : parserSPI.getInstances()) {
+ parserReg.add(service);
+ }
+
+ RDFWriterRegistry writerReg = RDFWriterRegistry.getInstance();
+ SPIRegistry<RDFWriterFactory> writerSPI = new SPIRegistry<>(
+ RDFWriterFactory.class);
+ for (RDFWriterFactory service : writerSPI.getInstances()) {
+ writerReg.add(service);
+ }
+
+ }
+
+ private Map<Path, T2Reference> fileToId = new HashMap<>();
+
+ private Map<Path, String> sha1sums = new HashMap<>();
+ private Map<Path, String> sha512sums = new HashMap<>();
+
+ private ReferenceService referenceService;
+
+ private InvocationContext context;
+
+ private String runId;
+
+ private Map<String, T2Reference> chosenReferences;
+
+ private Bundle bundle;
+
+ private Map<T2Reference, String> mediaTypes = new HashMap<>();
+
+ /**
+ * @return the bundle
+ */
+ public Bundle getBundle() {
+ return bundle;
+ }
+
+ public void saveData(Path bundlePath) throws FileNotFoundException,
+ IOException {
+ Bundle bundle = DataBundles.createBundle();
+ // String folderName = bundlePath.getFileName().toString();
+ // if (folderName.endsWith(".")) {
+ // bundlePath = bundlePath.resolveSibling(folderName.substring(0,
+ // folderName.length() - 1));
+ // }
+ setBundle(bundle);
+ saveToFolder(bundle.getRoot(), getChosenReferences(),
+ getReferenceService());
+ DataBundles.closeAndSaveBundle(bundle, bundlePath);
+ }
+
+ private void setBundle(Bundle bundle) {
+ this.bundle = bundle;
+ }
+
+ protected static Tika tika = new Tika();
+
+ public Path saveReference(T2Reference t2Ref, Path file) throws IOException {
+ ReferenceSetService refSet = getReferenceService()
+ .getReferenceSetService();
+ ReferenceSet referenceSet = refSet.getReferenceSet(t2Ref);
+ List<ExternalReferenceSPI> externalReferences = new ArrayList<ExternalReferenceSPI>(
+ referenceSet.getExternalReferences());
+ Collections.sort(externalReferences,
+ new Comparator<ExternalReferenceSPI>() {
+ public int compare(ExternalReferenceSPI o1,
+ ExternalReferenceSPI o2) {
+ return (int) (o1.getResolutionCost() - o2
+ .getResolutionCost());
+ }
+ });
+ String mimeType = findMimeType(externalReferences);
+ getMediaTypes().put(t2Ref, mimeType);
+
+ Path targetFile = writeIfLocal(externalReferences, file, mimeType);
+ if (targetFile == null) {
+ URI uri = referenceAsURI(externalReferences);
+ if (uri != null) {
+ targetFile = DataBundles.setReference(file, uri);
+ }
+ }
+
+ if (targetFile != null) {
+ getFileToId().put(targetFile, t2Ref);
+ } else {
+ logger.warn("Could not write out reference " + t2Ref);
+ }
+
+ return targetFile;
+
+ }
+
+ private Path writeIfLocal(List<ExternalReferenceSPI> externalReferences,
+ Path file, String mimeType) throws IOException {
+
+ ValueCarryingExternalReference<?> valRef = null;
+ for (ExternalReferenceSPI ref : externalReferences) {
+ if (ref instanceof ValueCarryingExternalReference) {
+ valRef = (ValueCarryingExternalReference<?>) ref;
+ break;
+ }
+ }
+
+ if (valRef == null) {
+ return null;
+ }
+
+ String fileExtension;
+ try {
+ fileExtension = MimeTypes.getDefaultMimeTypes().forName(mimeType)
+ .getExtension();
+ } catch (MimeTypeException e1) {
+ fileExtension = "";
+ }
+ Path targetFile = file.resolveSibling(file.getFileName()
+ + fileExtension);
+
+ MessageDigest sha = null;
+ MessageDigest sha512 = null;
+ OutputStream output = Files.newOutputStream(targetFile);
+ try {
+ try {
+ sha = MessageDigest.getInstance("SHA");
+ output = new DigestOutputStream(output, sha);
+
+ sha512 = MessageDigest.getInstance("SHA-512");
+ output = new DigestOutputStream(output, sha512);
+ } catch (NoSuchAlgorithmException e) {
+ logger.info("Could not find digest", e);
+ }
+
+ IOUtils.copyLarge(valRef.openStream(getContext()), output);
+ } finally {
+ output.close();
+ }
+
+ if (sha != null) {
+ getSha1sums().put(targetFile.toRealPath(), hexOfDigest(sha));
+ }
+ if (sha512 != null) {
+ sha512.digest();
+ getSha512sums().put(targetFile.toRealPath(), hexOfDigest(sha512));
+ }
+
+ return targetFile;
+ }
+
+ private URI referenceAsURI(List<ExternalReferenceSPI> externalReferences) {
+ for (ExternalReferenceSPI ref : externalReferences) {
+ String className = ref.getClass().getName();
+ if (className
+ .equals("net.sf.taverna.t2.reference.impl.external.http.HttpReference")) {
+ URL url = (URL) getProperty(ref, "httpUrl");
+ try {
+ return url.toURI();
+ } catch (URISyntaxException e) {
+ logger.warn("Can't convert HttpReference to URI: " + url, e);
+ continue;
+ }
+ } else if (className
+ .equals("net.sf.taverna.t2.reference.impl.external.file.FileReference")) {
+ File file = (File) getProperty(ref, "file");
+ return file.toURI();
+ } else if (className
+ .equals("de.uni_luebeck.inb.knowarc.usecases.invocation.ssh.SshReference")) {
+ String host = (String) getProperty(ref, "host");
+ int port = (int) getProperty(ref, "port");
+ String path = (String) getProperty(ref, "fullPath");
+ try {
+ return new URI("sftp", null, host, port, path, null, null);
+ } catch (URISyntaxException e) {
+ logger.warn("Can't convert SshReference to URI: sftp://"
+ + host + ":" + port + path, e);
+ continue;
+ }
+ }
+ }
+ return null;
+ }
+
+ protected Object getProperty(ExternalReferenceSPI ref, String propertyName) {
+ try {
+ return PropertyUtils.getSimpleProperty(ref, propertyName);
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("Can't look up " + propertyName
+ + " in bean " + ref, ex);
+ }
+ }
+
+ private String findMimeType(List<ExternalReferenceSPI> externalReferences)
+ throws IOException, ProtocolException {
+ String mimeType = null;
+ for (ExternalReferenceSPI externalReference : externalReferences) {
+ String className = externalReference.getClass().getName();
+ if (className
+ .equals("net.sf.taverna.t2.reference.impl.external.http.HttpReference")) {
+ URL url = (URL) getProperty(externalReference, "httpUrl");
+ mimeType = tika.detect(url);
+ } else if (className
+ .equals("net.sf.taverna.t2.reference.impl.external.file.FileReference")) {
+ File file = (File) getProperty(externalReference, "file");
+ mimeType = tika.detect(file);
+ } else if (className
+ .equals("de.uni_luebeck.inb.knowarc.usecases.invocation.ssh.SshReference")) {
+ String filename = (String) getProperty(externalReference,
+ "fileName");
+ try (InputStream instream = externalReference
+ .openStream(context)) {
+ mimeType = tika.detect(instream, filename);
+ }
+ } else if (className
+ .equals("net.sf.taverna.t2.reference.impl.external.object.VMObjectReference")) {
+ mimeType = "application/x-java-serialized-object";
+ } else {
+ try (InputStream instream = externalReference
+ .openStream(context)) {
+ mimeType = tika.detect(instream);
+ }
+ }
+ if (mimeType != null && !mimeType.equals(APPLICATION_OCTET_STREAM)) {
+ break;
+ }
+ }
+ if (mimeType == null || mimeType.isEmpty()) {
+ return APPLICATION_OCTET_STREAM;
+ }
+ return mimeType;
+ }
+
+ protected void saveToFolder(Path folder,
+ Map<String, T2Reference> chosenReferences,
+ ReferenceService referenceService) throws IOException,
+ FileNotFoundException {
+ logger.info("Saving provenance and outputs to " + folder.toRealPath());
+ Files.createDirectories(folder);
+ String connectorType = DataManagementConfiguration.getInstance()
+ .getConnectorType();
+ ProvenanceAccess provenanceAccess = new ProvenanceAccess(connectorType,
+ getContext());
+ W3ProvenanceExport export = new W3ProvenanceExport(provenanceAccess,
+ getRunId(), this);
+ export.setFileToT2Reference(getFileToId());
+ export.setBundle(bundle);
+
+ try {
+ logger.debug("Saving provenance");
+ export.exportAsW3Prov();
+ logger.info("Saved provenance");
+ } catch (Exception e) {
+ logger.error("Failed to save the provenance graph", e);
+ }
+ }
+
+ private String hexOfDigest(MessageDigest sha) {
+ return new String(Hex.encodeHex(sha.digest()));
+ }
+
+ public ReferenceService getReferenceService() {
+ return referenceService;
+ }
+
+ public void setReferenceService(ReferenceService referenceService) {
+ this.referenceService = referenceService;
+ }
+
+ public InvocationContext getContext() {
+ return context;
+ }
+
+ public void setContext(InvocationContext context) {
+ this.context = context;
+ }
+
+ public String getRunId() {
+ return runId;
+ }
+
+ public void setRunId(String runId) {
+ this.runId = runId;
+ }
+
+ public Map<String, T2Reference> getChosenReferences() {
+ return chosenReferences;
+ }
+
+ public void setChosenReferences(Map<String, T2Reference> chosenReferences) {
+ this.chosenReferences = chosenReferences;
+ }
+
+ public Map<Path, T2Reference> getFileToId() {
+ return fileToId;
+ }
+
+ public void setFileToId(Map<Path, T2Reference> fileToId) {
+ this.fileToId = fileToId;
+ }
+
+ public Map<Path, String> getSha1sums() {
+ return sha1sums;
+ }
+
+ public Map<T2Reference, String> getMediaTypes() {
+ return mediaTypes;
+ }
+
+ public Map<Path, String> getSha512sums() {
+ return sha512sums;
+ }
+
+ public void setMediaTypes(Map<T2Reference, String> mediaTypes) {
+ this.mediaTypes = mediaTypes;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-engine/blob/c1608c61/taverna-prov/src/main/java/org/apache/taverna/prov/W3ProvenanceExport.java
----------------------------------------------------------------------
diff --git a/taverna-prov/src/main/java/org/apache/taverna/prov/W3ProvenanceExport.java b/taverna-prov/src/main/java/org/apache/taverna/prov/W3ProvenanceExport.java
new file mode 100644
index 0000000..dab0288
--- /dev/null
+++ b/taverna-prov/src/main/java/org/apache/taverna/prov/W3ProvenanceExport.java
@@ -0,0 +1,1026 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+package org.apache.taverna.prov;
+
+import static org.apache.taverna.scufl2.translator.t2flow.T2FlowParser.ravenURI;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.math.BigDecimal;
+import java.net.URI;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.UUID;
+
+import javax.xml.datatype.DatatypeConfigurationException;
+import javax.xml.datatype.DatatypeFactory;
+import javax.xml.datatype.XMLGregorianCalendar;
+
+import net.sf.taverna.raven.appconfig.ApplicationConfig;
+import org.apache.taverna.provenance.api.ProvenanceAccess;
+import org.apache.taverna.provenance.lineageservice.URIGenerator;
+import org.apache.taverna.provenance.lineageservice.utils.DataflowInvocation;
+import org.apache.taverna.provenance.lineageservice.utils.Port;
+import org.apache.taverna.provenance.lineageservice.utils.ProcessorEnactment;
+import org.apache.taverna.provenance.lineageservice.utils.ProvenanceProcessor;
+import org.apache.taverna.provenance.lineageservice.utils.WorkflowRun;
+import org.apache.taverna.reference.ErrorDocument;
+import org.apache.taverna.reference.IdentifiedList;
+import org.apache.taverna.reference.StackTraceElementBean;
+import org.apache.taverna.reference.T2Reference;
+import org.apache.taverna.reference.T2ReferenceType;
+import org.apache.taverna.spi.SPIRegistry;
+
+import org.apache.commons.beanutils.BeanUtils;
+import org.apache.commons.beanutils.PropertyUtils;
+import org.apache.jena.riot.RDFDataMgr;
+import org.apache.jena.riot.RDFFormat;
+import org.apache.jena.riot.WriterGraphRIOT;
+import org.apache.jena.riot.system.RiotLib;
+import org.apache.log4j.Logger;
+import org.purl.wf4ever.provtaverna.owl.TavernaProvModel;
+import org.apache.taverna.robundle.Bundle;
+import org.apache.taverna.robundle.manifest.Agent;
+import org.apache.taverna.robundle.manifest.Manifest;
+import org.apache.taverna.robundle.manifest.PathAnnotation;
+import org.apache.taverna.robundle.manifest.PathMetadata;
+
+import org.apache.taverna.databundle.DataBundles;
+import org.apache.taverna.scufl2.api.common.URITools;
+import org.apache.taverna.scufl2.api.container.WorkflowBundle;
+import org.apache.taverna.scufl2.api.io.ReaderException;
+import org.apache.taverna.scufl2.api.io.WorkflowBundleIO;
+import org.apache.taverna.scufl2.api.io.WorkflowBundleReader;
+import org.apache.taverna.scufl2.api.io.WorkflowBundleWriter;
+import org.apache.taverna.scufl2.translator.t2flow.T2FlowReader;
+
+import org.apache.jena.datatypes.xsd.XSDDatatype;
+import org.apache.jena.ontology.Individual;
+import org.apache.jena.ontology.OntModel;
+import org.apache.jena.rdf.model.Literal;
+import org.apache.jena.rdf.model.Model;
+import org.apache.jena.rdf.model.ModelFactory;
+import org.apache.jena.sparql.util.Context;
+import org.apache.jena.sparql.vocabulary.FOAF;
+
+public class W3ProvenanceExport {
+
+ private static URITools uriTools = new URITools();
+
+ private static final URI osgiURI = URI.create("http://ns.taverna.org.uk/2013/osgibundle/");
+ private static final String TEXT = "text/";
+ private static final String WORKFLOW_BUNDLE = "application/vnd.taverna.scufl2.workflow-bundle";
+ // TODO: Avoid this Taverna 2 dependency
+ private static SPIRegistry<WorkflowBundleReader> readerSpi = new SPIRegistry<>(
+ WorkflowBundleReader.class);
+ private static SPIRegistry<WorkflowBundleWriter> writerSpi = new SPIRegistry<>(
+ WorkflowBundleWriter.class);
+
+ private static final String EN = "en";
+
+ private static final int EMBEDDED_MAX_FILESIZE = 1024;
+
+ private static final Charset UTF8 = Charset.forName("UTF-8");
+
+ private static ApplicationConfig applicationConfig = ApplicationConfig
+ .getInstance();
+
+ private static Logger logger = Logger.getLogger(W3ProvenanceExport.class);
+
+ protected Map<T2Reference, Path> seenReferences = new HashMap<>();
+
+ private static final int NANOSCALE = 9;
+
+ private ProvenanceAccess provenanceAccess;
+
+ private DatatypeFactory datatypeFactory;
+
+ private ProvenanceURIGenerator uriGenerator = new ProvenanceURIGenerator();
+
+ private String workflowRunId;
+
+ private Map<Path, T2Reference> fileToT2Reference = Collections.emptyMap();
+
+ private Saver saver;
+
+ private Map<URI, Individual> describedEntities = new HashMap<URI, Individual>();
+
+ private TavernaProvModel provModel = new TavernaProvModel();
+
+ public Path getBaseFolder() {
+ return bundle.getRoot();
+ }
+
+ public Map<Path, T2Reference> getFileToT2Reference() {
+ return fileToT2Reference;
+ }
+
+ // protected <I> void repopulateRegistry(ServiceRegistry<?, I> registry,
+ // Class<I> spi) {
+ // ClassLoader cl = classLoaderForServiceLoader(spi);
+ // logger.info("Selected classloader " + cl + " for registry of " + spi);
+ // for (I service : ServiceLoader.load(spi, cl)) {
+ // registry.add(service);
+ // }
+ // }
+
+ // private ClassLoader classLoaderForServiceLoader(Class<?> mustHave) {
+ // List<ClassLoader> possibles = Arrays.asList(Thread.currentThread()
+ // .getContextClassLoader(), getClass().getClassLoader(), mustHave
+ // .getClassLoader());
+ //
+ // for (ClassLoader cl : possibles) {
+ // if (cl == null) {
+ // continue;
+ // }
+ // try {
+ // if (cl.loadClass(mustHave.getCanonicalName()) == mustHave) {
+ // return cl;
+ // }
+ // } catch (ClassNotFoundException e) {
+ // }
+ // }
+ // // Final fall-back, the old..
+ // return ClassLoader.getSystemClassLoader();
+ // }
+
+ public W3ProvenanceExport(ProvenanceAccess provenanceAccess,
+ String workflowRunId, Saver saver) {
+ this.saver = saver;
+ this.setWorkflowRunId(workflowRunId);
+ this.setProvenanceAccess(provenanceAccess);
+
+ try {
+ datatypeFactory = DatatypeFactory.newInstance();
+ } catch (DatatypeConfigurationException e) {
+ throw new IllegalStateException(
+ "Can't find a DatatypeFactory implementation", e);
+ }
+ prepareScufl2();
+
+ }
+
+ protected void prepareScufl2() {
+ Thread.currentThread().setContextClassLoader(
+ getClass().getClassLoader());
+ wfBundleIO = new WorkflowBundleIO();
+ DataBundles.setWfBundleIO(wfBundleIO);
+ wfBundleIO.setReaders(readerSpi.getInstances());
+ wfBundleIO.setWriters(writerSpi.getInstances());
+ }
+
+ private final class ProvenanceURIGenerator extends URIGenerator {
+
+ // Make URIs match with Scufl2
+ @Override
+ public String makeWorkflowURI(String workflowID) {
+ return makeWorkflowBundleURI(workflowRunId) + "workflow/"
+ + provenanceAccess.getWorkflowNameByWorkflowID(workflowID)
+ + "/";
+ }
+
+ public String makeWorkflowBundleURI(String workflowRunId) {
+
+ return "http://ns.taverna.org.uk/2010/workflowBundle/"
+ + provenanceAccess.getTopLevelWorkflowID(workflowRunId)
+ + "/";
+ }
+
+ public String makePortURI(String wfId, String pName, String vName,
+ boolean inputPort) {
+ String base;
+ if (pName == null) {
+ base = makeWorkflowURI(wfId);
+ } else {
+ base = makeProcessorURI(pName, wfId);
+ }
+ return base + (inputPort ? "in/" : "out/") + escape(vName);
+ }
+
+ // public String makeDataflowInvocationURI(String workflowRunId,
+ // String dataflowInvocationId) {
+ // return makeWFInstanceURI(workflowRunId) + "workflow/"
+ // + dataflowInvocationId + "/";
+ // }
+
+ public String makeProcessExecution(String workflowRunId,
+ String processEnactmentId) {
+ return makeWFInstanceURI(workflowRunId) + "process/"
+ + processEnactmentId + "/";
+ }
+ }
+
+ enum Direction {
+ INPUTS("in"), OUTPUTS("out");
+ private final String path;
+
+ Direction(String path) {
+ this.path = path;
+
+ }
+
+ public String getPath() {
+ return path;
+ }
+ }
+
+ public void exportAsW3Prov() throws IOException {
+
+ Path provFile = DataBundles.getWorkflowRunProvenance(bundle);
+
+ // TODO: Make this thread safe using contexts?
+
+ GregorianCalendar startedProvExportAt = new GregorianCalendar();
+
+ runURI = URI.create(uriGenerator.makeWFInstanceURI(getWorkflowRunId()));
+
+ URI provFileUri = toURI(provFile);
+ Individual bundle = provModel.createBundle(provFileUri);
+
+ // Mini-provenance about this provenance trace. Unkown URI for
+ // agent/activity
+
+ Individual storeProvenance = provModel.createActivity(provFileUri
+ .resolve("#taverna-prov-export"));
+ storeProvenance.setLabel(
+ "taverna-prov export of workflow run provenance", EN);
+
+ provModel.setStartedAtTime(storeProvenance, startedProvExportAt);
+
+ // The agent is an execution of the Taverna software (e.g. also an
+ // Activity)
+ Individual tavernaAgent = provModel.createTavernaEngine(provFileUri
+ .resolve("#taverna-engine"));
+
+ Individual plan = provModel
+ .createPlan(getTavernaVersion());
+ plan.setLabel(applicationConfig.getTitle(), EN);
+
+ provModel.setWasAssociatedWith(storeProvenance, tavernaAgent, plan);
+ provModel.setWasGeneratedBy(bundle, storeProvenance);
+
+ Individual wfProcess = provModel.createWorkflowRun(runURI);
+
+ bundle.setPropertyValue(FOAF.primaryTopic, wfProcess);
+
+ DataflowInvocation dataflowInvocation = provenanceAccess
+ .getDataflowInvocation(getWorkflowRunId());
+
+ // TODO: Should we go through all of getDataflowInvocations() in order
+ // to find
+ // the plans etc. for the nested workflow executions and also cover
+ // empty
+ // nested workflow runs?
+
+ String workflowName = provenanceAccess
+ .getWorkflowNameByWorkflowID(dataflowInvocation.getWorkflowId());
+ label(wfProcess, "Workflow run of " + workflowName);
+
+ provModel.setWasInformedBy(storeProvenance, wfProcess);
+ String wfUri = uriGenerator.makeWorkflowURI(dataflowInvocation
+ .getWorkflowId());
+ Individual wfPlan = provModel.createWorkflow(URI.create(wfUri));
+ provModel.setWasEnactedBy(wfProcess, tavernaAgent, wfPlan);
+ provModel.setDescribedByWorkflow(wfProcess, wfPlan);
+
+ provModel.setStartedAtTime(wfProcess,
+ timestampToLiteral(dataflowInvocation.getInvocationStarted()));
+ provModel.setEndedAtTime(wfProcess,
+ timestampToLiteral(dataflowInvocation.getInvocationEnded()));
+
+ // Workflow inputs and outputs
+ storeEntitities(dataflowInvocation.getInputsDataBindingId(), wfProcess,
+ Direction.INPUTS, true);
+ // FIXME: These entities come out as "generated" by multiple processes
+ storeEntitities(dataflowInvocation.getOutputsDataBindingId(),
+ wfProcess, Direction.OUTPUTS, true);
+ List<ProcessorEnactment> processorEnactments = provenanceAccess
+ .getProcessorEnactments(getWorkflowRunId());
+ // This will also include processor enactments in nested workflows
+ for (ProcessorEnactment pe : processorEnactments) {
+ String parentId = pe.getParentProcessorEnactmentId();
+ URI parentURI;
+ if (parentId == null) {
+ // Top-level workflow
+ parentURI = runURI;
+ } else {
+ // inside nested wf - this will be parent processenactment
+ parentURI = URI.create(uriGenerator.makeProcessExecution(
+ pe.getWorkflowRunId(),
+ pe.getParentProcessorEnactmentId()));
+
+ // TODO: Find plan for nested workflow!
+ // String wfUri = uriGenerator.makeWorkflowURI(nestedWfId);
+ // Individual wfPlan =
+ // provModel.createWorkflow(URI.create(wfUri));
+ // provModel.setDescribedByWorkflow(wfProcess, wfPlan);
+ // provModel.setWasEnactedBy(wfProcess, tavernaAgent, wfPlan);
+ }
+
+ URI processURI = URI.create(uriGenerator.makeProcessExecution(
+ pe.getWorkflowRunId(), pe.getProcessEnactmentId()));
+
+ Individual process = provModel.createProcessRun(processURI);
+ Individual parentProcess = provModel.createWorkflowRun(parentURI);
+ provModel.setWasPartOfWorkflowRun(process, parentProcess);
+
+ provModel.setStartedAtTime(process,
+ timestampToLiteral(pe.getEnactmentStarted()));
+ provModel.setEndedAtTime(process,
+ timestampToLiteral(pe.getEnactmentEnded()));
+
+ ProvenanceProcessor provenanceProcessor = provenanceAccess
+ .getProvenanceProcessor(pe.getProcessorId());
+
+ URI processorURI = URI.create(uriGenerator.makeProcessorURI(
+ provenanceProcessor.getProcessorName(),
+ provenanceProcessor.getWorkflowId()));
+
+ label(process,
+ "Processor execution "
+ + provenanceProcessor.getProcessorName());
+ // The facade identifier is a bit too techie!
+ // + " ("
+ // + pe.getProcessIdentifier() + ")");
+ Individual procPlan = provModel.createProcess(processorURI);
+ label(procPlan,
+ "Processor " + provenanceProcessor.getProcessorName());
+ provModel.setWasEnactedBy(process, tavernaAgent, procPlan);
+ provModel.setDescribedByProcess(process, procPlan);
+
+ URI parentWfUri = URI.create(uriGenerator
+ .makeWorkflowURI(provenanceProcessor.getWorkflowId()));
+
+ Individual parentWf = provModel.createWorkflow(parentWfUri);
+ provModel.addSubProcess(parentWf, procPlan);
+
+ // TODO: How to link together iterations on a single processor and
+ // the collections
+ // they are iterating over and creating?
+ // Need 'virtual' ProcessExecution for iteration?
+
+ // TODO: Activity/service details from definition?
+
+ // Inputs and outputs
+ storeEntitities(pe.getInitialInputsDataBindingId(), process,
+ Direction.INPUTS, false);
+ storeEntitities(pe.getFinalOutputsDataBindingId(), process,
+ Direction.OUTPUTS, false);
+ }
+
+ storeFileReferences();
+
+ provModel.setEndedAtTime(storeProvenance, new GregorianCalendar());
+
+ // provModel.model.write(outStream, "TURTLE",
+ // provFileUri.toASCIIString());
+
+ OntModel model = provModel.model;
+ try (OutputStream outStream = Files.newOutputStream(provFile)) {
+ WriterGraphRIOT writer = RDFDataMgr
+ .createGraphWriter(RDFFormat.TURTLE_BLOCKS);
+ writer.write(outStream, model.getBaseModel().getGraph(),
+ RiotLib.prefixMap(model.getGraph()),
+ provFileUri.toString(), new Context());
+ } finally {
+ // Avoid registering the RIOT readers/writers from ARQ, as that
+ // won't
+ // work within Raven or OSGi
+ provModel.resetJena();
+ logger.warn("Reset Jena readers and writers");
+ }
+
+ byte[] dataflow = getDataflow(dataflowInvocation);
+ try {
+ WorkflowBundle wfBundle = wfBundleIO.readBundle(
+ new ByteArrayInputStream(dataflow),
+ T2FlowReader.APPLICATION_VND_TAVERNA_T2FLOW_XML);
+ writeBundle(wfBundle);
+ } catch (ReaderException e) {
+ logger.warn("Could not write bundle", e);
+ }
+
+ }
+
+ private URI getTavernaVersion() {
+ String versionName = applicationConfig.getName();
+ URI tavernaVersion = URI
+ .create("http://ns.taverna.org.uk/2011/software/" + versionName);
+ return tavernaVersion;
+ }
+
+ private byte[] getDataflow(DataflowInvocation dataflowInvocation) {
+ // you are not going to believe this...!
+ for (final WorkflowRun run : provenanceAccess.listRuns(
+ dataflowInvocation.getWorkflowId(), null)) {
+ if (getWorkflowRunId().equals(run.getWorkflowRunId())) {
+ return run.getDataflowBlob();
+ }
+ }
+ throw new IllegalStateException("Can't find dataflow blob for run "
+ + getWorkflowRunId());
+ }
+
+ protected void label(Individual obj, String label) {
+ obj.setLabel(label, EN);
+ }
+
+ protected Literal timestampToLiteral(Timestamp timestamp) {
+ if (timestamp == null) {
+ return null;
+ }
+ GregorianCalendar cal = new GregorianCalendar();
+ cal.setTime(timestamp);
+ XMLGregorianCalendar xmlCal = datatypeFactory
+ .newXMLGregorianCalendar(cal);
+ // Chop of the trailing 0-s of non-precission
+ xmlCal.setFractionalSecond(BigDecimal.valueOf(
+ timestamp.getNanos() / 1000000, NANOSCALE - 6));
+ return provModel.model.createTypedLiteral(xmlCal.toXMLFormat(),
+ XSDDatatype.XSDdateTime);
+ }
+
+ private static Map<URI, String> mediaTypes = new HashMap<>();
+
+ protected void storeFileReferences() {
+
+ for (Entry<Path, T2Reference> entry : getFileToT2Reference().entrySet()) {
+ Path file = entry.getKey();
+
+ try {
+ T2Reference t2Ref = entry.getValue();
+ URI dataURI = URI.create(uriGenerator.makeT2ReferenceURI(t2Ref
+ .toUri().toASCIIString()));
+
+ Individual entity = provModel.createArtifact(dataURI);
+
+ String mediaType = saver.getMediaTypes().get(t2Ref);
+
+ if (!Files.exists(file)) {
+ continue;
+ }
+ URI contentUri;
+ if (DataBundles.isReference(file)) {
+ // TODO: Do we really need to read this back again from the
+ // file?
+ contentUri = DataBundles.getReference(file);
+ } else {
+ contentUri = toURI(file);
+ }
+
+ Individual content = provModel.setContent(entity, contentUri);
+ if (mediaType != null) {
+ mediaTypes.put(contentUri, mediaType);
+ }
+ if (!DataBundles.isValue(file)) {
+ // Don't capture the checksum and content of references and
+ // lists
+ continue;
+ }
+
+ // Add checksums
+ String sha1 = saver.getSha1sums().get(file.toRealPath());
+ if (sha1 != null) {
+ content.addLiteral(provModel.sha1, sha1);
+ }
+ String sha512 = saver.getSha512sums().get(file.toRealPath());
+ if (sha512 != null) {
+ content.addLiteral(provModel.sha512, sha512);
+ }
+ long byteCount = Files.size(file);
+ content.addLiteral(provModel.byteCount, byteCount);
+
+ if (byteCount < EMBEDDED_MAX_FILESIZE) {
+ // Add content if it's "tiny"
+ byte[] bytes = Files.readAllBytes(file);
+ if (mediaType != null && mediaType.startsWith(TEXT)) {
+ // as string - assuming UTF8 (and declaring so)
+ String str = new String(bytes, UTF8);
+ content.addLiteral(provModel.chars, str);
+ content.addLiteral(provModel.characterEncoding,
+ UTF8.name());
+ content.addRDFType(provModel.ContentAsText);
+ } else {
+ // Or base64-encoded bytes
+ content.addRDFType(provModel.ContentAsBase64);
+ content.addLiteral(provModel.bytes, bytes);
+ }
+ }
+ } catch (IOException e) {
+ logger.warn("Could not read " + file + " as " + UTF8, e);
+ }
+ }
+ }
+
+ protected URI toURI(Path file) {
+ return file.toUri();
+ }
+
+ protected void storeEntitities(String dataBindingId, Individual activity,
+ Direction direction, boolean isTopLevel) throws IOException {
+
+ Map<Port, T2Reference> bindings = provenanceAccess
+ .getDataBindings(dataBindingId);
+
+ for (Entry<Port, T2Reference> binding : bindings.entrySet()) {
+ Port port = binding.getKey();
+ T2Reference t2Ref = binding.getValue();
+
+ Individual entity = describeEntity(t2Ref);
+
+ if (isTopLevel) {
+ Path ports;
+ if (direction == Direction.INPUTS) {
+ ports = DataBundles.getInputs(bundle);
+ } else {
+ ports = DataBundles.getOutputs(bundle);
+ }
+ Path portPath = DataBundles.getPort(ports, port.getPortName());
+ saveValue(t2Ref, portPath);
+ } else if (!seenReference(t2Ref)) {
+ saveIntermediate(t2Ref);
+ }
+
+ // String id = t2Ref.getLocalPart();
+ // String prefix = id.substring(0, 2);
+ Individual involvement;
+ if (direction == Direction.INPUTS) {
+ involvement = provModel.setUsedInput(activity, entity);
+ } else {
+ involvement = provModel.setWasOutputFrom(entity, activity);
+ }
+
+ String processorName = null;
+ if (port.getProcessorId() != null) {
+ // Not a workflow port
+ ProvenanceProcessor p = provenanceAccess
+ .getProvenanceProcessor(port.getProcessorId());
+ processorName = p.getProcessorName();
+ }
+ URI portURI = URI.create(uriGenerator.makePortURI(
+ port.getWorkflowId(), processorName, port.getPortName(),
+ port.isInputPort()));
+
+ Individual portRole;
+ if (port.isInputPort()) {
+ portRole = provModel.createInputParameter(portURI);
+ } else {
+ portRole = provModel.createOutputParameter(portURI);
+ }
+
+ portRole.setLabel(port.getPortName(), "");
+ if (processorName == null) {
+ portRole.setComment(
+ "Workflow"
+ + (port.isInputPort() ? " input " : " output ")
+ + port.getPortName(), EN);
+ } else {
+ portRole.setComment(
+ processorName
+ + (port.isInputPort() ? " input " : " output ")
+ + port.getPortName(), EN);
+ }
+ provModel.setDescribedByParameter(entity, portRole, involvement);
+
+ }
+
+ }
+
+ protected Individual describeEntity(T2Reference t2Ref) throws IOException {
+ URI dataURI = URI.create(uriGenerator.makeT2ReferenceURI(t2Ref.toUri()
+ .toASCIIString()));
+
+ Individual artifact = describedEntities.get(dataURI);
+ if (artifact != null) {
+ return artifact;
+ }
+ artifact = provModel.createArtifact(dataURI);
+ describedEntities.put(dataURI, artifact);
+
+ if (t2Ref.getReferenceType() == T2ReferenceType.ErrorDocument) {
+ Individual error = provModel.createError(dataURI);
+ ErrorDocument errorDoc = saver.getReferenceService()
+ .getErrorDocumentService().getError(t2Ref);
+ addMessageIfNonEmpty(error, errorDoc.getMessage());
+ // getExceptionMEssage added by addStackTrace
+ addStackTrace(error, errorDoc);
+ } else if (t2Ref.getReferenceType() == T2ReferenceType.IdentifiedList) {
+ IdentifiedList<T2Reference> list = saver.getReferenceService()
+ .getListService().getList(t2Ref);
+ Individual dictionary = provModel.createDictionary(dataURI);
+
+ int pos = 0;
+ for (T2Reference ref : list) {
+ URI itemURI = URI.create(uriGenerator.makeT2ReferenceURI(ref
+ .toUri().toASCIIString()));
+ Individual listItem = provModel.createArtifact(itemURI);
+ provModel.addKeyPair(dictionary, pos++, listItem);
+ describeEntity(ref);
+ }
+ if (list.isEmpty()) {
+ artifact.addRDFType(provModel.EmptyCollection);
+ artifact.addRDFType(provModel.EmptyDictionary);
+ }
+ }
+
+ return artifact;
+ }
+
+ private boolean seenReference(T2Reference t2Ref) {
+ return seenReferences.containsKey(t2Ref);
+ }
+
+ private Path saveIntermediate(T2Reference t2Ref) throws IOException {
+ // Avoid double-saving
+ Path f = seenReferences.get(t2Ref);
+ if (f != null) {
+ return f;
+ }
+ Path file = referencePath(t2Ref);
+
+ if (t2Ref.getReferenceType() == T2ReferenceType.IdentifiedList) {
+ IdentifiedList<T2Reference> list = saver.getReferenceService()
+ .getListService().getList(t2Ref);
+ for (T2Reference ref : list) {
+ saveIntermediate(ref);
+ }
+ seenReference(t2Ref, file);
+ return file;
+ } else {
+ return saveValue(t2Ref, file);
+ }
+
+ }
+
+ private Path saveValue(T2Reference t2Ref, Path file) throws IOException {
+ Path parent = file.getParent();
+
+ switch (t2Ref.getReferenceType()) {
+
+ case IdentifiedList:
+ DataBundles.createList(file);
+ IdentifiedList<T2Reference> list = saver.getReferenceService()
+ .getListService().getList(t2Ref);
+ long position = 0;
+ for (T2Reference ref : list) {
+ saveValue(ref, DataBundles.getListItem(file, position++));
+ }
+ break;
+ case ErrorDocument:
+ Files.createDirectories(parent);
+ file = saveError(t2Ref, file);
+ break;
+ case ReferenceSet:
+ Files.createDirectories(parent);
+ file = saver.saveReference(t2Ref, file);
+ }
+ seenReference(t2Ref, file);
+ return file;
+ }
+
+ private Path saveError(T2Reference t2Ref, Path file) throws IOException {
+ ErrorDocument errorDoc = saver.getReferenceService()
+ .getErrorDocumentService().getError(t2Ref);
+
+ StringBuilder trace = new StringBuilder();
+ addStackTrace(trace, errorDoc);
+
+ List<Path> causes = new ArrayList<>();
+ for (T2Reference cause : errorDoc.getErrorReferences()) {
+ causes.add(saveIntermediate(cause));
+ }
+ file = DataBundles.setError(file, errorDoc.getMessage(),
+ trace.toString(), causes.toArray(new Path[causes.size()]));
+ return file;
+ }
+
+ protected void addStackTrace(Individual error, ErrorDocument errorDoc)
+ throws IOException {
+ StringBuilder sb = new StringBuilder();
+ addStackTrace(sb, errorDoc);
+ if (sb.length() > 0) {
+ error.addLiteral(provModel.stackTrace, sb.toString());
+ }
+
+ for (T2Reference errRef : errorDoc.getErrorReferences()) {
+ URI errorURI = URI.create(uriGenerator.makeT2ReferenceURI(errRef
+ .toUri().toASCIIString()));
+ Individual nestedErr = provModel.createError(errorURI);
+ provModel.setWasDerivedFrom(error, nestedErr);
+ describeEntity(errRef);
+ }
+ }
+
+ protected void addStackTrace(StringBuilder sb, ErrorDocument errorDoc) {
+ if (errorDoc.getExceptionMessage() != null
+ && !errorDoc.getExceptionMessage().isEmpty()) {
+ sb.append(errorDoc.getExceptionMessage());
+ sb.append("\n");
+ }
+ if (errorDoc.getStackTraceStrings() == null) {
+ return;
+ }
+ if (sb.length() == 0) {
+ sb.append("Stack trace:\n");
+ }
+ // Attempt to recreate Java stacktrace style
+ for (StackTraceElementBean trace : errorDoc.getStackTraceStrings()) {
+ sb.append(" at ");
+ sb.append(trace.getClassName());
+ sb.append(".");
+ sb.append(trace.getMethodName());
+ sb.append("(");
+ sb.append(trace.getFileName());
+ sb.append(":");
+ sb.append(trace.getLineNumber());
+ sb.append(")");
+ sb.append("\n");
+ }
+ }
+
+ protected void addMessageIfNonEmpty(Individual error, String message) {
+ if (message == null || message.isEmpty()) {
+ return;
+ }
+ error.addLiteral(provModel.errorMessage, message);
+ }
+
+ private Path referencePath(T2Reference t2Ref) throws IOException {
+ String local = t2Ref.getLocalPart();
+ try {
+ return DataBundles.getIntermediate(bundle, UUID.fromString(local));
+ } catch (IllegalArgumentException ex) {
+ return DataBundles.getIntermediates(bundle)
+ .resolve(t2Ref.getNamespacePart())
+ .resolve(t2Ref.getLocalPart());
+ }
+
+ }
+
+ private boolean seenReference(T2Reference t2Ref, Path file) {
+ getFileToT2Reference().put(file, t2Ref);
+ if (seenReference(t2Ref)) {
+ return true;
+ }
+ return seenReferences.put(t2Ref, file) != null;
+ }
+
+ public ProvenanceAccess getProvenanceAccess() {
+ return provenanceAccess;
+ }
+
+ public void setProvenanceAccess(ProvenanceAccess provenanceAccess) {
+ this.provenanceAccess = provenanceAccess;
+ }
+
+ public String getWorkflowRunId() {
+ return workflowRunId;
+ }
+
+ public void setWorkflowRunId(String workflowRunId) {
+ this.workflowRunId = workflowRunId;
+ }
+
+ public void setFileToT2Reference(Map<Path, T2Reference> fileToT2Reference) {
+ this.fileToT2Reference = new HashMap<>();
+ for (Entry<Path, T2Reference> entry : fileToT2Reference.entrySet()) {
+ seenReference(entry.getValue(), entry.getKey());
+ }
+ }
+
+ private static final String WFDESC = "http://purl.org/wf4ever/wfdesc#";
+ private static WorkflowBundleIO wfBundleIO;
+ private Bundle bundle;
+ private URI runURI;
+
+ /**
+ * @return the bundle
+ */
+ public Bundle getBundle() {
+ return bundle;
+ }
+
+ public void writeBundle(WorkflowBundle wfBundle) throws IOException {
+
+ Bundle dataBundle = getBundle();
+
+ // Workflow
+ DataBundles.setWorkflowBundle(dataBundle, wfBundle);
+
+ // Generate Manifest
+ // TODO: This should be done automatically on close/save
+ Manifest manifest = new Manifest(dataBundle);
+ manifest.populateFromBundle();
+
+ Path workflowRunProvenance = DataBundles
+ .getWorkflowRunProvenance(dataBundle);
+ // Additional metadata
+ manifest.getAggregation(workflowRunProvenance).setMediatype(
+ "text/turtle");
+
+ Agent provPlugin = new Agent();
+ provPlugin.setName("Taverna-PROV plugin, " + applicationConfig.getTitle() + " " + applicationConfig.getName());
+ provPlugin.setUri(getPluginIdentifier(getClass()));
+ manifest.getAggregation(workflowRunProvenance).setCreatedBy(
+ provPlugin);
+ manifest.setCreatedBy(provPlugin);
+
+
+ // Media types:
+ for (Entry<URI, String> e : mediaTypes.entrySet()) {
+ URI uri = e.getKey();
+ String mediatype = e.getValue();
+ PathMetadata aggregation = manifest.getAggregation(uri);
+ if (aggregation == null) {
+ // An external reference? Add it.
+ aggregation = manifest.getAggregation(uri);
+ //aggregation = new PathMetadata();
+ //aggregation.setUri(uri);
+ //manifest.getAggregates().add(aggregation);
+ }
+ aggregation.setMediatype(mediatype);
+ }
+
+ // Add annotations
+
+ // This RO Bundle is about a run
+ PathAnnotation bundleAboutRun = new PathAnnotation();
+ bundleAboutRun.setAbout(runURI);
+ bundleAboutRun.setContent(URI.create("/"));
+ manifest.getAnnotations().add(bundleAboutRun);
+
+ // Also aggregate the run by ID, and that it was done by taverna
+ Agent taverna = new Agent();
+ taverna.setName(applicationConfig.getTitle());
+ taverna.setUri(getTavernaVersion());
+ manifest.getAggregation(runURI).setCreatedBy(taverna);
+
+ // TODO: Do we need both the "history" link and the annotation below?
+ manifest.setHistory(Arrays.asList(workflowRunProvenance));
+
+ // This RO Bundle is described in the provenance file
+ PathAnnotation provenanceAboutBundle = new PathAnnotation();
+ provenanceAboutBundle.setAbout(URI.create("/"));
+ provenanceAboutBundle.setContent(URI.create(workflowRunProvenance
+ .toUri().getPath()));
+ manifest.getAnnotations().add(provenanceAboutBundle);
+
+ // The wfdesc is about the workflow definition
+ Path workflow = DataBundles.getWorkflow(dataBundle);
+ // String workflowType = Files.probeContentType(workflow);
+ manifest.getAggregation(workflow).setMediatype(WORKFLOW_BUNDLE);
+ Path wfdesc = DataBundles.getWorkflowDescription(dataBundle);
+ if (Files.exists(wfdesc)) {
+ PathAnnotation wfdescAboutWfBundle = new PathAnnotation();
+ wfdescAboutWfBundle
+ .setAbout(URI.create(workflow.toUri().getPath()));
+ wfdescAboutWfBundle
+ .setContent(URI.create(wfdesc.toUri().getPath()));
+ manifest.getAnnotations().add(wfdescAboutWfBundle);
+ }
+
+ // And the workflow definition is about the workflow
+ PathAnnotation wfBundleAboutWf = new PathAnnotation();
+ URITools uriTools = new URITools();
+ URI mainWorkflow = uriTools.uriForBean(wfBundle.getMainWorkflow());
+ wfBundleAboutWf.setAbout(mainWorkflow);
+ URI wfBundlePath = URI.create(workflow.toUri().getPath());
+ wfBundleAboutWf.setContent(wfBundlePath);
+ manifest.getAnnotations().add(wfBundleAboutWf);
+ manifest.getAggregation(mainWorkflow);
+
+ // hasWorkflowDefinition
+ PathAnnotation hasWorkflowDefinition = new PathAnnotation();
+ hasWorkflowDefinition.setAbout(wfBundlePath);
+ UUID uuid = UUID.randomUUID();
+ hasWorkflowDefinition.setUri(URI.create("urn:uuid:" + uuid));
+ Path annotationBody = DataBundles.getAnnotations(dataBundle).resolve(
+ uuid + ".ttl");
+ hasWorkflowDefinition.setContent(URI.create(annotationBody.toUri()
+ .getPath()));
+ Model model = ModelFactory.createDefaultModel();
+ URI relPathToWfBundle = uriTools.relativePath(annotationBody.toUri(),
+ workflow.toUri());
+ model.setNsPrefix("wfdesc", WFDESC);
+ model.add(model.createResource(mainWorkflow.toASCIIString()),
+ model.createProperty(WFDESC + "hasWorkflowDefinition"),
+ model.createResource(relPathToWfBundle.toASCIIString()));
+ try (OutputStream out = Files.newOutputStream(annotationBody)) {
+ model.write(out, "TURTLE", annotationBody.toUri().toASCIIString());
+ }
+ manifest.getAnnotations().add(hasWorkflowDefinition);
+
+ PathAnnotation wfBundleAboutWfB = new PathAnnotation();
+ wfBundleAboutWfB.setAbout(wfBundle.getGlobalBaseURI());
+ wfBundleAboutWfB.setContent(URI.create(workflow.toUri().getPath()));
+ manifest.getAnnotations().add(wfBundleAboutWfB);
+
+ manifest.writeAsJsonLD();
+
+ // // Saving a data bundle:
+ // Path bundleFile = runPath.getParent().resolve(runPath.getFileName() +
+ // ".bundle.zip");
+ // DataBundles.closeAndSaveBundle(dataBundle, bundleFile);
+ // NOTE: From now dataBundle and its Path's are CLOSED
+ // and can no longer be accessed
+
+ }
+
+ /** Extract our own plugin version - if running within Raven */
+ protected static URI getPluginIdentifier(Class<?> pluginClass) {
+ ClassLoader classLoader = pluginClass.getClassLoader();
+ String className = pluginClass.getCanonicalName();
+
+ try {
+// org.osgi.framework.Bundle osgiBundle = FrameworkUtil
+// .getBundle(pluginClass);
+// if (osgiBundle != null) {
+// String symbolicName = osgiBundle.getSymbolicName();
+// Version version = osgiBundle.getVersion();
+// }
+
+ // equivalent as above without OSGi dependency:
+ Object bundle = PropertyUtils.getProperty(classLoader, "bundle");
+ String symbolicName = BeanUtils.getProperty(bundle, "symbolicName");
+ String version = BeanUtils.getProperty(bundle, "version")
+ .toString();
+
+ // NOTE: The above code has not been tested within OSGi as of 2013-12-18
+
+ return osgiURI.resolve(uriTools.validFilename(symbolicName) + "/"
+ + uriTools.validFilename(version));
+ } catch (IllegalAccessException | InvocationTargetException
+ | NullPointerException | NoSuchMethodException e) {
+ // Assume it's not OSGi
+ }
+
+ // Not OSGi, try as Raven:
+ try {
+
+// Artifact artifact = ((LocalArtifactClassLoader) classLoader)
+// .getArtifact();
+// String groupId = artifact.getGroupId();
+// String artifactId = artifact.getArtifactId();
+// String version = artifact.getVersion();
+
+ // Equivalent as above, but without Raven dependency:
+
+ Object artifact = PropertyUtils
+ .getProperty(classLoader, "artifact");
+ if (artifact == null) {
+ return null;
+ }
+ // If it worked, then we assume it is a
+ // net.sf.taverna.raven.repository.Artifact
+ // implementation
+ String groupId = BeanUtils.getProperty(artifact, "groupId");
+ String artifactId = BeanUtils.getProperty(artifact, "artifactId");
+ String version = BeanUtils.getProperty(artifact, "version");
+ // mimic scufl2-t2flow
+ return ravenURI.resolve(uriTools.validFilename(groupId) + "/"
+ + uriTools.validFilename(artifactId) + "/"
+ + uriTools.validFilename(version) + "/"
+ + uriTools.validFilename(className));
+ } catch (IllegalAccessException | InvocationTargetException
+ | NullPointerException | NoSuchMethodException e) {
+ // Assume it's not Raven
+ }
+
+ // Fallback based on the classname - mimic scufl2-t2flow
+ return ravenURI.resolve("undefined/" + uriTools.validFilename(className));
+
+ }
+
+ public void setBundle(Bundle bundle) {
+ this.bundle = bundle;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-engine/blob/c1608c61/taverna-prov/src/main/java/org/purl/wf4ever/provtaverna/export/SafeFileOutputStream.java
----------------------------------------------------------------------
diff --git a/taverna-prov/src/main/java/org/purl/wf4ever/provtaverna/export/SafeFileOutputStream.java b/taverna-prov/src/main/java/org/purl/wf4ever/provtaverna/export/SafeFileOutputStream.java
deleted file mode 100644
index 3731c76..0000000
--- a/taverna-prov/src/main/java/org/purl/wf4ever/provtaverna/export/SafeFileOutputStream.java
+++ /dev/null
@@ -1,119 +0,0 @@
-package org.purl.wf4ever.provtaverna.export;
-
-/**
- * Copied out from net.sf.taverna.t2.workbench.file.impl.SafeFileOutputStream
- * to avoid dependency on file-impl.
- *
- * TODO: Move to taverna utils!
- *
- */
-
-import java.io.FileNotFoundException;
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.StandardCopyOption;
-
-public class SafeFileOutputStream extends FilterOutputStream {
-
- private final Path desiredFile;
- private Path tempFile;
- boolean desiredAlreadyExisted;
-
- public SafeFileOutputStream(Path file) throws IOException {
- this(file, tempFile(file));
- }
-
- public SafeFileOutputStream(Path desiredFile, Path tempFile)
- throws IOException {
-
- super(Files.newOutputStream(tempFile));
- this.desiredFile = desiredFile;
- this.tempFile = tempFile;
- // Some useful things to check that we preferably don't want to fail on
- // close()
- desiredAlreadyExisted = Files.exists(desiredFile);
- Path desiredFolder = this.desiredFile.getParent();
- if (desiredAlreadyExisted) {
- if (!Files.isWritable(desiredFile)) {
- throw new FileNotFoundException("Can't write to " + desiredFile);
- }
- } else {
- if (!Files.exists(desiredFolder)) {
- throw new FileNotFoundException("Folder does not exist: "
- + desiredFolder);
- }
- if (!Files.isDirectory(desiredFolder)) {
- throw new FileNotFoundException("Not a directory: "
- + desiredFolder);
- }
- }
- if (!Files.isWritable(desiredFolder)) {
- throw new FileNotFoundException("Can't modify folder "
- + desiredFolder);
- }
- }
-
- private static Path tempFile(Path file) throws IOException {
- return Files.createTempFile(file.getParent(), file.getFileName()
- .toString(), ".tmp");
- }
-
- @Override
- public void close() throws IOException {
- // If super.close fails - we leave the tempfiles behind
- super.close();
- if (!Files.exists(tempFile)) {
- // Probably something went wrong before close called,
- // like rollback()
- return;
- }
- Path beforeDeletion = null;
- try {
- if (desiredAlreadyExisted) {
- // In case renaming goes wrong, we don't want to have already
- // deleted the
- // desired file. So we'll rename it to another temporary file
- // instead which
- // we can delete on successful rename.
- beforeDeletion = tempFile(desiredFile);
- if (!Files.deleteIfExists(beforeDeletion)) {
- // Should not happen, we just made it!
- throw new IOException("Can't delete temporary file "
- + beforeDeletion);
- }
- Files.move(desiredFile, beforeDeletion,
- StandardCopyOption.ATOMIC_MOVE);
- }
- try {
- Files.move(tempFile, desiredFile,
- StandardCopyOption.ATOMIC_MOVE);
- } catch (IOException e) {
- // We'll leave our tempFiles for recovery.
- tempFile = null;
- beforeDeletion = null;
-
- if (Files.exists(desiredFile)) {
- // Someone else added or replaced the file afterwards,
- // kind-a OK
- return;
- }
- throw e;
- }
- } finally {
- if (beforeDeletion != null) {
- Files.deleteIfExists(beforeDeletion);
- }
- if (tempFile != null) {
- Files.deleteIfExists(tempFile);
- }
- }
- }
-
- public void rollback() throws IOException {
- super.close();
- Files.deleteIfExists(tempFile);
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-engine/blob/c1608c61/taverna-prov/src/main/java/org/purl/wf4ever/provtaverna/export/Saver.java
----------------------------------------------------------------------
diff --git a/taverna-prov/src/main/java/org/purl/wf4ever/provtaverna/export/Saver.java b/taverna-prov/src/main/java/org/purl/wf4ever/provtaverna/export/Saver.java
deleted file mode 100644
index 27777b1..0000000
--- a/taverna-prov/src/main/java/org/purl/wf4ever/provtaverna/export/Saver.java
+++ /dev/null
@@ -1,388 +0,0 @@
-package org.purl.wf4ever.provtaverna.export;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.ProtocolException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.security.DigestOutputStream;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.taverna.invocation.InvocationContext;
-import org.apache.taverna.provenance.api.ProvenanceAccess;
-import org.apache.taverna.reference.ExternalReferenceSPI;
-import org.apache.taverna.reference.ReferenceService;
-import org.apache.taverna.reference.ReferenceSet;
-import org.apache.taverna.reference.ReferenceSetService;
-import org.apache.taverna.reference.T2Reference;
-import org.apache.taverna.reference.ValueCarryingExternalReference;
-import org.apache.taverna.spi.SPIRegistry;
-import org.apache.taverna.reference.config.DataManagementConfiguration;
-
-import org.apache.commons.beanutils.PropertyUtils;
-import org.apache.commons.codec.binary.Hex;
-import org.apache.commons.io.IOUtils;
-import org.apache.log4j.Logger;
-import org.apache.tika.Tika;
-import org.apache.tika.mime.MimeTypeException;
-import org.apache.tika.mime.MimeTypes;
-import org.openrdf.rio.RDFParserFactory;
-import org.openrdf.rio.RDFParserRegistry;
-import org.openrdf.rio.RDFWriterFactory;
-import org.openrdf.rio.RDFWriterRegistry;
-import org.apache.taverna.robundle.Bundle;
-
-import org.apache.taverna.databundle.DataBundles;
-
-public class Saver {
-
- private static final String APPLICATION_OCTET_STREAM = "application/octet-stream";
-
- private static Logger logger = Logger.getLogger(Saver.class);
-
- /**
- * @param saveProvAction
- */
- public Saver(ReferenceService referenceService, InvocationContext context,
- String runId, Map<String, T2Reference> chosenReferences) {
- this.setReferenceService(referenceService);
- this.setContext(context);
- this.setRunId(runId);
- this.setChosenReferences(chosenReferences);
- prepareSesame();
- }
-
- /**
- * Load
- */
- protected void prepareSesame() {
- RDFParserRegistry parserReg = RDFParserRegistry.getInstance();
- SPIRegistry<RDFParserFactory> parserSPI = new SPIRegistry<>(
- RDFParserFactory.class);
- for (RDFParserFactory service : parserSPI.getInstances()) {
- parserReg.add(service);
- }
-
- RDFWriterRegistry writerReg = RDFWriterRegistry.getInstance();
- SPIRegistry<RDFWriterFactory> writerSPI = new SPIRegistry<>(
- RDFWriterFactory.class);
- for (RDFWriterFactory service : writerSPI.getInstances()) {
- writerReg.add(service);
- }
-
- }
-
- private Map<Path, T2Reference> fileToId = new HashMap<>();
-
- private Map<Path, String> sha1sums = new HashMap<>();
- private Map<Path, String> sha512sums = new HashMap<>();
-
- private ReferenceService referenceService;
-
- private InvocationContext context;
-
- private String runId;
-
- private Map<String, T2Reference> chosenReferences;
-
- private Bundle bundle;
-
- private Map<T2Reference, String> mediaTypes = new HashMap<>();
-
- /**
- * @return the bundle
- */
- public Bundle getBundle() {
- return bundle;
- }
-
- public void saveData(Path bundlePath) throws FileNotFoundException,
- IOException {
- Bundle bundle = DataBundles.createBundle();
- // String folderName = bundlePath.getFileName().toString();
- // if (folderName.endsWith(".")) {
- // bundlePath = bundlePath.resolveSibling(folderName.substring(0,
- // folderName.length() - 1));
- // }
- setBundle(bundle);
- saveToFolder(bundle.getRoot(), getChosenReferences(),
- getReferenceService());
- DataBundles.closeAndSaveBundle(bundle, bundlePath);
- }
-
- private void setBundle(Bundle bundle) {
- this.bundle = bundle;
- }
-
- protected static Tika tika = new Tika();
-
- public Path saveReference(T2Reference t2Ref, Path file) throws IOException {
- ReferenceSetService refSet = getReferenceService()
- .getReferenceSetService();
- ReferenceSet referenceSet = refSet.getReferenceSet(t2Ref);
- List<ExternalReferenceSPI> externalReferences = new ArrayList<ExternalReferenceSPI>(
- referenceSet.getExternalReferences());
- Collections.sort(externalReferences,
- new Comparator<ExternalReferenceSPI>() {
- public int compare(ExternalReferenceSPI o1,
- ExternalReferenceSPI o2) {
- return (int) (o1.getResolutionCost() - o2
- .getResolutionCost());
- }
- });
- String mimeType = findMimeType(externalReferences);
- getMediaTypes().put(t2Ref, mimeType);
-
- Path targetFile = writeIfLocal(externalReferences, file, mimeType);
- if (targetFile == null) {
- URI uri = referenceAsURI(externalReferences);
- if (uri != null) {
- targetFile = DataBundles.setReference(file, uri);
- }
- }
-
- if (targetFile != null) {
- getFileToId().put(targetFile, t2Ref);
- } else {
- logger.warn("Could not write out reference " + t2Ref);
- }
-
- return targetFile;
-
- }
-
- private Path writeIfLocal(List<ExternalReferenceSPI> externalReferences,
- Path file, String mimeType) throws IOException {
-
- ValueCarryingExternalReference<?> valRef = null;
- for (ExternalReferenceSPI ref : externalReferences) {
- if (ref instanceof ValueCarryingExternalReference) {
- valRef = (ValueCarryingExternalReference<?>) ref;
- break;
- }
- }
-
- if (valRef == null) {
- return null;
- }
-
- String fileExtension;
- try {
- fileExtension = MimeTypes.getDefaultMimeTypes().forName(mimeType)
- .getExtension();
- } catch (MimeTypeException e1) {
- fileExtension = "";
- }
- Path targetFile = file.resolveSibling(file.getFileName()
- + fileExtension);
-
- MessageDigest sha = null;
- MessageDigest sha512 = null;
- OutputStream output = Files.newOutputStream(targetFile);
- try {
- try {
- sha = MessageDigest.getInstance("SHA");
- output = new DigestOutputStream(output, sha);
-
- sha512 = MessageDigest.getInstance("SHA-512");
- output = new DigestOutputStream(output, sha512);
- } catch (NoSuchAlgorithmException e) {
- logger.info("Could not find digest", e);
- }
-
- IOUtils.copyLarge(valRef.openStream(getContext()), output);
- } finally {
- output.close();
- }
-
- if (sha != null) {
- getSha1sums().put(targetFile.toRealPath(), hexOfDigest(sha));
- }
- if (sha512 != null) {
- sha512.digest();
- getSha512sums().put(targetFile.toRealPath(), hexOfDigest(sha512));
- }
-
- return targetFile;
- }
-
- private URI referenceAsURI(List<ExternalReferenceSPI> externalReferences) {
- for (ExternalReferenceSPI ref : externalReferences) {
- String className = ref.getClass().getName();
- if (className
- .equals("net.sf.taverna.t2.reference.impl.external.http.HttpReference")) {
- URL url = (URL) getProperty(ref, "httpUrl");
- try {
- return url.toURI();
- } catch (URISyntaxException e) {
- logger.warn("Can't convert HttpReference to URI: " + url, e);
- continue;
- }
- } else if (className
- .equals("net.sf.taverna.t2.reference.impl.external.file.FileReference")) {
- File file = (File) getProperty(ref, "file");
- return file.toURI();
- } else if (className
- .equals("de.uni_luebeck.inb.knowarc.usecases.invocation.ssh.SshReference")) {
- String host = (String) getProperty(ref, "host");
- int port = (int) getProperty(ref, "port");
- String path = (String) getProperty(ref, "fullPath");
- try {
- return new URI("sftp", null, host, port, path, null, null);
- } catch (URISyntaxException e) {
- logger.warn("Can't convert SshReference to URI: sftp://"
- + host + ":" + port + path, e);
- continue;
- }
- }
- }
- return null;
- }
-
- protected Object getProperty(ExternalReferenceSPI ref, String propertyName) {
- try {
- return PropertyUtils.getSimpleProperty(ref, propertyName);
- } catch (Exception ex) {
- throw new IllegalArgumentException("Can't look up " + propertyName
- + " in bean " + ref, ex);
- }
- }
-
- private String findMimeType(List<ExternalReferenceSPI> externalReferences)
- throws IOException, ProtocolException {
- String mimeType = null;
- for (ExternalReferenceSPI externalReference : externalReferences) {
- String className = externalReference.getClass().getName();
- if (className
- .equals("net.sf.taverna.t2.reference.impl.external.http.HttpReference")) {
- URL url = (URL) getProperty(externalReference, "httpUrl");
- mimeType = tika.detect(url);
- } else if (className
- .equals("net.sf.taverna.t2.reference.impl.external.file.FileReference")) {
- File file = (File) getProperty(externalReference, "file");
- mimeType = tika.detect(file);
- } else if (className
- .equals("de.uni_luebeck.inb.knowarc.usecases.invocation.ssh.SshReference")) {
- String filename = (String) getProperty(externalReference,
- "fileName");
- try (InputStream instream = externalReference
- .openStream(context)) {
- mimeType = tika.detect(instream, filename);
- }
- } else if (className
- .equals("net.sf.taverna.t2.reference.impl.external.object.VMObjectReference")) {
- mimeType = "application/x-java-serialized-object";
- } else {
- try (InputStream instream = externalReference
- .openStream(context)) {
- mimeType = tika.detect(instream);
- }
- }
- if (mimeType != null && !mimeType.equals(APPLICATION_OCTET_STREAM)) {
- break;
- }
- }
- if (mimeType == null || mimeType.isEmpty()) {
- return APPLICATION_OCTET_STREAM;
- }
- return mimeType;
- }
-
- protected void saveToFolder(Path folder,
- Map<String, T2Reference> chosenReferences,
- ReferenceService referenceService) throws IOException,
- FileNotFoundException {
- logger.info("Saving provenance and outputs to " + folder.toRealPath());
- Files.createDirectories(folder);
- String connectorType = DataManagementConfiguration.getInstance()
- .getConnectorType();
- ProvenanceAccess provenanceAccess = new ProvenanceAccess(connectorType,
- getContext());
- W3ProvenanceExport export = new W3ProvenanceExport(provenanceAccess,
- getRunId(), this);
- export.setFileToT2Reference(getFileToId());
- export.setBundle(bundle);
-
- try {
- logger.debug("Saving provenance");
- export.exportAsW3Prov();
- logger.info("Saved provenance");
- } catch (Exception e) {
- logger.error("Failed to save the provenance graph", e);
- }
- }
-
- private String hexOfDigest(MessageDigest sha) {
- return new String(Hex.encodeHex(sha.digest()));
- }
-
- public ReferenceService getReferenceService() {
- return referenceService;
- }
-
- public void setReferenceService(ReferenceService referenceService) {
- this.referenceService = referenceService;
- }
-
- public InvocationContext getContext() {
- return context;
- }
-
- public void setContext(InvocationContext context) {
- this.context = context;
- }
-
- public String getRunId() {
- return runId;
- }
-
- public void setRunId(String runId) {
- this.runId = runId;
- }
-
- public Map<String, T2Reference> getChosenReferences() {
- return chosenReferences;
- }
-
- public void setChosenReferences(Map<String, T2Reference> chosenReferences) {
- this.chosenReferences = chosenReferences;
- }
-
- public Map<Path, T2Reference> getFileToId() {
- return fileToId;
- }
-
- public void setFileToId(Map<Path, T2Reference> fileToId) {
- this.fileToId = fileToId;
- }
-
- public Map<Path, String> getSha1sums() {
- return sha1sums;
- }
-
- public Map<T2Reference, String> getMediaTypes() {
- return mediaTypes;
- }
-
- public Map<Path, String> getSha512sums() {
- return sha512sums;
- }
-
- public void setMediaTypes(Map<T2Reference, String> mediaTypes) {
- this.mediaTypes = mediaTypes;
- }
-
-}
\ No newline at end of file