TAVERNA-1017 Use Commons RDF API instead of Jena
Project: http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/commit/a748e824 Tree: http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/tree/a748e824 Diff: http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/diff/a748e824 Branch: refs/heads/TAVERNA-1017 Commit: a748e824fb43616bedf60c299934744c8eec98e1 Parents: 3002424 Author: Stian Soiland-Reyes <[email protected]> Authored: Mon Oct 31 17:37:11 2016 +0000 Committer: Stian Soiland-Reyes <[email protected]> Committed: Mon Oct 31 17:37:37 2016 +0000 ---------------------------------------------------------------------- pom.xml | 21 ++- taverna-scufl2-annotation/pom.xml | 11 +- .../scufl2/annotation/AnnotationTools.java | 154 ++++++++++--------- taverna-scufl2-api/pom.xml | 5 + .../taverna/scufl2/api/common/URITools.java | 15 ++ 5 files changed, 126 insertions(+), 80 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/a748e824/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index d5acb9b..43132cb 100644 --- a/pom.xml +++ b/pom.xml @@ -28,10 +28,14 @@ <version>0.16.0-incubating-SNAPSHOT</version> <packaging>pom</packaging> - <name>Apache Taverna Language APIs (Scufl2, Databundle)</name> + <name>Apache Taverna Language APIs (Scufl2, Databundle)</name> <description>Taverna Language API for workflow definitions (SCUFL2) and workflow inputs/outputs/run (DataBundle). </description> + <properties> + <!-- TODO: Use 0.3.0-incubating once it is released --> + <commons.rdf.version>0.4.0-incubating-SNAPSHOT</commons.rdf.version> + </properties> <build> <plugins> <plugin> @@ -252,11 +256,12 @@ <artifactId>commons-beanutils</artifactId> <version>${commons.beanutils.version}</version> </dependency> + </dependencies> </dependencyManagement> <dependencies> <!-- Common dependencies on jena-osgi, which requires some additional dependencies - to be a happy OSGi bundle --> + to be a happy OSGi bundle <dependency> <groupId>org.apache.jena</groupId> @@ -268,17 +273,23 @@ <artifactId>org.apache.servicemix.bundles.xerces</artifactId> <version>${servicemix.xerces.version}</version> </dependency> - <!-- JENA-1178 workaround: Upgrade jsonlld-java --> + --> + <!-- JENA-1178 workaround: Upgrade jsonlld-java <dependency> <groupId>com.github.jsonld-java</groupId> <artifactId>jsonld-java</artifactId> <version>${jsonldjava.version}</version> - </dependency> - <!-- Needed by Jena --> + </dependency>--> + <!-- Needed by Jena <dependency> <groupId>com.github.andrewoma.dexx</groupId> <artifactId>collection</artifactId> <version>${dexx.collection.version}</version> + </dependency>--> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-rdf-api</artifactId> + <version>${commons.rdf.version}</version> </dependency> </dependencies> <profiles> http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/a748e824/taverna-scufl2-annotation/pom.xml ---------------------------------------------------------------------- diff --git a/taverna-scufl2-annotation/pom.xml b/taverna-scufl2-annotation/pom.xml index b5d7ea2..23e2875 100644 --- a/taverna-scufl2-annotation/pom.xml +++ b/taverna-scufl2-annotation/pom.xml @@ -44,12 +44,11 @@ <version>${project.version}</version> <scope>test</scope> </dependency> - <dependency> - <groupId>org.apache.jena</groupId> - <artifactId>jena-osgi</artifactId> - <version>${jena.version}</version> - </dependency> - + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-rdf-jena</artifactId> + <version>${commons.rdf.version}</version> + </dependency> </dependencies> </project> http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/a748e824/taverna-scufl2-annotation/src/main/java/org/apache/taverna/scufl2/annotation/AnnotationTools.java ---------------------------------------------------------------------- diff --git a/taverna-scufl2-annotation/src/main/java/org/apache/taverna/scufl2/annotation/AnnotationTools.java b/taverna-scufl2-annotation/src/main/java/org/apache/taverna/scufl2/annotation/AnnotationTools.java index 9e8a157..4a5de12 100644 --- a/taverna-scufl2-annotation/src/main/java/org/apache/taverna/scufl2/annotation/AnnotationTools.java +++ b/taverna-scufl2-annotation/src/main/java/org/apache/taverna/scufl2/annotation/AnnotationTools.java @@ -26,12 +26,19 @@ import java.io.InputStream; import java.net.URI; import java.util.Calendar; import java.util.GregorianCalendar; -import java.util.Iterator; +import java.util.Optional; +import java.util.concurrent.ExecutionException; import java.util.logging.Logger; -import org.apache.jena.riot.Lang; -import org.apache.jena.riot.RDFDataMgr; -import org.apache.jena.riot.RDFLanguages; +import org.apache.commons.rdf.api.Dataset; +import org.apache.commons.rdf.api.Graph; +import org.apache.commons.rdf.api.IRI; +import org.apache.commons.rdf.api.Literal; +import org.apache.commons.rdf.api.Quad; +import org.apache.commons.rdf.jena.JenaIRI; +import org.apache.commons.rdf.jena.JenaLiteral; +import org.apache.commons.rdf.jena.JenaRDF; +import org.apache.commons.rdf.jena.experimental.JenaRDFParser; import org.apache.taverna.scufl2.api.annotation.Annotation; import org.apache.taverna.scufl2.api.common.Child; import org.apache.taverna.scufl2.api.common.Scufl2Tools; @@ -39,24 +46,14 @@ import org.apache.taverna.scufl2.api.common.URITools; import org.apache.taverna.scufl2.api.container.WorkflowBundle; import org.apache.taverna.scufl2.ucfpackage.UCFPackage.ResourceEntry; - -import org.apache.jena.graph.Node; -import org.apache.jena.graph.NodeFactory; -import org.apache.jena.query.Dataset; -import org.apache.jena.query.DatasetFactory; -import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.ModelFactory; -import org.apache.jena.sparql.core.Quad; - public class AnnotationTools { - private static final String EXAMPLE_DATA_PREDICATE = "http://biocatalogue.org/attribute/exampleData"; - public static final URI EXAMPLE_DATA = URI.create(EXAMPLE_DATA_PREDICATE); - private static final String TITLE_PREDICATE = "http://purl.org/dc/terms/title"; - public static final URI TITLE = URI.create(TITLE_PREDICATE); - private static final String DESCRIPTION_PREDICATE = "http://purl.org/dc/terms/description"; - public static final URI DESCRIPTION = URI.create(DESCRIPTION_PREDICATE); - private static final String CREATOR_PREDICATE = "http://purl.org/dc/elements/1.1/creator"; - public static final URI CREATOR = URI.create(CREATOR_PREDICATE); + + private static JenaRDF rdf = new JenaRDF(); + + public static final IRI EXAMPLE_DATA = rdf.createIRI("http://biocatalogue.org/attribute/exampleData"); + public static final IRI TITLE = rdf.createIRI("http://purl.org/dc/terms/title"); + public static final IRI DESCRIPTION = rdf.createIRI("http://purl.org/dc/terms/description"); + public static final IRI CREATOR = rdf.createIRI("http://purl.org/dc/elements/1.1/creator"); private static Logger logger = Logger.getLogger(AnnotationTools.class .getCanonicalName()); @@ -65,7 +62,7 @@ public class AnnotationTools { private URITools uritools = new URITools(); public Dataset annotationDatasetFor(Child<?> workflowBean) { - Dataset dataset = DatasetFactory.createMem(); + Dataset dataset = rdf.createDataset(); for (Annotation ann : scufl2Tools.annotationsFor(workflowBean)) { WorkflowBundle bundle = ann.getParent(); URI annUri = uritools.uriForBean(ann); @@ -87,57 +84,79 @@ public class AnnotationTools { continue; } String contentType = resourceEntry.getMediaType(); - Lang lang = RDFLanguages.contentTypeToLang(contentType); - if (lang == null) { - lang = RDFLanguages.filenameToLang(path); - } - if (lang == null) { - logger.warning("Can't find media type of annotation body: " - + ann.getBody()); - continue; - } - Model model = ModelFactory.createDefaultModel(); + try (InputStream inStream = bundle.getResources() .getResourceAsInputStream(path)) { - RDFDataMgr.read(model, inStream, bodyUri, lang); + + Optional<Graph> graph = graphForAnnotation(dataset, annUri); + + + JenaRDFParser parser = new JenaRDFParser() + .base(bodyUri).source(inStream) + .contentType(contentType) + .target(graph.get()); + // TODO: Do multiple parsings in one go to speed up outer + // for-loop? Would need thread-safe Dataset backed by say TDB in memory + try { + parser.parse().get(); + } catch (IllegalStateException | InterruptedException | ExecutionException e) { + logger.warning("Can't parse annotation body: " + path); + continue; + } } catch (IOException e) { logger.warning("Can't read annotation body: " + path); continue; } - dataset.addNamedModel(annUri.toString(), model); } return dataset; } + private Optional<Graph> graphForAnnotation(Dataset dataset, URI annUri) { + IRI graphUri = uritools.asIRI(annUri); + Optional<Graph> graph = dataset.getGraph(graphUri); + + if (! graph.isPresent()) { + // Need a dummy quad first? + JenaIRI example = rdf.createIRI("http://example.com/"); + dataset.add(graphUri, example, example, example); + graph = dataset.getGraph(graphUri); + if (! graph.isPresent()) { + logger.severe("Can't create named graph: " + graphUri.getIRIString()); + } + // Remove dummy triple :) This will crash if the above can't create graph. + graph.get().remove(example, example, example); + } + + return graph; + } + public String getTitle(Child<?> workflowBean) { - return getLiteral(workflowBean, TITLE_PREDICATE); + return getLiteral(workflowBean, TITLE).orElse(null); } - private String getLiteral(Child<?> workflowBean, String propertyUri) { + private Optional<String> getLiteral(Child<?> workflowBean, IRI property) { + // TODO: Cache dataset PARSING! Dataset annotations = annotationDatasetFor(workflowBean); - URI beanUri = uritools.uriForBean(workflowBean); - Node subject = NodeFactory.createURI(beanUri.toString()); - Node property = NodeFactory.createURI(propertyUri); - - Iterator<Quad> found = annotations.asDatasetGraph().find(null, subject, - property, null); - if (!found.hasNext()) { - return null; - } - return found.next().getObject().toString(false); + IRI beanIRI = uritools.asIRI(uritools.uriForBean(workflowBean)); + return annotations.stream(null, beanIRI, property, null).map(Quad::getObject) + // Pick any Literal property value, if it exist + .filter(Literal.class::isInstance).map(Literal.class::cast) + .map(Literal::getLexicalForm).findAny(); } public String getCreator(Child<?> workflowBean) { - return getLiteral(workflowBean, CREATOR_PREDICATE); + // TODO: Also support dcterms:creator and foaf:name ? + return getLiteral(workflowBean, CREATOR).orElse(null); } public String getExampleValue(Child<?> workflowBean) { - return getLiteral(workflowBean, EXAMPLE_DATA_PREDICATE); + // TODO: Also support example value as a path? + return getLiteral(workflowBean, EXAMPLE_DATA).orElse(null); } - public String getDescription(Child<?> workflowBean) { - return getLiteral(workflowBean, DESCRIPTION_PREDICATE); + public String getDescription(Child<?> workflowBean) { + return getLiteral(workflowBean, DESCRIPTION).orElse(null); } /** @@ -150,7 +169,7 @@ public class AnnotationTools { * @throws IOException */ public Annotation createNewAnnotation(WorkflowBundle workflowBundle, - Child<?> subject, URI predicate, String value) throws IOException { + Child<?> subject, IRI predicate, String value) throws IOException { Object parent = subject.getParent(); while (parent instanceof Child) parent = ((Child<?>) parent).getParent(); @@ -176,25 +195,22 @@ public class AnnotationTools { annotation.setAnnotatedAt(now); // annotation.setAnnotator();//FIXME annotation.setSerializedAt(now); - URI annotatedSubject = uritools.relativeUriForBean(subject, annotation); + IRI annotatedSubject = uritools.asIRI(uritools.relativeUriForBean(subject, annotation)); + + StringBuilder turtle = new StringBuilder(); - turtle.append("<"); - turtle.append(annotatedSubject.toASCIIString()); - turtle.append("> "); - - turtle.append("<"); - turtle.append(predicate.toASCIIString()); - turtle.append("> "); - - // A potentially multi-line string - turtle.append("\"\"\""); - // Escape existing \ to \\ - String escaped = value.replace("\\", "\\\\"); - // Escape existing " to \" (beware Java's escaping of \ and " below) - escaped = escaped.replace("\"", "\\\""); - turtle.append(escaped); - turtle.append("\"\"\""); - turtle.append(" ."); + turtle.append(annotatedSubject.ntriplesString()); + + turtle.append(" "); + turtle.append(predicate.ntriplesString()); + + turtle.append(" "); + JenaLiteral literal = rdf.createLiteral(value); + turtle.append(literal.ntriplesString()); + + turtle.append(" .\n"); + + // TODO: Save with Jena instead try { workflowBundle.getResources().addResource(turtle.toString(), path, "text/turtle"); http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/a748e824/taverna-scufl2-api/pom.xml ---------------------------------------------------------------------- diff --git a/taverna-scufl2-api/pom.xml b/taverna-scufl2-api/pom.xml index 4c0052a..a7ea442 100644 --- a/taverna-scufl2-api/pom.xml +++ b/taverna-scufl2-api/pom.xml @@ -50,6 +50,11 @@ <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-rdf-simple</artifactId> + <version>${commons.rdf.version}</version> + </dependency> </dependencies> <build> <plugins> http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/a748e824/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/api/common/URITools.java ---------------------------------------------------------------------- diff --git a/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/api/common/URITools.java b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/api/common/URITools.java index 85d96ac..dff04b4 100644 --- a/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/api/common/URITools.java +++ b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/api/common/URITools.java @@ -27,6 +27,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.apache.commons.rdf.api.IRI; +import org.apache.commons.rdf.api.RDF; +import org.apache.commons.rdf.simple.SimpleRDF; import org.apache.taverna.scufl2.api.annotation.Annotation; import org.apache.taverna.scufl2.api.annotation.Revision; import org.apache.taverna.scufl2.api.common.Visitor.VisitorWithPath; @@ -53,6 +56,18 @@ public class URITools { private static final String DATALINK = "datalink"; private static final URI DOT = URI.create("."); + private RDF rdf = new SimpleRDF(); + + public IRI asIRI(URI uri) { + // TODO: Handle internationalization? + return rdf.createIRI(uri.toString()); + } + + public URI asURI(IRI iri) { + // TODO: Handle internationalization? + return URI.create(iri.getIRIString()); + } + public URI relativePath(URI base, URI uri) { URI root = base.resolve("/"); if (!root.equals(uri.resolve("/")))
