Repository: marmotta Updated Branches: refs/heads/MARMOTTA-556 ec0b81967 -> 9f39e3283
MARMOTTA-556: moved ldf logic out of a RDFHandler to properly manage some errors Project: http://git-wip-us.apache.org/repos/asf/marmotta/repo Commit: http://git-wip-us.apache.org/repos/asf/marmotta/commit/157e44e2 Tree: http://git-wip-us.apache.org/repos/asf/marmotta/tree/157e44e2 Diff: http://git-wip-us.apache.org/repos/asf/marmotta/diff/157e44e2 Branch: refs/heads/MARMOTTA-556 Commit: 157e44e2b884db874a08509edf2a801766099fc7 Parents: ec0b819 Author: Sergio Fernández <[email protected]> Authored: Mon Oct 27 16:56:20 2014 +0100 Committer: Sergio Fernández <[email protected]> Committed: Mon Oct 27 16:56:20 2014 +0100 ---------------------------------------------------------------------- .../marmotta/platform/ldf/api/LdfService.java | 33 ++-- .../platform/ldf/services/LdfServiceImpl.java | 98 ++++++++-- .../platform/ldf/sesame/LdfRDFHandler.java | 185 ------------------- .../platform/ldf/webservices/LdfWebService.java | 17 +- 4 files changed, 106 insertions(+), 227 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/marmotta/blob/157e44e2/platform/marmotta-ldf/src/main/java/org/apache/marmotta/platform/ldf/api/LdfService.java ---------------------------------------------------------------------- diff --git a/platform/marmotta-ldf/src/main/java/org/apache/marmotta/platform/ldf/api/LdfService.java b/platform/marmotta-ldf/src/main/java/org/apache/marmotta/platform/ldf/api/LdfService.java index c2d7dff..e8f84ce 100644 --- a/platform/marmotta-ldf/src/main/java/org/apache/marmotta/platform/ldf/api/LdfService.java +++ b/platform/marmotta-ldf/src/main/java/org/apache/marmotta/platform/ldf/api/LdfService.java @@ -17,14 +17,11 @@ */ package org.apache.marmotta.platform.ldf.api; +import org.openrdf.model.Model; import org.openrdf.model.Resource; import org.openrdf.model.URI; -import org.openrdf.rio.RDFFormat; import org.openrdf.model.Value; import org.openrdf.repository.RepositoryException; -import org.openrdf.rio.RDFHandlerException; - -import java.io.OutputStream; /** * Linked Media Fragments service @@ -36,33 +33,31 @@ public interface LdfService { final static int PAGE_SIZE = 100; /** - * Writes a fragment matching the specified triple fragment pattern + * Gets a fragment matching the specified triple fragment pattern * specified (null values are wildcards). * * @param subject fragment subject * @param predicate fragmnent predicate * @param object fragment object * @param page number of page (starting with 1) - * @param format RDF serialization - * @param out output stream where write the fragment + * @return fragment */ - void writeFragment(String subject, String predicate, String object, int page, RDFFormat format, OutputStream out) throws RepositoryException, RDFHandlerException, IllegalArgumentException; + Model getFragment(String subject, String predicate, String object, int page) throws RepositoryException, IllegalArgumentException; /** - * Writes a fragment matching the specified triple fragment pattern + * Gets a fragment matching the specified triple fragment pattern * specified (null values are wildcards). * * @param subject fragment subject * @param predicate fragmnent predicate * @param object fragment object * @param page number of page (starting with 1) - * @param format RDF serialization - * @param out output stream where write the fragment + * @return fragment */ - void writeFragment(URI subject, URI predicate, Value object, int page, RDFFormat format, OutputStream out) throws RepositoryException, RDFHandlerException, IllegalArgumentException; + Model getFragment(URI subject, URI predicate, Value object, int page) throws RepositoryException, IllegalArgumentException; /** - * Writes a fragment matching the specified quad fragment pattern + * Gets a fragment matching the specified quad fragment pattern * specified (null values are wildcards). * * @param subject fragment subject @@ -70,13 +65,12 @@ public interface LdfService { * @param object fragment object * @param context named graph * @param page number of page (starting with 1) - * @param format RDF serialization - * @param out output stream where write the fragment + * @return fragment */ - void writeFragment(String subject, String predicate, String object, String context, int page, RDFFormat format, OutputStream out) throws RepositoryException, RDFHandlerException, IllegalArgumentException; + Model getFragment(String subject, String predicate, String object, String context, int page) throws RepositoryException, IllegalArgumentException; /** - * Writes a fragment matching the specified quad fragment pattern + * Gets a fragment matching the specified quad fragment pattern * specified (null values are wildcards). * * @param subject fragment subject @@ -84,9 +78,8 @@ public interface LdfService { * @param object fragment object * @param context named graph * @param page number of page (starting with 1) - * @param format RDF serialization - * @param out output stream where write the fragment + * @return fragment */ - void writeFragment(URI subject, URI predicate, Value object, Resource context, int page, RDFFormat format, OutputStream out) throws RepositoryException, RDFHandlerException, IllegalArgumentException; + Model getFragment(URI subject, URI predicate, Value object, Resource context, int page) throws RepositoryException, IllegalArgumentException; } http://git-wip-us.apache.org/repos/asf/marmotta/blob/157e44e2/platform/marmotta-ldf/src/main/java/org/apache/marmotta/platform/ldf/services/LdfServiceImpl.java ---------------------------------------------------------------------- diff --git a/platform/marmotta-ldf/src/main/java/org/apache/marmotta/platform/ldf/services/LdfServiceImpl.java b/platform/marmotta-ldf/src/main/java/org/apache/marmotta/platform/ldf/services/LdfServiceImpl.java index 4ccdfeb..09a4dc8 100644 --- a/platform/marmotta-ldf/src/main/java/org/apache/marmotta/platform/ldf/services/LdfServiceImpl.java +++ b/platform/marmotta-ldf/src/main/java/org/apache/marmotta/platform/ldf/services/LdfServiceImpl.java @@ -17,23 +17,30 @@ */ package org.apache.marmotta.platform.ldf.services; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableList; import org.apache.commons.lang3.StringUtils; import org.apache.marmotta.commons.sesame.repository.ResultUtils; +import org.apache.marmotta.commons.vocabulary.XSD; import org.apache.marmotta.platform.core.api.triplestore.SesameService; import org.apache.marmotta.platform.ldf.api.LdfService; -import org.apache.marmotta.platform.ldf.sesame.LdfRDFHandler; +import org.apache.marmotta.platform.ldf.vocab.HYDRA; +import org.apache.marmotta.platform.ldf.vocab.VOID; import org.openrdf.model.*; +import org.openrdf.model.impl.TreeModel; import org.openrdf.model.impl.ValueFactoryImpl; +import org.openrdf.model.vocabulary.RDF; import org.openrdf.repository.RepositoryConnection; import org.openrdf.repository.RepositoryException; import org.openrdf.repository.RepositoryResult; -import org.openrdf.rio.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; -import java.io.OutputStream; import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; /** * Linked Media Fragments service implementation @@ -48,17 +55,17 @@ public class LdfServiceImpl implements LdfService { private SesameService sesameService; @Override - public void writeFragment(String subjectStr, String predicateStr, String objectStr, int page, RDFFormat format, OutputStream out) throws RepositoryException, RDFHandlerException, IllegalArgumentException { - writeFragment(subjectStr, predicateStr, objectStr, null, page, format, out); + public Model getFragment(String subjectStr, String predicateStr, String objectStr, int page) throws RepositoryException, IllegalArgumentException { + return getFragment(subjectStr, predicateStr, objectStr, null, page); } @Override - public void writeFragment(URI subject, URI predicate, Value object, int page, RDFFormat format, OutputStream out) throws RepositoryException, RDFHandlerException, IllegalArgumentException { - writeFragment(subject, predicate, object, null, page, format, out); + public Model getFragment(URI subject, URI predicate, Value object, int page) throws RepositoryException, IllegalArgumentException { + return getFragment(subject, predicate, object, null, page); } @Override - public void writeFragment(String subjectStr, String predicateStr, String objectStr, String contextStr, int page, RDFFormat format, OutputStream out) throws RepositoryException, RDFHandlerException, IllegalArgumentException { + public Model getFragment(String subjectStr, String predicateStr, String objectStr, String contextStr, int page) throws RepositoryException, IllegalArgumentException { final ValueFactoryImpl vf = new ValueFactoryImpl(); URI subject = null; @@ -101,22 +108,77 @@ public class LdfServiceImpl implements LdfService { } } - writeFragment(subject, predicate, object, context, page, format, out); + return getFragment(subject, predicate, object, context, page); } @Override - public void writeFragment(URI subject, URI predicate, Value object, Resource context, int page, RDFFormat format, OutputStream out) throws RepositoryException, RDFHandlerException, IllegalArgumentException { + public Model getFragment(URI subject, URI predicate, Value object, Resource context, int page) throws RepositoryException, IllegalArgumentException { final RepositoryConnection conn = sesameService.getConnection(); - try { - conn.begin(); - RepositoryResult<Statement> statements = conn.getStatements(subject, predicate, object, true, context); - RDFHandler handler = new LdfRDFHandler(Rio.createWriter(format, out), context, page); - Rio.write(ResultUtils.iterable(statements), handler); - } finally { - if (conn != null && conn.isOpen()) { - conn.close(); + conn.begin(); + + //first get the triple fragment for ordering by a fixed criteria + //TODO: do this effectively + final RepositoryResult<Statement> results = conn.getStatements(subject, predicate, object, true, context); + final List<Statement> statements = FluentIterable.from(ResultUtils.iterable(results)).toSortedList(new Comparator<Statement>() { + @Override + public int compare(Statement s1, Statement s2) { + int subjectComparison = s1.getSubject().stringValue().compareTo(s2.getSubject().stringValue()); + int predicatedComparison = s1.getPredicate().stringValue().compareTo(s2.getPredicate().stringValue()); + if (subjectComparison != 0) { + return subjectComparison; + } else if (predicatedComparison != 0) { + return predicatedComparison; + } else if ((s1.getObject() instanceof Literal) && (s2.getObject() instanceof Resource)) { + return 1; + } else if ((s1.getObject() instanceof Resource) && (s2.getObject() instanceof Literal)) { + return -1; + } else { + return s1.getObject().stringValue().compareTo(s2.getObject().stringValue()); + } } + }); + //ResultUtils takes care of closing the connection when consuming the RepositoryResult + + //then filter + final int size = statements.size(); + final int offset = LdfService.PAGE_SIZE * (page - 1); + + if (offset > size) { + throw new IllegalArgumentException("page " + page + " can't be generated, empty fragment"); + } + + final Model model = new TreeModel(); + final ValueFactoryImpl vf = new ValueFactoryImpl(); + + final int limit = LdfService.PAGE_SIZE < size - offset ? LdfService.PAGE_SIZE : size - offset; + List<Statement> filteredStatements = statements.subList(offset, limit); + if (filteredStatements.isEmpty()) { + throw new IllegalArgumentException("empty fragment"); } + + //add the fragment + model.addAll(filteredStatements); + + //and add ldf metadata + Resource dataset = context != null ? context : vf.createBNode(); + model.add(dataset, RDF.TYPE, VOID.Dataset); + model.add(dataset, RDF.TYPE, HYDRA.Collection); + + Resource fragment = vf.createBNode(); //TODO + model.add(dataset, VOID.subset, fragment); + model.add(fragment, RDF.TYPE, HYDRA.Collection); + if (offset != 0 && limit != size) { + model.add(fragment, RDF.TYPE, HYDRA.PagedCollection); + } + model.add(fragment, VOID.triples, vf.createLiteral(Integer.toString(filteredStatements.size()), XSD.Integer)); + model.add(fragment, HYDRA.totalItems, vf.createLiteral(Integer.toString(filteredStatements.size()), XSD.Integer)); + model.add(fragment, HYDRA.itemsPerPage, vf.createLiteral(Integer.toString(LdfService.PAGE_SIZE), XSD.Integer)); + //TODO: HYDRA_FIRSTPAGE, HYDRA_PREVIOUSPAGE, HYDRA_NEXTPAGE + + //TODO: hydra controls + + return model; + } } http://git-wip-us.apache.org/repos/asf/marmotta/blob/157e44e2/platform/marmotta-ldf/src/main/java/org/apache/marmotta/platform/ldf/sesame/LdfRDFHandler.java ---------------------------------------------------------------------- diff --git a/platform/marmotta-ldf/src/main/java/org/apache/marmotta/platform/ldf/sesame/LdfRDFHandler.java b/platform/marmotta-ldf/src/main/java/org/apache/marmotta/platform/ldf/sesame/LdfRDFHandler.java deleted file mode 100644 index 34ed85b..0000000 --- a/platform/marmotta-ldf/src/main/java/org/apache/marmotta/platform/ldf/sesame/LdfRDFHandler.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * 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.marmotta.platform.ldf.sesame; - -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; -import org.apache.marmotta.commons.vocabulary.XSD; -import org.apache.marmotta.platform.ldf.api.LdfService; -import org.apache.marmotta.platform.ldf.vocab.HYDRA; -import org.apache.marmotta.platform.ldf.vocab.VOID; -import org.openrdf.model.Literal; -import org.openrdf.model.Resource; -import org.openrdf.model.Statement; -import org.openrdf.model.impl.StatementImpl; -import org.openrdf.model.impl.ValueFactoryImpl; -import org.openrdf.model.vocabulary.RDF; -import org.openrdf.model.vocabulary.RDFS; -import org.openrdf.rio.RDFHandler; -import org.openrdf.rio.RDFHandlerException; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -/** - * Specialized statements handler doing some LDF specific thing, - * such as paging or metadata generation, before sending them to - * the delegated RDFHandler. - * - * (TODO: find a more performance solution) - * - * @author Sergio Fernández - */ -public class LdfRDFHandler implements RDFHandler { - - private List<Statement> statements; - private final RDFHandler handler; - private final Resource context; - private final int page; - - /** - * Constructs a PagedRDFHandler with a delegate handler - * - * @param handler The handler to delegate the calls to - */ - public LdfRDFHandler(RDFHandler handler) { - this(handler, null, 1); - } - - /** - * Constructs a PagedRDFHandler with a delegate handler - * - * @param handler The handler to delegate the calls to - * @param page number of page (starting with 1) - */ - public LdfRDFHandler(RDFHandler handler, int page) { - this(handler, null, page); - } - - /** - * Constructs a PagedRDFHandler with a delegate handler - * - * @param handler The handler to delegate the calls to - * @param context dataset - */ - public LdfRDFHandler(RDFHandler handler, Resource context) { - this(handler, context, 1); - } - - /** - * Constructs a PagedRDFHandler with a delegate handler - * - * @param handler The handler to delegate the calls to - * @param context dataset - * @param page number of page (starting with 1) - */ - public LdfRDFHandler(RDFHandler handler, Resource context, int page) { - super(); - this.statements = new ArrayList<>(); - this.handler = handler; - this.context = context; - this.page = page; - } - - @Override - public void startRDF() throws RDFHandlerException { - handler.startRDF(); - } - - @Override - public void endRDF() throws RDFHandlerException { - final ValueFactoryImpl vf = new ValueFactoryImpl(); - - //first order by a fixed criteria - Collections.sort(statements, new Comparator<Statement>() { - @Override - public int compare(Statement s1, Statement s2) { - int subjectComparison = s1.getSubject().stringValue().compareTo(s2.getSubject().stringValue()); - int predicatedComparison = s1.getPredicate().stringValue().compareTo(s2.getPredicate().stringValue()); - if (subjectComparison != 0) { - return subjectComparison; - } else if (predicatedComparison != 0) { - return predicatedComparison; - } else if((s1.getObject() instanceof Literal) && (s2.getObject() instanceof Resource)) { - return 1; - } else if((s1.getObject() instanceof Resource) && (s2.getObject() instanceof Literal)) { - return -1; - } else { - return s1.getObject().stringValue().compareTo(s2.getObject().stringValue()); - } - } - }); - - //then filter - final int size = statements.size(); - final int offset = LdfService.PAGE_SIZE * (page - 1); - if (offset > size) { - //throw new RDFHandlerException("page " + page + " can't be generated"); - Resource error = this.context != null ? this.context : vf.createBNode(); - handler.handleStatement(new StatementImpl(error, RDF.TYPE, HYDRA.Error)); - handler.handleStatement(new StatementImpl(error, RDFS.COMMENT, vf.createLiteral("page " + page + " can't be generated", "en"))); - } else { - final int limit = LdfService.PAGE_SIZE < size - offset ? LdfService.PAGE_SIZE : size - offset; - List<Statement> filteredStatements = statements.subList(offset, limit); - - //send statements to delegate writer - for (Statement statement : filteredStatements) { - handler.handleStatement(statement); - } - - //add ldf metadata - Resource dataset = this.context != null ? this.context : vf.createBNode(); - handler.handleStatement(new StatementImpl(dataset, RDF.TYPE, VOID.Dataset)); - handler.handleStatement(new StatementImpl(dataset, RDF.TYPE, HYDRA.Collection)); - - Resource fragment = vf.createBNode(); //TODO - handler.handleStatement(new StatementImpl(dataset, VOID.subset, fragment)); - handler.handleStatement(new StatementImpl(fragment, RDF.TYPE, HYDRA.Collection)); - if (offset != 0 && limit != size) { - handler.handleStatement(new StatementImpl(fragment, RDF.TYPE, HYDRA.PagedCollection)); - } - handler.handleStatement(new StatementImpl(fragment, VOID.triples, vf.createLiteral(Integer.toString(filteredStatements.size()), XSD.Integer))); - handler.handleStatement(new StatementImpl(fragment, HYDRA.totalItems, vf.createLiteral(Integer.toString(filteredStatements.size()), XSD.Integer))); - handler.handleStatement(new StatementImpl(fragment, HYDRA.itemsPerPage, vf.createLiteral(Integer.toString(LdfService.PAGE_SIZE), XSD.Integer))); - //TODO: HYDRA_FIRSTPAGE, HYDRA_PREVIOUSPAGE, HYDRA_NEXTPAGE - - //TODO: hydra controls - } - - //and actually end the rdf - handler.endRDF(); - } - - @Override - public void handleNamespace(String prefix, String uri) throws RDFHandlerException { - handler.handleNamespace(prefix, uri); - } - - @Override - public void handleStatement(Statement statement) throws RDFHandlerException { - statements.add(statement); - } - - @Override - public void handleComment(String comment) throws RDFHandlerException { - handler.handleComment(comment); - } - -} http://git-wip-us.apache.org/repos/asf/marmotta/blob/157e44e2/platform/marmotta-ldf/src/main/java/org/apache/marmotta/platform/ldf/webservices/LdfWebService.java ---------------------------------------------------------------------- diff --git a/platform/marmotta-ldf/src/main/java/org/apache/marmotta/platform/ldf/webservices/LdfWebService.java b/platform/marmotta-ldf/src/main/java/org/apache/marmotta/platform/ldf/webservices/LdfWebService.java index 0cc1f09..37b554c 100644 --- a/platform/marmotta-ldf/src/main/java/org/apache/marmotta/platform/ldf/webservices/LdfWebService.java +++ b/platform/marmotta-ldf/src/main/java/org/apache/marmotta/platform/ldf/webservices/LdfWebService.java @@ -5,8 +5,8 @@ import org.apache.marmotta.commons.http.ContentType; import org.apache.marmotta.commons.http.MarmottaHttpUtils; import org.apache.marmotta.platform.core.api.config.ConfigurationService; import org.apache.marmotta.platform.core.api.exporter.ExportService; -import org.apache.marmotta.platform.core.util.WebServiceUtil; import org.apache.marmotta.platform.ldf.api.LdfService; +import org.openrdf.model.Model; import org.openrdf.repository.RepositoryException; import org.openrdf.rio.RDFFormat; import org.openrdf.rio.RDFHandlerException; @@ -72,14 +72,23 @@ public class LdfWebService { final String context, final int page, final String accept) { - final RDFFormat format = getFormat(accept); + final Model fragment; + try { + fragment = ldfService.getFragment(subject, predicate, object, context, page); + } catch (RepositoryException e) { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } catch (IllegalArgumentException e) { + return Response.status(Response.Status.BAD_REQUEST).entity(e.getMessage()).build(); + } + + final RDFFormat format = getFormat(accept); StreamingOutput stream = new StreamingOutput() { @Override public void write(OutputStream outputStream) throws IOException, WebApplicationException { try { - ldfService.writeFragment(subject, predicate, object, context, page, format, outputStream); - } catch (RepositoryException | RDFHandlerException | IllegalArgumentException e) { + Rio.write(fragment, outputStream, format); + } catch (RDFHandlerException e) { throw new WebApplicationException(e); } }
