/******************************************************************************* * Copyright (c) 2006-2010 eBay Inc. All Rights Reserved. * Licensed 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 *******************************************************************************/ package org.ebayopensource.turmeric.eclipse.utils.wsdl; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.util.Collection; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.logging.Logger; import javax.wsdl.Definition; import javax.wsdl.Import; import javax.wsdl.Types; import javax.wsdl.WSDLException; import javax.wsdl.extensions.UnknownExtensibilityElement; import javax.wsdl.extensions.schema.Schema; import javax.wsdl.extensions.schema.SchemaReference; import javax.wsdl.factory.WSDLFactory; import javax.wsdl.xml.WSDLReader; import javax.wsdl.xml.WSDLWriter; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringUtils; import org.ebayopensource.turmeric.eclipse.utils.lang.StringUtil; import org.ebayopensource.turmeric.eclipse.utils.plugin.WorkspaceUtil; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.osgi.framework.Bundle; import org.xml.sax.InputSource; /** * The Class WSDLUtil. * * @author yayu */ public final class WSDLUtil { /** The Constant URL_PREFIX_JAR_FILE. */ public static final String URL_PREFIX_JAR_FILE = "jar:file:"; /** The Constant JAR_FILE_SEPARATOR. */ public static final String JAR_FILE_SEPARATOR = "!" + WorkspaceUtil.PATH_SEPERATOR; /** The Constant ATTR_ID_TARGETNAMESPACE. */ public static final String ATTR_ID_TARGETNAMESPACE = "targetNamespace"; /** The Constant NAMESPACE_PREFIX_W3ORG. */ public static final String NAMESPACE_PREFIX_W3ORG = "http://www.w3.org/"; /** The Constant NAMESPACE_PREFIX_XMLSOAP. */ public static final String NAMESPACE_PREFIX_XMLSOAP = "http://schemas.xmlsoap.org"; private WSDLUtil() { super(); } /** * To url. * * @param file the file * @return the uRL */ public static URL toURL(final File file) { if (file == null) return null; try { return file.toURI().toURL(); } catch (final IOException e) { throw new RuntimeException(e); } } /** * Checks if is valid url. * * @param url the url * @return true, if is valid url */ public static boolean isValidURL(final String url) { //return Pattern.matches(VALID_URL_PATTERN, url); if (StringUtils.isBlank(url)) return false; try { new URL(url); } catch (MalformedURLException e) { return false; } return true; } /** * Validate url. * * @param url the url * @return the string */ public static String validateURL(final String url) { if (StringUtils.isBlank(url)) return "url is blank."; try { new URL(StringEscapeUtils.escapeHtml(url)); } catch (final MalformedURLException e) { return e.getLocalizedMessage(); } return ""; } /** * Read wsdl. * * @param wsdlLocation The fully qualified location of the WSDL file. * @return The WSDL definition instance. * @throws WSDLException If the any errors encountered during the deserialization. */ public static Definition readWSDL(final String wsdlLocation) throws WSDLException { final WSDLReader reader = WSDLFactory.newInstance().newWSDLReader(); return reader.readWSDL(wsdlLocation); } /** * This is intended to be used for wsdl file inside jar file. * * @param wsdlStream the wsdl stream * @return the definition * @throws WSDLException the wSDL exception */ public static Definition readWSDL(final InputStream wsdlStream) throws WSDLException { return readWSDL(null, wsdlStream); } /** * Reading wsdl from the provided jar file. * The format for documetn base URI would be "jar:file:[JAR_FILE_LOCATION]!/[WSDL_JAR_ENTRY_PATH]" * * @param file the file * @param jarEntryLocation the jar entry location * @return The instance of WSDL definition, or null is could not read it. * @throws WSDLException the wSDL exception * @throws IOException Signals that an I/O exception has occurred. */ public static Definition readWSDLFromJarFile(final File file, final String jarEntryLocation) throws WSDLException, IOException { InputStream wsdlStream = null; if (file.exists() && file.canRead()) { final JarFile jarFile = new JarFile(file); final JarEntry jarEntry = jarFile.getJarEntry(jarEntryLocation); if (jarEntry != null) { // found the wsdl file wsdlStream = jarFile.getInputStream(jarEntry); return WSDLUtil.readWSDL(StringUtil.toString( URL_PREFIX_JAR_FILE, file.getAbsolutePath(), JAR_FILE_SEPARATOR, jarEntryLocation), wsdlStream); } } return null; } /** * Read wsdl. * * @param documentBaseURI The parent folder of the wsdl file * @param wsdlStream The input stream * @return The WSDL definition instance. * @throws WSDLException If the any errors encountered during the deserialization. */ public static Definition readWSDL(final String documentBaseURI, final InputStream wsdlStream) throws WSDLException { try { final WSDLReader reader = WSDLFactory.newInstance().newWSDLReader(); InputSource inputSource = new InputSource(wsdlStream); return reader.readWSDL(documentBaseURI, inputSource); } finally { IOUtils.closeQuietly(wsdlStream); } } private static String[] getParentFolder(String fullFileName) { String[] result = new String[2]; final String slash = "/"; if (StringUtils.isNotBlank(fullFileName)) { fullFileName = StringUtils.replaceChars(fullFileName, File.separator, slash); result[0] = fullFileName.indexOf(slash) >= 0 ? StringUtils .substringBeforeLast(fullFileName, slash) : ""; result[1] = StringUtils.substringAfterLast(fullFileName, "/"); } return result; } /** * The use of this method is discouraged, because it will try to read the * WSDL first, which might cause some performance issues. * * @param wsdl the wsdl * @param fileName the file name * @throws WSDLException the wSDL exception * @throws IOException Signals that an I/O exception has occurred. */ public static void writeWSDL(final String wsdl, final String fileName) throws WSDLException, IOException { writeWSDL(readWSDL(wsdl), fileName); } /** * Write wsdl. * * @param wsdl the wsdl * @param fileName the file name * @throws WSDLException the wSDL exception * @throws IOException Signals that an I/O exception has occurred. */ public static void writeWSDL(final Definition wsdl, final String fileName) throws WSDLException, IOException { writeWSDL(wsdl, fileName, new HashSet<String>()); } private static InputStream readUrl(String locUri) throws MalformedURLException,IOException { URL url = URI.create(locUri).toURL(); return url.openStream(); } private static void handleImports(final Collection<List<Import>> imports, final String parentFolder, final Set<String> processedSchemas, final Map<InputStream, String> streamsToCopy) throws IOException { for (List<Import> list : imports) { for (Import _import : list) { if(isRelativeURI(_import.getLocationURI())){// it is using a relative path final String locUri = _import.getDefinition().getDocumentBaseURI(); if (processedSchemas.contains(locUri) == false) { final String schemaParentFolder = getParentFolder(_import .getLocationURI())[0]; if (schemaParentFolder != null && parentFolder != null) { String targetFileLocation = parentFolder + File.separator + schemaParentFolder + File.separator + FilenameUtils.getName(locUri);// a output // file // name; InputStream stream = readUrl(locUri); if (stream != null) { streamsToCopy.put(stream, targetFileLocation); } } processedSchemas.add(locUri); } } // as of now, we only support two levels of import if (_import.getDefinition().getTypes() != null) { final String schemaParentFolder = getParentFolder(_import .getLocationURI())[0]; if (schemaParentFolder != null && parentFolder != null) { String targetLocation = parentFolder + File.separator + schemaParentFolder; handleSchemas(_import.getDefinition().getTypes(), targetLocation, processedSchemas, streamsToCopy); } } } } } /** * <p> * Serialize the provided WSDL definition instance to the designated * location. * </p> * * @param wsdl * The WSDL definition instance * @param fileName * The fully qualified WSDL file name * @param processedSchemas * A list of schema file location which has already been * processed. This prevents the issue that two schemas import * each other. * @throws WSDLException * If failed to handle the WSDL serialization * @throws IOException */ private static void writeWSDL(final Definition wsdl, final String fileName, final Set<String> processedSchemas) throws WSDLException, IOException { final WSDLWriter writer = WSDLFactory.newInstance().newWSDLWriter(); final String parentFolder = getParentFolder(fileName)[0]; final Map<InputStream, String> streamsToCopy = new ConcurrentHashMap<InputStream, String>(); // if there are any schema imports, we should be able to handle it if (wsdl.getImports() != null && wsdl.getImports().isEmpty() == false) { handleImports( wsdl.getImports().values(), parentFolder, processedSchemas, streamsToCopy); } if (wsdl.getTypes() != null) { handleSchemas(wsdl.getTypes(), parentFolder, processedSchemas, streamsToCopy); } File parentDir = new File(parentFolder); if (parentDir.exists() == false) FileUtils.forceMkdir(parentDir); for (InputStream srcStream : streamsToCopy.keySet()) { File targetFile = new File(streamsToCopy.get(srcStream)); FileUtils.forceMkdir(targetFile.getParentFile()); try { FileOutputStream output = new FileOutputStream(targetFile); try { IOUtils.copy(srcStream, output); } finally { IOUtils.closeQuietly(output); } } finally { IOUtils.closeQuietly(srcStream); } } //output wsdl OutputStream out = null; try { out = new FileOutputStream(fileName); writer.writeWSDL(wsdl, out); } finally { IOUtils.closeQuietly(out); } } private static void handleSchemaReference(List<SchemaReference> schemaRefs, String parentFolder, final Set<String> processedSchemas, final Map<InputStream, String> streamsToCopy) throws IOException { for (SchemaReference schemaReference : schemaRefs) { if (!isRelativeURI(schemaReference.getSchemaLocationURI())) { continue; } final Schema refSchema = schemaReference.getReferencedSchema(); final String locUri = refSchema != null ? refSchema .getDocumentBaseURI() : schemaReference .getSchemaLocationURI(); if (processedSchemas.contains(locUri) == false && refSchema != null) { String schemaParentFolder = getParentFolder(schemaReference .getSchemaLocationURI())[0]; if (schemaParentFolder != null && parentFolder != null) { String targetFileLocation = parentFolder + File.separator + schemaParentFolder + File.separator + FilenameUtils.getName(locUri); InputStream stream = readUrl(locUri); if (stream != null) { streamsToCopy.put(stream, targetFileLocation); } } processedSchemas.add(locUri); // next round if (refSchema.getImports().isEmpty() == false) { for (Object o : refSchema.getImports().values()) { handleSchemaReference((List<SchemaReference>) o, parentFolder, processedSchemas, streamsToCopy); } } if (CollectionUtils.isNotEmpty(refSchema.getIncludes())) { handleSchemaReference(refSchema.getIncludes(), parentFolder, processedSchemas, streamsToCopy); } } } } private static void handleSchemas(Types types, String parentFolder, final Set<String> processedSchemas, final Map<InputStream, String> streamsToCopy) throws IllegalArgumentException, IOException { for (Object obj : types.getExtensibilityElements()) { if (obj instanceof Schema) { Schema schema = (Schema) obj; for (Object key : schema.getImports().keySet()) { Object value = schema.getImports().get(key); if (value instanceof List) { handleSchemaReference((List<SchemaReference>) value, parentFolder, processedSchemas, streamsToCopy); } } if (CollectionUtils.isNotEmpty(schema.getIncludes())) { handleSchemaReference(schema.getIncludes(), parentFolder, processedSchemas, streamsToCopy); } } else if (obj instanceof UnknownExtensibilityElement) { throw new IllegalArgumentException( "The WSDL4J in the current environment is rather old and does not support the \"Schema\" datatype. Please make sure your environment use a newer version."); } } } /** * Gets the service name from wsdl. * * @param wsdl the wsdl * @return the service name from wsdl */ public static String getServiceNameFromWSDL(final Definition wsdl) { if (wsdl != null && wsdl.getServices().isEmpty() == false) { return ((javax.wsdl.Service)wsdl.getServices() .values().toArray()[0]).getQName().getLocalPart(); } return null; } /** * Gets the service location from wsdl. * * @param wsdl the wsdl * @return the service location from wsdl */ public static String getServiceLocationFromWSDL(final Definition wsdl) { String serviceLocation = null; if (wsdl != null && wsdl.getServices().isEmpty() == false) { for (Object obj : ((javax.wsdl.Service) (wsdl .getServices().values().toArray()[0])) .getPorts().values()) { javax.wsdl.Port port = (javax.wsdl.Port) obj; if (port.getExtensibilityElements().size() > 0) { Object elem = port .getExtensibilityElements().get(0); if (elem instanceof javax.wsdl.extensions.http.HTTPAddress) { serviceLocation = ((javax.wsdl.extensions.http.HTTPAddress) elem) .getLocationURI(); break; } else if (elem instanceof javax.wsdl.extensions.soap.SOAPAddress) { serviceLocation = ((javax.wsdl.extensions.soap.SOAPAddress) elem) .getLocationURI(); break; } else { try { final Method method = elem.getClass().getMethod("getLocationURI"); final Object result = method.invoke(elem); if (result != null) { serviceLocation = result.toString(); //although we have found the service location, //but we would still prefer the http and soap addresses, //thus we will not break from the loop. } } catch (Exception e) { StackTraceElement[] elements = new Throwable().getStackTrace(); Logger.getLogger(WSDLUtil.class.getName()).throwing( elements[0].getClassName(), elements[0].getMethodName(), e); } } } } } return serviceLocation; } /** * Gets the target namespace. * * @param location The location of the WSDL file * @return the target namespace * @throws WSDLException the wSDL exception */ public static String getTargetNamespace(final String location) throws WSDLException { return readWSDL(location).getTargetNamespace(); } /** * Gets the target namespace. * * @param wsdl the wsdl * @return the target namespace * @throws WSDLException the wSDL exception */ public static String getTargetNamespace(final Definition wsdl) throws WSDLException { return wsdl.getTargetNamespace(); } /** * Gets the target namespace. * * @param documentBaseURI the document base uri * @param inpuStream the inpu stream * @return the target namespace * @throws WSDLException the wSDL exception */ public static String getTargetNamespace(final String documentBaseURI, final InputStream inpuStream) throws WSDLException { return readWSDL(documentBaseURI, inpuStream).getTargetNamespace(); } //Moving util methods from WSDLUtilTest class to here /** * Gets the plugin os path. * * @param pluginId the plugin id * @param subDirPath the sub dir path * @return the plugin os path */ public static String getPluginOSPath(String pluginId, String subDirPath) { URL platformContextURL = getPluginOSURL(pluginId, subDirPath); String fullPath = (new File(platformContextURL.getPath())).getAbsolutePath(); IPath osPath = new Path(fullPath); return osPath.toString(); } /** * Gets the plugin osurl. * * @param pluginId the plugin id * @param subDirPath the sub dir path * @return the plugin osurl */ public static URL getPluginOSURL(String pluginId, String subDirPath) { Bundle bundle = Platform.getBundle(pluginId); if (bundle == null) { return null; } URL installLocation = bundle.getEntry("/"); URL local = null; URL platformContextURL = null; try { local = FileLocator.toFileURL(installLocation); platformContextURL = subDirPath == null ? local : new URL(local, subDirPath.toString()); } catch (Exception _ex) { return null; } return platformContextURL; } private static boolean isRelativeURI(String url) { if(StringUtils.isEmpty(url)) return false; URI uri = null; try { uri = URI.create(url); } catch (RuntimeException e) { // ignore it } return uri != null && !uri.isAbsolute(); } /** * Known W3 and XMLSOAP namespaces will not be included. * * @param wsdl the wsdl * @return a list of TargetNamespaces from the schemas including the imported */ public static Collection<String> getAllTargetNamespaces(final Definition wsdl) { final Collection<String> result = new LinkedHashSet<String>(); if (wsdl == null) return result; /*if (StringUtils.isNotBlank(wsdl.getTargetNamespace())) result.add(wsdl.getTargetNamespace());*/ if (wsdl.getImports() != null && wsdl.getImports().isEmpty() == false) { for (List<Import> list : (Collection<List<Import>>) wsdl.getImports().values()) { for (Import _import : list) { result.addAll(getAllTargetNamespaces(_import.getDefinition())); } } } if (wsdl.getTypes() != null) { for (Object obj : wsdl.getTypes().getExtensibilityElements()) { if (obj instanceof Schema) { getAllTargetNamespaces((Schema)obj, result, new HashSet<String>()); } else if (obj instanceof UnknownExtensibilityElement) { throw new IllegalArgumentException( "The WSDL4J in the current environment is rather old and does not support the \"Schema\" datatype. Please make sure your environment use a newer version."); } } } return result; } private static void getAllTargetNamespaces(final Schema schema, final Collection<String> namespaces, final Set<String> processedSchemas) { if (schema != null) { final String locUri = schema.getDocumentBaseURI(); //we need to check whether this schema has been processed or not if (processedSchemas.contains(locUri)) return; processedSchemas.add(locUri); final String targetNamespace = schema.getElement().getAttribute(ATTR_ID_TARGETNAMESPACE); if (StringUtils.isNotBlank(targetNamespace) && targetNamespace.startsWith(NAMESPACE_PREFIX_W3ORG) == false && targetNamespace.startsWith(NAMESPACE_PREFIX_XMLSOAP) == false) namespaces.add(targetNamespace.trim()); for (Object key : schema.getImports().keySet()) { Object value = schema.getImports().get(key); if (value instanceof List) { for (final SchemaReference ref : (List<SchemaReference>)value) { getAllTargetNamespaces(ref.getReferencedSchema(), namespaces, processedSchemas); } } } if (CollectionUtils.isNotEmpty(schema.getIncludes())) { for (final SchemaReference ref : (List<SchemaReference>)schema.getIncludes()) { getAllTargetNamespaces(ref.getReferencedSchema(), namespaces, processedSchemas); } } } } }