diff --git a/oslc-java-client/src/main/java/org/eclipse/lyo/client/oslc/resources/OslcQueryResult.java b/oslc-java-client/src/main/java/org/eclipse/lyo/client/oslc/resources/OslcQueryResult.java index 9019398..d21d100 100644 --- a/oslc-java-client/src/main/java/org/eclipse/lyo/client/oslc/resources/OslcQueryResult.java +++ b/oslc-java-client/src/main/java/org/eclipse/lyo/client/oslc/resources/OslcQueryResult.java @@ -43,7 +43,8 @@ import org.apache.jena.rdf.model.Statement; import org.apache.jena.rdf.model.StmtIterator; import org.apache.jena.vocabulary.RDFS; - +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * The results of an OSLC query. If the query was paged, subsequent pages can be retrieved using the Iterator interface. @@ -51,11 +52,14 @@ * This class is not currently thread safe. */ public class OslcQueryResult implements Iterator { + + private final static Logger log = LoggerFactory.getLogger(OslcQueryResult.class); + /** * The default member property to look for in OSLC query results - * (rdfs:member). Can be changed using {@link #setMemberProperty(Property)}. + * (rdfs:member). Can be changed using {@link #setMemberProperty(String)}. */ - public final static Property DEFAULT_MEMBER_PROPERTY = RDFS.member; + public final static Property DEFAULT_MEMBER_PROPERTY = RDFS.member; /** * If system property {@value} is set to true, find any member in the @@ -73,14 +77,13 @@ private AnyMemberSelector(Resource subject) { } public boolean selects(Statement s) { - String fqPredicateName = s.getPredicate().getNameSpace() + s.getPredicate().getLocalName(); - if (OSLCConstants.RDF_TYPE_PROP.equals(fqPredicateName)) { - return false; - } + String fqPredicateName = s.getPredicate().getNameSpace() + s.getPredicate() + .getLocalName(); + return !OSLCConstants.RDF_TYPE_PROP.equals(fqPredicateName) && s.getObject() + .isResource(); - return s.getObject().isResource(); - } - } + } + } private final OslcQuery query; @@ -101,20 +104,15 @@ public boolean selects(Statement s) { public OslcQueryResult(OslcQuery query, ClientResponse response) { this.query = query; this.response = response; - this.pageNumber = 1; - - } private OslcQueryResult(OslcQueryResult prev) { - this.query = new OslcQuery(prev); + this.query = prev.query; this.response = this.query.getResponse(); this.membersResource = prev.membersResource; this.memberProperty = prev.memberProperty; - this.pageNumber = prev.pageNumber + 1; - } private synchronized void initializeRdf() { @@ -128,11 +126,10 @@ private synchronized void initializeRdf() { Property responseInfo = rdfModel.createProperty(OslcConstants.OSLC_CORE_NAMESPACE, "ResponseInfo"); ResIterator iter = rdfModel.listResourcesWithProperty(rdfType, responseInfo); - //There should only be one - take the first infoResource = null; - while (iter.hasNext()) { + if (iter.hasNext()) { + // There should only be one - take the first infoResource = iter.next(); - break; } membersResource = rdfModel.getResource(query.getCapabilityUrl()); } @@ -146,7 +143,14 @@ String getNextPageUrl() { StmtIterator iter = rdfModel.listStatements(select); if (iter.hasNext()) { Statement nextPage = iter.next(); - nextPageUrl = nextPage.getResource().getURI(); + final RDFNode nextPageObject = nextPage.getObject(); + if(nextPageObject != null && nextPageObject.isResource()) { + final Resource nextPageResource = nextPageObject.asResource(); + nextPageUrl = nextPageResource.getURI(); + } else { + log.warn("oslc:nextPage does not point to an RDF resource: {}", nextPageObject); + nextPageUrl = null; + } } else { nextPageUrl = ""; } @@ -158,12 +162,13 @@ String getNextPageUrl() { * @return whether there is another page of results after this */ public boolean hasNext() { - return (!"".equals(getNextPageUrl())); + final String nextPageUrl = getNextPageUrl(); + final boolean nextUrlNotEmpty = nextPageUrl != null && nextPageUrl.trim().length() > 0; + return nextUrlNotEmpty; } /** * @return the next page of results - * @throws NoSuchElementException if there is no next page */ public OslcQueryResult next() { return new OslcQueryResult(this); @@ -234,7 +239,7 @@ private Selector getMemberSelector() { */ public String[] getMembersUrls() { initializeRdf(); - ArrayList membersUrls = new ArrayList(); + ArrayList membersUrls = new ArrayList<>(); Selector select = getMemberSelector(); StmtIterator iter = rdfModel.listStatements(select); while (iter.hasNext()) { @@ -247,7 +252,6 @@ public String[] getMembersUrls() { /** * Return the enumeration of queried results from this page * - * @param T * @param clazz * * @return member statements from current page. @@ -257,47 +261,30 @@ public Iterable getMembers(final Class clazz) { Selector select = getMemberSelector(); final StmtIterator iter = rdfModel.listStatements(select); - Iterable result = new Iterable() { - public Iterator - iterator() { - return new Iterator() { - public boolean hasNext() { - return iter.hasNext(); - } - - @SuppressWarnings("unchecked") - public T next() { - Statement member = iter.next(); - - try { - return (T)JenaModelHelper.fromJenaResource((Resource)member.getObject(), clazz); - } catch (IllegalArgumentException e) { - throw new IllegalStateException(e.getMessage()); - } catch (SecurityException e) { - throw new IllegalStateException(e.getMessage()); - } catch (DatatypeConfigurationException e) { - throw new IllegalStateException(e.getMessage()); - } catch (IllegalAccessException e) { - throw new IllegalStateException(e.getMessage()); - } catch (InstantiationException e) { - throw new IllegalStateException(e.getMessage()); - } catch (InvocationTargetException e) { - throw new IllegalStateException(e.getMessage()); - } catch (OslcCoreApplicationException e) { - throw new IllegalStateException(e.getMessage()); - } catch (URISyntaxException e) { - throw new IllegalStateException(e.getMessage()); - } catch (NoSuchMethodException e) { - throw new IllegalStateException(e.getMessage()); - } - } - - public void remove() { - iter.remove(); - } - }; - } - }; + Iterable result = () -> new Iterator() { + public boolean hasNext() { + return iter.hasNext(); + } + + @SuppressWarnings("unchecked") + public T next() { + Statement member = iter.next(); + + try { + return (T) JenaModelHelper.fromJenaResource((Resource) member.getObject(), + clazz); + } catch (IllegalArgumentException | SecurityException | IllegalAccessException | + DatatypeConfigurationException | InvocationTargetException | + InstantiationException | URISyntaxException | OslcCoreApplicationException + | NoSuchMethodException e) { + throw new IllegalStateException(e.getMessage()); + } + } + + public void remove() { + iter.remove(); + } + }; return result; } diff --git a/oslc-java-client/src/test/java/org/eclipse/lyo/client/test/OslcQueryResultTest.java b/oslc-java-client/src/test/java/org/eclipse/lyo/client/test/OslcQueryResultTest.java index f37d0cd..92bb543 100644 --- a/oslc-java-client/src/test/java/org/eclipse/lyo/client/test/OslcQueryResultTest.java +++ b/oslc-java-client/src/test/java/org/eclipse/lyo/client/test/OslcQueryResultTest.java @@ -17,7 +17,7 @@ *******************************************************************************/ package org.eclipse.lyo.client.test; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; import static org.mockito.Mockito.when; import java.io.InputStream; @@ -72,6 +72,30 @@ public void testQuery() { assertEquals(2, result.getMembersUrls().length); } + @Test + public void testQueryResultHasNext() { + ClientResponse mockedResponse = mockClientResponse("/queryResponseWithNext.xml"); + + OslcQueryParameters params = new OslcQueryParameters(); + params.setWhere("ex:product=\"Product A\""); + OslcQuery query = new OslcQuery(new OslcClient(), "http://example.com/provider/query", + params); + OslcQueryResult result = new OslcQueryResult(query, mockedResponse); + assertTrue("Next URI resource should be detected", result.hasNext()); + } + + @Test + public void testQueryResultWithEmptyNext() { + ClientResponse mockedResponse = mockClientResponse("/queryResponseWithEmptyNext.xml"); + + OslcQueryParameters params = new OslcQueryParameters(); + params.setWhere("ex:product=\"Product A\""); + OslcQuery query = new OslcQuery(new OslcClient(), "http://example.com/provider/query", + params); + OslcQueryResult result = new OslcQueryResult(query, mockedResponse); + assertFalse("Empty Next URI resource should be ignored", result.hasNext()); + } + @Test public void testBlogQuery() { ClientResponse mockedResponse = mockClientResponse("/blogQuery.rdf"); diff --git a/oslc-java-client/src/test/resources/queryResponse.rdf b/oslc-java-client/src/test/resources/queryResponse.rdf index 3f8561f..27d5cd2 100644 --- a/oslc-java-client/src/test/resources/queryResponse.rdf +++ b/oslc-java-client/src/test/resources/queryResponse.rdf @@ -1,7 +1,7 @@ - + 2 diff --git a/oslc-java-client/src/test/resources/queryResponseWithEmptyNext.xml b/oslc-java-client/src/test/resources/queryResponseWithEmptyNext.xml new file mode 100644 index 0000000..78db7f2 --- /dev/null +++ b/oslc-java-client/src/test/resources/queryResponseWithEmptyNext.xml @@ -0,0 +1,12 @@ + + + + 2 + + + + + + + diff --git a/oslc-java-client/src/test/resources/queryResponseWithNext.xml b/oslc-java-client/src/test/resources/queryResponseWithNext.xml new file mode 100644 index 0000000..0ee6575 --- /dev/null +++ b/oslc-java-client/src/test/resources/queryResponseWithNext.xml @@ -0,0 +1,12 @@ + + + + 2 + + + + + + + diff --git a/oslc4j-client/src/test/resources/queryResponse.rdf b/oslc4j-client/src/test/resources/queryResponse.rdf index 3f8561f..27d5cd2 100644 --- a/oslc4j-client/src/test/resources/queryResponse.rdf +++ b/oslc4j-client/src/test/resources/queryResponse.rdf @@ -1,7 +1,7 @@ - + 2