From 23cf0b1119ccd9f6e5ff30456fc1c946af963825 Mon Sep 17 00:00:00 2001 From: Andrew Berezovskyi Date: Wed, 19 Jul 2017 20:10:15 +0200 Subject: [PATCH 1/4] Bug 393073 [WIP] - NPE in the hasNext method Current status is that the empty nextPage property: Causes Jena to deserialise a full triple statement with a non-empty Object: subject = "http://example.com/provider/query?oslc.where=ex%3Aproduct%3D%22Product%20A%2" predicate = "http://open-services.net/ns/core#nextPage" object = "http://example.com/provider/query" Change-Id: I8c1088ebc7101165834fe0a0a4a870e600020812 Signed-off-by: Andrew Berezovskyi --- .../oslc/resources/OslcQueryResult.java | 103 +++++++----------- .../lyo/client/test/OslcQueryResultTest.java | 26 ++++- .../resources/queryResponseWithEmptyNext.xml | 12 ++ .../test/resources/queryResponseWithNext.xml | 13 +++ 4 files changed, 90 insertions(+), 64 deletions(-) create mode 100644 org.eclipse.lyo.client.java/src/test/resources/queryResponseWithEmptyNext.xml create mode 100644 org.eclipse.lyo.client.java/src/test/resources/queryResponseWithNext.xml diff --git a/org.eclipse.lyo.client.java/src/main/java/org/eclipse/lyo/client/oslc/resources/OslcQueryResult.java b/org.eclipse.lyo.client.java/src/main/java/org/eclipse/lyo/client/oslc/resources/OslcQueryResult.java index 478cfe9..f7cfc0e 100644 --- a/org.eclipse.lyo.client.java/src/main/java/org/eclipse/lyo/client/oslc/resources/OslcQueryResult.java +++ b/org.eclipse.lyo.client.java/src/main/java/org/eclipse/lyo/client/oslc/resources/OslcQueryResult.java @@ -53,7 +53,7 @@ public class OslcQueryResult implements Iterator { /** * 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; @@ -73,14 +73,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 +100,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 +122,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 +139,8 @@ String getNextPageUrl() { StmtIterator iter = rdfModel.listStatements(select); if (iter.hasNext()) { Statement nextPage = iter.next(); - nextPageUrl = nextPage.getResource().getURI(); + final Resource nextPageResource = nextPage.getResource(); + nextPageUrl = nextPageResource.getURI(); } else { nextPageUrl = ""; } @@ -158,12 +152,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 +229,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 +242,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 +251,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/org.eclipse.lyo.client.java/src/test/java/org/eclipse/lyo/client/test/OslcQueryResultTest.java b/org.eclipse.lyo.client.java/src/test/java/org/eclipse/lyo/client/test/OslcQueryResultTest.java index f37d0cd..92bb543 100644 --- a/org.eclipse.lyo.client.java/src/test/java/org/eclipse/lyo/client/test/OslcQueryResultTest.java +++ b/org.eclipse.lyo.client.java/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/org.eclipse.lyo.client.java/src/test/resources/queryResponseWithEmptyNext.xml b/org.eclipse.lyo.client.java/src/test/resources/queryResponseWithEmptyNext.xml new file mode 100644 index 0000000..70967f5 --- /dev/null +++ b/org.eclipse.lyo.client.java/src/test/resources/queryResponseWithEmptyNext.xml @@ -0,0 +1,12 @@ + + + + 2 + + + + + + + diff --git a/org.eclipse.lyo.client.java/src/test/resources/queryResponseWithNext.xml b/org.eclipse.lyo.client.java/src/test/resources/queryResponseWithNext.xml new file mode 100644 index 0000000..eec723b --- /dev/null +++ b/org.eclipse.lyo.client.java/src/test/resources/queryResponseWithNext.xml @@ -0,0 +1,13 @@ + + + + 2 + + + + + + + From 952a583033859527a0a36d70441d864cce72f7c4 Mon Sep 17 00:00:00 2001 From: Andrew Berezovskyi Date: Sat, 16 Feb 2019 01:03:20 +0100 Subject: [PATCH 2/4] Move test resources under the right dir Change-Id: I614efb392be24a435aad8ec1ab3743ba3bc10ed9 Signed-off-by: Andrew Berezovskyi --- .../src/test/resources/queryResponseWithEmptyNext.xml | 0 .../src/test/resources/queryResponseWithNext.xml | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {org.eclipse.lyo.client.java => oslc-java-client}/src/test/resources/queryResponseWithEmptyNext.xml (100%) rename {org.eclipse.lyo.client.java => oslc-java-client}/src/test/resources/queryResponseWithNext.xml (100%) diff --git a/org.eclipse.lyo.client.java/src/test/resources/queryResponseWithEmptyNext.xml b/oslc-java-client/src/test/resources/queryResponseWithEmptyNext.xml similarity index 100% rename from org.eclipse.lyo.client.java/src/test/resources/queryResponseWithEmptyNext.xml rename to oslc-java-client/src/test/resources/queryResponseWithEmptyNext.xml diff --git a/org.eclipse.lyo.client.java/src/test/resources/queryResponseWithNext.xml b/oslc-java-client/src/test/resources/queryResponseWithNext.xml similarity index 100% rename from org.eclipse.lyo.client.java/src/test/resources/queryResponseWithNext.xml rename to oslc-java-client/src/test/resources/queryResponseWithNext.xml From d7a09b848d5dc6b08f3477c21cb67b94a2c33f16 Mon Sep 17 00:00:00 2001 From: Andrew Berezovskyi Date: Sat, 16 Feb 2019 02:53:03 +0100 Subject: [PATCH 3/4] Fix bad URis Change-Id: I95f6fb002de836f091483dcc7f5a6f6d99623e40 Signed-off-by: Andrew Berezovskyi --- oslc-java-client/src/test/resources/queryResponse.rdf | 2 +- .../src/test/resources/queryResponseWithEmptyNext.xml | 2 +- .../src/test/resources/queryResponseWithNext.xml | 5 ++--- oslc4j-client/src/test/resources/queryResponse.rdf | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) 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 index 70967f5..13c0e42 100644 --- a/oslc-java-client/src/test/resources/queryResponseWithEmptyNext.xml +++ b/oslc-java-client/src/test/resources/queryResponseWithEmptyNext.xml @@ -1,7 +1,7 @@ - + 2 diff --git a/oslc-java-client/src/test/resources/queryResponseWithNext.xml b/oslc-java-client/src/test/resources/queryResponseWithNext.xml index eec723b..0ee6575 100644 --- a/oslc-java-client/src/test/resources/queryResponseWithNext.xml +++ b/oslc-java-client/src/test/resources/queryResponseWithNext.xml @@ -1,10 +1,9 @@ - + 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 From d2026d991ce572f8731b634b15c361c7460adbd2 Mon Sep 17 00:00:00 2001 From: Andrew Berezovskyi Date: Sat, 16 Feb 2019 03:25:45 +0100 Subject: [PATCH 4/4] Modify test and emit log warn Change-Id: I019092e760ae6a8337fb16b3b9e117810f0e3139 Signed-off-by: Andrew Berezovskyi --- .../client/oslc/resources/OslcQueryResult.java | 18 ++++++++++++++---- .../resources/queryResponseWithEmptyNext.xml | 2 +- 2 files changed, 15 insertions(+), 5 deletions(-) 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 6eda510..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(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 @@ -139,8 +143,14 @@ String getNextPageUrl() { StmtIterator iter = rdfModel.listStatements(select); if (iter.hasNext()) { Statement nextPage = iter.next(); - final Resource nextPageResource = nextPage.getResource(); - nextPageUrl = nextPageResource.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 = ""; } diff --git a/oslc-java-client/src/test/resources/queryResponseWithEmptyNext.xml b/oslc-java-client/src/test/resources/queryResponseWithEmptyNext.xml index 13c0e42..78db7f2 100644 --- a/oslc-java-client/src/test/resources/queryResponseWithEmptyNext.xml +++ b/oslc-java-client/src/test/resources/queryResponseWithEmptyNext.xml @@ -3,7 +3,7 @@ xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"> 2 - +