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 }