You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2017/10/30 14:57:10 UTC

[10/22] commons-rdf git commit: Module names, directory names, and artifact names should match.

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/ConvertedStatements.java
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/ConvertedStatements.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/ConvertedStatements.java
new file mode 100644
index 0000000..6d8cd57
--- /dev/null
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/ConvertedStatements.java
@@ -0,0 +1,71 @@
+/**
+ * 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.commons.rdf.rdf4j.impl;
+
+import java.util.Iterator;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import org.apache.commons.rdf.rdf4j.ClosableIterable;
+import org.eclipse.rdf4j.model.Resource;
+import org.eclipse.rdf4j.model.Statement;
+import org.eclipse.rdf4j.model.Value;
+import org.eclipse.rdf4j.repository.RepositoryConnection;
+import org.eclipse.rdf4j.repository.RepositoryResult;
+
+final class ConvertedStatements<T> implements ClosableIterable<T> {
+
+    private final RepositoryConnection conn;
+    private final RepositoryResult<Statement> results;
+    private final Function<Statement, T> statementAdapter;
+
+    ConvertedStatements(final Supplier<RepositoryConnection> repositoryConnector, final Function<Statement, T> statementAdapter,
+            final Resource subj, final org.eclipse.rdf4j.model.IRI pred, final Value obj, final Resource... contexts) {
+        this.statementAdapter = statementAdapter;
+        this.conn = repositoryConnector.get();
+        this.results = conn.getStatements(subj, pred, obj, contexts);
+    }
+
+    @Override
+    public Iterator<T> iterator() {
+        return new ConvertedIterator();
+    }
+
+    @Override
+    public void close() {
+        results.close();
+        conn.close();
+    }
+
+    private final class ConvertedIterator implements Iterator<T> {
+        @Override
+        public boolean hasNext() {
+            final boolean hasNext = results.hasNext();
+            if (!hasNext) {
+                close();
+            }
+            return hasNext;
+        }
+
+        @Override
+        public T next() {
+            return statementAdapter.apply(results.next());
+        }
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/IRIImpl.java
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/IRIImpl.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/IRIImpl.java
new file mode 100644
index 0000000..e2522d6
--- /dev/null
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/IRIImpl.java
@@ -0,0 +1,65 @@
+/**
+ * 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.commons.rdf.rdf4j.impl;
+
+import org.apache.commons.rdf.rdf4j.RDF4JIRI;
+
+final class IRIImpl extends AbstractRDFTerm<org.eclipse.rdf4j.model.IRI> implements RDF4JIRI {
+
+    IRIImpl(final org.eclipse.rdf4j.model.IRI iri) {
+        super(iri);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (obj instanceof IRIImpl) {
+            final IRIImpl impl = (IRIImpl) obj;
+            return asValue().equals(impl.asValue());
+        }
+        if (obj instanceof org.apache.commons.rdf.api.IRI) {
+            final org.apache.commons.rdf.api.IRI iri = (org.apache.commons.rdf.api.IRI) obj;
+            return value.toString().equals(iri.getIRIString());
+        }
+        return false;
+    }
+
+    @Override
+    public String getIRIString() {
+        return value.toString();
+    }
+
+    @Override
+    public int hashCode() {
+        // Same definition
+        return value.hashCode();
+    }
+
+    @Override
+    public String ntriplesString() {
+        return "<" + value.toString() + ">";
+    }
+
+    @Override
+    public String toString() {
+        return value.toString();
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/InternalRDF4JFactory.java
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/InternalRDF4JFactory.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/InternalRDF4JFactory.java
new file mode 100644
index 0000000..f344bf1
--- /dev/null
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/InternalRDF4JFactory.java
@@ -0,0 +1,184 @@
+/**
+ * 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.commons.rdf.rdf4j.impl;
+
+import java.util.UUID;
+
+import org.apache.commons.rdf.api.BlankNode;
+import org.apache.commons.rdf.rdf4j.RDF4JBlankNode;
+import org.apache.commons.rdf.rdf4j.RDF4JDataset;
+import org.apache.commons.rdf.rdf4j.RDF4JGraph;
+import org.apache.commons.rdf.rdf4j.RDF4JIRI;
+import org.apache.commons.rdf.rdf4j.RDF4JLiteral;
+import org.apache.commons.rdf.rdf4j.RDF4JQuad;
+import org.apache.commons.rdf.rdf4j.RDF4JTerm;
+import org.apache.commons.rdf.rdf4j.RDF4J;
+import org.apache.commons.rdf.rdf4j.RDF4J.Option;
+import org.apache.commons.rdf.rdf4j.RDF4JTriple;
+import org.eclipse.rdf4j.model.BNode;
+import org.eclipse.rdf4j.model.IRI;
+import org.eclipse.rdf4j.model.Literal;
+import org.eclipse.rdf4j.model.Model;
+import org.eclipse.rdf4j.model.Resource;
+import org.eclipse.rdf4j.model.Statement;
+import org.eclipse.rdf4j.repository.Repository;
+
+/**
+ * Factory for {@link RDF4JTerm} instances.
+ * <p>
+ * <strong>Internal class:</strong> This "abstract" class is intended for
+ * internal use by Commons RDF and may change in any minor update. Use instead
+ * {@link RDF4J} methods like {@link RDF4J#createBlankNode()},
+ * {@link RDF4J#asRDFTerm(org.eclipse.rdf4j.model.Value)} and
+ * {@link RDF4J#asGraph(Repository, Option...)}
+ * <p>
+ * This class exists as a <code>public</code> bridge between the packages
+ * {@link org.apache.commons.rdf.rdf4j} and
+ * {@link org.apache.commons.rdf.rdf4j.impl} by exposing the package-public
+ * constructors.
+ * 
+ * @see RDF4J
+ */
+public abstract class InternalRDF4JFactory {
+
+    /**
+     * Construct a {@link RDF4JBlankNode} from a RDF4J {@link BNode}.
+     * 
+     * @param bNode
+     *            RDF4J {@link BNode} to adapt
+     * @param salt
+     *            {@link UUID} to use for {@link BlankNode#uniqueReference()} in
+     *            combination with {@link BNode#getID()}
+     * @return Adapted {@link RDF4JBlankNode}
+     */
+    public RDF4JBlankNode createBlankNodeImpl(final BNode bNode, final UUID salt) {
+        return new BlankNodeImpl(bNode, salt);
+    }
+
+    /**
+     * Construct a {@link RDF4JIRI} from a RDF4J {@link IRI}.
+     * 
+     * @param iri
+     *            RDF4J {@link IRI} to adapt
+     * @return Adapted {@link RDF4JIRI}
+     */
+    public RDF4JIRI createIRIImpl(final IRI iri) {
+        return new IRIImpl(iri);
+    }
+
+    /**
+     * Construct a {@link RDF4JLiteral} from a RDF4J {@link Literal}.
+     * 
+     * @param literal
+     *            RDF4J {@link Literal}
+     * @return Adapted {@link RDF4JLiteral}
+     */
+    public RDF4JLiteral createLiteralImpl(final Literal literal) {
+        return new LiteralImpl(literal);
+    }
+
+    /**
+     * Construct a {@link RDF4JGraph} from a RDF4J {@link Model}.
+     * <p>
+     * Changes in the graph will be reflected in the model, and vice versa.
+     * 
+     * @param model
+     *            RDF4J {@link Model} to adapt
+     * @param rdf4jTermFactory
+     *            factory to use for adapting graph triples
+     * @return Adapted {@link RDF4JGraph}
+     */
+    public RDF4JGraph createModelGraphImpl(final Model model, final RDF4J rdf4jTermFactory) {
+        return new ModelGraphImpl(model, rdf4jTermFactory);
+    }
+
+    /**
+     * Construct a {@link RDF4JQuad} from a RDF4J {@link Statement}.
+     * 
+     * @param statement
+     *            RDF4J {@link Statement} to adapt
+     * @param salt
+     *            {@link UUID} for adapting any {@link BNode}s
+     * @return Adapted {@link RDF4JQuad}
+     */
+    public RDF4JQuad createQuadImpl(final Statement statement, final UUID salt) {
+        return new QuadImpl(statement, salt);
+    }
+
+    /**
+     * Construct a {@link RDF4JDataset} from a RDF4J {@link Repository}.
+     * <p>
+     * Changes in the dataset will be reflected in the repsitory, and vice
+     * versa.
+     * 
+     * @param repository
+     *            RDF4J {@link Repository} to adapt
+     * @param handleInitAndShutdown
+     *            If <code>true</code>, the {@link RDF4JDataset} will initialize
+     *            the repository (if needed), and shut it down on
+     *            {@link RDF4JDataset#close()}.
+     * @param includeInferred
+     *            If true, any inferred quads are included in the dataset
+     * 
+     * @return Adapted {@link RDF4JDataset}
+     */
+    public RDF4JDataset createRepositoryDatasetImpl(final Repository repository, final boolean handleInitAndShutdown,
+            final boolean includeInferred) {
+        return new RepositoryDatasetImpl(repository, UUID.randomUUID(), handleInitAndShutdown, includeInferred);
+    }
+
+    /**
+     * Construct a {@link RDF4JGraph} from a RDF4J {@link Model}.
+     * <p>
+     * Changes in the graph will be reflected in the model, and vice versa.
+     * 
+     * @param repository
+     *            RDF4J {@link Repository} to adapt
+     * @param handleInitAndShutdown
+     *            If <code>true</code>, the {@link RDF4JGraph} will initialize
+     *            the repository (if needed), and shut it down on
+     *            {@link RDF4JGraph#close()}.
+     * @param includeInferred
+     *            If true, any inferred quads are included in the dataset
+     * @param contextMask
+     *            Zero or more {@link Resource}s contexts. The array may contain
+     *            the value <code>null</code> for the default graph - however
+     *            care must be taken to not provide a null-array
+     *            <code>(Resource[]) null</code>.
+     * @return Adapted {@link RDF4JGraph}
+     */
+    public RDF4JGraph createRepositoryGraphImpl(final Repository repository, final boolean handleInitAndShutdown,
+            final boolean includeInferred, final Resource... contextMask) {
+        return new RepositoryGraphImpl(repository, UUID.randomUUID(), handleInitAndShutdown, includeInferred,
+                contextMask);
+    }
+
+    /**
+     * Construct a {@link RDF4JTriple} from a RDF4J {@link Statement}.
+     * 
+     * @param statement
+     *            RDF4J {@link Statement} to adapt
+     * @param salt
+     *            {@link UUID} for adapting any {@link BNode}s
+     * @return Adapted {@link RDF4JTriple}
+     */
+    public RDF4JTriple createTripleImpl(final Statement statement, final UUID salt) {
+        return new TripleImpl(statement, salt);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/LiteralImpl.java
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/LiteralImpl.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/LiteralImpl.java
new file mode 100644
index 0000000..253b645
--- /dev/null
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/LiteralImpl.java
@@ -0,0 +1,93 @@
+/**
+ * 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.commons.rdf.rdf4j.impl;
+
+import java.util.Locale;
+import java.util.Objects;
+import java.util.Optional;
+
+import org.apache.commons.rdf.rdf4j.RDF4JLiteral;
+import org.eclipse.rdf4j.model.vocabulary.XMLSchema;
+import org.eclipse.rdf4j.rio.turtle.TurtleUtil;
+
+final class LiteralImpl extends AbstractRDFTerm<org.eclipse.rdf4j.model.Literal> implements RDF4JLiteral {
+
+    private static final String QUOTE = "\"";
+
+    LiteralImpl(final org.eclipse.rdf4j.model.Literal literal) {
+        super(literal);
+    }
+
+    private static String lowerCase(String langTag) { 
+        return langTag.toLowerCase(Locale.ROOT);
+    }
+    
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (obj instanceof org.apache.commons.rdf.api.Literal) {
+            final org.apache.commons.rdf.api.Literal other = (org.apache.commons.rdf.api.Literal) obj;
+            return getLexicalForm().equals(other.getLexicalForm()) && 
+                    getDatatype().equals(other.getDatatype()) &&
+                    getLanguageTag().map(LiteralImpl::lowerCase).equals(
+                            other.getLanguageTag().map(LiteralImpl::lowerCase));
+        }
+        return false;
+    }
+
+    @Override
+    public org.apache.commons.rdf.api.IRI getDatatype() {
+        return new IRIImpl(value.getDatatype());
+    }
+
+    @Override
+    public Optional<String> getLanguageTag() {
+        return value.getLanguage();
+    }
+
+    @Override
+    public String getLexicalForm() {
+        return value.getLabel();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(value.getLabel(), value.getDatatype(), 
+                getLanguageTag().map(LiteralImpl::lowerCase));
+    }
+
+    @Override
+    public String ntriplesString() {
+        // TODO: Use a more efficient StringBuffer
+        final String escaped = QUOTE + TurtleUtil.encodeString(value.getLabel()) + QUOTE;
+        if (value.getLanguage().isPresent()) {
+            return escaped + "@" + value.getLanguage().get();
+        }
+        if (value.getDatatype().equals(XMLSchema.STRING)) {
+            return escaped;
+        }
+        return escaped + "^^<" + TurtleUtil.encodeURIString(value.getDatatype().toString()) + ">";
+    }
+
+    @Override
+    public String toString() {
+        return ntriplesString();
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/ModelGraphImpl.java
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/ModelGraphImpl.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/ModelGraphImpl.java
new file mode 100644
index 0000000..f8753fb
--- /dev/null
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/ModelGraphImpl.java
@@ -0,0 +1,153 @@
+/**
+ * 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.commons.rdf.rdf4j.impl;
+
+import java.util.Collections;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import org.apache.commons.rdf.api.BlankNodeOrIRI;
+import org.apache.commons.rdf.api.IRI;
+import org.apache.commons.rdf.api.RDFTerm;
+import org.apache.commons.rdf.api.Triple;
+import org.apache.commons.rdf.rdf4j.ClosableIterable;
+import org.apache.commons.rdf.rdf4j.RDF4JBlankNodeOrIRI;
+import org.apache.commons.rdf.rdf4j.RDF4JGraph;
+import org.apache.commons.rdf.rdf4j.RDF4J;
+import org.apache.commons.rdf.rdf4j.RDF4JTriple;
+import org.eclipse.rdf4j.model.Model;
+import org.eclipse.rdf4j.model.Resource;
+import org.eclipse.rdf4j.repository.Repository;
+
+final class ModelGraphImpl implements RDF4JGraph {
+
+    private final Model model;
+    private final RDF4J rdf4jTermFactory;
+
+    ModelGraphImpl(final Model model, final RDF4J rdf4jTermFactory) {
+        this.model = model;
+        this.rdf4jTermFactory = rdf4jTermFactory;
+    }
+
+    @Override
+    public void add(final BlankNodeOrIRI subject, final org.apache.commons.rdf.api.IRI predicate, final RDFTerm object) {
+        model.add((Resource) rdf4jTermFactory.asValue(subject),
+                (org.eclipse.rdf4j.model.IRI) rdf4jTermFactory.asValue(predicate), rdf4jTermFactory.asValue(object));
+    }
+
+    @Override
+    public void add(final Triple triple) {
+        model.add(rdf4jTermFactory.asStatement(triple));
+    }
+
+    @Override
+    public Optional<Model> asModel() {
+        return Optional.of(model);
+    }
+
+    @Override
+    public Optional<Repository> asRepository() {
+        return Optional.empty();
+    }
+
+    @Override
+    public void clear() {
+        model.clear();
+    }
+
+    @Override
+    public boolean contains(final BlankNodeOrIRI subject, final org.apache.commons.rdf.api.IRI predicate, final RDFTerm object) {
+        return model.contains((Resource) rdf4jTermFactory.asValue(subject),
+                (org.eclipse.rdf4j.model.IRI) rdf4jTermFactory.asValue(predicate), rdf4jTermFactory.asValue(object));
+    }
+
+    @Override
+    public boolean contains(final Triple triple) {
+        return model.contains(rdf4jTermFactory.asStatement(triple));
+    }
+
+    @Override
+    public void remove(final BlankNodeOrIRI subject, final org.apache.commons.rdf.api.IRI predicate, final RDFTerm object) {
+        model.remove((Resource) rdf4jTermFactory.asValue(subject),
+                (org.eclipse.rdf4j.model.IRI) rdf4jTermFactory.asValue(predicate), rdf4jTermFactory.asValue(object));
+    }
+
+    @Override
+    public void remove(final Triple triple) {
+        model.remove(rdf4jTermFactory.asStatement(triple));
+    }
+
+    @Override
+    public long size() {
+        final int size = model.size();
+        if (size < Integer.MAX_VALUE) {
+            return size;
+        } else {
+            // TODO: Check if this can really happen with RDF4J models
+            // Collection.size() can't help us, we'll have to count
+            return model.parallelStream().count();
+        }
+    }
+
+    @Override
+    public Stream<RDF4JTriple> stream() {
+        return model.parallelStream().map(rdf4jTermFactory::asTriple);
+    }
+
+    @Override
+    public Stream<RDF4JTriple> stream(final BlankNodeOrIRI subject, final org.apache.commons.rdf.api.IRI predicate,
+            final RDFTerm object) {
+        return model.filter((Resource) rdf4jTermFactory.asValue(subject),
+                (org.eclipse.rdf4j.model.IRI) rdf4jTermFactory.asValue(predicate), rdf4jTermFactory.asValue(object))
+                .parallelStream().map(rdf4jTermFactory::asTriple);
+    }
+
+    @Override
+    public Set<RDF4JBlankNodeOrIRI> getContextMask() {
+        // ModelGraph always do the unionGraph
+        return Collections.emptySet();
+        // TODO: Should we support contextMask like in RepositoryGraphImpl?
+    }
+
+    @Override
+    public ClosableIterable<Triple> iterate(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
+        return new ClosableIterable<Triple>() {
+            @SuppressWarnings("unchecked")
+            @Override
+            public Iterator<Triple> iterator() {
+                // double-cast to fight Java generics..
+                final Stream<? extends Triple> s = stream(subject, predicate, object);
+                return (Iterator<Triple>) s.iterator();
+            }
+
+            @Override
+            public void close() throws Exception {
+                // no-op as Model don't have transaction
+            }
+        };
+    }
+
+    @Override
+    public ClosableIterable<Triple> iterate() throws ConcurrentModificationException, IllegalStateException {
+        return iterate(null, null, null);
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/QuadImpl.java
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/QuadImpl.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/QuadImpl.java
new file mode 100644
index 0000000..1adc1e5
--- /dev/null
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/QuadImpl.java
@@ -0,0 +1,100 @@
+/**
+ * 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.commons.rdf.rdf4j.impl;
+
+import java.util.Objects;
+import java.util.Optional;
+import java.util.UUID;
+
+import org.apache.commons.rdf.api.BlankNodeOrIRI;
+import org.apache.commons.rdf.api.Quad;
+import org.apache.commons.rdf.api.RDFTerm;
+import org.apache.commons.rdf.api.Triple;
+import org.apache.commons.rdf.rdf4j.RDF4J;
+import org.apache.commons.rdf.rdf4j.RDF4JQuad;
+import org.eclipse.rdf4j.model.Statement;
+
+final class QuadImpl implements RDF4JQuad {
+    private transient int hashCode = 0;
+    private final UUID salt;
+    private final Statement statement;
+
+    QuadImpl(final Statement statement, final UUID salt) {
+        this.statement = statement;
+        this.salt = salt;
+    }
+
+    @Override
+    public Statement asStatement() {
+        return statement;
+    }
+
+    @Override
+    public Triple asTriple() {
+        return new TripleImpl(statement, salt);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj instanceof Quad) {
+            final Quad quad = (Quad) obj;
+            return getGraphName().equals(quad.getGraphName()) &&
+                    getSubject().equals(quad.getSubject()) && 
+                    getPredicate().equals(quad.getPredicate()) &&
+                    getObject().equals(quad.getObject());
+        }
+        return false;
+    }
+
+    @Override
+    public Optional<BlankNodeOrIRI> getGraphName() {
+        if (statement.getContext() == null) {
+            return Optional.empty();
+        }
+        final BlankNodeOrIRI g = (BlankNodeOrIRI) RDF4J.asRDFTerm(statement.getContext(), salt);
+        return Optional.of(g);
+    }
+
+    @Override
+    public RDFTerm getObject() {
+        return RDF4J.asRDFTerm(statement.getObject(), salt);
+    }
+
+    @Override
+    public org.apache.commons.rdf.api.IRI getPredicate() {
+        return (org.apache.commons.rdf.api.IRI) RDF4J.asRDFTerm(statement.getPredicate(), null);
+    }
+
+    @Override
+    public BlankNodeOrIRI getSubject() {
+        return (BlankNodeOrIRI) RDF4J.asRDFTerm(statement.getSubject(), salt);
+    }
+
+    @Override
+    public int hashCode() {
+        if (hashCode != 0) {
+            return hashCode;
+        }
+        return hashCode = Objects.hash(getSubject(), getPredicate(), getObject(), getGraphName());
+    }
+
+    @Override
+    public String toString() {
+        return statement.toString();
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/RepositoryDatasetImpl.java
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/RepositoryDatasetImpl.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/RepositoryDatasetImpl.java
new file mode 100644
index 0000000..6ce8b46
--- /dev/null
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/RepositoryDatasetImpl.java
@@ -0,0 +1,223 @@
+/**
+ * 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.commons.rdf.rdf4j.impl;
+
+import java.util.ConcurrentModificationException;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.stream.Stream;
+
+import org.apache.commons.rdf.api.BlankNodeOrIRI;
+import org.apache.commons.rdf.api.Graph;
+import org.apache.commons.rdf.api.IRI;
+import org.apache.commons.rdf.api.Quad;
+import org.apache.commons.rdf.api.RDFTerm;
+import org.apache.commons.rdf.rdf4j.ClosableIterable;
+import org.apache.commons.rdf.rdf4j.RDF4JDataset;
+import org.apache.commons.rdf.rdf4j.RDF4JQuad;
+import org.eclipse.rdf4j.common.iteration.Iterations;
+import org.eclipse.rdf4j.model.Resource;
+import org.eclipse.rdf4j.model.Statement;
+import org.eclipse.rdf4j.model.Value;
+import org.eclipse.rdf4j.repository.Repository;
+import org.eclipse.rdf4j.repository.RepositoryConnection;
+import org.eclipse.rdf4j.repository.RepositoryResult;
+
+class RepositoryDatasetImpl extends AbstractRepositoryGraphLike<Quad> implements RDF4JDataset {
+
+    RepositoryDatasetImpl(final Repository repository, final UUID salt, final boolean handleInitAndShutdown, final boolean includeInferred) {
+        super(repository, salt, handleInitAndShutdown, includeInferred);
+    }
+
+    @Override
+    public void add(final Quad tripleLike) {
+        final Statement statement = rdf4jTermFactory.asStatement(tripleLike);
+        try (RepositoryConnection conn = getRepositoryConnection()) {
+            conn.add(statement);
+            conn.commit();
+        }
+    }
+
+    @Override
+    public boolean contains(final Quad tripleLike) {
+        final Statement statement = rdf4jTermFactory.asStatement(tripleLike);
+        try (RepositoryConnection conn = getRepositoryConnection()) {
+            return conn.hasStatement(statement, includeInferred);
+        }
+    }
+
+    @Override
+    public void remove(final Quad tripleLike) {
+        final Statement statement = rdf4jTermFactory.asStatement(tripleLike);
+        try (RepositoryConnection conn = getRepositoryConnection()) {
+            conn.remove(statement);
+            conn.commit();
+        }
+    }
+
+    @Override
+    public void clear() {
+        try (RepositoryConnection conn = getRepositoryConnection()) {
+            conn.clear();
+            conn.commit();
+        }
+    }
+
+    @Override
+    public long size() {
+        if (includeInferred) {
+            // We'll need to count them all
+            return stream().count();
+        }
+        // else: Ask directly
+        try (RepositoryConnection conn = getRepositoryConnection()) {
+            return conn.size();
+        }
+    }
+
+    @Override
+    public void add(final BlankNodeOrIRI graphName, final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
+        final Resource context = (Resource) rdf4jTermFactory.asValue(graphName);
+        final Resource subj = (Resource) rdf4jTermFactory.asValue(subject);
+        final org.eclipse.rdf4j.model.IRI pred = (org.eclipse.rdf4j.model.IRI) rdf4jTermFactory.asValue(predicate);
+        final Value obj = rdf4jTermFactory.asValue(object);
+        try (RepositoryConnection conn = getRepositoryConnection()) {
+            conn.add(subj, pred, obj, context);
+            conn.commit();
+        }
+    }
+
+    @Override
+    public boolean contains(final Optional<BlankNodeOrIRI> graphName, final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
+        final Resource subj = (Resource) rdf4jTermFactory.asValue(subject);
+        final org.eclipse.rdf4j.model.IRI pred = (org.eclipse.rdf4j.model.IRI) rdf4jTermFactory.asValue(predicate);
+        final Value obj = rdf4jTermFactory.asValue(object);
+        final Resource[] contexts = asContexts(graphName);
+        try (RepositoryConnection conn = getRepositoryConnection()) {
+            return conn.hasStatement(subj, pred, obj, includeInferred, contexts);
+        }
+    }
+
+    private Resource[] asContexts(final Optional<BlankNodeOrIRI> graphName) {
+        Resource[] contexts;
+        if (graphName == null) {
+            // no contexts == any contexts
+            contexts = new Resource[0];
+        } else {
+            final BlankNodeOrIRI g = graphName.orElse(null);
+            final Resource context = (Resource) rdf4jTermFactory.asValue(g);
+            contexts = new Resource[] { context };
+        }
+        return contexts;
+    }
+
+    @Override
+    public void remove(final Optional<BlankNodeOrIRI> graphName, final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
+        final Resource subj = (Resource) rdf4jTermFactory.asValue(subject);
+        final org.eclipse.rdf4j.model.IRI pred = (org.eclipse.rdf4j.model.IRI) rdf4jTermFactory.asValue(predicate);
+        final Value obj = rdf4jTermFactory.asValue(object);
+        final Resource[] contexts = asContexts(graphName);
+
+        try (RepositoryConnection conn = getRepositoryConnection()) {
+            conn.remove(subj, pred, obj, contexts);
+            conn.commit();
+        }
+    }
+
+    @Override
+    public Stream<RDF4JQuad> stream() {
+        return stream(null, null, null, null);
+    }
+
+    @Override
+    public Stream<RDF4JQuad> stream(final Optional<BlankNodeOrIRI> graphName, final BlankNodeOrIRI subject, final IRI predicate,
+            final RDFTerm object) {
+        final Resource subj = (Resource) rdf4jTermFactory.asValue(subject);
+        final org.eclipse.rdf4j.model.IRI pred = (org.eclipse.rdf4j.model.IRI) rdf4jTermFactory.asValue(predicate);
+        final Value obj = rdf4jTermFactory.asValue(object);
+        final Resource[] contexts = asContexts(graphName);
+
+        // NOTE: We can't do the usual try..with closing of the
+        // RepositoryConnection here as it will have to be closed outside
+        // by the user of the returned stream
+        final RepositoryConnection conn = getRepositoryConnection();
+        Stream<RDF4JQuad> stream = null;
+        try {
+            final RepositoryResult<Statement> statements = conn.getStatements(subj, pred, obj, includeInferred, contexts);
+            // NOTE: Iterations.stream should close RepositoryResult as long as
+            // our caller closes the stream
+            stream = Iterations.stream(statements).map(rdf4jTermFactory::asQuad);
+        } finally {
+            if (stream == null) {
+                // Some exception before we made the stream, close connection
+                // here
+                conn.close();
+            }
+        }
+        // Make sure the RepositoryConnection is closed
+        return stream.onClose(conn::close);
+
+    }
+
+    @Override
+    public ClosableIterable<Quad> iterate() throws ConcurrentModificationException, IllegalStateException {
+        return iterate(null, null, null, null);
+    }
+
+    @Override
+    public ClosableIterable<Quad> iterate(final Optional<BlankNodeOrIRI> graphName, final BlankNodeOrIRI subject, final IRI predicate,
+            final RDFTerm object) throws ConcurrentModificationException, IllegalStateException {
+        final Resource[] contexts = asContexts(graphName);
+        final Resource subj = (Resource) rdf4jTermFactory.asValue(subject);
+        final org.eclipse.rdf4j.model.IRI pred = (org.eclipse.rdf4j.model.IRI) rdf4jTermFactory.asValue(predicate);
+        final Value obj = rdf4jTermFactory.asValue(object);
+        return new ConvertedStatements<>(this::getRepositoryConnection, rdf4jTermFactory::asQuad, subj, pred, obj,
+                contexts);
+    }
+
+    @Override
+    protected RDF4JQuad asTripleLike(final Statement s) {
+        return rdf4jTermFactory.asQuad(s);
+    }
+
+    @Override
+    public Graph getGraph() {
+        // default context only
+        // NOTE: We carry over the 'salt' as the graph's BlankNode should be
+        // equal to our BlankNodes
+        return new RepositoryGraphImpl(repository, salt, false, includeInferred, (Resource) null);
+    }
+
+    @Override
+    public Optional<Graph> getGraph(final BlankNodeOrIRI graphName) {
+        // NOTE: May be null to indicate default context
+        final Resource context = (Resource) rdf4jTermFactory.asValue(graphName);
+        // NOTE: We carry over the 'salt' as the graph's BlankNode should be
+        // equal to our BlankNodes
+        return Optional.of(new RepositoryGraphImpl(repository, salt, false, includeInferred, context));
+    }
+
+    @Override
+    public Stream<BlankNodeOrIRI> getGraphNames() {
+       final RepositoryConnection conn = getRepositoryConnection();
+       final RepositoryResult<Resource> contexts = conn.getContextIDs();
+        return Iterations.stream(contexts).map(g -> (BlankNodeOrIRI) rdf4jTermFactory.asRDFTerm(g))
+                .onClose(conn::close);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/RepositoryGraphImpl.java
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/RepositoryGraphImpl.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/RepositoryGraphImpl.java
new file mode 100644
index 0000000..06bcded
--- /dev/null
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/RepositoryGraphImpl.java
@@ -0,0 +1,194 @@
+/**
+ * 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.commons.rdf.rdf4j.impl;
+
+import java.util.Collections;
+import java.util.ConcurrentModificationException;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+import java.util.stream.Stream;
+
+import org.apache.commons.rdf.api.BlankNodeOrIRI;
+import org.apache.commons.rdf.api.IRI;
+import org.apache.commons.rdf.api.RDFTerm;
+import org.apache.commons.rdf.api.Triple;
+import org.apache.commons.rdf.rdf4j.ClosableIterable;
+import org.apache.commons.rdf.rdf4j.RDF4JBlankNodeOrIRI;
+import org.apache.commons.rdf.rdf4j.RDF4JGraph;
+import org.apache.commons.rdf.rdf4j.RDF4JTriple;
+import org.eclipse.rdf4j.common.iteration.Iterations;
+import org.eclipse.rdf4j.model.Resource;
+import org.eclipse.rdf4j.model.Statement;
+import org.eclipse.rdf4j.model.Value;
+import org.eclipse.rdf4j.repository.Repository;
+import org.eclipse.rdf4j.repository.RepositoryConnection;
+import org.eclipse.rdf4j.repository.RepositoryResult;
+
+class RepositoryGraphImpl extends AbstractRepositoryGraphLike<Triple> implements RDF4JGraph {
+
+    private final Resource[] contextMask;
+
+    RepositoryGraphImpl(final Repository repository, final UUID salt, final boolean handleInitAndShutdown, final boolean includeInferred,
+            final Resource... contextMask) {
+        super(repository, salt, handleInitAndShutdown, includeInferred);
+        this.contextMask = Objects.requireNonNull(contextMask);
+    }
+
+    @Override
+    public void add(final Triple tripleLike) {
+        final Statement statement = rdf4jTermFactory.asStatement(tripleLike);
+        try (RepositoryConnection conn = getRepositoryConnection()) {
+            conn.add(statement, contextMask);
+            conn.commit();
+        }
+    }
+
+    @Override
+    public boolean contains(final Triple tripleLike) {
+        final Statement statement = rdf4jTermFactory.asStatement(tripleLike);
+        try (RepositoryConnection conn = getRepositoryConnection()) {
+            return conn.hasStatement(statement, includeInferred, contextMask);
+        }
+    }
+
+    @Override
+    public void remove(final Triple tripleLike) {
+        final Statement statement = rdf4jTermFactory.asStatement(tripleLike);
+        try (RepositoryConnection conn = getRepositoryConnection()) {
+            conn.remove(statement, contextMask);
+            conn.commit();
+        }
+    }
+
+    @Override
+    public void clear() {
+        try (RepositoryConnection conn = getRepositoryConnection()) {
+            conn.clear(contextMask);
+            conn.commit();
+        }
+    }
+
+    @Override
+    public long size() {
+        if (!includeInferred && contextMask.length == 0) {
+            try (RepositoryConnection conn = getRepositoryConnection()) {
+                return conn.size();
+            }
+        } else {
+            try (Stream<RDF4JTriple> stream = stream()) {
+                return stream.count();
+            }
+        }
+    }
+
+    @Override
+    public void add(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
+        final Resource subj = (Resource) rdf4jTermFactory.asValue(subject);
+        final org.eclipse.rdf4j.model.IRI pred = (org.eclipse.rdf4j.model.IRI) rdf4jTermFactory.asValue(predicate);
+        final Value obj = rdf4jTermFactory.asValue(object);
+        try (RepositoryConnection conn = getRepositoryConnection()) {
+            conn.add(subj, pred, obj, contextMask);
+            conn.commit();
+        }
+    }
+
+    @Override
+    public boolean contains(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
+        final Resource subj = (Resource) rdf4jTermFactory.asValue(subject);
+        final org.eclipse.rdf4j.model.IRI pred = (org.eclipse.rdf4j.model.IRI) rdf4jTermFactory.asValue(predicate);
+        final Value obj = rdf4jTermFactory.asValue(object);
+        try (RepositoryConnection conn = getRepositoryConnection()) {
+            return conn.hasStatement(subj, pred, obj, includeInferred, contextMask);
+        }
+    }
+
+    @Override
+    public void remove(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
+        final Resource subj = (Resource) rdf4jTermFactory.asValue(subject);
+        final org.eclipse.rdf4j.model.IRI pred = (org.eclipse.rdf4j.model.IRI) rdf4jTermFactory.asValue(predicate);
+        final Value obj = rdf4jTermFactory.asValue(object);
+        try (RepositoryConnection conn = getRepositoryConnection()) {
+            conn.remove(subj, pred, obj, contextMask);
+            conn.commit();
+        }
+    }
+
+    @Override
+    public ClosableIterable<Triple> iterate() throws ConcurrentModificationException, IllegalStateException {
+        return iterate(null, null, null);
+    }
+
+    @Override
+    public ClosableIterable<Triple> iterate(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object)
+            throws ConcurrentModificationException, IllegalStateException {
+        final Resource subj = (Resource) rdf4jTermFactory.asValue(subject);
+        final org.eclipse.rdf4j.model.IRI pred = (org.eclipse.rdf4j.model.IRI) rdf4jTermFactory.asValue(predicate);
+        final Value obj = rdf4jTermFactory.asValue(object);
+        return new ConvertedStatements<>(this::getRepositoryConnection, rdf4jTermFactory::asTriple, subj, pred,
+                obj, contextMask);
+    }
+
+    @Override
+    public Stream<RDF4JTriple> stream() {
+        return stream(null, null, null);
+    }
+
+    @Override
+    public Stream<RDF4JTriple> stream(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
+        final Resource subj = (Resource) rdf4jTermFactory.asValue(subject);
+        final org.eclipse.rdf4j.model.IRI pred = (org.eclipse.rdf4j.model.IRI) rdf4jTermFactory.asValue(predicate);
+        final Value obj = rdf4jTermFactory.asValue(object);
+
+        // NOTE: We can't do the usual try..with closing of the
+        // RepositoryConnection here as it will have to be closed outside
+        // by the user of the returned stream
+        final RepositoryConnection conn = getRepositoryConnection();
+        Stream<RDF4JTriple> stream = null;
+        try {
+            final RepositoryResult<Statement> statements = conn.getStatements(subj, pred, obj, includeInferred, contextMask);
+            // NOTE: Iterations.stream should close RepositoryResult as long as
+            // our caller closes the stream
+            stream = Iterations.stream(statements).map(this::asTripleLike);
+        } finally {
+            if (stream == null) {
+                // Some exception before we made the stream, close connection
+                // here
+                conn.close();
+            }
+        }
+        // Make sure the RepositoryConnection is closed
+        return stream.onClose(conn::close);
+    }
+
+    @Override
+    protected RDF4JTriple asTripleLike(final Statement statement) {
+        return rdf4jTermFactory.asTriple(statement);
+    }
+
+    @Override
+    public Set<RDF4JBlankNodeOrIRI> getContextMask() {
+        final Set<RDF4JBlankNodeOrIRI> mask = new HashSet<>();
+        for (final Resource s : contextMask) {
+            mask.add((RDF4JBlankNodeOrIRI) rdf4jTermFactory.asRDFTerm(s));
+        }
+        return Collections.unmodifiableSet(mask);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/TripleImpl.java
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/TripleImpl.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/TripleImpl.java
new file mode 100644
index 0000000..c59e4ca
--- /dev/null
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/TripleImpl.java
@@ -0,0 +1,78 @@
+/**
+ * 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.commons.rdf.rdf4j.impl;
+
+import java.util.Objects;
+import java.util.UUID;
+
+import org.apache.commons.rdf.api.BlankNodeOrIRI;
+import org.apache.commons.rdf.api.RDFTerm;
+import org.apache.commons.rdf.api.Triple;
+import org.apache.commons.rdf.rdf4j.RDF4J;
+import org.apache.commons.rdf.rdf4j.RDF4JTriple;
+import org.eclipse.rdf4j.model.Statement;
+
+final class TripleImpl implements RDF4JTriple {
+    private final UUID salt;
+    private final Statement statement;
+
+    TripleImpl(final Statement statement, final UUID salt) {
+        this.statement = statement;
+        this.salt = salt;
+    }
+
+    @Override
+    public Statement asStatement() {
+        return statement;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj instanceof Triple) {
+            final Triple triple = (Triple) obj;
+            return getSubject().equals(triple.getSubject()) && getPredicate().equals(triple.getPredicate())
+                    && getObject().equals(triple.getObject());
+        }
+        return false;
+    }
+
+    @Override
+    public RDFTerm getObject() {
+        return RDF4J.asRDFTerm(statement.getObject(), salt);
+    }
+
+    @Override
+    public org.apache.commons.rdf.api.IRI getPredicate() {
+        return (org.apache.commons.rdf.api.IRI) RDF4J.asRDFTerm(statement.getPredicate(), null);
+    }
+
+    @Override
+    public BlankNodeOrIRI getSubject() {
+        return (BlankNodeOrIRI) RDF4J.asRDFTerm(statement.getSubject(), salt);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getSubject(), getPredicate(), getObject());
+    }
+
+    @Override
+    public String toString() {
+        return statement.toString();
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/package-info.java
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/package-info.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/package-info.java
new file mode 100644
index 0000000..e40e985
--- /dev/null
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/package-info.java
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+/**
+ * Commons RDF integration with <a href="http://rdf4j.org/">RDF4J</a>.
+ * <p>
+ * Use the {@link RDF4J} to convert between Commons RDF and RDF4J types, for
+ * instance {@link RDF4J#asQuad(org.eclipse.rdf4j.model.Statement)} converts a
+ * RDF4J {@link org.eclipse.rdf4j.model.Statement} to a
+ * {@link org.apache.commons.rdf.api.Quad}. Converted RDF terms implement the
+ * {@link RDF4JTerm} interface, and converted statements the
+ * {@link RDF4JTripleLike} interface, which provide convenience access to the
+ * underlying RDF4J implementations.
+ * <p>
+ * RDF4J {@link org.eclipse.rdf4j.model.Model}s and
+ * {@link org.eclipse.rdf4j.repository.Repository} instances can be adapted to
+ * Commons RDF {@link Graph} and {@link Dataset}, e.g. using
+ * {@link RDF4J#asGraph(org.eclipse.rdf4j.model.Model)} or
+ * {@link RDF4J#asDataset(org.eclipse.rdf4j.repository.Repository, RDF4J.Option...)}
+ * The returned adapted graph/dataset is directly mapped, so changes are
+ * propagated both ways. For convenience, the marker interface
+ * {@link RDF4JGraph} and {@link RDF4JDataset} provide access to the underlying
+ * RDF4J implementations.
+ * <p>
+ * The {@link RDF4JParser} can be used to parse RDF files using RDF4j. It should
+ * be most efficient if used with {@link RDF4JParser#target(Dataset)} and an
+ * adapted {@link RDF4JDataset}, or {@link RDF4JParser#target(Graph)} and a an
+ * adapted {@link RDF4JGraph}
+ * 
+ *
+ */
+package org.apache.commons.rdf.rdf4j;
+
+// Imports for Javadoc, do not remove
+import org.apache.commons.rdf.api.Dataset;
+import org.apache.commons.rdf.api.Graph;
+import org.apache.commons.rdf.rdf4j.*;
+import org.apache.commons.rdf.rdf4j.RDF4J.Option;
+import org.apache.commons.rdf.rdf4j.experimental.RDF4JParser;

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/main/resources/META-INF/services/org.apache.commons.rdf.api.RDF
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/main/resources/META-INF/services/org.apache.commons.rdf.api.RDF b/commons-rdf-rdf4j/src/main/resources/META-INF/services/org.apache.commons.rdf.api.RDF
new file mode 100644
index 0000000..f8a94bb
--- /dev/null
+++ b/commons-rdf-rdf4j/src/main/resources/META-INF/services/org.apache.commons.rdf.api.RDF
@@ -0,0 +1 @@
+org.apache.commons.rdf.rdf4j.RDF4J

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/site/resources/profile.jacoco
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/site/resources/profile.jacoco b/commons-rdf-rdf4j/src/site/resources/profile.jacoco
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/BlankNodeTest.java
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/BlankNodeTest.java b/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/BlankNodeTest.java
new file mode 100644
index 0000000..485330c
--- /dev/null
+++ b/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/BlankNodeTest.java
@@ -0,0 +1,37 @@
+/**
+ * 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.commons.rdf.rdf4j;
+
+import org.apache.commons.rdf.api.AbstractBlankNodeTest;
+import org.apache.commons.rdf.api.BlankNode;
+
+public class BlankNodeTest extends AbstractBlankNodeTest {
+
+    RDF4J factory = new RDF4J();
+
+    @Override
+    protected BlankNode getBlankNode() {
+        return factory.createBlankNode();
+    }
+
+    @Override
+    protected BlankNode getBlankNode(final String identifier) {
+        return factory.createBlankNode(identifier);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/DatasetTest.java
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/DatasetTest.java b/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/DatasetTest.java
new file mode 100644
index 0000000..6008bd0
--- /dev/null
+++ b/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/DatasetTest.java
@@ -0,0 +1,30 @@
+/**
+ * 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.commons.rdf.rdf4j;
+
+import org.apache.commons.rdf.api.AbstractDatasetTest;
+import org.apache.commons.rdf.api.RDF;
+
+public class DatasetTest extends AbstractDatasetTest {
+
+    @Override
+    public RDF createFactory() {
+        return new RDF4J();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/GraphTest.java
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/GraphTest.java b/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/GraphTest.java
new file mode 100644
index 0000000..81c77da
--- /dev/null
+++ b/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/GraphTest.java
@@ -0,0 +1,30 @@
+/**
+ * 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.commons.rdf.rdf4j;
+
+import org.apache.commons.rdf.api.AbstractGraphTest;
+import org.apache.commons.rdf.api.RDF;
+
+public class GraphTest extends AbstractGraphTest {
+
+    @Override
+    public RDF createFactory() {
+        return new RDF4J();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/MemoryGraphTest.java
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/MemoryGraphTest.java b/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/MemoryGraphTest.java
new file mode 100644
index 0000000..597c7d7
--- /dev/null
+++ b/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/MemoryGraphTest.java
@@ -0,0 +1,99 @@
+/**
+ * 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.commons.rdf.rdf4j;
+
+import org.apache.commons.rdf.api.AbstractGraphTest;
+import org.apache.commons.rdf.api.BlankNodeOrIRI;
+import org.apache.commons.rdf.api.Dataset;
+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.api.RDFTerm;
+import org.apache.commons.rdf.api.RDF;
+import org.eclipse.rdf4j.repository.Repository;
+import org.eclipse.rdf4j.repository.sail.SailRepository;
+import org.eclipse.rdf4j.sail.Sail;
+import org.eclipse.rdf4j.sail.memory.MemoryStore;
+import org.eclipse.rdf4j.sail.memory.model.MemValueFactory;
+
+public class MemoryGraphTest extends AbstractGraphTest {
+
+    public static final class MemoryStoreRDF implements RDF {
+
+        RDF4J rdf4jFactory = new RDF4J(new MemValueFactory());
+
+        @Override
+        public RDF4JBlankNode createBlankNode() {
+            return rdf4jFactory.createBlankNode();
+        }
+
+        @Override
+        public RDF4JBlankNode createBlankNode(final String name) {
+            return rdf4jFactory.createBlankNode(name);
+        }
+
+        @Override
+        public Dataset createDataset() {
+            return rdf4jFactory.createDataset();
+        }
+
+        @Override
+        public RDF4JIRI createIRI(final String iri) throws IllegalArgumentException, UnsupportedOperationException {
+            return rdf4jFactory.createIRI(iri);
+        }
+
+        @Override
+        public RDF4JLiteral createLiteral(final String lexicalForm) {
+            return rdf4jFactory.createLiteral(lexicalForm);
+        }
+
+        @Override
+        public Literal createLiteral(final String lexicalForm, final IRI dataType) {
+            return rdf4jFactory.createLiteral(lexicalForm, dataType);
+        }
+
+        @Override
+        public Literal createLiteral(final String lexicalForm, final String languageTag) {
+            return rdf4jFactory.createLiteral(lexicalForm, languageTag);
+        }
+
+        @Override
+        public RDF4JTriple createTriple(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
+            return rdf4jFactory.createTriple(subject, predicate, object);
+        }
+
+        @Override
+        public Quad createQuad(final BlankNodeOrIRI graphName, final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
+            return rdf4jFactory.createQuad(graphName, subject, predicate, object);
+        }
+
+        @Override
+        public RDF4JGraph createGraph() {
+            final Sail sail = new MemoryStore();
+            final Repository repository = new SailRepository(sail);
+            repository.initialize();
+            return rdf4jFactory.asGraph(repository);
+        }
+    }
+
+    @Override
+    public RDF createFactory() {
+        return new MemoryStoreRDF();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/MemoryStoreRDFTest.java
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/MemoryStoreRDFTest.java b/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/MemoryStoreRDFTest.java
new file mode 100644
index 0000000..5954d13
--- /dev/null
+++ b/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/MemoryStoreRDFTest.java
@@ -0,0 +1,37 @@
+/**
+ * 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.commons.rdf.rdf4j;
+
+import org.apache.commons.rdf.api.AbstractRDFTest;
+import org.apache.commons.rdf.api.RDF;
+import org.junit.Assume;
+
+public class MemoryStoreRDFTest extends AbstractRDFTest {
+
+    @Override
+    public RDF createFactory() {
+        return new MemoryGraphTest.MemoryStoreRDF();
+    }
+
+    @Override
+    public void testInvalidLiteralLang() throws Exception {
+        Assume.assumeTrue("RDF4J doesn't check Lang strings", false);
+        super.testInvalidLiteralLang();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/NativeStoreGraphTest.java
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/NativeStoreGraphTest.java b/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/NativeStoreGraphTest.java
new file mode 100644
index 0000000..dc146ff
--- /dev/null
+++ b/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/NativeStoreGraphTest.java
@@ -0,0 +1,182 @@
+/**
+ * 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.commons.rdf.rdf4j;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.util.Collections;
+import java.util.Set;
+
+import org.apache.commons.rdf.api.AbstractGraphTest;
+import org.apache.commons.rdf.api.BlankNodeOrIRI;
+import org.apache.commons.rdf.api.Dataset;
+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.api.RDFTerm;
+import org.apache.commons.rdf.api.RDF;
+import org.eclipse.rdf4j.repository.RepositoryConnection;
+import org.eclipse.rdf4j.repository.RepositoryResult;
+import org.eclipse.rdf4j.repository.sail.SailRepository;
+import org.eclipse.rdf4j.sail.Sail;
+import org.eclipse.rdf4j.sail.nativerdf.NativeStore;
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.rules.TemporaryFolder;
+import org.junit.rules.Timeout;
+
+/**
+ * Test a graph within a file-based RDF4J {@link SailRepository}.
+ * <p>
+ * TIP: If the {@link #shutdownAndDelete()} take about 20 seconds this is a hint
+ * that a {@link RepositoryConnection} or {@link RepositoryResult} was not
+ * closed correctly.
+ * 
+ */
+public class NativeStoreGraphTest extends AbstractGraphTest {
+
+    public final class NativeStoreRDF implements RDF {
+
+        RDF4J rdf4jFactory = new RDF4J(getRepository().getValueFactory());
+
+        @Override
+        public RDF4JGraph createGraph() {
+            // We re-use the repository connection, but use a different context
+            // every time
+            final Set<RDF4JBlankNode> context = Collections.singleton(rdf4jFactory.createBlankNode());
+            return rdf4jFactory.asGraph(getRepository(), context);
+        }
+
+        @Override
+        public Dataset createDataset() {
+            throw new UnsupportedOperationException("Can't create more than one Dataset in this test");
+            // ...as the below would re-use the same repository:
+            // return rdf4jFactory.asRDFTermDataset(getRepository());
+        }
+
+        // Delegate methods
+        @Override
+        public RDF4JBlankNode createBlankNode() {
+            return rdf4jFactory.createBlankNode();
+        }
+
+        @Override
+        public RDF4JBlankNode createBlankNode(final String name) {
+            return rdf4jFactory.createBlankNode(name);
+        }
+
+        @Override
+        public RDF4JIRI createIRI(final String iri) throws IllegalArgumentException, UnsupportedOperationException {
+            return rdf4jFactory.createIRI(iri);
+        }
+
+        @Override
+        public RDF4JLiteral createLiteral(final String lexicalForm) {
+            return rdf4jFactory.createLiteral(lexicalForm);
+        }
+
+        @Override
+        public Literal createLiteral(final String lexicalForm, final IRI dataType) {
+            return rdf4jFactory.createLiteral(lexicalForm, dataType);
+        }
+
+        @Override
+        public Literal createLiteral(final String lexicalForm, final String languageTag) {
+            return rdf4jFactory.createLiteral(lexicalForm, languageTag);
+        }
+
+        @Override
+        public RDF4JTriple createTriple(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
+            return rdf4jFactory.createTriple(subject, predicate, object);
+        }
+
+        @Override
+        public Quad createQuad(final BlankNodeOrIRI graphName, final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object)
+                throws IllegalArgumentException {
+            return rdf4jFactory.createQuad(graphName, subject, predicate, object);
+        }
+    }
+
+    @Rule
+    public TemporaryFolder tempDir = new TemporaryFolder();
+
+    private SailRepository repository;
+
+    public void createRepository() throws IOException {
+        final Sail sail = new NativeStore(tempDir.newFolder());
+        repository = new SailRepository(sail);
+        repository.initialize();
+    }
+
+    public synchronized SailRepository getRepository() {
+        if (repository == null) {
+            try {
+                createRepository();
+            } catch (final IOException e) {
+                throw new UncheckedIOException(e);
+            }
+        }
+        return repository;
+    }
+
+    @Rule
+    /**
+     * A timeout of more than 15 seconds pr test indicates typically that
+     * shutdownAndDelete failed.
+     */
+    public Timeout globalTimeout = Timeout.seconds(15);
+
+    @After
+    public void shutdownAndDelete() {
+        // must shutdown before we delete
+        if (repository != null) {
+            System.out.print("Shutting down rdf4j repository " + repository + "...");
+            // NOTE:
+            // If this takes about 20 seconds it means the code forgot to close
+            // a
+            // RepositoryConnection or RespositoryResult
+            // e.g. missing a try-with-resources block
+            repository.shutDown();
+            System.out.println("OK");
+        }
+    }
+
+    // private static void deleteAll(Path dir) throws IOException {
+    // Files.walkFileTree(dir, new SimpleFileVisitor<Path>(){
+    // @Override
+    // public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
+    // throws IOException {
+    // Files.delete(file);
+    // return FileVisitResult.CONTINUE;
+    // }
+    // @Override
+    // public FileVisitResult postVisitDirectory(Path dir, IOException exc)
+    // throws IOException {
+    // FileVisitResult r = super.postVisitDirectory(dir, exc);
+    // Files.delete(dir);
+    // return r;
+    // }
+    // });
+    // }
+
+    @Override
+    public RDF createFactory() {
+        return new NativeStoreRDF();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/RDF4JMethodOverloadsTest.java
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/RDF4JMethodOverloadsTest.java b/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/RDF4JMethodOverloadsTest.java
new file mode 100644
index 0000000..10762e8
--- /dev/null
+++ b/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/RDF4JMethodOverloadsTest.java
@@ -0,0 +1,50 @@
+/**
+ * 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.commons.rdf.rdf4j;
+
+import org.eclipse.rdf4j.model.BNode;
+import org.eclipse.rdf4j.model.IRI;
+import org.eclipse.rdf4j.model.Literal;
+import org.eclipse.rdf4j.model.Resource;
+import org.eclipse.rdf4j.model.Value;
+import org.eclipse.rdf4j.model.ValueFactory;
+import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class RDF4JMethodOverloadsTest {
+
+    @Test
+    public void testAsRDFTermOverloads() {
+        RDF4J rdf4J = new RDF4J();
+
+        final ValueFactory valueFactory = SimpleValueFactory.getInstance();
+
+        final Value bNode = valueFactory.createBNode("b1");
+        final Value iri = valueFactory.createIRI("http://ex.org");
+        final Value literal = valueFactory.createLiteral("b1");
+
+        assertEquals(rdf4J.asRDFTerm(bNode), rdf4J.asRDFTerm((BNode) bNode));
+        assertEquals(rdf4J.asRDFTerm(iri), rdf4J.asRDFTerm((IRI) iri));
+        assertEquals(rdf4J.asRDFTerm(literal), rdf4J.asRDFTerm((Literal) literal));
+        assertEquals(rdf4J.asRDFTerm(bNode), rdf4J.asRDFTerm((Resource) bNode));
+        assertEquals(rdf4J.asRDFTerm(iri), rdf4J.asRDFTerm((Resource) iri));
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/RDF4JServiceLoaderTest.java
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/RDF4JServiceLoaderTest.java b/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/RDF4JServiceLoaderTest.java
new file mode 100644
index 0000000..f07a0cb
--- /dev/null
+++ b/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/RDF4JServiceLoaderTest.java
@@ -0,0 +1,40 @@
+/**
+ * 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.commons.rdf.rdf4j;
+
+import static org.junit.Assert.fail;
+
+import java.util.ServiceLoader;
+
+import org.apache.commons.rdf.api.RDF;
+import org.junit.Test;
+
+public class RDF4JServiceLoaderTest {
+
+    @Test
+    public void testServiceLoaderLookup() {
+        final ServiceLoader<RDF> loader = ServiceLoader.load(RDF.class);
+        for (final RDF impl : loader) {
+            if (impl instanceof RDF4J) {
+                return; // yay
+            }
+        }
+        fail("RDF4J not found in ServiceLoader");
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/RDF4JTest.java
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/RDF4JTest.java b/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/RDF4JTest.java
new file mode 100644
index 0000000..29674ae
--- /dev/null
+++ b/commons-rdf-rdf4j/src/test/java/org/apache/commons/rdf/rdf4j/RDF4JTest.java
@@ -0,0 +1,37 @@
+/**
+ * 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.commons.rdf.rdf4j;
+
+import org.apache.commons.rdf.api.AbstractRDFTest;
+import org.apache.commons.rdf.api.RDF;
+import org.junit.Assume;
+
+public class RDF4JTest extends AbstractRDFTest {
+
+    @Override
+    public RDF createFactory() {
+        return new RDF4J();
+    }
+
+    @Override
+    public void testInvalidLiteralLang() throws Exception {
+        Assume.assumeTrue("RDF4J doesn't check Lang strings", false);
+        super.testInvalidLiteralLang();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-simple/pom.xml
----------------------------------------------------------------------
diff --git a/commons-rdf-simple/pom.xml b/commons-rdf-simple/pom.xml
new file mode 100644
index 0000000..1438f07
--- /dev/null
+++ b/commons-rdf-simple/pom.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.commons</groupId>
+        <artifactId>commons-rdf-parent</artifactId>
+        <version>0.5.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>commons-rdf-simple</artifactId>
+    <packaging>jar</packaging>
+
+    <name>Commons RDF impl: Simple</name>
+    <description>Simple (if not naive) implementation of Commons RDF API</description>
+
+    <distributionManagement>
+      <site>
+        <id>commonsrdf-api-site</id>
+        <url>scm:svn:${commons.scmPubUrl}/simple/</url>
+      </site>
+    </distributionManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>${project.parent.groupId}</groupId>
+            <artifactId>commons-rdf-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.parent.groupId}</groupId>
+            <artifactId>commons-rdf-api</artifactId>
+            <version>${project.version}</version>
+            <classifier>tests</classifier>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <configuration>
+          <instructions>
+            <Bundle-SymbolicName>org.apache.commons.rdf.simple</Bundle-SymbolicName>
+            <Automatic-Module-Name>org.apache.commons.rdf.simple</Automatic-Module-Name>
+            <Require-Capability>osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)";resolution:=optional</Require-Capability>
+            <Provide-Capability>osgi.serviceloader; osgi.serviceloader=org.apache.commons.rdf.api.RDF</Provide-Capability>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/BlankNodeImpl.java
----------------------------------------------------------------------
diff --git a/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/BlankNodeImpl.java b/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/BlankNodeImpl.java
new file mode 100644
index 0000000..f604f3e
--- /dev/null
+++ b/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/BlankNodeImpl.java
@@ -0,0 +1,113 @@
+/**
+ * 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.commons.rdf.simple;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.commons.rdf.api.BlankNode;
+import org.apache.commons.rdf.simple.SimpleRDF.SimpleRDFTerm;
+
+/**
+ * A simple implementation of BlankNode.
+ */
+final class BlankNodeImpl implements BlankNode, SimpleRDFTerm {
+
+    private static final UUID SALT = UUID.randomUUID();
+    private static final AtomicLong COUNTER = new AtomicLong();
+
+    private final String uniqueReference;
+
+    public BlankNodeImpl() {
+        this(SALT, Long.toString(COUNTER.incrementAndGet()));
+    }
+
+    public BlankNodeImpl(final UUID uuidSalt, final String name) {
+        if (Objects.requireNonNull(name).isEmpty()) {
+            throw new IllegalArgumentException("Invalid blank node id: " + name);
+        }
+
+        // Build a semi-URN - to be hashed for a name-based UUID below
+        // Both the scope and the id are used to create the UUID, ensuring that
+        // a caller can reliably create the same bnode if necessary by sending
+        // in the same scope to RDF.createBlankNode(String)
+        final String uuidInput = "urn:uuid:" + uuidSalt + "#" + name;
+
+        // The above is not a good value for this.id, as the id
+        // needs to be further escaped for
+        // ntriplesString() (there are no restrictions on
+        // RDF.createBlankNode(String) ).
+
+        // Rather than implement ntriples escaping here, and knowing
+        // our uniqueReference() contain a UUID anyway, we simply
+        // create another name-based UUID, and use it within both
+        // uniqueReference() and within ntriplesString().
+        //
+        // A side-effect from this is that the blank node identifier
+        // is not preserved or shown in ntriplesString. In a way
+        // this is a feature, not a bug. as the contract for RDF
+        // has no such requirement.
+        this.uniqueReference = UUID.nameUUIDFromBytes(uuidInput.getBytes(StandardCharsets.UTF_8)).toString();
+    }
+
+    @Override
+    public String uniqueReference() {
+        return uniqueReference;
+    }
+
+    @Override
+    public String ntriplesString() {
+        return "_:" + uniqueReference;
+    }
+
+    @Override
+    public String toString() {
+        return ntriplesString();
+    }
+
+    @Override
+    public int hashCode() {
+        return uniqueReference.hashCode();
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        // We don't support equality with other implementations
+        if (!(obj instanceof BlankNodeImpl)) {
+            return false;
+        }
+        final BlankNodeImpl other = (BlankNodeImpl) obj;
+        if (uniqueReference == null) {
+            if (other.uniqueReference != null) {
+                return false;
+            }
+        } else if (!uniqueReference.equals(other.uniqueReference)) {
+            return false;
+        }
+        return true;
+    }
+
+}