WSDL Parsing with Eclipse’s metamodel for WSDL

I recently had to work with the WSDL metamodel from Eclipse WTP.
I thought it may be useful to compare it with other WSDL parsing solutions. Notice that I will focus on Java solutions here.

A quick introduction to WSDL

WSDL stands for Web Services Description Language.
It is a W3C specification to describe the interface / contract of a service. A WSDL contract is defined in a *.wsdl file. This file describes the service operations, their input and output parameters, the declaration of services, their reach location and the communication protocol to use to interact with them. A *.wsdl file may import other WSDL and also XML schemas (they contain the type definition of operation parameters).

There are two versions of WSDL that may be worked with: WSDL 1.1 and WSDL 2.0.
Although WSDL 2.0 is conceptually better (in particular because of MEP – Message Exchange Patterns), WSDL 1.1 is the most used. There are very few tools and libraries libraries which support it (Axis 2 supports it, CXF does not, JAX-WS did not plan its support). And it must be said that the two versions are different in the approach and some concepts do not match at all.

And in WSDL 1.1, there are some distinctions, such as the style (document / wrapped, xml-rpc…).
Information about this can be found on the internet.

Parsing solutions

For WSDL 1.1, the most famous solution is WSDL4j.
This library provides an easy (and light!) solution to parse WSDL definitions. Its only limitation in my opinion, is that it is not easy to introspect XML schemas with it.

For WSDL 2.0, there is Apache Woden.
This library allows to parse WSDL (2.0) definitions and to convert WSDL (1.1) definitions to WSDL 2.0 (again, both versions are very different).

Then, there is a more recent project called EasyWSDL.
The versions 1.x and 2.x aimed at providing a single API to parse and edit WSDL 1.1 and WSDL 2.0 definitions. Unfortunately, this API makes an important use of Java generics. However, it is easy to navigate in XML schema with it. The version 3.x is still in incubation but took a very different turn. To simplify the API, and because it was ambiguous, it was decided to separate the API for the various versions of the specification. For the moment, EasyWSDL 3.x only supports WSDL 1.1. The API is much more simple and it is still easy to navigate in XML schemas. Besides, it can execute XPath queries on a macro document (the main document and all the import as a single virtual document). However, it misses some helpers to work with imports (writing XPath queries is not very user-friendly).

Eventually, there is the EMF metamodel for WSDL.
The metamodel is provided by the WTP project at Eclipse. It can only parse WSDL 1.1. In fact, I found out that it relies on and extends WSDL4j. One of the main improvements is that it can introspect XML schemas. To use it, you have to put the EMF libraries and some WTP libraries in your dependencies:

  • org.eclipse.xsd: the metamodel for XML schemas.
  • org.eclipse.wst.wsdl: the metamodel for WSDL.
  • javax.wsdl: a wrapper for WSDL4j.

Reading a WSDL with the WSDL metamodel

/**
 * Finds all the port types declared in the WSDL or in its imported documents.
 * @param emfUri an EMF URI pointing to a WSDL definition
 */
List<PortType> findAllPortTypes( URI emfUri ) {
	
	// Register the basic elements of the specification
	ResourceSet resourceSet = new ResourceSetImpl();
	resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put( "xml", new XMLResourceFactoryImpl());
	resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put( "xsd", new XSDResourceFactoryImpl());
	resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put( "wsdl", new WSDLResourceFactoryImpl());
	resourceSet.getPackageRegistry().put( XSDPackage.eNS_URI, XSDPackage.eINSTANCE );
	resourceSet.getPackageRegistry().put( WSDLPackage.eNS_URI, WSDLPackage.eINSTANCE );

	// Register the required extensions
	// None here
	
	// Load the main document
	Resource resource = resourceSet.getResource( emfUri, true );
    Definition def = (Definition) resource.getContents().iterator().next();

	// Add the initial definition
	Set<Definition> definitions = new HashSet<Definition> ();
	definitions.add( alreadyLoadedDefinition );

	// Process the imports
	processImports( alreadyLoadedDefinition.getImports(), definitions );
	
	// Get all the port types
	List<PortType> portTypes = new ArrayList<PortType> ();
	for( Definition def : definitions ) {
		for( Object o : def.getPortTypes().values())
			portTypes.add((PortType) o );
	}
	
	return portTypes;
}

/**
 * Finds the definitions from imports and processes them recursively.
 * @param imports a map of imports (see {@link Definition#getImports()})
 * @param definitions a list of definitions, found from import declarations
 */
private static void processImports( Map<?,?> imports, Collection<Definition> definitions ) {

	for( Object o : imports.values()) {

		// Case "java.util.list"
		if( o instanceof List<?> ) {
			for( Object oo : ((List<?>) o)) {
				Definition d = ((Import) oo).getEDefinition();
				if( d != null && ! definitions.contains( d )) {
					definitions.add( d );
					processImports( d.getImports(), definitions );
				}
			}
		}

		// Case "org.eclipse.wst.Definition"
		else if( o instanceof Definition ) {
			Definition d = (Definition) o;;
			if( ! definitions.contains( d )) {
				definitions.add( d );
				processImports( d.getImports(), definitions );
			}
		}
	}
}

XML schema can be accessed with this solution.
This is possible thanks to the metamodel for XML schemas.
As an example, here is how to parse a XML schema with this metamodel. Note that this metamodel is not only an EMF library, it is also bridged with a DOM model. In fact, this is also true for the WSDL metamodel (generally, this is not the case with EMF metamodels).

/**
 * Loads a XML schema.
 * @param emfUri an EMF URI
 * @return an instance of {@link XSDSchema}
 * <p>
 * This object already supports inclusions, which means there is no need to
 * get the imports and parse them.
 * </p>
 */
public static XSDSchema loadXmlSchema( URI emfUri ) {

	// Register the basic elements
	ResourceSet resourceSet = new ResourceSetImpl();
	resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put( "xml", new XMLResourceFactoryImpl());
	resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put( "xsd", new XSDResourceFactoryImpl());
	resourceSet.getPackageRegistry().put( XSDPackage.eNS_URI, XSDPackage.eINSTANCE );

	// Load the resource
	Resource resource = resourceSet.getResource( emfUri, true );
	return (XSDSchema) resource.getContents().iterator().next();
}

Creating a WSDL with the WSDL metamodel

Like most of standards, WSDL supports extensions.
In this sample, I use an extension for BPEL.

Definition createWsdlArtifact( String newWsdlUrl ) {

	// Initial data we got from another WSDL definition
	PortType portType = this.portTypePage.getPortType();
	Definition businessDefinition = (Definition) portType.eContainer();
		
	// The new definition to create
	Definition artifactsDefinition = WSDLFactory.eINSTANCE.createDefinition();
	artifactsDefinition.setTargetNamespace( businessDefinition.getTargetNamespace() + "Artifacts" );

	// Hack for the role: we need to define manually the name space prefix for the TNS of the business WSDL
	artifactsDefinition.getNamespaces().put( "tns", businessDefinition.getTargetNamespace());

	// WSDL import
	Import wsdlImport = WSDLFactory.eINSTANCE.createImport();
	wsdlImport.setLocationURI( newWsdlUrl );
	wsdlImport.setNamespaceURI( businessDefinition.getTargetNamespace());
	artifactsDefinition.addImport( wsdlImport );

	// Partner Link Type
	PartnerLinkType plType = PartnerlinktypeFactory.eINSTANCE.createPartnerLinkType();
	plType.setName( portType.getQName().getLocalPart() + "PLT" );

	Role plRole = PartnerlinktypeFactory.eINSTANCE.createRole();
	plRole.setName( portType.getQName().getLocalPart() + "Role" );
	plRole.setPortType( portType );
	plType.getRole().add( plRole );
	plType.setEnclosingDefinition( artifactsDefinition );
		
	// This is an extension, here is how to add it
	artifactsDefinition.getEExtensibilityElements().add( plType );
	return artifactsDefinition;
}

And here is how to write it.

void writeDefinition( Definition def, File targetFile ) {
	
	// Register the basic elements of the specification
	ResourceSet resourceSet = new ResourceSetImpl();
	resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put( "xml", new XMLResourceFactoryImpl());
	resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put( "xsd", new XSDResourceFactoryImpl());
	resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put( "wsdl", new WSDLResourceFactoryImpl());
	resourceSet.getPackageRegistry().put( XSDPackage.eNS_URI, XSDPackage.eINSTANCE );
	resourceSet.getPackageRegistry().put( WSDLPackage.eNS_URI, WSDLPackage.eINSTANCE );

	// Register the required extensions
	resourceSet.getPackageRegistry().put( PartnerlinktypePackage.eNS_URI, PartnerlinktypePackage.eINSTANCE );
	
	// Create a resource...
	URI emfUri = URI.createFileURI( targetFile.getAbsolutePath());
	Resource resource = resourceSet.createResource( emfUri );
	resource.getContents().add( def );
		
	// ... and save it
	// See @link{org.eclipse.emf.ecore.xmi.XMLResource} for examples of SAVE options
	Map<Object,Object> saveOptions = new HashMap<Object,Object> ();
	resource.save( saveOptions );
}

Conclusion

I have been working with WSDL for several years. And to be honest, I had never thought about using the WSDL metamodel outside Eclipse. I knew it was there, I knew how to work with EMF. But for me, it was more a tool than a real library. And somehow, this is true. But the fact is that it is not complicated to use. And it can also work in standalone applications, not only in Eclipse.

So, this may be a solution to consider, depending on your needs.

17 thoughts on “WSDL Parsing with Eclipse’s metamodel for WSDL

  1. I have previously created my own WSDL and XSD metamodels from their XSD schemas and recently discovered WTP’s WSDL (and XSD) metamodels by accident (when WTP’s resource class loaded the wsdl files and not my own). In general, using EMF instead of XML is a lot cleaner (if you know EMF, of course), and WTP’s metamodels are easy to work with (and better than duplicating their work). This is one of many hidden gems within the Eclipse ecosystem!

    1. I want to load or transform an existing WSDL file to an instance of an ecore WSDL metamodel
      Please, some one can help me?

      1. Hi,

        I think the first code sample in this article (about reading a WSDL) answers your question.
        The ecore is only used to generate code. When a load a WSDL file with the EMF meta-model for WSDL, it is compliant with the ecore model. The loaded WSDL is an instance of the WSDL meta-model. Or maybe you meant something else?

      2. Hi,

        The first code sample of this article (about reading a WSDL) do not work for me. Definitions or portTypes returned are not instances of the wsdl ecore metamodel.

        What i want is to transform a WSDL file (Myfile.wsdl) which is an instance of the WSDL xsd schema (WSDL.xsd) to a wsdl file (Myfile.xmi) which is an instance of an WSDL ecore Schema (WSDL.ecore).

        Please, can you provide me some guidelines on how to do that?
        Thanks in advance

      3. Hi,

        The code you have published generates a XMI representation of the WSDL.

        However, when I try to open the XMI file with Sample Reflective Ecore Model Editor, I get the following exception;

        org.eclipse.emf.ecore.xmi.PackageNotFoundException: Package with uri ‘http://www.eclipse.org/wsdl/2003/WSDL’ not found. (platform:/resource/XMLtransf/src/Datasource/Myfile.xmi, 2, 264)
        at org.eclipse.emf.ecore.xmi.impl.XMLHandler.getPackageForURI(XMLHandler.java:2599)
        at org.eclipse.emf.ecore.xmi.impl.XMLHandler.getFactoryForPrefix(XMLHandler.java:2429)

        Does anyone have any ideas why I get this error?

        Thanks

  2. Quick question: Have you tried using the WTP library for creating SOAP messages, with both the envelope and request/response?

    1. Hi,

      No, I have not yet tested it. I guess it works somehow like SoapUI.
      But I will probably try it. I am not really interested in the SOAP envelop itself, but in its content (the XML payload which matches the XML schema associated with the invoked operation).

  3. Yes, that’s actually what I meant: Create the SOAP response message, based on the result of the invoked operation. How do you create the response XML message, can the Ecore model of the SOAP structure and SOAPFactory be used?

    1. I think it would be easier using the XML schema metamodel.
      I don’t know if there are existing methods for this. But it should be quite easy to implement. Simple types are processed by hand. And complex types are processed recursively. And then, you can add the SOAP envelop. I will try to impement it later.

      1. HI,

        I found this article very helpful. Thanks !!!
        EMF wsdl metamodel is cleaner approach for parsing the valid WSDL. I ‘m exploring this metamodel and utilities to make best out of it.
        Could it be possible to direct me to achieve one more goal ( to find the probable xpaths from the underneath schema of the wsdl) .I could achieve it using the DOM API after I introspect the XSDTypeDefinition from the schema using the metamodel API, but would like to avoid it and want to achieve the same directly using the API of the metamodel . Help here would be appreciated.

    1. Hello,

      I have not worked with EasyWSDL for a very long time. And unfortunately, I think the project is dead. It could have been a very nice project, but it remained confined into a little space.

      If you want to use it anyway, you should rather add it through a Maven dependency. EasyWSDL depends on several libraries. So, only adding EasyWSDL to your project classpath will not be enough.

  4. Hi Vincent,

    Thanks for showing how to use the EMF metamodel for WSDL. I’ve managed to get as far as parsing the top-level WSDL structure with this approach. But there’s one thing puzzling me. Suppose I have something like

    I can navigate from the top-level down to the message and part. And from there I can access the element. But how do I get from there to the type itself? You mentioned that XML schemas can be accessed with this solution, but it wasn’t clear to me whether these links were automatically resolved and navigable in some way.

    Thanks for any hints,

    Kevin

  5. Ah, ignore my previous comment/question. I hadn’t noticed there was a getElementDeclaration method in addition to the getElement method 😦

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s