diff --git a/graphdb/graphcli/src/main/java/me/schaertl/graphcli/GraphDB.java b/graphdb/graphcli/src/main/java/me/schaertl/graphcli/GraphDB.java
new file mode 100644
index 0000000000000000000000000000000000000000..2b3cc447e76cca6e4322934effe4dde7dd667806
--- /dev/null
+++ b/graphdb/graphcli/src/main/java/me/schaertl/graphcli/GraphDB.java
@@ -0,0 +1,127 @@
+package me.schaertl.graphcli;
+
+import org.eclipse.rdf4j.model.ValueFactory;
+import org.eclipse.rdf4j.repository.Repository;
+import org.eclipse.rdf4j.repository.RepositoryConnection;
+import org.eclipse.rdf4j.repository.RepositoryException;
+import org.eclipse.rdf4j.repository.manager.RemoteRepositoryManager;
+import org.eclipse.rdf4j.repository.manager.RepositoryManager;
+
+/**
+ * Wrapper around various RDF4J methods. Represents a connection to a GraphDB
+ * instance on the network.
+ *
+ * For creating a new connection, simply create a new object of this class. To
+ * then close all associated resources, call {@link GraphDB#close}. As this class
+ * implements {@link AutoCloseable}, you can also use the convenient try-with-resources.
+ */
+public class GraphDB implements AutoCloseable {
+    private final RepositoryManager manager;
+    private final Repository repository;
+    private final RepositoryConnection connection;
+
+    private boolean closed = false;
+
+    /**
+     * Establish a new connection to a remote GraphDB instance.
+     *
+     * @param serverURL The URL under which the instance is reachable.
+     * @param repository The repository to connect to.
+     * @throws RepositoryException If connecting to the server or repository failed.
+     */
+    public GraphDB(String serverURL, String repository) throws RepositoryException {
+        this.manager = new RemoteRepositoryManager(serverURL);
+        this.manager.init();
+
+        this.repository = this.manager.getRepository(repository);
+        if (this.repository == null) {
+            this.manager.shutDown();
+            throw new RepositoryException(String.format("no repository with name %s", repository));
+        }
+
+        try {
+            this.connection = this.repository.getConnection();
+        } finally {
+            this.manager.shutDown();
+            this.repository.shutDown();
+        }
+    }
+
+    /**
+     * Release all associated resources
+     *
+     * @throws IllegalStateException If this method called multiple times on a single object.
+     */
+    @Override
+    public synchronized void close() throws Exception {
+        if (this.closed) {
+            throw new IllegalStateException("multiple calls to close()");
+        }
+
+        this.closed = true;
+
+        // What comes now is very very ugly. From what I can tell the way
+        // the RDF4j API works this is the only way to make sure that everything
+        // gets closed properly. Bummer.
+
+        Exception error = null;
+
+        try {
+            if (this.connection != null) {
+                this.connection.close();
+            }
+        } catch (Exception e) {
+            error = e;
+        }
+
+        try {
+            if (this.repository != null) {
+                this.repository.shutDown();
+            }
+        } catch (Exception e) {
+            if (error != null) {
+                error = e;
+            }
+        }
+
+        try {
+            if (this.manager != null) {
+                this.manager.shutDown();
+            }
+        } catch (Exception e) {
+            if (error != null) {
+                error = e;
+            }
+        }
+
+        if (error != null) {
+            throw error;
+        }
+    }
+
+    /**
+     * Return a connection for interaction with the underlying database.
+     *
+     * @throws IllegalStateException If {@link GraphDB#close} was called on this object before.
+     */
+    public synchronized RepositoryConnection connection() {
+        if (this.closed) {
+            throw new IllegalStateException("called connection() after close()");
+        }
+
+        return this.connection;
+    }
+
+    /**
+     * Return a factory for create new objects.
+     *
+     * @throws IllegalStateException If {@link GraphDB#close} was called on this object before.
+     */
+    public synchronized ValueFactory factory() {
+        if (this.closed) {
+            throw new IllegalStateException("called factory() after close()");
+        }
+
+        return this.repository.getValueFactory();
+    }
+}
diff --git a/graphdb/graphcli/src/main/java/me/schaertl/graphcli/Main.java b/graphdb/graphcli/src/main/java/me/schaertl/graphcli/Main.java
index 39610402de327fc9a5ac253bb3907ed99a4d7723..8143cb9bdd1b54c48fa6a019b4b10fd2ab157402 100644
--- a/graphdb/graphcli/src/main/java/me/schaertl/graphcli/Main.java
+++ b/graphdb/graphcli/src/main/java/me/schaertl/graphcli/Main.java
@@ -1,64 +1,47 @@
 package me.schaertl.graphcli;
 
-import org.eclipse.rdf4j.model.Resource;
-import org.eclipse.rdf4j.model.impl.TreeModel;
-import org.eclipse.rdf4j.model.vocabulary.RDF;
-import org.eclipse.rdf4j.repository.Repository;
+import org.eclipse.rdf4j.model.Statement;
 import org.eclipse.rdf4j.repository.RepositoryConnection;
-import org.eclipse.rdf4j.repository.config.RepositoryConfig;
-import org.eclipse.rdf4j.repository.config.RepositoryConfigSchema;
-import org.eclipse.rdf4j.repository.manager.LocalRepositoryManager;
-import org.eclipse.rdf4j.repository.manager.RemoteRepositoryManager;
-import org.eclipse.rdf4j.repository.manager.RepositoryManager;
-import org.eclipse.rdf4j.rio.RDFFormat;
-import org.eclipse.rdf4j.rio.RDFParser;
-import org.eclipse.rdf4j.rio.Rio;
-import org.eclipse.rdf4j.rio.helpers.StatementCollector;
+import org.eclipse.rdf4j.repository.RepositoryResult;
 
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
 
 public class Main {
-    private static void hello() throws IOException {
-        // Code taken from http://graphdb.ontotext.com/documentation/standard/using-graphdb-with-the-rdf4j-api.html
+    private static final String SERVER_URL = "http://rdf:7200";
+    private static final String REPOSITORY = "dump";
+    private static final int LIMIT = 100;
 
-        // Instantiate a local repository manager and initialize it
-        final RepositoryManager repositoryManager = new RemoteRepositoryManager("http://rdf:7200");
-        repositoryManager.initialize();
+    private static void hello() throws Exception {
+        try (final GraphDB db = new GraphDB(SERVER_URL, REPOSITORY)) {
+            final RepositoryConnection connection = db.connection();
 
-        // Instantiate a repository graph model
-        // final TreeModel graph = new TreeModel();
+            final RepositoryResult<Statement> result = connection.getStatements(null, ULO.INDUCTIVE_ON, null, true);
 
-        // Read repository configuration file
-        // InputStream config = EmbeddedGraphDB.class.getResourceAsStream("/repo-defaults.ttl");
-        // RDFParser rdfParser = Rio.createParser(RDFFormat.TURTLE);
-        // rdfParser.setRDFHandler(new StatementCollector(graph));
-        // rdfParser.parse(config, RepositoryConfigSchema.NAMESPACE);
-        // config.close();
-
-        // Retrieve the repository node as a resource
-        // Resource repositoryNode = GraphUtil.getUniqueSubject(graph, RDF.TYPE, RepositoryConfigSchema.REPOSITORY);
-
-        // Create a repository configuration object and add it to the repositoryManager
-        // RepositoryConfig repositoryConfig = RepositoryConfig.create(graph, repositoryNode);
-        // repositoryManager.addRepositoryConfig(repositoryConfig);
-
-        // Get the repository from repository manager, note the repository id set in configuration .ttl file
-        final Repository repository = repositoryManager.getRepository("chem");
-
-        // Open a connection to this repository
-        final RepositoryConnection repositoryConnection = repository.getConnection();
-
-        // ... use the repository
+            for (Statement s : take(result, LIMIT)) {
+                System.out.printf("%s INDUCTIVE ON %s", s.getSubject(), s.getObject());
+            }
+        }
+    }
 
-        // Shutdown connection, repository and manager
-        repositoryConnection.close();
-        repository.shutDown();
-        repositoryManager.shutDown();
+    /**
+     * Return the first n results from result.
+     *
+     * @param result The result from which the results will be returned.
+     * @param n The maximum number of items to return.
+     */
+    private static List<Statement> take(RepositoryResult<Statement> result, int n) {
+        final List<Statement> l = new ArrayList<>(n);
+
+        for (int i = 0; i < LIMIT && result.hasNext(); i++) {
+            final Statement s = result.next();
+            l.add(s);
+        }
+
+        return l;
     }
 
-    public static void main(String[] args) throws IOException {
+    public static void main(String[] args) throws Exception {
         Main.hello();
         System.out.println("done running the program");
     }
diff --git a/graphdb/graphcli/src/main/java/me/schaertl/graphcli/ULO.java b/graphdb/graphcli/src/main/java/me/schaertl/graphcli/ULO.java
new file mode 100644
index 0000000000000000000000000000000000000000..ae4d9da24345376babe415713e6fa680eb5449fe
--- /dev/null
+++ b/graphdb/graphcli/src/main/java/me/schaertl/graphcli/ULO.java
@@ -0,0 +1,39 @@
+package me.schaertl.graphcli;
+
+import org.eclipse.rdf4j.model.IRI;
+import org.eclipse.rdf4j.model.Namespace;
+import org.eclipse.rdf4j.model.ValueFactory;
+import org.eclipse.rdf4j.model.impl.SimpleNamespace;
+import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
+import org.eclipse.rdf4j.model.vocabulary.FOAF;
+
+/**
+ * Constants for use with ULO, the Upper Library Ontology [1].
+ *
+ * [1] https://gl.mathhub.info/ulo/ulo/-/tree/master/
+ */
+public class ULO {
+    private ULO() {}
+
+    /**
+     * The ULO namespace: https://mathhub.info/ulo
+     */
+    public static final String NAMESPACE = "https://mathhub.info/ulo";
+
+    /**
+     * The recommended prefix for the ULO namespace: "ulo"
+     */
+    public static final String PREFIX = "ulo";
+
+    /**
+     * An immutable {@link Namespace} constant that represents the ULO namespace.
+     */
+    public static final Namespace NS = new SimpleNamespace(PREFIX, NAMESPACE);
+
+    public final static IRI INDUCTIVE_ON;
+
+    static {
+        final ValueFactory factory = SimpleValueFactory.getInstance();
+        INDUCTIVE_ON = factory.createIRI(ULO.NAMESPACE, "inductive-on");
+    }
+}
diff --git a/timeline/week19.txt b/timeline/week19.txt
index 764cd52ab3b86b2c9cafb5d380617ed4fe0bc5f7..ff831237829ad9feb59667ee0e049837f6904004 100644
--- a/timeline/week19.txt
+++ b/timeline/week19.txt
@@ -22,7 +22,9 @@ Week 19 (04.05.-10.05.)
 	   the programming interface. The docs are in Java, switching
 	   to Scala should not be hard.
 
-	[ ] Look at the RDF4J tutorial [1]
+	[x] Look at the RDF4J tutorial [1]
+
+	[ ] Create ULO class in the vein of [2]
 
 
 [ ] familiarize w/ MathHub infastructure
@@ -49,3 +51,4 @@ References
 ==========
 
 [1] https://rdf4j.org/documentation/programming/
+[2] https://jar-download.com/artifacts/org.eclipse.rdf4j/rdf4j-model/3.0.3/source-code/org/eclipse/rdf4j/model/vocabulary/FOAF.java