org.fabric3.wsdl.contribution.impl.WsdlResourceProcessor Maven / Gradle / Ivy
/*
* Fabric3
* Copyright (c) 2009-2013 Metaform Systems
*
* Fabric3 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version, with the
* following exception:
*
* Linking this software statically or dynamically with other
* modules is making a combined work based on this software.
* Thus, the terms and conditions of the GNU General Public
* License cover the whole combination.
*
* As a special exception, the copyright holders of this software
* give you permission to link this software with independent
* modules to produce an executable, regardless of the license
* terms of these independent modules, and to copy and distribute
* the resulting executable under terms of your choice, provided
* that you also meet, for each linked independent module, the
* terms and conditions of the license of that module. An
* independent module is a module which is not derived from or
* based on this software. If you modify this software, you may
* extend this exception to your version of the software, but
* you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version.
*
* Fabric3 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the
* GNU General Public License along with Fabric3.
* If not, see .
*/
package org.fabric3.wsdl.contribution.impl;
import javax.wsdl.Binding;
import javax.wsdl.Definition;
import javax.wsdl.Port;
import javax.wsdl.PortType;
import javax.wsdl.Service;
import javax.wsdl.Types;
import javax.wsdl.WSDLException;
import javax.wsdl.extensions.schema.Schema;
import javax.wsdl.xml.WSDLLocator;
import javax.wsdl.xml.WSDLReader;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.apache.ws.commons.schema.XmlSchemaCollection;
import org.apache.ws.commons.schema.XmlSchemaException;
import org.apache.ws.commons.schema.resolver.DefaultURIResolver;
import org.apache.ws.commons.schema.resolver.URIResolver;
import org.fabric3.host.contribution.InstallException;
import org.fabric3.host.contribution.StoreException;
import org.fabric3.host.stream.Source;
import org.fabric3.spi.contribution.MetaDataStore;
import org.fabric3.spi.contribution.ProcessorRegistry;
import org.fabric3.spi.contribution.Resource;
import org.fabric3.spi.contribution.ResourceElement;
import org.fabric3.spi.contribution.ResourceProcessor;
import org.fabric3.spi.contribution.ResourceState;
import org.fabric3.spi.introspection.IntrospectionContext;
import org.fabric3.wsdl.contribution.BindingSymbol;
import org.fabric3.wsdl.contribution.PortSymbol;
import org.fabric3.wsdl.contribution.PortTypeSymbol;
import org.fabric3.wsdl.contribution.ServiceSymbol;
import org.fabric3.wsdl.contribution.WsdlResourceProcessorExtension;
import org.fabric3.wsdl.contribution.WsdlServiceContractSymbol;
import org.fabric3.wsdl.contribution.WsdlSymbol;
import org.fabric3.wsdl.factory.Wsdl4JFactory;
import org.fabric3.wsdl.model.WsdlServiceContract;
import org.fabric3.wsdl.processor.WsdlContractProcessor;
import org.oasisopen.sca.Constants;
import org.oasisopen.sca.annotation.EagerInit;
import org.oasisopen.sca.annotation.Init;
import org.oasisopen.sca.annotation.Reference;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI;
/**
* Indexes and processes a WSDL document, referenced schemas, and introspected service contracts deriving from port types in a contribution. This implementation
* uses the WSDL4J to represent the document.
*/
@EagerInit
public class WsdlResourceProcessor implements ResourceProcessor {
private static final QName SCHEMA_NAME = new QName(W3C_XML_SCHEMA_NS_URI, "schema");
private static final QName IMPORT_NAME = new QName(W3C_XML_SCHEMA_NS_URI, "import");
private static final QName SCHEMA_LOCATION = new QName("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation");
private static final QName CALLBACK_ATTRIBUTE = new QName(Constants.SCA_NS, "callback");
private static final String MIME_TYPE = "text/wsdl+xml";
private ProcessorRegistry registry;
private WsdlContractProcessor processor;
private MetaDataStore store;
private Wsdl4JFactory factory;
private DocumentBuilderFactory documentBuilderFactory;
private List extensions = new ArrayList();
public WsdlResourceProcessor(@Reference ProcessorRegistry registry,
@Reference WsdlContractProcessor processor,
@Reference MetaDataStore store,
@Reference Wsdl4JFactory factory) {
this.registry = registry;
this.processor = processor;
this.store = store;
this.factory = factory;
documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true);
}
@Reference(required = false)
public void setExtensions(List extensions) {
this.extensions = extensions;
}
@Init
public void init() {
registry.register(this);
}
public String getContentType() {
return MIME_TYPE;
}
public void index(Resource resource, IntrospectionContext context) throws InstallException {
InputStream stream = null;
try {
Source source = resource.getSource();
stream = source.openStream();
// eagerly process the WSDL since port types need to be available during contribution processing.
parse(resource, context);
} catch (IOException e) {
throw new InstallException(e);
} finally {
try {
if (stream != null) {
stream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void process(Resource resource, IntrospectionContext context) throws InstallException {
Definition definition = resolveLocalWsdl(resource);
// Process callbacks here (as opposed to eagerly in #index(..) since the SCA callback attribute may reference a portType in another document.
// Processing at this point guarantees the callback portType will be indexed and referenceable.
for (ResourceElement, ?> element : resource.getResourceElements()) {
if (element.getSymbol() instanceof WsdlServiceContractSymbol) {
WsdlServiceContract contract = (WsdlServiceContract) element.getValue();
PortType portType = contract.getPortType();
QName callbackPortTypeName = (QName) portType.getExtensionAttribute(CALLBACK_ATTRIBUTE);
if (callbackPortTypeName != null) {
PortType callbackPortType = definition.getPortType(callbackPortTypeName);
if (callbackPortType != null) {
resolveLocalCallbackContract(callbackPortTypeName, contract, resource, context);
} else {
resolveExternalCallbackContract(callbackPortTypeName, contract, context);
}
}
}
}
resource.setState(ResourceState.PROCESSED);
}
/**
* Parse all WSDL-related elements in a resource pointing to a WSDL document.
*
* @param resource the resource
* @param context introspection context
* @throws InstallException if an unexpected error occurs
*/
@SuppressWarnings({"unchecked"})
private void parse(Resource resource, IntrospectionContext context) throws InstallException {
// parse the WSDL
Source source = resource.getSource();
Definition definition = parseWsdl(source, context);
QName wsdlQName = definition.getQName();
WsdlSymbol wsdlSymbol = new WsdlSymbol(wsdlQName);
ResourceElement wsdlElement = new ResourceElement(wsdlSymbol, definition);
resource.addResourceElement(wsdlElement);
Map services = definition.getServices();
for (Service service : services.values()) {
ServiceSymbol serviceSymbol = new ServiceSymbol(service.getQName());
ResourceElement serviceElement = new ResourceElement(serviceSymbol, service);
resource.addResourceElement(serviceElement);
Map ports = service.getPorts();
for (Port port : ports.values()) {
QName portName = new QName(definition.getTargetNamespace(), port.getName());
PortSymbol portSymbol = new PortSymbol(portName);
ResourceElement portElement = new ResourceElement(portSymbol, port);
resource.addResourceElement(portElement);
}
}
for (Object object : definition.getPortTypes().values()) {
PortType portType = (PortType) object;
QName name = portType.getQName();
PortTypeSymbol symbol = new PortTypeSymbol(name);
ResourceElement element = new ResourceElement(symbol, portType);
resource.addResourceElement(element);
}
// parse WSDL schemas
XmlSchemaCollection schemaCollection = parseSchema(definition, context);
// TODO index XML schema elements and types?
// introspect port type service contracts
for (Object object : definition.getPortTypes().values()) {
PortType portType = (PortType) object;
WsdlServiceContract contract = processor.introspect(portType, definition, schemaCollection, context);
QName name = portType.getQName();
WsdlServiceContractSymbol symbol = new WsdlServiceContractSymbol(name);
ResourceElement element
= new ResourceElement(symbol, contract);
resource.addResourceElement(element);
}
// introspect bindings
Collection bindings = definition.getBindings().values();
for (Binding binding : bindings) {
BindingSymbol bindingSymbol = new BindingSymbol(binding.getQName());
ResourceElement serviceElement = new ResourceElement(bindingSymbol, binding);
resource.addResourceElement(serviceElement);
}
// callback processor extensions
for (WsdlResourceProcessorExtension extension : extensions) {
extension.process(resource, definition);
}
}
/**
* Parses the WSDL document.
*
* @param source the Source for reading the document
* @param context the introspection context
* @return the parsed WSDL
* @throws InstallException if an unexpected error occurs
*/
private Definition parseWsdl(Source source, IntrospectionContext context) throws InstallException {
WSDLLocator locator = new SourceWsdlLocator(source, context);
try {
WSDLReader reader = factory.newReader();
Definition definition = reader.readWSDL(locator);
if (!definition.getNamespaces().values().contains("http://schemas.xmlsoap.org/wsdl/soap/")) {
// Workaround for a bug in WSDL4J where a WSDL document does not reference the SOAP namespace and an attempt is made to serialize it,
// an exception is thrown.
definition.addNamespace("soap11", "http://schemas.xmlsoap.org/wsdl/soap/");
}
parseSchemaLocation(definition, context);
return definition;
} catch (WSDLException e) {
throw new InstallException(e);
} finally {
locator.close();
}
}
/**
* Parses the schemaLocation attribute if present.
*
* @param definition the WSDL
* @param context the introspection context
* @throws InstallException if an unexpected error parsing the schema occurs
*/
private void parseSchemaLocation(Definition definition, IntrospectionContext context) throws InstallException {
QName schemaLocation = (QName) definition.getExtensionAttribute(SCHEMA_LOCATION);
if (schemaLocation == null) {
// no schema location present, skip
return;
}
// strip extra whitespace
String trimmed = schemaLocation.getLocalPart().replaceAll("\\s{2,}", " ");
String[] locationValue = trimmed.split(" ");
int len = locationValue.length;
if (len == 1) {
populateSchemaTypes(definition, "", locationValue[0]);
} else if (len > 1) {
if (len % 2 != 0) {
// make sure the schema location contains pairs of values if more than one
InvalidSchemaLocation error = new InvalidSchemaLocation("Invalid schemaLocation value: " + schemaLocation.getLocalPart());
context.addError(error);
return;
}
for (int i = 0; i < locationValue.length - 1; i++) {
String namespace = locationValue[i];
String location = locationValue[i + 1];
populateSchemaTypes(definition, namespace, location);
}
}
}
/**
* Populate the WSDL with an import for the schema location value.
*
* @param definition the WSDL
* @param targetNamespace the schema target namespace
* @param schemaLocation the de-referenceable schema location
* @throws InstallException if an unexpected error parsing the schema occurs
*/
private void populateSchemaTypes(Definition definition, String targetNamespace, String schemaLocation) throws InstallException {
Types types = definition.createTypes();
Schema schema;
try {
schema = (Schema) definition.getExtensionRegistry().createExtension(Types.class, SCHEMA_NAME);
} catch (WSDLException e) {
throw new InstallException(e);
}
Document document;
try {
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
document = documentBuilder.newDocument();
} catch (ParserConfigurationException e) {
throw new InstallException(e);
}
Element schemaElement = document.createElementNS(SCHEMA_NAME.getNamespaceURI(), "xsd:schema");
schema.setElement(schemaElement);
Element importElement = document.createElementNS(IMPORT_NAME.getNamespaceURI(), "xsd:import");
importElement.setAttribute("namespace", targetNamespace);
importElement.setAttribute("schemaLocation", schemaLocation);
schemaElement.appendChild(importElement);
types.addExtensibilityElement(schema);
definition.setTypes(types);
}
/**
* Parses the contents of schema entries and imported documents using Apache Commons XmlSchema.
*
* @param definition the WSDL
* @param context the introspection context
* @return the parsed schema values
*/
private XmlSchemaCollection parseSchema(Definition definition, IntrospectionContext context) {
XmlSchemaCollection collection = new XmlSchemaCollection();
collection.setSchemaResolver(createResolvers(collection));
try {
Types types = definition.getTypes();
if (types == null) {
// types not defined
return collection;
}
for (Object obj : types.getExtensibilityElements()) {
if (obj instanceof Schema) {
Schema schema = (Schema) obj;
Element element = schema.getElement();
collection.setBaseUri(schema.getDocumentBaseURI());
// create a synthetic id to work around issue where XmlSchema cannot handle elements with the same target namespace
String syntheticId = definition.getDocumentBaseURI() + "#" + UUID.randomUUID().toString();
collection.read(element, syntheticId);
}
}
} catch (RuntimeException e) {
// For some reason, Apache XmlSchema wraps schema exceptions in a generic RuntimeException. Check if that is the case.
if (!(e.getCause() instanceof XmlSchemaException)) {
throw e;
}
InvalidWsdl error = new InvalidWsdl("Error parsing Schema", e.getCause());
context.addError(error);
}
return collection;
}
private URIResolver createResolvers(XmlSchemaCollection collection) {
DefaultURIResolver defaultResolver = new DefaultURIResolver();
ContextClassLoaderResolver classLoaderResolver = new ContextClassLoaderResolver(defaultResolver);
return new RelativeUrlResolver(collection, classLoaderResolver);
}
private Definition resolveLocalWsdl(Resource resource) {
Definition definition = null;
for (ResourceElement, ?> element : resource.getResourceElements()) {
if (element.getSymbol() instanceof WsdlSymbol) {
definition = (Definition) element.getValue();
break;
}
}
if (definition == null) {
// should not happen
throw new AssertionError("WSDL document not found");
}
return definition;
}
private void resolveExternalCallbackContract(QName callbackPortTypeName, WsdlServiceContract contract, IntrospectionContext context) {
WsdlServiceContractSymbol symbol = new WsdlServiceContractSymbol(callbackPortTypeName);
URI contributionUri = context.getContributionUri();
ResourceElement resolved;
try {
resolved = store.resolve(contributionUri, WsdlServiceContract.class, symbol, context);
} catch (StoreException e) {
CallbackContractLoadError error = new CallbackContractLoadError("Error resolving callback port type:" + callbackPortTypeName, e);
context.addError(error);
return;
}
if (resolved == null) {
PortTypeNotFound error = new PortTypeNotFound("Callback port type not found: " + callbackPortTypeName);
context.addError(error);
return;
}
WsdlServiceContract callbackContract = resolved.getValue();
contract.setCallbackContract(callbackContract);
}
private void resolveLocalCallbackContract(QName callbackPortTypeName, WsdlServiceContract contract, Resource resource, IntrospectionContext context) {
WsdlServiceContractSymbol symbol = new WsdlServiceContractSymbol(callbackPortTypeName);
for (ResourceElement, ?> resourceElement : resource.getResourceElements()) {
if (resourceElement.getSymbol().equals(symbol)) {
WsdlServiceContract callbackContract = (WsdlServiceContract) resourceElement.getValue();
contract.setCallbackContract(callbackContract);
break;
}
}
if (contract.getCallbackContract() == null) {
PortTypeNotFound error = new PortTypeNotFound("Callback port type not found: " + callbackPortTypeName);
context.addError(error);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy