Downloading WSDL files for offline use

Another blog post from the past.

We had the problem that one of the developer worked on the service layer of a project and the other wanted to use the functions of it. The service layer was exposed to the internet as web services. As the function signatures of the service layer had not been finalized yet there was the question how to re-generate the client side code on the other side each times the api had been changed.

It was pretty clear that as using maven for compiling the source the ws-import maven plugin should be used. However an url of the wsdl file must be provided for that plugin and this is where the problems came into the picture. The wsdl contained several xsd:import xml tags which made it pretty hard to download always the wsdl files with the xsd files it imports by hand. Also the value of schemalocation attribute had to be always changed to the location of the downloaded xsd files.

Luckily it was not hard to write a code snippet that downloads a wsdl (which is an xsd as well) from an url and if it imports other xsd files it downloads them as well. Also the code snippet changes the schemalocation in the downloaded wsdl files.

The code snippet is the following:

 
 package hu.everit.utils.xsd.downloader;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class XsdDownloader {

	public static class XsdNameSpaceContext implements NamespaceContext {

		private final Map<String, String> nameSpaceUrisByPrefixes;

		public XsdNameSpaceContext() {
			nameSpaceUrisByPrefixes = new HashMap<String, String>();
			nameSpaceUrisByPrefixes.put("xsd",
					"http://www.w3.org/2001/XMLSchema");
		}

		public String getNamespaceURI(final String prefix) {
			return nameSpaceUrisByPrefixes.get(prefix);
		}

		public String getPrefix(final String namespaceURI) {
			// TODO Auto-generated method stub
			return null;
		}

		public Iterator getPrefixes(final String namespaceURI) {
			// TODO Auto-generated method stub
			return null;
		}

	}

	/**
	 * @param args
	 */
	public static void main(final String[] args) {
		if (args.length != 2) {
			System.out.println("Only two parameters: 1-url 2-downloadPrefix");
			return;
		}
		String xsdUrl = args[0];
		String filePrefix = args[1];
		XsdDownloader xsdDownloader = new XsdDownloader();
		xsdDownloader.setDownloadPrefix(filePrefix);
		try {
			xsdDownloader.downloadXsdRecurse(xsdUrl);
		} catch (TransformerConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (TransformerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	Map<String, String> fileNamesByprocessedUrls =
			new HashMap<String, String>();

	private String downloadPrefix;

	private void downloadXsdRecurse(final String xsdUrl) throws IOException,
			ParserConfigurationException, SAXException, TransformerException {

		String outputFileName = downloadPrefix;
		if (fileNamesByprocessedUrls.size() > 0) {
			outputFileName =
					outputFileName + "." + fileNamesByprocessedUrls.size();
		}
		outputFileName = outputFileName + ".xsd";
		fileNamesByprocessedUrls.put(xsdUrl, outputFileName);

		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		dbf.setNamespaceAware(true);
		DocumentBuilder db = dbf.newDocumentBuilder();
		Document doc = db.parse(xsdUrl);

		processElementRecurse(doc.getDocumentElement());

		File outputFile = new File(outputFileName);
		TransformerFactory trf = TransformerFactory.newInstance();
		Transformer tr = trf.newTransformer();
		Source source = new DOMSource(doc);
		Result result = new StreamResult(outputFile);
		tr.transform(source, result);
	}

	private void processElementRecurse(final Element node) throws IOException,
			ParserConfigurationException, SAXException, TransformerException {
		NodeList nl = node.getChildNodes();
		for (int i = 0, n = nl.getLength(); i < n; i++) {
			Node childNode = nl.item(i);
			if (childNode instanceof Element) {
				Element childElement = (Element) childNode;
				if ("http://www.w3.org/2001/XMLSchema".equals(childElement
						.getNamespaceURI())
						&& childElement.getLocalName().equals("import")) {
					System.out.println("foundElement");
					String schLoc = childElement.getAttribute("schemaLocation");
					if (!fileNamesByprocessedUrls.containsKey(schLoc)) {
						downloadXsdRecurse(schLoc);
						String newLoc = fileNamesByprocessedUrls.get(schLoc);
						if (newLoc != null) {
							childElement.setAttribute("schemaLocation", newLoc);
						}
					} else {
						String newLoc = fileNamesByprocessedUrls.get(schLoc);
						childElement.setAttribute("schemaLocation", newLoc);
					}
				} else if ("http://schemas.xmlsoap.org/wsdl/"
						.equals(childElement.getNamespaceURI())
						&& childElement.getLocalName().equals("import")) {
					System.out.println("foundWsdlElement");
					String schLoc = childElement.getAttribute("location");
					if (!fileNamesByprocessedUrls.containsKey(schLoc)) {
						downloadXsdRecurse(schLoc);
						String newLoc = fileNamesByprocessedUrls.get(schLoc);
						if (newLoc != null) {
							childElement.setAttribute("location", newLoc);
						}
					} else {
						String newLoc = fileNamesByprocessedUrls.get(schLoc);
						childElement.setAttribute("location", newLoc);
					}
				} else {
					processElementRecurse(childElement);
				}
			}
		}
	}

	public void setDownloadPrefix(final String downloadPrefix) {
		this.downloadPrefix = downloadPrefix;
	}

}
The main function of the class above accepts two parameters. The first one is the url of the wsdl file and the second is the prefix of the file that will be the beginning of the downloaded files. The downloaded files will be named as PREFIX.xsd, PREFIX.1.xsd, PREFIX.2.xsd, … The first one is the main one that the specified url contained, the others are the imported. The code snippet also takes care of the cyclic imports so the files that were downloaded ones already will not be downloaded again.
The mavenized project with the compiled executable jar is available here. To run it enter the following command: java -jar everit-xsd-downloader-1.0.jar URL FILEPREFIX 
Advertisements

About Balázs Zsoldos

Balazs Zsoldos is the co-founder of Everit. He is the leader of the development of Everit OpenSource Components. Developing Java based solutions is not only his job but also his passion. He believes in simplicity. That is why he decided to design and build as many simple, but useful goal-oriented modules as he can. As the base of the stack, he chose OSGi. Balazs does not believe in monoholitic frameworks, therefore all of the solutions that was designed by him can be used separately. In the beginning of his career, Balazs was a big fan of JEE and Spring. After a while, he changed his mind and started to try replacing everything with non-magical solutions that do not contain interceptors, weaving, etc.

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

%d bloggers like this: