Blogs Blogs

«Back

Downloading WSDL files for offline use

 

 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 
Comments
Trackback URL: