Java tutorial
/** * Copyright (c) 2012 centeractive ag. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA */ package com.centeractive.ws.legacy; import com.centeractive.ws.SoapBuilderException; import com.centeractive.ws.common.ResourceUtils; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; import org.apache.xmlbeans.*; import org.w3c.dom.*; import javax.xml.namespace.QName; import java.io.File; import java.net.URL; import java.util.*; /** * This class was extracted from the soapUI code base by centeractive ag in October 2011. * The main reason behind the extraction was to separate the code that is responsible * for the generation of the SOAP messages from the rest of the soapUI's code that is * tightly coupled with other modules, such as soapUI's graphical user interface, etc. * The goal was to create an open-source java project whose main responsibility is to * handle SOAP message generation and SOAP transmission purely on an XML level. * <br/> * centeractive ag would like to express strong appreciation to SmartBear Software and * to the whole team of soapUI's developers for creating soapUI and for releasing its * source code under a free and open-source licence. centeractive ag extracted and * modifies some parts of the soapUI's code in good faith, making every effort not * to impair any existing functionality and to supplement it according to our * requirements, applying best practices of software design. * * Changes done: * - changing location in the package structure * - removal of dependencies and code parts that are out of scope of SOAP message generation * - minor fixes to make the class compile out of soapUI's code base * - changing the mechanism using which external resources are loaded */ /** * XML-Schema related tools * * @author Ole.Matzura */ class SchemaUtils { private final static Logger log = Logger.getLogger(SchemaUtils.class); private static Map<String, XmlObject> defaultSchemas = new HashMap<String, XmlObject>(); public static final boolean STRICT_SCHEMA_TYPES = false; static { initDefaultSchemas(); } public static URL loadResoruce(String resourceName) { return ResourceUtils.getResourceWithAbsolutePackagePath(SchemaUtils.class, "/xsds/", resourceName); } public static void initDefaultSchemas() { try { defaultSchemas.clear(); loadDefaultSchema(loadResoruce("xop.xsd")); loadDefaultSchema(loadResoruce("XMLSchema.xsd")); loadDefaultSchema(loadResoruce("xml.xsd")); loadDefaultSchema(loadResoruce("swaref.xsd")); loadDefaultSchema(loadResoruce("xmime200505.xsd")); loadDefaultSchema(loadResoruce("xmime200411.xsd")); loadDefaultSchema(loadResoruce("soapEnvelope.xsd")); loadDefaultSchema(loadResoruce("soapEncoding.xsd")); loadDefaultSchema(loadResoruce("soapEnvelope12.xsd")); loadDefaultSchema(loadResoruce("soapEncoding12.xsd")); } catch (Exception e) { throw new SoapBuilderException(e); } } private static void loadDefaultSchema(URL url) throws Exception { XmlObject xmlObject = XmlUtils.createXmlObject(url); if (!((Document) xmlObject.getDomNode()).getDocumentElement().getNamespaceURI().equals(Constants.XSD_NS)) { return; } String targetNamespace = getTargetNamespace(xmlObject); if (defaultSchemas.containsKey(targetNamespace)) { log.warn("Overriding schema for targetNamespace " + targetNamespace); } defaultSchemas.put(targetNamespace, xmlObject); log.debug("Added default schema from " + url.getPath() + " with targetNamespace " + targetNamespace); } public static SchemaTypeSystem loadSchemaTypes(String wsdlUrl, SchemaLoader loader) { try { log.debug("Loading schema types from [" + wsdlUrl + "]"); ArrayList<XmlObject> schemas = new ArrayList<XmlObject>(getSchemas(wsdlUrl, loader).values()); return buildSchemaTypes(schemas); } catch (Exception e) { throw new SoapBuilderException(e); } } public static SchemaTypeSystem buildSchemaTypes(List<XmlObject> schemas) { XmlOptions options = new XmlOptions(); options.setCompileNoValidation(); options.setCompileNoPvrRule(); options.setCompileDownloadUrls(); options.setCompileNoUpaRule(); options.setValidateTreatLaxAsSkip(); for (int c = 0; c < schemas.size(); c++) { XmlObject xmlObject = schemas.get(c); if (xmlObject == null || !((Document) xmlObject.getDomNode()).getDocumentElement().getNamespaceURI() .equals(Constants.XSD_NS)) { schemas.remove(c); c--; } } // TODO boolean strictSchemaTypes = STRICT_SCHEMA_TYPES;//SoapUI.getSettings().getBoolean( WsdlSettings.STRICT_SCHEMA_TYPES ); if (!strictSchemaTypes) { Set<String> mdefNamespaces = new HashSet<String>(); for (XmlObject xObj : schemas) { mdefNamespaces.add(getTargetNamespace(xObj)); } options.setCompileMdefNamespaces(mdefNamespaces); } ArrayList<?> errorList = new ArrayList<Object>(); options.setErrorListener(errorList); XmlCursor cursor = null; try { // remove imports for (int c = 0; c < schemas.size(); c++) { XmlObject s = schemas.get(c); Map<?, ?> map = new HashMap<String, String>(); cursor = s.newCursor(); cursor.toStartDoc(); if (toNextContainer(cursor)) { cursor.getAllNamespaces(map); } else { log.warn("Can not get namespaces for " + s); } String tns = getTargetNamespace(s); // log.info( "schema for [" + tns + "] contained [" + map.toString() // + "] namespaces" ); if (strictSchemaTypes && defaultSchemas.containsKey(tns)) { schemas.remove(c); c--; } else { removeImports(s); } cursor.dispose(); cursor = null; } // schemas.add( soapVersion.getSoapEncodingSchema()); // schemas.add( soapVersion.getSoapEnvelopeSchema()); schemas.addAll(defaultSchemas.values()); SchemaTypeSystem sts = XmlBeans.compileXsd(schemas.toArray(new XmlObject[schemas.size()]), XmlBeans.getBuiltinTypeSystem(), options); return sts; // return XmlBeans.typeLoaderUnion(new SchemaTypeLoader[] { sts, // XmlBeans.getBuiltinTypeSystem() }); } catch (Exception e) { throw new SoapBuilderException(e); } finally { for (int c = 0; c < errorList.size(); c++) { log.warn("Error: " + errorList.get(c)); } if (cursor != null) { cursor.dispose(); } } } public static boolean toNextContainer(XmlCursor cursor) { while (!cursor.isContainer() && !cursor.isEnddoc()) { cursor.toNextToken(); } return cursor.isContainer(); } public static String getTargetNamespace(XmlObject s) { return ((Document) s.getDomNode()).getDocumentElement().getAttribute("targetNamespace"); } public static Map<String, XmlObject> getSchemas(String wsdlUrl, SchemaLoader loader) { Map<String, XmlObject> result = new HashMap<String, XmlObject>(); getSchemas(wsdlUrl, result, loader, null /* , false */); return result; } /** * Returns a map mapping urls to corresponding XmlSchema XmlObjects for the specified wsdlUrl */ public static void getSchemas(String wsdlUrl, Map<String, XmlObject> existing, SchemaLoader loader, String tns) { if (existing.containsKey(wsdlUrl)) { return; } log.debug("Getting schema " + wsdlUrl); ArrayList<?> errorList = new ArrayList<Object>(); Map<String, XmlObject> result = new HashMap<String, XmlObject>(); boolean common = false; try { XmlOptions options = new XmlOptions(); options.setCompileNoValidation(); options.setSaveUseOpenFrag(); options.setErrorListener(errorList); options.setSaveSyntheticDocumentElement(new QName(Constants.XSD_NS, "schema")); XmlObject xmlObject = loader.loadXmlObject(wsdlUrl, options); if (xmlObject == null) { throw new Exception("Failed to load schema from [" + wsdlUrl + "]"); } Document dom = (Document) xmlObject.getDomNode(); Node domNode = dom.getDocumentElement(); // is this an xml schema? if (domNode.getLocalName().equals("schema") && Constants.XSD_NS.equals(domNode.getNamespaceURI())) { // set targetNamespace (this happens if we are following an include // statement) if (tns != null) { Element elm = ((Element) domNode); if (!elm.hasAttribute("targetNamespace")) { common = true; elm.setAttribute("targetNamespace", tns); } // check for namespace prefix for targetNamespace NamedNodeMap attributes = elm.getAttributes(); int c = 0; for (; c < attributes.getLength(); c++) { Node item = attributes.item(c); if (item.getNodeName().equals("xmlns")) { break; } if (item.getNodeValue().equals(tns) && item.getNodeName().startsWith("xmlns")) { break; } } if (c == attributes.getLength()) { elm.setAttribute("xmlns", tns); } } if (common && !existing.containsKey(wsdlUrl + "@" + tns)) { result.put(wsdlUrl + "@" + tns, xmlObject); } else { result.put(wsdlUrl, xmlObject); } } else { existing.put(wsdlUrl, null); XmlObject[] schemas = xmlObject .selectPath("declare namespace s='" + Constants.XSD_NS + "' .//s:schema"); for (int i = 0; i < schemas.length; i++) { XmlCursor xmlCursor = schemas[i].newCursor(); String xmlText = xmlCursor.getObject().xmlText(options); // schemas[i] = XmlObject.Factory.parse( xmlText, options ); schemas[i] = XmlUtils.createXmlObject(xmlText, options); schemas[i].documentProperties().setSourceName(wsdlUrl); result.put(wsdlUrl + "@" + (i + 1), schemas[i]); } XmlObject[] wsdlImports = xmlObject .selectPath("declare namespace s='" + Constants.WSDL11_NS + "' .//s:import/@location"); for (int i = 0; i < wsdlImports.length; i++) { String location = ((SimpleValue) wsdlImports[i]).getStringValue(); if (location != null) { if (!location.startsWith("file:") && location.indexOf("://") == -1) { location = joinRelativeUrl(wsdlUrl, location); } getSchemas(location, existing, loader, null); } } XmlObject[] wadl10Imports = xmlObject.selectPath( "declare namespace s='" + Constants.WADL10_NS + "' .//s:grammars/s:include/@href"); for (int i = 0; i < wadl10Imports.length; i++) { String location = ((SimpleValue) wadl10Imports[i]).getStringValue(); if (location != null) { if (!location.startsWith("file:") && location.indexOf("://") == -1) { location = joinRelativeUrl(wsdlUrl, location); } getSchemas(location, existing, loader, null); } } XmlObject[] wadlImports = xmlObject.selectPath( "declare namespace s='" + Constants.WADL11_NS + "' .//s:grammars/s:include/@href"); for (int i = 0; i < wadlImports.length; i++) { String location = ((SimpleValue) wadlImports[i]).getStringValue(); if (location != null) { if (!location.startsWith("file:") && location.indexOf("://") == -1) { location = joinRelativeUrl(wsdlUrl, location); } getSchemas(location, existing, loader, null); } } } existing.putAll(result); XmlObject[] schemas = result.values().toArray(new XmlObject[result.size()]); for (int c = 0; c < schemas.length; c++) { xmlObject = schemas[c]; XmlObject[] schemaImports = xmlObject .selectPath("declare namespace s='" + Constants.XSD_NS + "' .//s:import/@schemaLocation"); for (int i = 0; i < schemaImports.length; i++) { String location = ((SimpleValue) schemaImports[i]).getStringValue(); Element elm = ((Attr) schemaImports[i].getDomNode()).getOwnerElement(); if (location != null && !defaultSchemas.containsKey(elm.getAttribute("namespace"))) { if (!location.startsWith("file:") && location.indexOf("://") == -1) { location = joinRelativeUrl(wsdlUrl, location); } getSchemas(location, existing, loader, null); } } XmlObject[] schemaIncludes = xmlObject .selectPath("declare namespace s='" + Constants.XSD_NS + "' .//s:include/@schemaLocation"); for (int i = 0; i < schemaIncludes.length; i++) { String location = ((SimpleValue) schemaIncludes[i]).getStringValue(); if (location != null) { String targetNS = getTargetNamespace(xmlObject); if (!location.startsWith("file:") && location.indexOf("://") == -1) { location = joinRelativeUrl(wsdlUrl, location); } getSchemas(location, existing, loader, targetNS); } } } } catch (Exception e) { throw new SoapBuilderException(e); } } public static void getDefinitionParts(String origWsdlUrl, Map<String, XmlObject> existing, SchemaLoader loader) throws Exception { String wsdlUrl = origWsdlUrl; if (existing.containsKey(wsdlUrl)) { return; } XmlObject xmlObject = loader.loadXmlObject(wsdlUrl, null); existing.put(wsdlUrl, xmlObject); // wsdlUrl = loader.getBaseURI(); selectDefinitionParts(wsdlUrl, existing, loader, xmlObject, "declare namespace s='" + Constants.WSDL11_NS + "' .//s:import/@location"); selectDefinitionParts(wsdlUrl, existing, loader, xmlObject, "declare namespace s='" + Constants.WADL10_NS + "' .//s:grammars/s:include/@href"); selectDefinitionParts(wsdlUrl, existing, loader, xmlObject, "declare namespace s='" + Constants.WADL11_NS + "' .//s:grammars/s:include/@href"); selectDefinitionParts(wsdlUrl, existing, loader, xmlObject, "declare namespace s='" + Constants.XSD_NS + "' .//s:import/@schemaLocation"); selectDefinitionParts(wsdlUrl, existing, loader, xmlObject, "declare namespace s='" + Constants.XSD_NS + "' .//s:include/@schemaLocation"); } public static String joinRelativeUrl(String baseUrl, String url) { if (baseUrl.indexOf('?') > 0) { baseUrl = baseUrl.substring(0, baseUrl.indexOf('?')); } boolean isWindowsUrl = baseUrl.indexOf('\\') >= 0; boolean isUsedInUnix = File.separatorChar == '/'; if (isUsedInUnix && isWindowsUrl) { baseUrl = baseUrl.replace('\\', '/'); url = url.replace('\\', '/'); } boolean isFile = baseUrl.startsWith("file:"); int ix = baseUrl.lastIndexOf('\\'); if (ix == -1) { ix = baseUrl.lastIndexOf('/'); } // absolute? if (url.startsWith("/") && !isFile) { ix = baseUrl.indexOf("/", baseUrl.indexOf("//") + 2); return baseUrl.substring(0, ix) + url; } // remove leading "./" while (url.startsWith(".\\") || url.startsWith("./")) { url = url.substring(2); } // remove leading "../" while (url.startsWith("../") || url.startsWith("..\\")) { int ix2 = baseUrl.lastIndexOf('\\', ix - 1); if (ix2 == -1) { ix2 = baseUrl.lastIndexOf('/', ix - 1); } if (ix2 == -1) { break; } baseUrl = baseUrl.substring(0, ix2 + 1); ix = ix2; url = url.substring(3); } // remove "/./" while (url.indexOf("/./") != -1 || url.indexOf("\\.\\") != -1) { int ix2 = url.indexOf("/./"); if (ix2 == -1) { ix2 = url.indexOf("\\.\\"); } url = url.substring(0, ix2) + url.substring(ix2 + 2); } // remove "/../" while (url.indexOf("/../") != -1 || url.indexOf("\\..\\") != -1) { int ix2 = -1; int ix3 = url.indexOf("/../"); if (ix3 == -1) { ix3 = url.indexOf("\\..\\"); ix2 = url.lastIndexOf('\\', ix3 - 1); } else { ix2 = url.lastIndexOf('/', ix3 - 1); } if (ix2 == -1) { break; } url = url.substring(0, ix2) + url.substring(ix3 + 3); } String result = baseUrl.substring(0, ix + 1) + url; if (isFile) { result = result.replace('/', File.separatorChar); } return result; } private static void selectDefinitionParts(String wsdlUrl, Map<String, XmlObject> existing, SchemaLoader loader, XmlObject xmlObject, String path) throws Exception { XmlObject[] wsdlImports = xmlObject.selectPath(path); for (int i = 0; i < wsdlImports.length; i++) { String location = ((SimpleValue) wsdlImports[i]).getStringValue(); if (location != null) { if (StringUtils.isNotBlank(location)) { if (!location.startsWith("file:") && location.indexOf("://") == -1) { location = joinRelativeUrl(wsdlUrl, location); } getDefinitionParts(location, existing, loader); } else { Node domNode = ((Attr) wsdlImports[i].getDomNode()).getOwnerElement(); domNode.getParentNode().removeChild(domNode); } } } } /** * Extracts namespaces - used in tool integrations for mapping.. */ public static Collection<String> extractNamespaces(SchemaTypeSystem schemaTypes, boolean removeDefault) { Set<String> namespaces = new HashSet<String>(); SchemaType[] globalTypes = schemaTypes.globalTypes(); for (int c = 0; c < globalTypes.length; c++) { namespaces.add(globalTypes[c].getName().getNamespaceURI()); } if (removeDefault) { namespaces.removeAll(defaultSchemas.keySet()); namespaces.remove(Constants.SOAP11_ENVELOPE_NS); namespaces.remove(Constants.SOAP_ENCODING_NS); } return namespaces; } /** * Used when creating a TypeSystem from a complete collection of SchemaDocuments so that referenced types are not downloaded (again) */ public static void removeImports(XmlObject xmlObject) throws XmlException { XmlObject[] imports = xmlObject.selectPath("declare namespace s='" + Constants.XSD_NS + "' .//s:import"); for (int c = 0; c < imports.length; c++) { XmlCursor cursor = imports[c].newCursor(); cursor.removeXml(); cursor.dispose(); } XmlObject[] includes = xmlObject.selectPath("declare namespace s='" + Constants.XSD_NS + "' .//s:include"); for (int c = 0; c < includes.length; c++) { XmlCursor cursor = includes[c].newCursor(); cursor.removeXml(); cursor.dispose(); } } }