blob: 80bdeb1a4169c6433900ff6a4eb6b5b06a61993b [file] [log] [blame]
/*
* 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.tuscany.sca.xsd.xml;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.tuscany.sca.assembly.xsd.Constants;
import org.apache.tuscany.sca.common.xml.XMLDocumentHelper;
import org.apache.tuscany.sca.contribution.Artifact;
import org.apache.tuscany.sca.contribution.Contribution;
import org.apache.tuscany.sca.contribution.DefaultImport;
import org.apache.tuscany.sca.contribution.Import;
import org.apache.tuscany.sca.contribution.namespace.NamespaceImport;
import org.apache.tuscany.sca.contribution.processor.ContributionRuntimeException;
import org.apache.tuscany.sca.contribution.processor.ProcessorContext;
import org.apache.tuscany.sca.contribution.resolver.ModelResolver;
import org.apache.tuscany.sca.core.FactoryExtensionPoint;
import org.apache.tuscany.sca.xsd.DefaultXSDFactory;
import org.apache.tuscany.sca.xsd.XSDFactory;
import org.apache.tuscany.sca.xsd.XSDefinition;
import org.apache.tuscany.sca.xsd.impl.XSDefinitionImpl;
import org.apache.ws.commons.schema.XmlSchema;
import org.apache.ws.commons.schema.XmlSchemaCollection;
import org.apache.ws.commons.schema.XmlSchemaInclude;
import org.apache.ws.commons.schema.resolver.URIResolver;
import org.xml.sax.InputSource;
/**
* A Model Resolver for XSD models.
*
* @version $Rev$ $Date$
*/
public class XSDModelResolver implements ModelResolver {
private static final String AGGREGATED_XSD = "http://tuscany.apache.org/aggregated.xsd";
private XSDFactory factory;
private Contribution contribution;
private Map<String, List<XSDefinition>> map = new HashMap<String, List<XSDefinition>>();
private XmlSchemaCollection schemaCollection;
public XSDModelResolver(Contribution contribution, FactoryExtensionPoint modelFactories) {
this.contribution = contribution;
this.schemaCollection = new XmlSchemaCollection();
this.factory = new DefaultXSDFactory();
}
public void addModel(Object resolved, ProcessorContext context) {
XSDefinition definition = (XSDefinition)resolved;
List<XSDefinition> list = map.get(definition.getNamespace());
if (list == null) {
list = new ArrayList<XSDefinition>();
map.put(definition.getNamespace(), list);
}
list.add(definition);
}
public Object removeModel(Object resolved, ProcessorContext context) {
XSDefinition definition = (XSDefinition)resolved;
List<XSDefinition> list = map.get(definition.getNamespace());
if (list == null) {
return null;
} else {
return list.remove(definition);
}
}
public <T> T resolveModel(Class<T> modelClass, T unresolved, ProcessorContext context) {
schemaCollection.setSchemaResolver(new URIResolverImpl(contribution, context));
XSDefinition definition = (XSDefinition)unresolved;
String namespace = definition.getNamespace();
XSDefinition resolved = null;
// Lookup a definition for the given namespace, within the contribution
List<XSDefinition> list = map.get(namespace);
if (list == null ||
(list != null && list.size() == 0)){
// if no schema is found locally delegate to other
// contributions via the imports
resolved = resolutionDelegation(namespace, context);
return modelClass.cast(resolved);
}
XSDefinition modelXSD = null;
if (list != null && definition.getDocument() != null) {
// Set the document for the inline schema
int index = list.indexOf(definition);
if (index != -1) { // a matching (not identical) document was found
modelXSD = list.get(index);
modelXSD.setDocument(definition.getDocument());
}
}
if (list == null && definition.getDocument() != null) {
// Hit for the 1st time
list = new ArrayList<XSDefinition>();
list.add(definition);
map.put(namespace, list);
}
try {
resolved = aggregate(list);
} catch (IOException e) {
throw new ContributionRuntimeException(e);
}
if (resolved != null && !resolved.isUnresolved()) {
if (definition.isUnresolved() && definition.getSchema() == null && modelXSD != null) {
// Update the unresolved model with schema information and mark it
// resolved. This information in the unresolved model is needed when
// this method is called by WSDLModelResolver.readInlineSchemas().
definition.setSchema(modelXSD.getSchema());
definition.setSchemaCollection(modelXSD.getSchemaCollection());
definition.setUnresolved(false);
}
return modelClass.cast(resolved);
}
return modelClass.cast(unresolved);
}
private void loadOnDemand(XSDefinition definition) throws IOException {
if (definition.getSchema() != null) {
return;
}
if (definition.getDocument() != null) {
String uri = null;
if (definition.getLocation() != null) {
uri = definition.getLocation().toString();
}
XmlSchema schema = null;
try {
schema = schemaCollection.read(definition.getDocument(), uri, null);
} catch (RuntimeException e) {
// find original cause of the problem
Throwable cause = e;
while (cause.getCause() != null && cause != cause.getCause()) {
cause = cause.getCause();
}
throw new ContributionRuntimeException(cause);
}
definition.setSchemaCollection(schemaCollection);
definition.setSchema(schema);
definition.setUnresolved(false);
} else if (definition.getLocation() != null) {
if (definition.getLocation().getFragment() != null) {
// It's an inline schema
return;
}
// Read an XSD document
XmlSchema schema = null;
for (XmlSchema d : schemaCollection.getXmlSchemas()) {
if (isSameNamespace(d.getTargetNamespace(), definition.getNamespace())) {
if (d.getSourceURI().equals(definition.getLocation().toString())) {
schema = d;
break;
}
}
}
if (schema == null) {
InputSource xsd = null;
try {
xsd = XMLDocumentHelper.getInputSource(definition.getLocation().toURL());
} catch (IOException e) {
throw new ContributionRuntimeException(e);
}
try {
schema = schemaCollection.read(xsd, null);
} catch (RuntimeException e) {
// find original cause of the problem
Throwable cause = e;
while (cause.getCause() != null && cause != cause.getCause()) {
cause = cause.getCause();
}
throw new ContributionRuntimeException(cause);
}
}
definition.setSchemaCollection(schemaCollection);
definition.setSchema(schema);
}
}
private boolean isSameNamespace(String ns1, String ns2) {
if (ns1 == null) {
return ns2 == null;
} else {
return ns1.equals(ns2);
}
}
/**
* Create a facade XmlSchema which includes all the definitions
*
* @param definitions A list of the XmlSchema under the same target
* namespace
* @return The aggregated XmlSchema
*/
private XSDefinition aggregate(List<XSDefinition> definitions) throws IOException {
if (definitions == null || definitions.size() == 0) {
return null;
}
if (definitions.size() == 1) {
XSDefinition d = definitions.get(0);
loadOnDemand(d);
return d;
}
XSDefinition aggregated = factory.createXSDefinition();
for (XSDefinition d : definitions) {
loadOnDemand(d);
}
String ns = definitions.get(0).getNamespace();
XmlSchema facade = null;
// Check if the facade XSD is already in the collection
for (XmlSchema s : schemaCollection.getXmlSchema(AGGREGATED_XSD)) {
if (ns.equals(s.getTargetNamespace())) {
facade = s;
break;
}
}
if (facade == null) {
// This will add the facade into the collection
facade = new XmlSchema(ns, AGGREGATED_XSD, schemaCollection);
}
for (XmlSchema d : schemaCollection.getXmlSchemas()) {
if (ns.equals(d.getTargetNamespace())) {
if (d == facade) {
continue;
}
XmlSchemaInclude include = new XmlSchemaInclude();
include.setSchema(d);
include.setSourceURI(d.getSourceURI());
include.setSchemaLocation(d.getSourceURI());
facade.getIncludes().add(include);
facade.getItems().add(include);
}
}
aggregated.setUnresolved(true);
aggregated.setSchema(facade);
aggregated.setNamespace(ns);
aggregated.setAggregatedDefinitions(definitions);
aggregated.setUnresolved(false);
// FIXME: [rfeng] This is hacky
//definitions.clear();
//definitions.add(aggregated);
return aggregated;
}
private XSDefinition resolutionDelegation(String namespace, ProcessorContext context){
// Delegate the resolution to namespace imports
XSDefinition resolved = null;
XSDefinition unresolved = new XSDefinitionImpl();
unresolved.setUnresolved(true);
unresolved.setNamespace(namespace);
for (Import import_ : this.contribution.getImports()) {
if (import_ instanceof NamespaceImport) {
NamespaceImport namespaceImport = (NamespaceImport)import_;
if (namespaceImport.getNamespace().equals(namespace)) {
// Delegate the resolution to the namespace import resolver
resolved =
namespaceImport.getModelResolver().resolveModel(XSDefinition.class, (XSDefinition)unresolved, context);
if (!resolved.isUnresolved()) {
return resolved;
}
}
} else if (import_ instanceof DefaultImport) {
// Delegate the resolution to the default import resolver
resolved =
import_.getModelResolver().resolveModel(XSDefinition.class, (XSDefinition)unresolved, context);
if (!resolved.isUnresolved()) {
return resolved;
}
}
}
return resolved;
}
/**
* URI resolver implementation for XML schema
*/
public static class URIResolverImpl implements URIResolver {
private Contribution contribution;
private ProcessorContext context;
public URIResolverImpl(Contribution contribution, ProcessorContext context) {
this.contribution = contribution;
this.context = context;
}
public org.xml.sax.InputSource resolveEntity(java.lang.String targetNamespace,
java.lang.String schemaLocation,
java.lang.String baseUri) {
try {
if (schemaLocation == null) {
return null;
}
URL url = null;
// Delegate the resolution to namespace imports
XSDefinition resolved = null;
XSDefinition unresolved = new XSDefinitionImpl();
unresolved.setUnresolved(true);
unresolved.setLocation(new URI(schemaLocation));
unresolved.setNamespace(targetNamespace);
for (Import import_ : this.contribution.getImports()) {
if (import_ instanceof NamespaceImport) {
NamespaceImport namespaceImport = (NamespaceImport)import_;
if (namespaceImport.getNamespace().equals(targetNamespace)) {
// Delegate the resolution to the namespace import resolver
resolved =
namespaceImport.getModelResolver().resolveModel(XSDefinition.class, (XSDefinition)unresolved, context);
if (!resolved.isUnresolved()) {
return XMLDocumentHelper.getInputSource(resolved.getLocation().toURL());
}
}
} else if (import_ instanceof DefaultImport) {
// Delegate the resolution to the default import resolver
resolved =
import_.getModelResolver().resolveModel(XSDefinition.class, (XSDefinition)unresolved, context);
if (!resolved.isUnresolved()) {
return XMLDocumentHelper.getInputSource(resolved.getLocation().toURL());
}
}
}
// Not found, lookup a definition for the given namespace
// within the current contribution.
if (schemaLocation.startsWith("/")) {
// The URI is relative to the contribution
String uri = schemaLocation.substring(1);
for (Artifact a : contribution.getArtifacts()) {
if (a.getURI().equals(uri)) {
url = new URL(a.getLocation());
break;
}
}
if (url == null) {
// URI not found in the contribution; return a default InputSource
// so that the XmlSchema code will produce a useful diagnostic
return new InputSource(schemaLocation);
}
} else {
url = new URL(new URL(baseUri), schemaLocation);
String scheme = url.getProtocol();
if ("file".equalsIgnoreCase(scheme) || "jar".equalsIgnoreCase(scheme)
|| "zip".equalsIgnoreCase(scheme)
|| "wsjar".equalsIgnoreCase(scheme)) {
// For local URLs, use as-is
} else {
// look to see whether Tuscany has a local version of the
// required schema. It can load the local version rather
// than going out to the network in order to improve performance
URL cached = Constants.CACHED_XSDS.get(targetNamespace);
if (cached != null) {
url = cached;
}
}
}
return XMLDocumentHelper.getInputSource(url);
} catch (IOException e) {
// Invalid URI; return a default InputSource so that the
// XmlSchema code will produce a useful diagnostic
return new InputSource(schemaLocation);
} catch (URISyntaxException e) {
// Invalid URI; return a default InputSource so that the
// XmlSchema code will produce a useful diagnostic
return new InputSource(schemaLocation);
}
}
}
}