/*
* 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.ode.bpel.compiler;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.wsdl.Definition;
import javax.wsdl.Import;
import javax.wsdl.Message;
import javax.wsdl.PortType;
import javax.wsdl.Types;
import javax.wsdl.extensions.ExtensibilityElement;
import javax.xml.namespace.QName;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.ode.bpel.compiler.api.CompilationException;
import org.apache.ode.bpel.compiler.api.CompilerContext;
import org.apache.ode.bpel.compiler.bom.PartnerLinkType;
import org.apache.ode.bpel.compiler.bom.PropertyAlias;
import org.apache.ode.bpel.compiler.wsdl.Definition4BPEL;
import org.apache.ode.bpel.compiler.wsdl.XMLSchemaType;
import org.apache.ode.utils.DOMUtils;
import org.apache.ode.utils.Namespaces;
import org.apache.ode.utils.StreamUtils;
import org.apache.ode.utils.msg.MessageBundle;
import org.apache.ode.utils.xsd.SchemaModel;
import org.apache.ode.utils.xsd.SchemaModelImpl;
import org.apache.ode.utils.xsd.XSUtils;
import org.apache.ode.utils.xsd.XsdException;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
/**
* A parsed collection of WSDL definitions, including BPEL-specific extensions.
*/
class WSDLRegistry {
private static final Logger __log = LoggerFactory.getLogger(WSDLRegistry.class);
private static final CommonCompilationMessages __cmsgs =
MessageBundle.getMessages(CommonCompilationMessages.class);
private final HashMap<String, ArrayList<Definition4BPEL>> _definitions = new HashMap<String, ArrayList<Definition4BPEL>>();
private final Map<URI, byte[]> _schemas = new HashMap<URI,byte[]>();
private final Map<URI, byte[]> _internalSchemas = new HashMap<URI, byte[]>();
private final Map<URI, Document> _documentSchemas = new HashMap<URI, Document>();
private SchemaModel _model;
private CompilerContext _ctx;
WSDLRegistry(CompilerContext cc) {
// bogus schema to force schema creation
_schemas.put(URI.create("http://fivesight.com/bogus/namespace"),
("<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""
+ " targetNamespace=\"http://fivesight.com/bogus/namespace\">"
+ "<xsd:simpleType name=\"__bogusType__\">"
+ "<xsd:restriction base=\"xsd:normalizedString\"/>"
+ "</xsd:simpleType>" + "</xsd:schema>").getBytes());
try {
_schemas.put(URI.create(Namespaces.WSDL_11), StreamUtils.read(getClass().getResource("/wsdl.xsd")));
_schemas.put(URI.create("http://www.w3.org/2001/xml.xsd"), StreamUtils.read(getClass().getResource("/xml.xsd")));
} catch (IOException e) {
throw new RuntimeException("Couldn't load default schemas.", e);
}
_ctx = cc;
}
public Definition4BPEL[] getDefinitions(){
ArrayList<Definition4BPEL> result = new ArrayList<Definition4BPEL>();
for (ArrayList<Definition4BPEL> definition4BPELs : _definitions.values()) {
for (Definition4BPEL definition4BPEL : definition4BPELs) {
result.add(definition4BPEL);
}
}
return result.toArray(new Definition4BPEL[result.size()]);
}
/**
* Get the schema model (XML Schema).
*
* @return schema model
*/
public SchemaModel getSchemaModel() {
if (_model == null) {
_model = SchemaModelImpl.newModel(_schemas);
}
assert _model != null;
return _model;
}
/**
* Adds a WSDL definition for use in resolving MessageType, PortType,
* Operation and BPEL properties and property aliases
*
* @param def WSDL definition
*/
@SuppressWarnings("unchecked")
public void addDefinition(Definition4BPEL def, ResourceFinder rf, URI defuri) throws CompilationException {
if (def == null)
throw new NullPointerException("def=null");
if (__log.isDebugEnabled()) {
__log.debug("addDefinition(" + def.getTargetNamespace() + " from " + def.getDocumentBaseURI() + ")");
}
if (_definitions.containsKey(def.getTargetNamespace())) {
// This indicates that we imported a WSDL with the same namespace from
// two different locations. This is not an error, but should be a warning.
if (__log.isInfoEnabled()) {
__log.info("WSDL at " + defuri + " is a duplicate import, your documents " +
"should all be in different namespaces (its's not nice but will still work).");
}
for (Definition4BPEL aDef : _definitions.get(def.getTargetNamespace())) {
if (aDef.getDocumentBaseURI().equals(def.getDocumentBaseURI())) {
if (__log.isInfoEnabled()) {
__log.info("WSDL at " + defuri + " is already imported, this denotes a circular reference.");
// no need to keep going: either return or throw an error
}
return;
}
}
}
ArrayList<Definition4BPEL> defs = null;
if (_definitions.get(def.getTargetNamespace()) == null) defs = new ArrayList<Definition4BPEL>();
else defs = _definitions.get(def.getTargetNamespace());
defs.add(def);
_definitions.put(def.getTargetNamespace(), defs);
captureSchemas(def, rf, defuri);
if (__log.isDebugEnabled())
__log.debug("Processing <imports> in " + def.getDocumentBaseURI());
for (List<Import> imports : ((Map<String, List<Import>>)def.getImports()).values()) {
HashSet<String> imported = new HashSet<String>();
for (Import im : imports) {
// If there are several imports in the same WSDL all importing the same namespace
// that is a sure sign of programmer error.
if (imported.contains(im.getNamespaceURI())) {
if (__log.isInfoEnabled()) {
__log.info("WSDL at " + im.getLocationURI() + " imports several documents in the same " +
"namespace (" + im.getNamespaceURI() + "), your documents should all be in different " +
"namespaces (its's not nice but will still work).");
}
}
Definition4BPEL importDef = (Definition4BPEL) im.getDefinition();
// The assumption here is that if the definition is not set on the
// import object then there was some problem parsing the thing,
// although it would have been nice to actually get the parse
// error.
if (importDef == null) {
CompilationException ce = new CompilationException(
__cmsgs.errWsdlImportNotFound(im.getNamespaceURI(),
im.getLocationURI()).setSource(new SourceLocationImpl(defuri)));
if (_ctx == null)
throw ce;
_ctx.recoveredFromError(new SourceLocationImpl(defuri), ce);
continue;
}
imported.add(im.getNamespaceURI());
addDefinition((Definition4BPEL) im.getDefinition(), rf, defuri.resolve(im.getLocationURI()));
}
}
}
public void addSchemas(Map<URI, byte[]> capture) {
_schemas.putAll(capture);
}
@SuppressWarnings("unchecked")
private void captureSchemas(Definition def, ResourceFinder rf, URI defuri) throws CompilationException {
assert def != null;
if (__log.isDebugEnabled())
__log.debug("Processing XSD schemas in " + def.getDocumentBaseURI());
Types types = def.getTypes();
if (types != null) {
addAllInternalSchemas(def);
int localSchemaId = 0;
for (Iterator<ExtensibilityElement> iter =
((List<ExtensibilityElement>)def.getTypes().getExtensibilityElements()).iterator();
iter.hasNext();) {
ExtensibilityElement ee = iter.next();
if (ee instanceof XMLSchemaType) {
byte[] schema = ((XMLSchemaType)ee).getXMLSchema();
WsdlFinderXMLEntityResolver resolver = new WsdlFinderXMLEntityResolver(rf, defuri, _internalSchemas, false);
try {
Map<URI, byte[]> capture = XSUtils.captureSchema(defuri, schema, resolver, localSchemaId);
for (URI uri : capture.keySet()) {
if (!_schemas.containsKey(uri)) {
_schemas.put(uri, capture.get(uri));
}
}
// _schemas.putAll(capture);
try {
Document doc = DOMUtils.parse(new InputSource(new ByteArrayInputStream(schema)));
String schemaTargetNS = doc.getDocumentElement().getAttribute("targetNamespace");
if (schemaTargetNS != null && schemaTargetNS.length() > 0) {
URI schemaNamespace = new URI(schemaTargetNS);
if (!_internalSchemas.containsKey(schemaNamespace)) {
_internalSchemas.put(schemaNamespace, schema);
}
if (!_documentSchemas.containsKey(schemaNamespace)) {
_documentSchemas.put(schemaNamespace, doc);
}
}
} catch (Exception e) {
throw new RuntimeException("Couldn't parse schema in " + def.getTargetNamespace(), e);
}
} catch (XsdException xsde) {
__log.debug("captureSchemas: capture failed for " + defuri,xsde);
LinkedList<XsdException> exceptions = new LinkedList<XsdException>();
while (xsde != null) {
exceptions.addFirst(xsde);
xsde = xsde.getPrevious();
}
for (XsdException ex : exceptions) {
// TODO: the line number here is going to be wrong for the in-line schema.
// String location = ex.getSystemId() + ":" + ex.getLineNumber();
CompilationException ce = new CompilationException(
__cmsgs.errSchemaError(ex.getDetailMessage()).setSource(new SourceLocationImpl(defuri)));
if (_ctx != null)
_ctx.recoveredFromError(new SourceLocationImpl(defuri),ce);
else
throw ce;
}
}
// invalidate model
_model = null;
localSchemaId ++;
}
}
}
}
@SuppressWarnings("unchecked")
private void addAllInternalSchemas(Definition def) {
for (Iterator<ExtensibilityElement> iter = ((List<ExtensibilityElement>) def.getTypes().getExtensibilityElements()).iterator(); iter.hasNext();) {
ExtensibilityElement ee = iter.next();
if (ee instanceof XMLSchemaType) {
byte[] schema = ((XMLSchemaType) ee).getXMLSchema();
try {
Document doc = DOMUtils.parse(new InputSource(new ByteArrayInputStream(schema)));
String schemaTargetNS = doc.getDocumentElement().getAttribute("targetNamespace");
if (schemaTargetNS != null && schemaTargetNS.length() > 0) {
_internalSchemas.put(new URI(schemaTargetNS), schema);
_documentSchemas.put(new URI(schemaTargetNS), doc);
}
} catch (Exception e) {
throw new RuntimeException("Couldn't parse schema in " + def.getTargetNamespace(), e);
}
}
}
}
public org.apache.ode.bpel.compiler.bom.Property getProperty(QName name) {
ArrayList<Definition4BPEL> defs = _definitions.get(name.getNamespaceURI());
if (defs == null) return null;
for (Definition4BPEL definition4BPEL : defs) {
if (definition4BPEL != null && definition4BPEL.getProperty(name) != null)
return definition4BPEL.getProperty(name);
}
return null;
}
public PropertyAlias getPropertyAlias(QName propertyName, QName messageType) {
ArrayList<Definition4BPEL> defs = _definitions.get(propertyName.getNamespaceURI());
if (defs == null) return null;
for (Definition4BPEL definition4BPEL : defs) {
if (definition4BPEL != null && definition4BPEL.getPropertyAlias(propertyName, messageType) != null)
return definition4BPEL.getPropertyAlias(propertyName, messageType);
}
return null;
}
public PartnerLinkType getPartnerLinkType(QName partnerLinkType) {
ArrayList<Definition4BPEL> defs = _definitions.get(partnerLinkType.getNamespaceURI());
if (defs == null) return null;
for (Definition4BPEL definition4BPEL : defs) {
if (definition4BPEL != null && definition4BPEL.getPartnerLinkType(partnerLinkType) != null)
return definition4BPEL.getPartnerLinkType(partnerLinkType);
}
return null;
}
public PortType getPortType(QName portType) {
ArrayList<Definition4BPEL> defs = _definitions.get(portType.getNamespaceURI());
if (defs == null) return null;
for (Definition4BPEL definition4BPEL : defs) {
if (definition4BPEL != null && definition4BPEL.getPortType(portType) != null)
return definition4BPEL.getPortType(portType);
}
return null;
}
public Message getMessage(QName msgType) {
ArrayList<Definition4BPEL> defs = _definitions.get(msgType.getNamespaceURI());
if (defs == null) return null;
for (Definition4BPEL definition4BPEL : defs) {
if (definition4BPEL != null && definition4BPEL.getMessage(msgType) != null)
return definition4BPEL.getMessage(msgType);
}
return null;
}
/**
* @return All parsed schemas. This doesn't include schemas from bpel imports.
*/
Map<URI, Document> getSchemaDocuments() {
return _documentSchemas;
}
/**
* @return All captured schema sources including those from bpel imports.
*/
Map<URI, Source> getSchemaSources() {
Map<URI, Source> schemaSources = new HashMap<URI, Source>();
for (URI uri : _documentSchemas.keySet()) {
Document document = _documentSchemas.get(uri);
schemaSources.put(uri, new DOMSource(document));
}
for (URI uri : _schemas.keySet()) {
schemaSources.put(uri, new StreamSource(new ByteArrayInputStream(_schemas.get(uri))));
}
return schemaSources;
}
}