Commits

Sam Adams  committed d7172f3

Refactoring triple store

  • Participants
  • Parent commits 20aaf39

Comments (0)

Files changed (15)

File chempound-api/src/main/java/net/chempound/datastore/ItemInfoMapper.java

+package net.chempound.datastore;
+
+import com.hp.hpl.jena.query.QuerySolution;
+import com.hp.hpl.jena.rdf.model.RDFNode;
+import net.chempound.bean.ItemInfo;
+import net.chempound.bean.ItemInfoBuilder;
+
+/**
+ * @author Sam Adams
+ */
+public class ItemInfoMapper implements QuerySolutionMapper<ItemInfo> {
+
+    @Override
+    public ItemInfo map(final QuerySolution querySolution) {
+        final RDFNode uri = querySolution.get("uri");
+        final RDFNode title = querySolution.get("title");
+        final RDFNode thumbnail = querySolution.get("thumbnail");
+        final RDFNode dataType = querySolution.get("type");
+        final ItemInfoBuilder builder = new ItemInfoBuilder(uri.asResource().getURI());
+        if (title != null) {
+            builder.setTitle(title.asLiteral().getString());
+        }
+        if (thumbnail != null) {
+            builder.setThumbnailUri(thumbnail.asResource().getURI());
+        }
+        if (dataType != null) {
+            builder.setDataType(dataType.asLiteral().getString());
+        }
+        return builder.build();
+    }
+
+}

File chempound-api/src/main/java/net/chempound/datastore/QuerySolutionHandler.java

-package net.chempound.datastore;
-
-import com.hp.hpl.jena.query.QuerySolution;
-
-/**
- * @author Sam Adams
- */
-public interface QuerySolutionHandler<T> {
-
-    T handleQuerySolution(QuerySolution querySolution);
-
-}

File chempound-api/src/main/java/net/chempound/datastore/QuerySolutionMapper.java

+package net.chempound.datastore;
+
+import com.hp.hpl.jena.query.QuerySolution;
+
+/**
+ * @author Sam Adams
+ */
+public interface QuerySolutionMapper<T> {
+
+    T map(QuerySolution result);
+
+}

File chempound-api/src/main/java/net/chempound/datastore/ResourceInfoMapper.java

+package net.chempound.datastore;
+
+import com.hp.hpl.jena.query.QuerySolution;
+import com.hp.hpl.jena.query.ResultSet;
+import com.hp.hpl.jena.rdf.model.RDFNode;
+import net.chempound.bean.ResourceInfo;
+import net.chempound.util.MimeType;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Sam Adams
+ */
+public class ResourceInfoMapper implements ResultSetMapper<ResourceInfo> {
+
+    @Override
+    public ResourceInfo map(final ResultSet resultSet) {
+        final List<URI> types = new ArrayList<URI>();
+        final List<MimeType> mimeTypes = new ArrayList<MimeType>();
+
+        while (resultSet.hasNext()) {
+            final QuerySolution qs = resultSet.next();
+            final RDFNode s = qs.get("t");
+            final RDFNode s1 = qs.get("m");
+
+            final String type = s.asResource().getURI();
+            types.add(URI.create(type));
+
+            if (s1 != null) {
+                final String mime = s1.asLiteral().getString();
+                mimeTypes.add(new MimeType(mime));
+            }
+        }
+
+        return new ResourceInfo(types, mimeTypes);
+    }
+
+}

File chempound-api/src/main/java/net/chempound/datastore/ResourceMapVariantsMapper.java

+package net.chempound.datastore;
+
+import com.hp.hpl.jena.query.QuerySolution;
+import com.hp.hpl.jena.query.ResultSet;
+import com.hp.hpl.jena.rdf.model.Literal;
+import com.hp.hpl.jena.rdf.model.RDFNode;
+import com.hp.hpl.jena.rdf.model.Resource;
+import net.chempound.util.MimeType;
+
+import java.net.URI;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * @author Sam Adams
+ */
+public class ResourceMapVariantsMapper implements ResultSetMapper<Map<MimeType, URI>> {
+
+    @Override
+    public Map<MimeType, URI> map(final ResultSet resultSet) {
+        final Map<MimeType,URI> variantMap = new LinkedHashMap<MimeType,URI>();
+        while (resultSet.hasNext()) {
+            final QuerySolution qs = resultSet.next();
+            final RDFNode remNode = qs.get("rem");
+            final RDFNode mimeNode = qs.get("mime");
+
+            final Resource remResource = remNode.asResource();
+            final String rem = remResource.getURI();
+            final Literal mimeLiteral = mimeNode.asLiteral();
+            final String mime = mimeLiteral.getString();
+
+            final MimeType key = new MimeType(mime);
+            variantMap.put(key, URI.create(rem));
+            // TODO log duplicates
+        }
+        return variantMap;
+    }
+
+}

File chempound-api/src/main/java/net/chempound/datastore/ResultSetMapper.java

+package net.chempound.datastore;
+
+import com.hp.hpl.jena.query.ResultSet;
+
+/**
+ * @author Sam Adams
+ */
+public interface ResultSetMapper<T> {
+    
+    T map(ResultSet resultSet);
+    
+}

File chempound-api/src/main/java/net/chempound/datastore/StringMapper.java

+package net.chempound.datastore;
+
+import com.hp.hpl.jena.query.QuerySolution;
+
+/**
+ * @author Sam Adams
+ */
+public class StringMapper implements QuerySolutionMapper<String> {
+
+    private final String key;
+
+    public StringMapper(final String key) {
+        this.key = key;
+    }
+
+    @Override
+    public String map(final QuerySolution result) {
+        return result.getLiteral(key).getString();
+    }
+
+}

File chempound-api/src/main/java/net/chempound/datastore/TripleStore.java

 
     String getMediaType(URI uri);
 
-    <T> List<T> executeSelect(Query query, QuerySolution initialBindings, QuerySolutionHandler<T> querySolutionHandler);
+    <T> List<T> executeSelect(Query query, QuerySolution initialBindings, QuerySolutionMapper<T> mapper);
+    
+    <T> T executeSelect(Query query, QuerySolution initialBindings, ResultSetMapper<T> mapper);
 
     boolean containsResourceOfType(Resource resource, Resource aggregation);
 

File chempound-api/src/main/java/net/chempound/datastore/UriMapper.java

+package net.chempound.datastore;
+
+import com.hp.hpl.jena.query.QuerySolution;
+import com.hp.hpl.jena.rdf.model.RDFNode;
+
+import java.net.URI;
+
+/**
+ * @author Sam Adams
+ */
+public class UriMapper implements QuerySolutionMapper<URI> {
+
+    private final String key;
+
+    public UriMapper() {
+        this("uri");
+    }
+
+    public UriMapper(final String key) {
+        this.key = key;
+    }
+
+    @Override
+    public URI map(final QuerySolution querySolution) {
+        final RDFNode u = querySolution.get(key);
+        return URI.create(u.asResource().getURI());
+    }
+
+}

File chempound-api/src/main/java/net/chempound/datastore/UriResultHandler.java

 /**
  * @author Sam Adams
  */
-public class UriResultHandler implements QuerySolutionHandler<URI> {
+public class UriResultHandler implements QuerySolutionMapper<URI> {
 
     private static final String DEFAULT_URI_FIELD = "uri";
 
     }
 
     @Override
-    public URI handleQuerySolution(final QuerySolution querySolution) {
-        final String uri = querySolution.getResource(field).getURI();
+    public URI map(final QuerySolution result) {
+        final String uri = result.getResource(field).getURI();
         return URI.create(uri);
     }
 

File chempound-api/src/main/java/net/chempound/rdf/RdfIO.java

 
 import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
+import java.io.StringWriter;
 
 /**
  * @author Sam Adams
         return buffer.toByteArray();
     }
 
+    public static String toString(final Model model, final String format) {
+        final StringWriter buffer = new StringWriter();
+        model.write(buffer, format);
+        return buffer.toString();
+    }
+
 }

File chempound-app/src/main/java/net/chempound/datastore/AbstractTripleStore.java

+package net.chempound.datastore;
+
+import com.hp.hpl.jena.query.*;
+import com.hp.hpl.jena.rdf.model.Property;
+import com.hp.hpl.jena.rdf.model.RDFNode;
+import com.hp.hpl.jena.rdf.model.Resource;
+import com.hp.hpl.jena.rdf.model.ResourceFactory;
+import com.hp.hpl.jena.shared.Lock;
+import com.hp.hpl.jena.sparql.vocabulary.FOAF;
+import com.hp.hpl.jena.vocabulary.RDF;
+import com.hp.hpl.jena.vocabulary.RDFS;
+import net.chempound.bean.ItemInfo;
+import net.chempound.bean.ResourceInfo;
+import net.chempound.rdf.ChempoundPersonalities;
+import net.chempound.rdf.ORE;
+import net.chempound.util.MimeType;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import static com.hp.hpl.jena.sparql.util.FmtUtils.*;
+import static java.lang.String.format;
+
+/**
+ * @author Sam Adams
+ */
+public abstract class AbstractTripleStore implements TripleStore {
+
+    static {
+        ChempoundPersonalities.init();
+    }
+
+    private static final String DCTERMS_NS = com.hp.hpl.jena.vocabulary.DCTerms.getURI();
+    private static final String FOAF_NS = FOAF.getURI();
+    private static final String RDFS_NS = RDFS.getURI();
+    private static final String RDF_NS = RDF.getURI();
+    private static final String ORE_NS = ORE.getURI();
+
+    protected static final Query QUERY_RESOURCE_INFO = QueryFactory.create("" +
+        "PREFIX rdf: <" + RDF_NS + ">\n" +
+        "PREFIX dct: <" + DCTERMS_NS + ">\n" +
+        "" +
+        "SELECT ?t ?m" +
+        "  WHERE {" +
+        "    ?uri rdf:type ?t ." +
+        "    OPTIONAL {" +
+        "      ?uri dct:format [" +
+        "        rdf:value ?m" +
+        "      ]" +
+        "    }" +
+        "  }"
+    );
+
+    protected static final Query QUERY_RESOURCE_MAP_VARIANTS = QueryFactory.create("" +
+             "PREFIX rdf: <" + RDF_NS + ">\n" +
+             "PREFIX dct: <" + DCTERMS_NS + ">\n" +
+             "PREFIX ore: <" + ORE_NS + ">\n" +
+             "" +
+             "SELECT ?rem ?mime" +
+             "  WHERE {" +
+             "    ?rem ore:describes ?uri ;" +
+             "         dct:format [" +
+             "              a dct:IMT ;" +
+             "              rdf:value ?mime" +
+             "         ]" +
+             "    ." +
+             "  }"
+    );
+
+    protected static final Query QUERY_AGGREGATED_ITEMS = QueryFactory.create("" +
+             "PREFIX rdfs: <" + RDFS_NS + ">\n" +
+             "PREFIX dct: <" + DCTERMS_NS + ">\n" +
+             "PREFIX ore: <" + ORE_NS + ">\n" +
+             "PREFIX foaf: <" + FOAF_NS + ">\n" +
+             "" +
+             "SELECT ?uri ?title ?type ?thumbnail" +
+             "  WHERE {" +
+             "    ?aggregation ore:aggregates ?uri ." +
+             "    OPTIONAL {" +
+             "      ?uri dct:title ?title ." +
+             "    }" +
+             "    OPTIONAL {" +
+             "      ?uri foaf:thumbnail ?thumbnail ." +
+             "    }" +
+             "    OPTIONAL {" +
+             "      ?uri dct:type [ rdfs:label ?type ] ." +
+             "    }" +
+            "  }"
+    );
+
+    protected static final Query QUERY_RECENT_ITEMS_INFO = QueryFactory.create("" +
+             "PREFIX rdfs: <" + RDFS_NS + ">\n" +
+             "PREFIX dct: <" + DCTERMS_NS + ">\n" +
+             "PREFIX ore: <" + ORE_NS + ">\n" +
+             "PREFIX foaf: <" + FOAF_NS + ">\n" +
+             "" +
+             "SELECT ?uri ?title ?type ?thumbnail" +
+             "  WHERE {" +
+             "    ?uri a <http://wwmm.ch.cam.ac.uk/chempound/terms/Item> ." +
+             "    OPTIONAL {" +
+             "      ?uri dct:title ?title ." +
+             "    }" +
+             "    OPTIONAL {" +
+             "      ?uri foaf:thumbnail ?thumbnail ." +
+             "    }" +
+             "    OPTIONAL {" +
+             "      ?uri dct:type [ rdfs:label ?type ] ." +
+             "    }" +
+             "    ?uri dct:modified ?modified" +
+             "  }" +
+             " ORDER BY DESC(?modified)" +
+            " LIMIT 5"
+    );
+
+    protected static final Query QUERY_RECENT_ITEM_URIS = QueryFactory.create("" +
+                 "PREFIX rdfs: <" + RDFS_NS + ">\n" +
+                 "PREFIX dct: <" + DCTERMS_NS + ">\n" +
+                 "PREFIX ore: <" + ORE_NS + ">\n" +
+                 "PREFIX foaf: <" + FOAF_NS + ">\n" +
+                 "" +
+                 "SELECT ?uri" +
+                 "  WHERE {" +
+                 "    ?uri a <http://wwmm.ch.cam.ac.uk/chempound/terms/Item> ." +
+                 "    ?uri dct:modified ?modified ." +
+                 "  }" +
+                 " ORDER BY DESC(?modified) LIMIT 5"
+    );
+
+    protected static final Query QUERY_ITEM_INFO = QueryFactory.create("" +
+             "PREFIX rdfs: <" + RDFS_NS + ">\n" +
+             "PREFIX dct: <" + DCTERMS_NS + ">\n" +
+             "PREFIX ore: <" + ORE_NS + ">\n" +
+             "PREFIX foaf: <" + FOAF_NS + ">\n" +
+             "" +
+             "SELECT ?uri ?title ?type ?thumbnail" +
+             "  WHERE {" +
+             "    ?uri a <http://wwmm.ch.cam.ac.uk/chempound/terms/Item> ." +
+             "    OPTIONAL {" +
+             "      ?uri dct:title ?title ." +
+             "    }" +
+             "    OPTIONAL {" +
+             "      ?uri foaf:thumbnail ?thumbnail ." +
+             "    }" +
+             "    OPTIONAL {" +
+             "      ?uri dct:type [ rdfs:label ?type ] ." +
+             "    }" +
+             "  }"
+    );
+
+    protected static final Query QUERY_MEDIA_TYPE = QueryFactory.create("" +
+             "PREFIX rdf: <" + RDF_NS + ">\n" +
+             "PREFIX dct: <" + DCTERMS_NS + ">\n" +
+             "" +
+             "SELECT ?uri ?mime" +
+             "  WHERE {" +
+             "    ?uri dct:format [" +
+             "          a dct:IMT ;" +
+             "          rdf:value ?mime" +
+             "     ]" +
+             "  }"
+    );
+
+    protected static final Query QUERY_REM_AGGREGATION = QueryFactory.create("" +
+        "PREFIX ore: <" + ORE_NS + ">\n" +
+        "" +
+        "SELECT ?aggr" +
+        "  WHERE {" +
+        "    ?rem ore:describes ?aggr" +
+        "  }"
+    );
+
+
+    @Override
+    public final ResourceInfo getResouceInfo(final URI uri) {
+        final QuerySolutionMap initialBinding = new QuerySolutionMap();
+        initialBinding.add("uri", ResourceFactory.createResource(uri.toString()));
+        return executeSelect(QUERY_RESOURCE_INFO, initialBinding, new ResourceInfoMapper());
+    }
+
+    @Override
+    public final Map<MimeType, URI> getResourceVariants(final URI uri) {
+        final QuerySolutionMap initialBinding = new QuerySolutionMap();
+        initialBinding.add("uri", ResourceFactory.createResource(uri.toString()));
+        return executeSelect(QUERY_RESOURCE_MAP_VARIANTS, initialBinding, new ResourceMapVariantsMapper());
+    }
+
+    @Override
+    public final List<ItemInfo> getItems(final URI aggregation) {
+        final QuerySolutionMap initialBinding = new QuerySolutionMap();
+        initialBinding.add("aggregation", ResourceFactory.createResource(aggregation.toString()));
+        return executeSelect(QUERY_AGGREGATED_ITEMS, initialBinding, new ItemInfoMapper());
+    }
+
+    @Override
+    public final List<ItemInfo> getRecentItemsInfo(final int n) {
+        return executeSelect(QUERY_RECENT_ITEMS_INFO, null, new ItemInfoMapper());
+    }
+
+    @Override
+    public final List<URI> getRecentItems() {
+        return executeSelect(QUERY_RECENT_ITEM_URIS, null, new UriMapper());
+    }
+
+    @Override
+    public final ItemInfo getItemInfo(final URI uri) {
+        final QuerySolutionMap initialBinding = new QuerySolutionMap();
+        initialBinding.add("uri", ResourceFactory.createResource(uri.toString()));
+        return executeSelectFirst(QUERY_ITEM_INFO, initialBinding, new ItemInfoMapper());
+    }
+
+    @Override
+    public final String getMediaType(final URI uri) {
+        final QuerySolutionMap initialBinding = new QuerySolutionMap();
+        initialBinding.add("uri", ResourceFactory.createResource(uri.toString()));
+        return executeSelectFirst(QUERY_MEDIA_TYPE, initialBinding, new StringMapper("mime"));
+    }
+
+
+    @Override
+    public URI getAggregationUriForResourceMap(final Resource resource) {
+        final QuerySolutionMap initialBinding = new QuerySolutionMap();
+        initialBinding.add("rem", resource);
+        return executeSelectFirst(QUERY_REM_AGGREGATION, initialBinding, new UriMapper("aggr"));
+    }
+
+    @Override
+    public boolean containsResource(final URI uri) {
+        return executeAsk(format("ASK { %s ?p ?o }", stringForURI(uri.toString())));
+    }
+
+    @Override
+    public boolean containsResourceOfType(final Resource resource, final Resource aggregation) {
+        return containsTriple(resource, RDF.type, aggregation);
+    }
+
+    @Override
+    public boolean containsTriple(final Resource subject, final Property predicate, final RDFNode object) {
+        return executeAsk(format("ASK { %s %s %s }",
+            stringForResource(subject), stringForResource(predicate), stringForRDFNode(object)));
+    }
+
+
+    public final <T> T executeSelect(final Query query, final QuerySolution initialBinding, final ResultSetMapper<T> mapper) {
+        final QueryExecution qExec = createQueryExecution(query, initialBinding);
+        try {
+            final Lock lock = getLock();
+            try {
+                lock.enterCriticalSection(Lock.READ);
+                return mapper.map(qExec.execSelect());
+            } finally {
+                lock.leaveCriticalSection();
+            }
+        } finally {
+            qExec.close();
+        }
+    }
+
+    public final <T> List<T> executeSelect(final Query query, final QuerySolution initialBinding, final QuerySolutionMapper<T> mapper) {
+        final QueryExecution qExec = createQueryExecution(query, initialBinding);
+        try {
+            final Lock lock = getLock();
+            try {
+                lock.enterCriticalSection(Lock.READ);
+                return mapResults(qExec, mapper);
+            } finally {
+                lock.leaveCriticalSection();
+            }
+        } finally {
+            qExec.close();
+        }
+    }
+
+    public final <T> T executeSelectFirst(final Query query, final QuerySolution initialBinding, final QuerySolutionMapper<T> mapper) {
+        final QueryExecution qExec = createQueryExecution(query, initialBinding);
+        try {
+            final Lock lock = getLock();
+            try {
+                lock.enterCriticalSection(Lock.READ);
+                final ResultSet resultSet = qExec.execSelect();
+                return resultSet.hasNext() ? mapper.map(resultSet.next()) : null;
+            } finally {
+                lock.leaveCriticalSection();
+            }
+        } finally {
+            qExec.close();
+        }
+    }
+
+    public final boolean executeAsk(final String query) {
+        return createQueryExecution(QueryFactory.create(query)).execAsk();
+    }
+
+
+    @Override
+    public abstract QueryExecution createQueryExecution(final Query query);
+
+    protected abstract QueryExecution createQueryExecution(final Query query, final QuerySolution initialBindings);
+
+    protected abstract Lock getLock();
+
+
+    protected static <T> List<T> mapResults(final QueryExecution qExec, final QuerySolutionMapper<T> mapper) {
+        final ResultSet resultSet = qExec.execSelect();
+        final List<T> results = new ArrayList<T>();
+        while (resultSet.hasNext()) {
+            results.add(mapper.map(resultSet.next()));
+        }
+        return results;
+    }
+
+}

File chempound-app/src/main/java/net/chempound/datastore/JenaTripleStore.java

 import com.hp.hpl.jena.shared.Lock;
 import com.hp.hpl.jena.sparql.core.DatasetGraph;
 import com.hp.hpl.jena.sparql.core.Quad;
-import com.hp.hpl.jena.sparql.vocabulary.FOAF;
 import com.hp.hpl.jena.tdb.TDB;
 import com.hp.hpl.jena.tdb.TDBFactory;
 import com.hp.hpl.jena.vocabulary.RDF;
-import com.hp.hpl.jena.vocabulary.RDFS;
-import net.chempound.bean.ItemInfo;
-import net.chempound.bean.ItemInfoBuilder;
-import net.chempound.bean.ResourceInfo;
 import net.chempound.config.ChempoundConfiguration;
-import net.chempound.rdf.CPTerms;
-import net.chempound.rdf.ChempoundPersonalities;
-import net.chempound.rdf.DCTerms;
 import net.chempound.rdf.ORE;
-import net.chempound.util.MimeType;
 import org.apache.commons.io.FileUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URI;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
 
 /**
  * @author sea36
  */
 @Singleton
-public class JenaTripleStore implements TripleStore {
-
-    private static final Logger LOG = LoggerFactory.getLogger(JenaTripleStore.class);
-
-    static {
-        ChempoundPersonalities.init();
-    }
-
-    private static final Query QUERY_RESOURCE_INFO = QueryFactory.create("" +
-             "PREFIX rdf: <" + RDF.getURI() + ">\n" +
-             "PREFIX dct: <" + DCTerms.getURI() + ">\n" +
-             "" +
-             "SELECT ?t ?m" +
-             "  WHERE {" +
-             "    ?uri rdf:type ?t ." +
-             "    OPTIONAL {" +
-             "      ?uri dct:format [" +
-             "        rdf:value ?m" +
-             "      ]" +
-             "    }" +
-             "  }"
-    );
-
-    private static final Query QUERY_RESOURCE_MAP_VARIANTS = QueryFactory.create("" +
-             "PREFIX rdf: <" + RDF.getURI() + ">\n" +
-             "PREFIX dct: <" + DCTerms.getURI() + ">\n" +
-             "PREFIX ore: <" + ORE.getURI() + ">\n" +
-             "" +
-             "SELECT ?rem ?mime" +
-             "  WHERE {" +
-             "    ?rem ore:describes ?uri ;" +
-             "         dct:format [" +
-             "              a dct:IMT ;" +
-             "              rdf:value ?mime" +
-             "         ]" +
-             "    ." +
-             "  }"
-    );
-
-    private static final Query QUERY_AGGREGATED_ITEMS = QueryFactory.create("" +
-             "PREFIX rdfs: <" + RDFS.getURI() + ">\n" +
-             "PREFIX dct: <" + DCTerms.getURI() + ">\n" +
-             "PREFIX ore: <" + ORE.getURI() + ">\n" +
-             "PREFIX foaf: <" + FOAF.getURI() + ">\n" +
-             "" +
-             "SELECT ?uri ?title ?type ?thumbnail" +
-             "  WHERE {" +
-             "    ?aggregation ore:aggregates ?uri ." +
-             "    OPTIONAL {" +
-             "      ?uri dct:title ?title ." +
-             "    }" +
-             "    OPTIONAL {" +
-             "      ?uri foaf:thumbnail ?thumbnail ." +
-             "    }" +
-             "    OPTIONAL {" +
-             "      ?uri dct:type [ rdfs:label ?type ] ." +
-             "    }" +
-            "  }"
-    );
-
-
-    private static final Query QUERY_RECENT_ITEMS_INFO = QueryFactory.create("" +
-             "PREFIX rdfs: <" + RDFS.getURI() + ">\n" +
-             "PREFIX dct: <" + DCTerms.getURI() + ">\n" +
-             "PREFIX ore: <" + ORE.getURI() + ">\n" +
-             "PREFIX foaf: <" + FOAF.getURI() + ">\n" +
-             "" +
-             "SELECT ?uri ?title ?type ?thumbnail" +
-             "  WHERE {" +
-             "    ?uri a <http://wwmm.ch.cam.ac.uk/chempound/terms/Item> ." +
-             "    OPTIONAL {" +
-             "      ?uri dct:title ?title ." +
-             "    }" +
-             "    OPTIONAL {" +
-             "      ?uri foaf:thumbnail ?thumbnail ." +
-             "    }" +
-             "    OPTIONAL {" +
-             "      ?uri dct:type [ rdfs:label ?type ] ." +
-             "    }" +
-             "    ?uri dct:modified ?modified" +
-             "  }" +
-             " ORDER BY DESC(?modified)" +
-            " LIMIT 5"
-    );
-
-    private static final Query QUERY_RECENT_ITEM_URIS = QueryFactory.create("" +
-                 "PREFIX rdfs: <" + RDFS.getURI() + ">\n" +
-                 "PREFIX dct: <" + DCTerms.getURI() + ">\n" +
-                 "PREFIX ore: <" + ORE.getURI() + ">\n" +
-                 "PREFIX foaf: <" + FOAF.getURI() + ">\n" +
-                 "" +
-                 "SELECT ?uri" +
-                 "  WHERE {" +
-                 "    ?uri a <http://wwmm.ch.cam.ac.uk/chempound/terms/Item> ." +
-                 "    ?uri dct:modified ?modified ." +
-                 "  }" +
-                 " ORDER BY DESC(?modified) LIMIT 5"
-    );
-
-    public static final Query QUERY_ITEM_INFO = QueryFactory.create("" +
-             "PREFIX rdfs: <" + RDFS.getURI() + ">\n" +
-             "PREFIX dct: <" + DCTerms.getURI() + ">\n" +
-             "PREFIX ore: <" + ORE.getURI() + ">\n" +
-             "PREFIX foaf: <" + FOAF.getURI() + ">\n" +
-             "" +
-             "SELECT ?uri ?title ?type ?thumbnail" +
-             "  WHERE {" +
-             "    ?uri a <http://wwmm.ch.cam.ac.uk/chempound/terms/Item> ." +
-             "    OPTIONAL {" +
-             "      ?uri dct:title ?title ." +
-             "    }" +
-             "    OPTIONAL {" +
-             "      ?uri foaf:thumbnail ?thumbnail ." +
-             "    }" +
-             "    OPTIONAL {" +
-             "      ?uri dct:type [ rdfs:label ?type ] ." +
-             "    }" +
-             "  }"
-    );
-
-    private static final Query QUERY_MEDIA_TYPE = QueryFactory.create("" +
-             "PREFIX rdf: <" + RDF.getURI() + ">\n" +
-             "PREFIX dct: <" + DCTerms.getURI() + ">\n" +
-             "" +
-             "SELECT ?uri ?mime" +
-             "  WHERE {" +
-             "    ?uri dct:format [" +
-             "          a dct:IMT ;" +
-             "          rdf:value ?mime" +
-             "     ]" +
-             "  }"
-    );
+public class JenaTripleStore extends AbstractTripleStore implements TripleStore {
 
     private Dataset dataset;
 
         return dataset;
     }
 
-    public void initialise() {
-        final Model model = getDataset().getDefaultModel();
-        try {
-            model.add(CPTerms.Collection, RDFS.subClassOf, ORE.Aggregation);
-            model.add(CPTerms.Item, RDFS.subClassOf, ORE.Aggregation);
-            model.commit();
-        } catch (RuntimeException e) {
-            model.abort();
-            throw e;
-        } finally {
-            model.close();
-        }
-    }
-
     private Dataset getDataset() {
         checkDataset();
         return dataset;
     }
 
     @Override
-    public ResourceInfo getResouceInfo(final URI uri) {
-
-        final QuerySolutionMap initialBinding = new QuerySolutionMap();
-        initialBinding.add("uri", ResourceFactory.createResource(uri.toString()));
-
-        final List<URI> types = new ArrayList<URI>();
-        final List<MimeType> mimeTypes = new ArrayList<MimeType>();
-
-        final Lock lock = getLock();
-        try {
-            lock.enterCriticalSection(Lock.READ);
-
-            final QueryExecution qExec = QueryExecutionFactory.create(QUERY_RESOURCE_INFO, dataset, initialBinding);
-
-            try {
-                qExec.getContext().set(TDB.symUnionDefaultGraph, true);
-                final ResultSet resultSet = qExec.execSelect();
-                while (resultSet.hasNext()) {
-                    final QuerySolution qs = resultSet.next();
-                    final RDFNode s = qs.get("t");
-                    final RDFNode s1 = qs.get("m");
-
-                    final String type = s.asResource().getURI();
-                    types.add(URI.create(type));
-
-                    if (s1 != null) {
-                        final String mime = s1.asLiteral().getString();
-                        mimeTypes.add(new MimeType(mime));
-                    }
-                }
-            } finally {
-                qExec.close();
-            }
-
-        } finally {
-            lock.leaveCriticalSection();
-        }
-
-        return new ResourceInfo(types, mimeTypes);
-    }
-
-
-    @Override
-    public Map<MimeType, URI> getResourceVariants(final URI uri) {
-        final QuerySolutionMap initialBinding = new QuerySolutionMap();
-        initialBinding.add("uri", ResourceFactory.createResource(uri.toString()));
-
-        final Map<MimeType,URI> variantMap = new LinkedHashMap<MimeType,URI>();
-
-        final Lock lock = getLock();
-        try {
-            lock.enterCriticalSection(Lock.READ);
-            final QueryExecution qExec = QueryExecutionFactory.create(QUERY_RESOURCE_MAP_VARIANTS, dataset, initialBinding);
-
-            try {
-                qExec.getContext().set(TDB.symUnionDefaultGraph, true);
-                final ResultSet resultSet = qExec.execSelect();
-                while (resultSet.hasNext()) {
-                    final QuerySolution qs = resultSet.next();
-                    final RDFNode remNode = qs.get("rem");
-                    final RDFNode mimeNode = qs.get("mime");
-
-                    final Resource remResource = remNode.asResource();
-                    final String rem = remResource.getURI();
-                    final Literal mimeLiteral = mimeNode.asLiteral();
-                    final String mime = mimeLiteral.getString();
-
-                    final MimeType key = new MimeType(mime);
-                    if (variantMap.containsKey(key)) {
-                        LOG.warn("Duplicate variant (" + mime + ") for aggregation [" + uri + "]");
-                    } else {
-                        variantMap.put(key, URI.create(rem));
-                    }
-                }
-            } finally {
-                qExec.close();
-            }
-
-        } finally {
-            lock.leaveCriticalSection();
-        }
-
-        return variantMap;
-    }
-
-    @Override
-    public List<ItemInfo> getItems(final URI aggregation) {
-        final QuerySolutionMap initialBinding = new QuerySolutionMap();
-        initialBinding.add("aggregation", ResourceFactory.createResource(aggregation.toString()));
-
-        final List<ItemInfo> items = new ArrayList<ItemInfo>();
-        final Lock lock = getLock();
-        try {
-            lock.enterCriticalSection(Lock.READ);
-            final QueryExecution qExec = QueryExecutionFactory.create(QUERY_AGGREGATED_ITEMS, dataset, initialBinding);
-            try {
-                qExec.getContext().set(TDB.symUnionDefaultGraph, true);
-                final ResultSet resultSet = qExec.execSelect();
-                while (resultSet.hasNext()) {
-                    final QuerySolution qs = resultSet.next();
-                    final ItemInfo itemInfo = createItemInfo(qs);
-                    items.add(itemInfo);
-                }
-            } finally {
-                qExec.close();
-            }
-
-        } finally {
-            lock.leaveCriticalSection();
-        }
-
-        return items;
-    }
-
-
     public void close() {
         if (dataset != null) {
             dataset.close();
         dataset = null;
     }
 
-
     public void load(final InputStream in, final URI base, final String format) {
         checkDataset();
         dataset.getDefaultModel().read(in, base.toString(), format);
     }
 
-    public void loadIntoModel(final URI model, final InputStream in, final URI base, final String format) {
-        checkDataset();
-        dataset.getNamedModel(model.toString()).read(in, base.toString(), format);
-    }
-
     private Model getNamedModel(final String uri) {
         checkDataset();
         return dataset.getNamedModel(uri);
     }
 
+    @Override
     public Model getModel(final URI uri) {
         final Model copy = ModelFactory.createDefaultModel();
         final Lock lock = getLock();
         return containsNamedModel(uri.toString());
     }
 
-    protected Lock getLock() {
-        return dataset.getLock();
-    }
-
     @Override
     public boolean containsResource(final URI uri) {
         checkDataset();
     }
 
     @Override
-    public QueryExecution createQueryExecution(final Query query) {
-        final QueryExecution qExec = QueryExecutionFactory.create(query, dataset);
-        qExec.getContext().set(TDB.symUnionDefaultGraph, true);
-        return qExec;
-    }
-
-
-    @Override
-    public List<ItemInfo> getRecentItemsInfo(final int n) {
-        final List<ItemInfo> items = new ArrayList<ItemInfo>();
-
-//        QuerySolutionMap initialBinding = new QuerySolutionMap();
-//        initialBinding.add("limit", ResourceFactory.createPlainLiteral(String.valueOf(n)));
-
-        final Lock lock = getLock();
-        try {
-            lock.enterCriticalSection(Lock.READ);
-            final QueryExecution qExec = QueryExecutionFactory.create(QUERY_RECENT_ITEMS_INFO, dataset);
-            try {
-                qExec.getContext().set(TDB.symUnionDefaultGraph, true);
-                final ResultSet resultSet = qExec.execSelect();
-                while (resultSet.hasNext()) {
-                    final QuerySolution qs = resultSet.next();
-                    final ItemInfo itemInfo = createItemInfo(qs);
-                    items.add(itemInfo);
-                }
-            } finally {
-                qExec.close();
-            }
-
-        } finally {
-            lock.leaveCriticalSection();
-        }
-
-        return items;
-    }
-
-    @Override
-    public List<URI> getRecentItems() {
-        final List<URI> items = new ArrayList<URI>();
-
-
-        final Lock lock = getLock();
-        try {
-            lock.enterCriticalSection(Lock.READ);
-            final QueryExecution qExec = QueryExecutionFactory.create(QUERY_RECENT_ITEM_URIS, dataset);
-            try {
-                qExec.getContext().set(TDB.symUnionDefaultGraph, true);
-                final ResultSet resultSet = qExec.execSelect();
-                while (resultSet.hasNext()) {
-                    final QuerySolution qs = resultSet.next();
-                    final RDFNode u = qs.get("uri");
-                    final URI uri = URI.create(u.asResource().getURI());
-                    items.add(uri);
-                }
-            } finally {
-                qExec.close();
-            }
-
-        } finally {
-            lock.leaveCriticalSection();
-        }
-
-        return items;
-    }
-
-    @Override
-    public ItemInfo getItemInfo(final URI uri) {
-
-        final QuerySolutionMap initialBinding = new QuerySolutionMap();
-        initialBinding.add("uri", ResourceFactory.createResource(uri.toString()));
-
-
-        final Lock lock = getLock();
-        try {
-            lock.enterCriticalSection(Lock.READ);
-
-            final QueryExecution qExec = QueryExecutionFactory.create(QUERY_ITEM_INFO, dataset);
-            try {
-                qExec.setInitialBinding(initialBinding);
-                qExec.getContext().set(TDB.symUnionDefaultGraph, true);
-                final ResultSet resultSet = qExec.execSelect();
-                while (resultSet.hasNext()) {
-                    final QuerySolution qs = resultSet.next();
-                    final ItemInfo itemInfo = createItemInfo(qs);
-                    return itemInfo;
-                }
-            } finally {
-                qExec.close();
-            }
-
-        } finally {
-            lock.leaveCriticalSection();
-        }
-
-        return null;
-    }
-
-    @Override
-    public String getMediaType(final URI uri) {
-        final QuerySolutionMap initialBinding = new QuerySolutionMap();
-        initialBinding.add("uri", ResourceFactory.createResource(uri.toString()));
-
-        final Lock lock = getLock();
-        try {
-            lock.enterCriticalSection(Lock.READ);
-            final QueryExecution qExec = QueryExecutionFactory.create(QUERY_MEDIA_TYPE, dataset, initialBinding);
-            try {
-                qExec.getContext().set(TDB.symUnionDefaultGraph, true);
-                final ResultSet resultSet = qExec.execSelect();
-                while (resultSet.hasNext()) {
-                    final QuerySolution qs = resultSet.next();
-                    final RDFNode mime = qs.get("mime");
-                    if (mime.isLiteral()) {
-                        return mime.asLiteral().getString();
-                    }
-                }
-            } finally {
-                qExec.close();
-            }
-
-        } finally {
-            lock.leaveCriticalSection();
-        }
-
-        return null;
-    }
-
-    @Override
-    public <T> List<T> executeSelect(final Query query, final QuerySolution initialBinding, final QuerySolutionHandler<T> querySolutionHandler) {
-
-        final List<T> list = new ArrayList<T>();
-
-        final Lock lock = getLock();
-        try {
-            lock.enterCriticalSection(Lock.READ);
-            final QueryExecution qExec = QueryExecutionFactory.create(query, dataset, initialBinding);
-            try {
-                qExec.getContext().set(TDB.symUnionDefaultGraph, true);
-                final ResultSet resultSet = qExec.execSelect();
-                while (resultSet.hasNext()) {
-                    final QuerySolution qs = resultSet.next();
-                    final T result = querySolutionHandler.handleQuerySolution(qs);
-                    list.add(result);
-                }
-            } finally {
-                qExec.close();
-            }
-
-        } finally {
-            lock.leaveCriticalSection();
-        }
-
-        return list;
-    }
-
-    @Override
     public boolean containsResourceOfType(final Resource resource, final Resource type) {
         final DatasetGraph datasetGraph = getDataset().asDatasetGraph();
         final Iterator<Quad> it = datasetGraph.find(Node.ANY, resource.asNode(), RDF.type.asNode(), type.asNode());
         }
     }
 
-    private ItemInfo createItemInfo(final QuerySolution qs) {
-        final RDFNode uri = qs.get("uri");
-        final RDFNode title = qs.get("title");
-        final RDFNode thumbnail = qs.get("thumbnail");
-        final RDFNode dataType = qs.get("type");
-        final ItemInfoBuilder builder = new ItemInfoBuilder(uri.asResource().getURI());
-        if (title != null) {
-            builder.setTitle(title.asLiteral().getString());
-        }
-        if (thumbnail != null) {
-            builder.setThumbnailUri(thumbnail.asResource().getURI());
-        }
-        if (dataType != null) {
-            builder.setDataType(dataType.asLiteral().getString());
-        }
-        return builder.build();
+    @Override
+    public QueryExecution createQueryExecution(final Query query) {
+        final QueryExecution qExec = QueryExecutionFactory.create(query, dataset);
+        qExec.getContext().set(TDB.symUnionDefaultGraph, true);
+        return qExec;
     }
 
-    private void closeQuietly(final Iterable<Model> models) {
+    @Override
+    protected QueryExecution createQueryExecution(final Query query, final QuerySolution initialBindings) {
+        final QueryExecution qExec = createQueryExecution(query);
+        qExec.setInitialBinding(initialBindings);
+        return qExec;
+    }
+
+    @Override
+    protected Lock getLock() {
+        return dataset.getLock();
+    }
+
+    private static void closeQuietly(final Iterable<Model> models) {
         for (final Model model : models) {
             closeQuietly(model);
         }
     }
 
-    private void closeQuietly(final Model model) {
+    private static void closeQuietly(final Model model) {
         if (model != null) {
             try {
                 model.close();

File chempound-app/src/test/java/net/chempound/datastore/JenaTripleStoreTest.java

+package net.chempound.datastore;
+
+import com.hp.hpl.jena.rdf.model.*;
+import com.hp.hpl.jena.tdb.TDBFactory;
+import com.hp.hpl.jena.vocabulary.RDF;
+import net.chempound.bean.ResourceInfo;
+import net.chempound.rdf.DCTerms;
+import net.chempound.util.MimeType;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.net.URI;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+
+import static java.util.Arrays.asList;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Sam Adams
+ */
+public class JenaTripleStoreTest {
+    
+    private static final URI ITEM1 = URI.create("http://repo.example.net/content/item1/");
+    private static final URI ITEM2 = URI.create("http://repo.example.net/content/item2/");
+    private static final RDFNode ANY = (RDFNode) null;
+
+    private TripleStore tripleStore;
+
+    @Before
+    public void setUp() throws Exception {
+        tripleStore = new JenaTripleStore(TDBFactory.createDataset());
+    }
+
+    @Test
+    public void testGetModelReturnsIndependentObjects() {
+        tripleStore.saveModel(ITEM1, createItem1Model());
+        
+        Model model1 = tripleStore.getModel(ITEM1);
+        model1.getResource(ITEM1.toString()).addLiteral(DCTerms.title, "Other item");
+
+        Model model2 = tripleStore.getModel(ITEM1);
+        List<Statement> list = model2.listStatements(model2.getResource(ITEM1.toString()), DCTerms.title, ANY).toList();
+        assertEquals(1, list.size());
+        assertEquals("Item 1", list.get(0).getString());
+    }
+
+    @Test
+    public void testGetResourceInfoReturnsRdfTypes() {
+        tripleStore.saveModel(ITEM1, createItem1Model());
+        
+        ResourceInfo resourceInfo = tripleStore.getResouceInfo(ITEM1);
+        assertEquals(new LinkedHashSet<URI>(asList(URI.create("http://type/foo"), URI.create("http://type/bar"))), resourceInfo.getTypes());
+        assertEquals(new LinkedHashSet<MimeType>(), resourceInfo.getMimeTypes());
+    }
+
+    @Test
+    public void testGetResourceInfoReturnsMimeTypes() {
+        tripleStore.saveModel(ITEM2, createItem2Model());
+
+        ResourceInfo resourceInfo = tripleStore.getResouceInfo(ITEM2);
+        assertEquals(new LinkedHashSet<URI>(asList(URI.create("http://type/xyzzy"))), resourceInfo.getTypes());
+        assertEquals(new LinkedHashSet<MimeType>(asList(MimeType.TEXT_PLAIN)), resourceInfo.getMimeTypes());
+    }
+
+
+    private Model createItem1Model() {
+        final Model model = ModelFactory.createDefaultModel();
+        model.getResource(ITEM1.toString()).addLiteral(DCTerms.title, "Item 1");
+        model.getResource(ITEM1.toString()).addProperty(RDF.type, model.getResource("http://type/foo"));
+        model.getResource(ITEM1.toString()).addProperty(RDF.type, model.getResource("http://type/bar"));
+        return model;
+    }
+
+    private Model createItem2Model() {
+        final Model model = ModelFactory.createDefaultModel();
+        model.getResource(ITEM2.toString()).addLiteral(DCTerms.title, "Item 2");
+        model.getResource(ITEM2.toString()).addProperty(RDF.type, model.getResource("http://type/xyzzy"));
+        Resource format = model.createResource();
+        format.addLiteral(RDF.value, "text/plain");
+        model.getResource(ITEM2.toString()).addProperty(DCTerms.format, format);
+        return model;
+    }
+
+}

File chempound-webapp/src/main/java/net/chempound/webapp/sparql/SparqlResource.java

         final QueryExecution qExec = tripleStore.createQueryExecution(query);
         qExec.getContext().set(TDB.symUnionDefaultGraph, true);
 
-        
+
         return html ? new HTMLRepresentation(qExec, queryString) : new XMLRepresentation(qExec);
     }