blob: 00d335079d6f61ab029862da898502feb485669c [file] [log] [blame]
package org.apache.taverna.scufl2.translator.t2flow;
/*
*
* 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.
*
*/
import static java.util.Collections.singleton;
import static org.apache.taverna.scufl2.api.container.WorkflowBundle.WORKFLOW_BUNDLE_ROOT;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.Set;
import java.util.UUID;
import javax.xml.bind.JAXBException;
import org.apache.taverna.scufl2.api.common.Scufl2Tools;
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.WorkflowBundleReader;
public class T2FlowReader implements WorkflowBundleReader {
private final class DelayedCloseBufferedInputStream extends
BufferedInputStream {
private DelayedCloseBufferedInputStream(InputStream in, int size) {
super(in, size);
}
@Override
public void close() throws IOException {
return;
}
public void reallyClose() throws IOException {
super.close();
}
}
protected static final URI PROV = URI.create("http://www.w3.org/ns/prov#");
protected static final URI HAD_ORIGINAL_SOURCE = PROV.resolve("#hadOriginalSource");
protected static final URI ENTITY = PROV.resolve("#Entity");
private static final int MEGABYTE = 1024 * 1024;
private static final int PRESERVE_ORIGINAL_MAXIMUM_BYTES = 5 * MEGABYTE;
public static final String APPLICATION_VND_TAVERNA_T2FLOW_XML = "application/vnd.taverna.t2flow+xml";
private T2FlowParser parser;
private Scufl2Tools scufl2Tools = new Scufl2Tools();
private URITools uriTools = new URITools();
@Override
public Set<String> getMediaTypes() {
return singleton(APPLICATION_VND_TAVERNA_T2FLOW_XML);
}
@Override
public WorkflowBundle readBundle(File bundleFile, String mediaType)
throws ReaderException, IOException {
try {
WorkflowBundle bundle = getParser().parseT2Flow(bundleFile);
scufl2Tools.setParents(bundle);
preserveOriginal(bundle, new FileInputStream(bundleFile));
return bundle;
} catch (JAXBException e) {
if (e.getCause() instanceof IOException) {
IOException ioException = (IOException) e.getCause();
throw ioException;
}
throw new ReaderException("Can't parse t2flow " + bundleFile, e);
}
}
protected void preserveOriginal(WorkflowBundle bundle,
InputStream t2flowStream) throws IOException {
String path = "history/" + bundleUUID(bundle) + ".t2flow";
bundle.getResources().addResource(t2flowStream, path,
APPLICATION_VND_TAVERNA_T2FLOW_XML);
// TODO: Add annotation recording the provenance of this history file
// PropertyResource original = new PropertyResource();
// original.setTypeURI(ENTITY);
// original.setResourceURI(URI.create("../../" + path));
// // TODO: dcterms:format ?
// // TODO: Add this annotation to the workflow bundle rather than main workflow
// bundle.getMainWorkflow().getCurrentRevision().addProperty(HAD_ORIGINAL_SOURCE, original);
}
protected String bundleUUID(WorkflowBundle bundle) {
String uuidPath = uriTools.relativePath(WORKFLOW_BUNDLE_ROOT,
bundle.getGlobalBaseURI()).getPath();
UUID uuid = UUID
.fromString(uuidPath.substring(0, uuidPath.length() - 1));
return uuid.toString();
}
@Override
public WorkflowBundle readBundle(InputStream inputStream, String mediaType)
throws ReaderException, IOException {
DelayedCloseBufferedInputStream buffered = new DelayedCloseBufferedInputStream(
inputStream, PRESERVE_ORIGINAL_MAXIMUM_BYTES);
try {
buffered.mark(PRESERVE_ORIGINAL_MAXIMUM_BYTES);
WorkflowBundle bundle;
try {
bundle = getParser().parseT2Flow(buffered);
scufl2Tools.setParents(bundle);
} catch (JAXBException e) {
if (e.getCause() instanceof IOException) {
IOException ioException = (IOException) e.getCause();
throw ioException;
}
throw new ReaderException("Can't parse t2flow", e);
}
// Rewind and try to copy out the original t2flow
try {
buffered.reset();
} catch (IOException ex) {
// Too big workflow, can't preserve it.
// TODO: Store to Bundle first?
// FIXME: Can we close BufferedInputStream without closing
// inputStream from our parameter?
return bundle;
}
preserveOriginal(bundle, buffered);
return bundle;
} finally {
buffered.reallyClose();
}
}
public void setParser(T2FlowParser parser) {
this.parser = parser;
}
public T2FlowParser getParser() throws JAXBException {
if (parser == null)
parser = new T2FlowParser();
return parser;
}
@Override
public String guessMediaTypeForSignature(byte[] firstBytes) {
if (firstBytes.length < 100)
return null;
// FIXME: Does not deal with potential UTF-16 encoding
// Latin 1 can deal with nasty bytes in binaries
Charset latin1 = Charset.forName("ISO-8859-1");
String asLatin1 = new String(firstBytes, latin1);
if (!asLatin1.contains("workflow"))
return null;
if (!asLatin1.contains("http://taverna.sf.net/2008/xml/t2flow"))
return null;
if (!asLatin1.contains("dataflow"))
return null;
// Good enough - XML is hard to check on so few bytes
return APPLICATION_VND_TAVERNA_T2FLOW_XML;
}
}