001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 package org.apache.geronimo.jaxws.builder; 019 020 import java.io.ByteArrayOutputStream; 021 import java.io.File; 022 import java.io.FileFilter; 023 import java.io.IOException; 024 import java.io.OutputStream; 025 import java.lang.reflect.Method; 026 import java.net.MalformedURLException; 027 import java.net.URL; 028 import java.net.URLClassLoader; 029 import java.util.ArrayList; 030 import java.util.Collection; 031 import java.util.List; 032 import java.util.Random; 033 import java.util.SortedSet; 034 035 import javax.xml.namespace.QName; 036 037 import org.apache.commons.logging.Log; 038 import org.apache.commons.logging.LogFactory; 039 import org.apache.geronimo.common.DeploymentException; 040 import org.apache.geronimo.deployment.DeploymentConfigurationManager; 041 import org.apache.geronimo.deployment.DeploymentContext; 042 import org.apache.geronimo.j2ee.deployment.EARContext; 043 import org.apache.geronimo.j2ee.deployment.Module; 044 import org.apache.geronimo.jaxws.PortInfo; 045 import org.apache.geronimo.kernel.repository.Artifact; 046 import org.apache.geronimo.kernel.repository.ListableRepository; 047 import org.apache.geronimo.kernel.repository.Version; 048 049 public class WsdlGenerator { 050 051 private static final Log LOG = LogFactory.getLog(WsdlGenerator.class); 052 053 private final static Artifact AXIS2_JAXWS_API_ARTIFACT = new Artifact("org.apache.axis2","axis2-jaxws-api", (Version)null, "jar"); 054 private final static Artifact AXIS2_SAAJ_API_ARTIFACT = new Artifact("org.apache.axis2","axis2-saaj-api", (Version)null, "jar"); 055 private final static Artifact AXIS2_SAAJ_IMPL_ARTIFACT = new Artifact("org.apache.axis2","axis2-saaj", (Version)null, "jar"); 056 private final static Artifact JAXB_API_ARTIFACT = new Artifact("javax.xml.bind","jaxb-api", (Version)null, "jar"); 057 private final static Artifact JAXB_IMPL_ARTIFACT = new Artifact("com.sun.xml.bind","jaxb-impl", (Version)null, "jar"); 058 private final static Artifact JAXB_XJC_ARTIFACT = new Artifact("com.sun.xml.bind","jaxb-xjc", (Version)null, "jar"); 059 private final static Artifact JAXWS_TOOLS_ARTIFACT = new Artifact("com.sun.xml.ws","jaxws-tools", (Version)null, "jar"); 060 private final static Artifact JAXWS_RT_ARTIFACT = new Artifact("com.sun.xml.ws","jaxws-rt", (Version)null, "jar"); 061 private final static Artifact GERONIMO_ACTIVATION_SPEC_ARTIFACT = new Artifact("org.apache.geronimo.specs","geronimo-activation_1.1_spec", (Version)null, "jar"); 062 private final static Artifact GERONIMO_ANNOTATION_ARTIFACT = new Artifact("org.apache.geronimo.specs","geronimo-annotation_1.0_spec", (Version)null, "jar"); 063 private final static Artifact GERONIMO_WS_METADATA_ARTIFACT = new Artifact("org.apache.geronimo.specs","geronimo-ws-metadata_2.0_spec", (Version)null, "jar"); 064 private final static Artifact GERONIMO_EJB_SPEC_ARTIFACT = new Artifact("org.apache.geronimo.specs","geronimo-ejb_3.0_spec", (Version)null, "jar"); 065 private final static Artifact SUN_SAAJ_IMPL_ARTIFACT = new Artifact("com.sun.xml.messaging.saaj","saaj-impl", (Version)null, "jar"); 066 private final static String TOOLS = "tools.jar"; 067 068 private Artifact saajImpl; 069 private QName wsdlService; 070 private QName wsdlPort; 071 072 public WsdlGenerator() { 073 } 074 075 public void setSunSAAJ() { 076 this.saajImpl = SUN_SAAJ_IMPL_ARTIFACT; 077 } 078 079 public void setAxis2SAAJ() { 080 this.saajImpl = AXIS2_SAAJ_IMPL_ARTIFACT; 081 } 082 083 public void setWsdlService(QName name) { 084 this.wsdlService = name; 085 } 086 087 public QName getWsdlService() { 088 return this.wsdlService; 089 } 090 091 public void setWsdlPort(QName port) { 092 this.wsdlPort = port; 093 } 094 095 public QName getWsdlPort() { 096 return this.wsdlPort; 097 } 098 099 private URL[] getWsgenClasspath(DeploymentContext context) 100 throws DeploymentException, MalformedURLException { 101 ArrayList<URL> jars = new ArrayList<URL>(); 102 103 DeploymentConfigurationManager cm = (DeploymentConfigurationManager)context.getConfigurationManager(); 104 Collection<ListableRepository> repositories = cm.getRepositories(); 105 106 jars.add(getLocation(repositories, JAXB_API_ARTIFACT)); 107 jars.add(getLocation(repositories, JAXB_IMPL_ARTIFACT)); 108 jars.add(getLocation(repositories, JAXB_XJC_ARTIFACT)); 109 jars.add(getLocation(repositories, JAXWS_TOOLS_ARTIFACT)); 110 jars.add(getLocation(repositories, JAXWS_RT_ARTIFACT)); 111 jars.add(getLocation(repositories, AXIS2_JAXWS_API_ARTIFACT)); 112 jars.add(getLocation(repositories, AXIS2_SAAJ_API_ARTIFACT)); 113 jars.add(getLocation(repositories, GERONIMO_ACTIVATION_SPEC_ARTIFACT)); 114 jars.add(getLocation(repositories, GERONIMO_ANNOTATION_ARTIFACT)); 115 jars.add(getLocation(repositories, GERONIMO_WS_METADATA_ARTIFACT)); 116 jars.add(getLocation(repositories, GERONIMO_EJB_SPEC_ARTIFACT)); 117 if (this.saajImpl != null) { 118 jars.add(getLocation(repositories, this.saajImpl)); 119 } 120 jars.add(new File(getToolsJarLoc()).toURL()); 121 122 return jars.toArray(new URL[jars.size()]); 123 } 124 125 private static String getModuleClasspath(Module module, DeploymentContext context) throws DeploymentException { 126 File moduleBase = module.getEarContext().getBaseDir(); 127 File moduleBaseDir = (moduleBase.isFile()) ? moduleBase.getParentFile() : moduleBase; 128 String baseDir = moduleBaseDir.getAbsolutePath(); 129 List<String> moduleClassPath = context.getConfiguration().getClassPath(); 130 StringBuilder classpath = new StringBuilder(); 131 for (String s : moduleClassPath) { 132 s = s.replace("/", File.separator); 133 134 classpath.append(baseDir); 135 classpath.append(File.separator); 136 classpath.append(s); 137 classpath.append(File.pathSeparator); 138 } 139 return classpath.toString(); 140 } 141 142 private static URL getLocation(Collection<ListableRepository> repositories, Artifact artifactQuery) throws DeploymentException, MalformedURLException { 143 File file = null; 144 145 for (ListableRepository repository : repositories) { 146 SortedSet artifactSet = repository.list(artifactQuery); 147 // if we have exactly one artifact found 148 if (artifactSet.size() == 1) { 149 file = repository.getLocation((Artifact) artifactSet.first()); 150 return file.getAbsoluteFile().toURL(); 151 } else if (artifactSet.size() > 1) {// if we have more than 1 artifacts found use the latest one. 152 file = repository.getLocation((Artifact) artifactSet.last()); 153 return file.getAbsoluteFile().toURL(); 154 } 155 } 156 if (file == null) { 157 throw new DeploymentException("Missing artifact in repositories: " + artifactQuery.toString()); 158 } 159 return null; 160 } 161 162 private static String getToolsJarLoc() throws DeploymentException { 163 //create a new File then check exists() 164 String jreHomePath = System.getProperty("java.home"); 165 String javaHomePath = ""; 166 int jreHomePathLength = jreHomePath.length(); 167 if (jreHomePathLength > 0) { 168 int i = jreHomePath.substring(0, jreHomePathLength -1).lastIndexOf(java.io.File.separator); 169 javaHomePath = jreHomePath.substring(0, i); 170 } 171 File jdkhomelib = new File(javaHomePath + java.io.File.separator + "lib"); 172 if (!jdkhomelib.exists()) { 173 throw new DeploymentException("Missing " + jdkhomelib.getAbsolutePath() 174 + ". This is required for wsgen to run. "); 175 } 176 else { 177 File tools = new File(jdkhomelib + java.io.File.separator + TOOLS); 178 if (!tools.exists()) { 179 throw new DeploymentException("Missing tools.jar in" + jdkhomelib.getAbsolutePath() 180 + ". This is required for wsgen to run. "); 181 } else { 182 return tools.getAbsolutePath(); 183 } 184 } 185 } 186 187 private static File toFile(URL url) { 188 if (url == null || !url.getProtocol().equals("file")) { 189 return null; 190 } else { 191 String filename = url.getFile().replace('/', File.separatorChar); 192 int pos =0; 193 while ((pos = filename.indexOf('%', pos)) >= 0) { 194 if (pos + 2 < filename.length()) { 195 String hexStr = filename.substring(pos + 1, pos + 3); 196 char ch = (char) Integer.parseInt(hexStr, 16); 197 filename = filename.substring(0, pos) + ch + filename.substring(pos + 3); 198 } 199 } 200 return new File(filename); 201 } 202 } 203 204 private String[] buildArguments(String sei, String classPath, File moduleBaseDir, PortInfo portInfo) { 205 List<String> arguments = new ArrayList<String>(); 206 207 arguments.add("-cp"); 208 arguments.add(classPath); 209 arguments.add("-keep"); 210 arguments.add("-wsdl"); 211 arguments.add("-d"); 212 arguments.add(moduleBaseDir.getAbsolutePath()); 213 214 QName serviceName = getWsdlService(); 215 if (serviceName != null) { 216 arguments.add("-servicename"); 217 arguments.add(serviceName.toString()); 218 } 219 220 QName portName = getWsdlPort(); 221 if (portName != null) { 222 arguments.add("-portname"); 223 arguments.add(portName.toString()); 224 } 225 226 arguments.add(sei); 227 228 return arguments.toArray(new String[]{}); 229 } 230 231 private File getFirstWsdlFile(File baseDir) throws IOException { 232 LOG.debug("Looking for service wsdl file in " + baseDir.getAbsolutePath()); 233 File[] files = baseDir.listFiles(new FileFilter() { 234 public boolean accept(File file) { 235 return (file.isFile() && file.getName().endsWith(".wsdl")); 236 } 237 }); 238 239 if (files.length == 1) { 240 return files[0]; 241 } else { 242 return null; 243 } 244 } 245 246 private File findWsdlFile(File baseDir, PortInfo portInfo) throws IOException { 247 QName serviceName = getWsdlService(); 248 249 if (serviceName != null) { 250 // check if serviceName.wsdl locates at the baseDir, if so, return its path. 251 String wsdlFileName = serviceName.getLocalPart() + ".wsdl"; 252 if (Character.isLowerCase(wsdlFileName.charAt(0))) { 253 wsdlFileName = Character.toUpperCase(wsdlFileName.charAt(0)) + wsdlFileName.substring(1); 254 } 255 File wsdlFile = new File(baseDir, wsdlFileName); 256 if (wsdlFile.exists()) { 257 return wsdlFile; 258 } else { 259 return getFirstWsdlFile(baseDir); 260 } 261 } else { 262 return getFirstWsdlFile(baseDir); 263 } 264 } 265 266 private static String getRelativeNameOrURL(File baseDir, File file) { 267 String basePath = baseDir.getAbsolutePath(); 268 String path = file.getAbsolutePath(); 269 270 if (path.startsWith(basePath)) { 271 if (File.separatorChar == path.charAt(basePath.length())) { 272 return path.substring(basePath.length() + 1); 273 } else { 274 return path.substring(basePath.length()); 275 } 276 } else { 277 return file.toURI().toString(); 278 } 279 } 280 281 private static File createTempDirectory(File baseDir) throws IOException { 282 Random rand = new Random(); 283 while(true) { 284 String dirName = String.valueOf(Math.abs(rand.nextInt())); 285 File dir = new File(baseDir, dirName); 286 if (!dir.exists()) { 287 if (!dir.mkdir()) { 288 throw new IOException("Failed to create temporary directory: " + dir); 289 } else { 290 return dir; 291 } 292 } 293 } 294 } 295 296 public String generateWsdl(Module module, 297 String serviceClass, 298 DeploymentContext context, 299 PortInfo portInfo) throws DeploymentException { 300 //call wsgen tool to generate the wsdl file based on the bindingtype. 301 //let's set the outputDir as the module base directory in server repository. 302 File moduleBase = module.getEarContext().getBaseDir(); 303 File moduleBaseDir = (moduleBase.isFile()) ? moduleBase.getParentFile() : moduleBase; 304 File baseDir; 305 306 try { 307 baseDir = createTempDirectory(moduleBaseDir); 308 } catch (IOException e) { 309 throw new DeploymentException(e); 310 } 311 312 URL[] urls; 313 StringBuilder classPath = new StringBuilder(); 314 //let's figure out the classpath for wsgen tools 315 try { 316 urls = getWsgenClasspath(context); 317 } catch (MalformedURLException e) { 318 throw new DeploymentException("unable to generate the wsdl file using wsgen. - unable to get the location of the required artifact(s).", e); 319 } 320 //let's figure out the classpath string for the module and wsgen tools. 321 if (urls != null && urls.length > 0) { 322 for (int i = 0; i< urls.length; i++) { 323 classPath.append(toFile(urls[i]).getAbsolutePath() + File.pathSeparator); 324 } 325 } 326 classPath.append(getModuleClasspath(module, context)); 327 328 //create arguments; 329 String[] arguments = buildArguments(serviceClass, classPath.toString(), baseDir, portInfo); 330 331 try { 332 URLClassLoader loader = new URLClassLoader(urls, ClassLoader.getSystemClassLoader()); 333 Class clazz = loader.loadClass("com.sun.tools.ws.spi.WSToolsObjectFactory"); 334 Method method = clazz.getMethod("newInstance"); 335 Object factory = method.invoke(null); 336 Method method2 = clazz.getMethod("wsgen", OutputStream.class, String[].class); 337 ByteArrayOutputStream os = new ByteArrayOutputStream(); 338 Boolean result = (Boolean) method2.invoke(factory, os, arguments); 339 os.close(); 340 341 byte [] arr = os.toByteArray(); 342 String wsgenOutput = new String(arr, 0, arr.length); 343 344 if (LOG.isDebugEnabled()) { 345 LOG.debug("wsgen output: " + wsgenOutput); 346 } 347 348 if (result) { 349 //check to see if the file is created. 350 File wsdlFile = findWsdlFile(baseDir, portInfo); 351 if (wsdlFile == null) { 352 throw new DeploymentException("Unable to find the service wsdl file"); 353 } 354 return getRelativeNameOrURL(moduleBase, wsdlFile); 355 } else { 356 throw new DeploymentException("wsgen failed: " + wsgenOutput); 357 } 358 } catch (DeploymentException e) { 359 throw e; 360 } catch (Exception e) { 361 throw new DeploymentException("Unable to generate the wsdl file using wsgen.", e); 362 } 363 } 364 }