diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/MetadataParser.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/MetadataParser.java index e8122d77a..b56479269 100644 --- a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/MetadataParser.java +++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/MetadataParser.java @@ -18,9 +18,12 @@ */ package org.apache.olingo.server.core; +import java.io.IOException; import java.io.Reader; +import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; +import java.net.URL; import java.util.ArrayList; import java.util.Arrays; @@ -33,6 +36,7 @@ import javax.xml.stream.events.EndElement; import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; +import org.apache.olingo.commons.api.edm.EdmException; import org.apache.olingo.commons.api.edm.FullQualifiedName; import org.apache.olingo.commons.api.edm.geo.SRID; import org.apache.olingo.commons.api.edm.provider.CsdlAction; @@ -42,7 +46,6 @@ import org.apache.olingo.commons.api.edm.provider.CsdlAnnotation; import org.apache.olingo.commons.api.edm.provider.CsdlAnnotations; import org.apache.olingo.commons.api.edm.provider.CsdlBindingTarget; import org.apache.olingo.commons.api.edm.provider.CsdlComplexType; -import org.apache.olingo.commons.api.edm.provider.CsdlEdmProvider; import org.apache.olingo.commons.api.edm.provider.CsdlEntityContainer; import org.apache.olingo.commons.api.edm.provider.CsdlEntitySet; import org.apache.olingo.commons.api.edm.provider.CsdlEntityType; @@ -92,16 +95,22 @@ import org.apache.olingo.server.api.edmx.EdmxReferenceIncludeAnnotation; */ public class MetadataParser { private boolean parseAnnotations = false; - + private final String XML_LINK_NS = "http://www.w3.org/1999/xlink"; + private ReferenceResolver referenceResolver = new DefaultReferenceResolver(); + public void setParseAnnotations(boolean f) { this.parseAnnotations = true; } + public void setReferenceResolver(ReferenceResolver resolver) { + this.referenceResolver = resolver; + } + public ServiceMetadata buildServiceMetadata(Reader csdl) throws XMLStreamException { XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance(); XMLEventReader reader = xmlInputFactory.createXMLEventReader(csdl); - SchemaBasedEdmProvider provider = new SchemaBasedEdmProvider(); + SchemaBasedEdmProvider provider = new SchemaBasedEdmProvider(referenceResolver); final ArrayList references = new ArrayList(); new ElementReader() { @@ -109,6 +118,8 @@ public class MetadataParser { void build(XMLEventReader reader, StartElement element, SchemaBasedEdmProvider provider, String name) throws XMLStreamException { String version = attr(element, "Version"); + String xmlBase = attrNS(element, XML_LINK_NS, "base"); + provider.setXMLBase(xmlBase); if ("4.0".equals(version)) { readDataServicesAndReference(reader, element, provider, references); } else { @@ -124,14 +135,18 @@ public class MetadataParser { event.asStartElement().getName().getLocalPart() : event.asEndElement().getName().getLocalPart())); } + provider.addReferences(references); return new ServiceMetadataImpl(provider, references, null); } - CsdlEdmProvider buildEdmProvider(Reader csdl) throws XMLStreamException { + SchemaBasedEdmProvider buildEdmProvider(Reader csdl) throws XMLStreamException { XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance(); XMLEventReader reader = xmlInputFactory.createXMLEventReader(csdl); - - SchemaBasedEdmProvider provider = new SchemaBasedEdmProvider(); + return buildEdmProvider(reader); + } + + SchemaBasedEdmProvider buildEdmProvider(XMLEventReader reader) throws XMLStreamException { + SchemaBasedEdmProvider provider = new SchemaBasedEdmProvider(this.referenceResolver); new ElementReader() { @Override void build(XMLEventReader reader, StartElement element, SchemaBasedEdmProvider provider, @@ -141,8 +156,7 @@ public class MetadataParser { readDataServicesAndReference(reader, element, provider, new ArrayList()); } } - }.read(reader, null, provider, "Edmx"); - + }.read(reader, null, provider, "Edmx"); return provider; } @@ -776,6 +790,14 @@ public class MetadataParser { return null; } + private String attrNS(StartElement element, String ns, String name) { + Attribute attr = element.getAttributeByName(new QName(ns, name)); + if (attr != null) { + return attr.getValue(); + } + return null; + } + private CsdlProperty readProperty(XMLEventReader reader, StartElement element) throws XMLStreamException { CsdlProperty property = new CsdlProperty(); @@ -1020,4 +1042,32 @@ public class MetadataParser { abstract void build(XMLEventReader reader, StartElement element, T t, String name) throws XMLStreamException; } + + class DefaultReferenceResolver implements ReferenceResolver{ + @Override + public SchemaBasedEdmProvider resolveReference(URI referenceUri, String xmlBase) { + URL schemaURL = null; + try { + if (referenceUri.isAbsolute()) { + schemaURL = referenceUri.toURL(); + } else { + if (xmlBase != null) { + schemaURL = new URL(xmlBase+referenceUri.toString()); + } else { + throw new EdmException("No xml:base set to read the references from the metadata"); + } + } + + XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance(); + XMLEventReader reader = xmlInputFactory.createXMLEventReader(schemaURL.openStream()); + return buildEdmProvider(reader); + } catch (MalformedURLException e) { + throw new EdmException(e); + } catch (XMLStreamException e) { + throw new EdmException(e); + } catch (IOException e) { + throw new EdmException(e); + } + } + } } diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ReferenceResolver.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ReferenceResolver.java new file mode 100644 index 000000000..f7d510156 --- /dev/null +++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ReferenceResolver.java @@ -0,0 +1,32 @@ +/* + * 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.olingo.server.core; + +import java.net.URI; + +public interface ReferenceResolver { + /** + * Resolve the reference locally or from redirection from different source than defined in the + * metadata document. + * @param referenceUri reference URI for the schema file + * @param xmlBase xml:base if provided by the metadata document; null otherwise + * @return + */ + SchemaBasedEdmProvider resolveReference(URI referenceUri, String xmlBase); +} diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/SchemaBasedEdmProvider.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/SchemaBasedEdmProvider.java index 06123029e..a2f23f648 100644 --- a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/SchemaBasedEdmProvider.java +++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/SchemaBasedEdmProvider.java @@ -19,8 +19,9 @@ package org.apache.olingo.server.core; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import org.apache.olingo.commons.api.edm.FullQualifiedName; import org.apache.olingo.commons.api.edm.provider.CsdlAction; @@ -41,11 +42,22 @@ import org.apache.olingo.commons.api.edm.provider.CsdlSingleton; import org.apache.olingo.commons.api.edm.provider.CsdlTerm; import org.apache.olingo.commons.api.edm.provider.CsdlTypeDefinition; import org.apache.olingo.commons.api.ex.ODataException; +import org.apache.olingo.server.api.edmx.EdmxReference; +import org.apache.olingo.server.api.edmx.EdmxReferenceInclude; public class SchemaBasedEdmProvider implements CsdlEdmProvider { private final List edmSchemas = new ArrayList(); + private final Map references = new ConcurrentHashMap(); + private final Map referenceSchemas + = new ConcurrentHashMap(); + private String xmlBase; + private ReferenceResolver referenceResolver; + + public SchemaBasedEdmProvider(ReferenceResolver referenceResolver) { + this.referenceResolver = referenceResolver; + } - public void addSchema(CsdlSchema schema) { + void addSchema(CsdlSchema schema) { this.edmSchemas.add(schema); } @@ -55,6 +67,39 @@ public class SchemaBasedEdmProvider implements CsdlEdmProvider { return s; } } + return getReferenceSchema(ns); + } + + private CsdlSchema getReferenceSchema(String ns) { + if (ns == null) { + return null; + } + if (this.referenceSchemas.get(ns) == null) { + EdmxReference reference = this.references.get(ns); + if (reference != null) { + SchemaBasedEdmProvider provider = this.referenceResolver.resolveReference(reference.getUri(), xmlBase); + for (EdmxReferenceInclude include : reference.getIncludes()) { + this.referenceSchemas.put(include.getNamespace(), provider); + if (include.getAlias() != null) { + CsdlSchema schema = provider.getSchema(include.getNamespace()); + schema.setAlias(include.getAlias()); + this.referenceSchemas.put(include.getAlias(), provider); + } + } + } + } + + if (this.referenceSchemas.get(ns) != null) { + return this.referenceSchemas.get(ns).getSchema(ns); + } + + // it is possible that we may be looking for Reference schema of Reference + for (SchemaBasedEdmProvider provider:this.referenceSchemas.values()) { + CsdlSchema schema = provider.getSchema(ns); + if (schema != null) { + return schema; + } + } return null; } @@ -220,22 +265,26 @@ public class SchemaBasedEdmProvider implements CsdlEdmProvider { @Override public List getAliasInfos() throws ODataException { - CsdlSchema schema = null; + ArrayList list = new ArrayList(); for (CsdlSchema s : this.edmSchemas) { - if (s.getEntityContainer() != null) { - schema = s; - break; + if (s.getAlias() != null) { + CsdlAliasInfo ai = new CsdlAliasInfo(); + ai.setAlias(s.getAlias()); + ai.setNamespace(s.getNamespace()); + list.add(ai); } } - - if (schema == null) { - schema = this.edmSchemas.get(0); + for(EdmxReference reference:this.references.values()) { + for(EdmxReferenceInclude include:reference.getIncludes()) { + if (include.getAlias() != null) { + CsdlAliasInfo ai = new CsdlAliasInfo(); + ai.setAlias(include.getAlias()); + ai.setNamespace(include.getNamespace()); + list.add(ai); + } + } } - - CsdlAliasInfo ai = new CsdlAliasInfo(); - ai.setAlias(schema.getAlias()); - ai.setNamespace(schema.getNamespace()); - return Arrays.asList(ai); + return list; } @Override @@ -304,6 +353,33 @@ public class SchemaBasedEdmProvider implements CsdlEdmProvider { @Override public CsdlAnnotations getAnnotationsGroup(FullQualifiedName targetName, String qualifier) throws ODataException { + CsdlSchema schema = getSchema(targetName.getNamespace()); + if (schema != null) { + return schema.getAnnotationGroup(targetName.getName(), qualifier); + } return null; } + + void addReferences(ArrayList references) { + if (references != null && !references.isEmpty()) { + for (EdmxReference ref:references) { + for (EdmxReferenceInclude include : ref.getIncludes()) { + if (include.getAlias() != null) { + this.references.put(include.getAlias(), ref); + } + this.references.put(include.getNamespace(), ref); + } + } + } + } + + public void setXMLBase(String base) { + if (base != null) { + if (base.endsWith("/")) { + this.xmlBase = base; + } else { + this.xmlBase = base+"/"; + } + } + } }