OLINGO-1252: adding ability load reference schemas recursively, but locallized to the schema they represent

This commit is contained in:
Ramesh Reddy 2018-04-05 12:41:42 -05:00
parent d9aff6300f
commit fd8bfa33d4
3 changed files with 124 additions and 60 deletions

View File

@ -27,6 +27,8 @@ import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javax.xml.namespace.QName; import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLEventReader;
@ -100,7 +102,8 @@ public class MetadataParser {
private ReferenceResolver referenceResolver = new DefaultReferenceResolver(); private ReferenceResolver referenceResolver = new DefaultReferenceResolver();
private boolean useLocalCoreVocabularies = true; private boolean useLocalCoreVocabularies = true;
private boolean implicitlyLoadCoreVocabularies = false; private boolean implicitlyLoadCoreVocabularies = false;
private boolean recusivelyLoadReferences = false; private boolean recursivelyLoadReferences = false;
private Map<String, SchemaBasedEdmProvider> globalReferenceMap = new HashMap<String, SchemaBasedEdmProvider>();
/** /**
* Avoid reading the annotations in the $metadata * Avoid reading the annotations in the $metadata
@ -138,7 +141,7 @@ public class MetadataParser {
* @return * @return
*/ */
public MetadataParser recursivelyLoadReferences(boolean load) { public MetadataParser recursivelyLoadReferences(boolean load) {
this.recusivelyLoadReferences = load; this.recursivelyLoadReferences = load;
return this; return this;
} }
@ -154,35 +157,37 @@ public class MetadataParser {
public ServiceMetadata buildServiceMetadata(Reader csdl) throws XMLStreamException { public ServiceMetadata buildServiceMetadata(Reader csdl) throws XMLStreamException {
SchemaBasedEdmProvider provider = buildEdmProvider(csdl, this.referenceResolver, SchemaBasedEdmProvider provider = buildEdmProvider(csdl, this.referenceResolver,
this.implicitlyLoadCoreVocabularies, this.useLocalCoreVocabularies, true); this.implicitlyLoadCoreVocabularies, this.useLocalCoreVocabularies, true, null);
return new ServiceMetadataImpl(provider, provider.getReferences(), null); return new ServiceMetadataImpl(provider, provider.getReferences(), null);
} }
public SchemaBasedEdmProvider buildEdmProvider(Reader csdl) throws XMLStreamException { public SchemaBasedEdmProvider buildEdmProvider(Reader csdl) throws XMLStreamException {
XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance(); XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
XMLEventReader reader = xmlInputFactory.createXMLEventReader(csdl); XMLEventReader reader = xmlInputFactory.createXMLEventReader(csdl);
return buildEdmProvider(reader, this.referenceResolver, return buildEdmProvider(reader, this.referenceResolver, this.implicitlyLoadCoreVocabularies,
this.implicitlyLoadCoreVocabularies, this.useLocalCoreVocabularies, true); this.useLocalCoreVocabularies, true, null);
} }
protected SchemaBasedEdmProvider buildEdmProvider(Reader csdl, protected SchemaBasedEdmProvider buildEdmProvider(Reader csdl, ReferenceResolver resolver,
ReferenceResolver resolver, boolean loadCore, boolean useLocal, boolean loadReferenceSchemas) boolean loadCore, boolean useLocal,
boolean loadReferenceSchemas, String namespace)
throws XMLStreamException { throws XMLStreamException {
XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance(); XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
XMLEventReader reader = xmlInputFactory.createXMLEventReader(csdl); XMLEventReader reader = xmlInputFactory.createXMLEventReader(csdl);
return buildEdmProvider(reader, resolver, loadCore, useLocal, loadReferenceSchemas); return buildEdmProvider(reader, resolver, loadCore, useLocal, loadReferenceSchemas, namespace);
} }
protected SchemaBasedEdmProvider buildEdmProvider(InputStream csdl, protected SchemaBasedEdmProvider buildEdmProvider(InputStream csdl, ReferenceResolver resolver,
ReferenceResolver resolver, boolean loadCore, boolean useLocal, boolean loadReferenceSchemas) boolean loadCore, boolean useLocal,
boolean loadReferenceSchemas, String namespace)
throws XMLStreamException { throws XMLStreamException {
XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance(); XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
XMLEventReader reader = xmlInputFactory.createXMLEventReader(csdl); XMLEventReader reader = xmlInputFactory.createXMLEventReader(csdl);
return buildEdmProvider(reader, resolver, loadCore, useLocal, loadReferenceSchemas); return buildEdmProvider(reader, resolver, loadCore, useLocal, loadReferenceSchemas, namespace);
} }
protected SchemaBasedEdmProvider buildEdmProvider(XMLEventReader reader, protected SchemaBasedEdmProvider buildEdmProvider(XMLEventReader reader,
ReferenceResolver resolver, boolean loadCore, boolean useLocal, boolean loadReferenceSchemas) ReferenceResolver resolver, boolean loadCore, boolean useLocal, boolean loadReferenceSchemas, String namespace)
throws XMLStreamException { throws XMLStreamException {
SchemaBasedEdmProvider provider = new SchemaBasedEdmProvider(); SchemaBasedEdmProvider provider = new SchemaBasedEdmProvider();
@ -221,6 +226,10 @@ public class MetadataParser {
loadCoreVocabulary(provider, "Org.OData.Measures.V1"); loadCoreVocabulary(provider, "Org.OData.Measures.V1");
} }
if (namespace != null && !namespace.equals("") && !globalReferenceMap.containsKey(namespace)) {
globalReferenceMap.put(namespace, provider);
}
// load all the reference schemas // load all the reference schemas
if (resolver != null && loadReferenceSchemas) { if (resolver != null && loadReferenceSchemas) {
loadReferencesSchemas(provider, xmlBase.length() == 0 ? null loadReferencesSchemas(provider, xmlBase.length() == 0 ? null
@ -239,8 +248,8 @@ public class MetadataParser {
for (EdmxReferenceInclude include : reference.getIncludes()) { for (EdmxReferenceInclude include : reference.getIncludes()) {
// check if the schema is already loaded before. // check if the schema is already loaded before in current provider.
if (provider.getSchema(include.getNamespace()) != null) { if (provider.getSchemaDirectly(include.getNamespace()) != null) {
continue; continue;
} }
@ -249,6 +258,9 @@ public class MetadataParser {
continue; continue;
} }
// check if the schema is already loaded before in parent providers
refProvider = this.globalReferenceMap.get(include.getNamespace());
if (refProvider == null) { if (refProvider == null) {
InputStream is = this.referenceResolver.resolveReference(reference.getUri(), xmlBase); InputStream is = this.referenceResolver.resolveReference(reference.getUri(), xmlBase);
if (is == null) { if (is == null) {
@ -256,7 +268,8 @@ public class MetadataParser {
} else { } else {
// do not implicitly load core vocabularies any more. But if the // do not implicitly load core vocabularies any more. But if the
// references loading the core vocabularies try to use local if we can // references loading the core vocabularies try to use local if we can
refProvider = buildEdmProvider(is, resolver, false, useLocal, this.recusivelyLoadReferences); refProvider = buildEdmProvider(is, resolver, false, useLocal,
this.recursivelyLoadReferences, include.getNamespace());
} }
} }
@ -308,7 +321,7 @@ public class MetadataParser {
if (schema == null) { if (schema == null) {
InputStream is = this.getClass().getClassLoader().getResourceAsStream(resource); InputStream is = this.getClass().getClassLoader().getResourceAsStream(resource);
if (is != null) { if (is != null) {
SchemaBasedEdmProvider childProvider = buildEdmProvider(is, null, false, false, true); SchemaBasedEdmProvider childProvider = buildEdmProvider(is, null, false, false, true, "");
provider.addVocabularySchema(namespace, childProvider); provider.addVocabularySchema(namespace, childProvider);
} else { } else {
throw new XMLStreamException("failed to load "+resource+" core vocabulary"); throw new XMLStreamException("failed to load "+resource+" core vocabulary");

View File

@ -19,8 +19,10 @@
package org.apache.olingo.server.core; package org.apache.olingo.server.core;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import org.apache.olingo.commons.api.edm.FullQualifiedName; import org.apache.olingo.commons.api.edm.FullQualifiedName;
@ -88,38 +90,44 @@ public class SchemaBasedEdmProvider implements CsdlEdmProvider {
} }
CsdlSchema getSchema(String ns, boolean checkReferences) { CsdlSchema getSchema(String ns, boolean checkReferences) {
if (checkReferences) {
return getSchemaRecursively(ns, new HashSet<String>());
} else {
return getSchemaDirectly(ns);
}
}
CsdlSchema getSchemaDirectly(String ns) {
for (CsdlSchema s : this.edmSchemas) { for (CsdlSchema s : this.edmSchemas) {
if (s.getNamespace().equals(ns)) { if (s.getNamespace().equals(ns)) {
return s; return s;
} }
} }
CsdlSchema s = null;
if (checkReferences) {
s = getReferenceSchema(ns);
if (s == null) {
s = getVocabularySchema(ns);
}
}
return s;
}
CsdlSchema getReferenceSchema(String ns) {
if (ns == null) {
return null; return null;
} }
if (this.referenceSchemas.get(ns) != null) { CsdlSchema getSchemaRecursively(String ns, Set<String> parsedPath) {
return this.referenceSchemas.get(ns).getSchema(ns); // find the schema by namespace in current provider
CsdlSchema schema = getSchemaDirectly(ns);
if (schema != null) {
return schema;
} }
// it is possible that we may be looking for Reference schema of Reference // find the schema by namespace in the reference schema provider
for (SchemaBasedEdmProvider provider:this.referenceSchemas.values()) { for (Map.Entry<String, SchemaBasedEdmProvider> entry : this.referenceSchemas.entrySet()) {
CsdlSchema schema = provider.getSchema(ns); String namespace = entry.getKey();
if (parsedPath.contains(namespace)) {
continue;
}
SchemaBasedEdmProvider provider = entry.getValue();
parsedPath.add(namespace);
schema = provider.getSchemaRecursively(ns, parsedPath);
if (schema != null) { if (schema != null) {
return schema; return schema;
} }
} }
return null;
return getVocabularySchema(ns);
} }
@Override @Override

View File

@ -46,6 +46,7 @@ import org.apache.olingo.commons.api.edm.provider.CsdlNavigationPropertyBinding;
import org.apache.olingo.commons.api.edm.provider.CsdlParameter; import org.apache.olingo.commons.api.edm.provider.CsdlParameter;
import org.apache.olingo.commons.api.edm.provider.CsdlProperty; import org.apache.olingo.commons.api.edm.provider.CsdlProperty;
import org.apache.olingo.commons.api.edm.provider.CsdlSingleton; import org.apache.olingo.commons.api.edm.provider.CsdlSingleton;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -56,6 +57,21 @@ public class MetadataParserTest {
CsdlEdmProvider provider = null; CsdlEdmProvider provider = null;
ReferenceResolver testReferenceResolver = new ReferenceResolver() {
@Override
public InputStream resolveReference(URI uri, String xmlBase) {
String str = uri.toASCIIString();
if (str.startsWith("http://localhost/")) {
try {
return new FileInputStream("src/test/resources/"+str.substring(17));
} catch (FileNotFoundException e) {
return null;
}
}
return null;
}
};
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
MetadataParser parser = new MetadataParser(); MetadataParser parser = new MetadataParser();
@ -197,20 +213,47 @@ public class MetadataParserTest {
public void testReferenceLoad() throws Exception { public void testReferenceLoad() throws Exception {
MetadataParser parser = new MetadataParser(); MetadataParser parser = new MetadataParser();
parser.recursivelyLoadReferences(false); parser.recursivelyLoadReferences(false);
parser.referenceResolver(new ReferenceResolver() { parser.referenceResolver(this.testReferenceResolver);
@Override
public InputStream resolveReference(URI uri, String xmlBase) {
String str = uri.toASCIIString();
if (str.startsWith("http://localhost/")) {
try {
return new FileInputStream("src/test/resources/"+str.substring(17));
} catch (FileNotFoundException e) {
return null;
}
}
return null;
}
});
provider = (CsdlEdmProvider) parser.buildEdmProvider(new FileReader("src/test/resources/test.xml")); provider = (CsdlEdmProvider) parser.buildEdmProvider(new FileReader("src/test/resources/test.xml"));
} }
@Test
public void testReferenceLoadRecursively() throws Exception {
MetadataParser parser = new MetadataParser();
parser.recursivelyLoadReferences(true);
parser.referenceResolver(testReferenceResolver);
SchemaBasedEdmProvider providerTest = parser.buildEdmProvider(new FileReader("src/test/resources/test.xml"));
Assert.assertNotNull(providerTest.getSchema("Microsoft.OData.SampleService.Models.TripPin", false));
Assert.assertNull(providerTest.getSchema("org.apache.olingo.a", false));
Assert.assertNull(providerTest.getSchema("org.apache.olingo.b", false));
Assert.assertNotNull(providerTest.getSchema("org.apache.olingo.a", true));
Assert.assertNotNull(providerTest.getSchema("org.apache.olingo.b", true));
}
@Test
public void testCircleReferenceShouldNotStackOverflow() throws Exception {
MetadataParser parser = new MetadataParser();
parser.recursivelyLoadReferences(true);
parser.referenceResolver(testReferenceResolver);
SchemaBasedEdmProvider providerTest = parser.buildEdmProvider(new FileReader("src/test/resources/test.xml"));
Assert.assertNull(providerTest.getSchema("Not Found", true));
}
@Test
public void testLoadCoreVocabulary() throws Exception {
MetadataParser parser = new MetadataParser();
parser.implicitlyLoadCoreVocabularies(true);
parser.referenceResolver(testReferenceResolver);
SchemaBasedEdmProvider provider = parser.buildEdmProvider(new FileReader("src/test/resources/test.xml"));
Assert.assertNotNull(provider.getVocabularySchema("Org.OData.Core.V1"));
Assert.assertNotNull(provider.getSchema("Org.OData.Core.V1"));
}
} }