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: */package org.apache.geronimo.jaxws.builder;
017:
018: import java.io.ByteArrayOutputStream;
019: import java.io.File;
020: import java.io.FileFilter;
021: import java.io.IOException;
022: import java.io.InputStream;
023: import java.io.OutputStream;
024: import java.net.URL;
025: import java.net.MalformedURLException;
026: import java.util.ArrayList;
027: import java.util.Arrays;
028: import java.util.Collection;
029: import java.util.List;
030: import java.util.Random;
031: import java.util.Set;
032:
033: import javax.xml.namespace.QName;
034:
035: import org.apache.commons.logging.Log;
036: import org.apache.commons.logging.LogFactory;
037: import org.apache.geronimo.common.DeploymentException;
038: import org.apache.geronimo.deployment.DeploymentConfigurationManager;
039: import org.apache.geronimo.deployment.DeploymentContext;
040: import org.apache.geronimo.j2ee.deployment.Module;
041: import org.apache.geronimo.jaxws.PortInfo;
042: import org.apache.geronimo.kernel.config.Configuration;
043: import org.apache.geronimo.kernel.config.ConfigurationResolver;
044: import org.apache.geronimo.kernel.config.NoSuchConfigException;
045: import org.apache.geronimo.kernel.repository.Repository;
046:
047: public class WsdlGenerator {
048:
049: private static final Log LOG = LogFactory
050: .getLog(WsdlGenerator.class);
051:
052: private final static String FORK_WSGEN_PROPERTY = "org.apache.geronimo.jaxws.wsgen.fork";
053:
054: private final static String FORK_TIMEOUT_WSGEN_PROPERTY = "org.apache.geronimo.jaxws.wsgen.fork.timeout";
055:
056: private final static long FORK_POLL_FREQUENCY = 1000 * 2; // 2 seconds
057:
058: private QName wsdlService;
059: private QName wsdlPort;
060: private boolean forkWsgen = getForkWsgen();
061: private long forkTimeout = getForTimeout();
062: private JAXWSTools jaxwsTools;
063:
064: private static boolean getForkWsgen() {
065: String value = System.getProperty(FORK_WSGEN_PROPERTY);
066: if (value != null) {
067: return Boolean.valueOf(value).booleanValue();
068: } else {
069: String osName = System.getProperty("os.name");
070: if (osName == null) {
071: return false;
072: }
073: osName = osName.toLowerCase();
074: // Fork on Windows only
075: return (osName.indexOf("windows") != -1);
076: }
077: }
078:
079: private static long getForTimeout() {
080: String value = System.getProperty(FORK_TIMEOUT_WSGEN_PROPERTY);
081: if (value != null) {
082: return Long.parseLong(value);
083: } else {
084: return 1000 * 60; // 60 seconds
085: }
086: }
087:
088: public WsdlGenerator() {
089: this .jaxwsTools = new JAXWSTools();
090: }
091:
092: public void setSunSAAJ() {
093: this .jaxwsTools.setUseSunSAAJ();
094: }
095:
096: public void setAxis2SAAJ() {
097: this .jaxwsTools.setUseAxis2SAAJ();
098: }
099:
100: public void setWsdlService(QName name) {
101: this .wsdlService = name;
102: }
103:
104: public QName getWsdlService() {
105: return this .wsdlService;
106: }
107:
108: public void setWsdlPort(QName port) {
109: this .wsdlPort = port;
110: }
111:
112: public QName getWsdlPort() {
113: return this .wsdlPort;
114: }
115:
116: private URL[] getWsgenClasspath(DeploymentContext context)
117: throws Exception {
118: DeploymentConfigurationManager cm = (DeploymentConfigurationManager) context
119: .getConfigurationManager();
120: Collection<? extends Repository> repositories = cm
121: .getRepositories();
122: File[] jars = this .jaxwsTools.getClasspath(repositories);
123: return JAXWSTools.toURL(jars);
124: }
125:
126: private static void getModuleClasspath(Module module,
127: DeploymentContext context, StringBuilder classpath)
128: throws DeploymentException {
129: getModuleClasspath(classpath, module.getEarContext());
130: if (module.getRootEarContext() != module.getEarContext()) {
131: getModuleClasspath(classpath, module.getRootEarContext());
132: }
133: }
134:
135: private static void getModuleClasspath(StringBuilder classpath,
136: DeploymentContext deploymentContext)
137: throws DeploymentException {
138: Configuration configuration = deploymentContext
139: .getConfiguration();
140: ConfigurationResolver resolver = configuration
141: .getConfigurationResolver();
142: List<String> moduleClassPath = configuration.getClassPath();
143: for (String pattern : moduleClassPath) {
144: try {
145: Set<URL> files = resolver.resolve(pattern);
146: for (URL url : files) {
147: String path = toFileName(url);
148: classpath.append(path).append(File.pathSeparator);
149: }
150: } catch (MalformedURLException e) {
151: throw new DeploymentException(
152: "Could not resolve pattern: " + pattern, e);
153: } catch (NoSuchConfigException e) {
154: throw new DeploymentException(
155: "Could not resolve pattern: " + pattern, e);
156: }
157: }
158: }
159:
160: private static File toFile(URL url) {
161: if (url == null || !url.getProtocol().equals("file")) {
162: return null;
163: } else {
164: String filename = toFileName(url);
165: return new File(filename);
166: }
167: }
168:
169: private static String toFileName(URL url) {
170: String filename = url.getFile()
171: .replace('/', File.separatorChar);
172: int pos = 0;
173: while ((pos = filename.indexOf('%', pos)) >= 0) {
174: if (pos + 2 < filename.length()) {
175: String hexStr = filename.substring(pos + 1, pos + 3);
176: char ch = (char) Integer.parseInt(hexStr, 16);
177: filename = filename.substring(0, pos) + ch
178: + filename.substring(pos + 3);
179: }
180: }
181: return filename;
182: }
183:
184: private String[] buildArguments(String sei, String classPath,
185: File moduleBaseDir, PortInfo portInfo) {
186: List<String> arguments = new ArrayList<String>();
187:
188: arguments.add("-cp");
189: arguments.add(classPath);
190: arguments.add("-keep");
191: arguments.add("-wsdl");
192: arguments.add("-d");
193: arguments.add(moduleBaseDir.getAbsolutePath());
194:
195: QName serviceName = getWsdlService();
196: if (serviceName != null) {
197: arguments.add("-servicename");
198: arguments.add(serviceName.toString());
199: }
200:
201: QName portName = getWsdlPort();
202: if (portName != null) {
203: arguments.add("-portname");
204: arguments.add(portName.toString());
205: }
206:
207: arguments.add(sei);
208:
209: return arguments.toArray(new String[] {});
210: }
211:
212: private File getFirstWsdlFile(File baseDir) throws IOException {
213: LOG.debug("Looking for service wsdl file in "
214: + baseDir.getAbsolutePath());
215: File[] files = baseDir.listFiles(new FileFilter() {
216: public boolean accept(File file) {
217: return (file.isFile() && file.getName().endsWith(
218: ".wsdl"));
219: }
220: });
221:
222: if (files.length == 1) {
223: return files[0];
224: } else {
225: return null;
226: }
227: }
228:
229: private File findWsdlFile(File baseDir, PortInfo portInfo)
230: throws IOException {
231: QName serviceName = getWsdlService();
232:
233: if (serviceName != null) {
234: // check if serviceName.wsdl locates at the baseDir, if so, return its path.
235: String wsdlFileName = serviceName.getLocalPart() + ".wsdl";
236: if (Character.isLowerCase(wsdlFileName.charAt(0))) {
237: wsdlFileName = Character.toUpperCase(wsdlFileName
238: .charAt(0))
239: + wsdlFileName.substring(1);
240: }
241: File wsdlFile = new File(baseDir, wsdlFileName);
242: if (wsdlFile.exists()) {
243: return wsdlFile;
244: } else {
245: return getFirstWsdlFile(baseDir);
246: }
247: } else {
248: return getFirstWsdlFile(baseDir);
249: }
250: }
251:
252: private static String getRelativeNameOrURL(File baseDir, File file) {
253: String basePath = baseDir.getAbsolutePath();
254: String path = file.getAbsolutePath();
255:
256: if (path.startsWith(basePath)) {
257: if (File.separatorChar == path.charAt(basePath.length())) {
258: return path.substring(basePath.length() + 1);
259: } else {
260: return path.substring(basePath.length());
261: }
262: } else {
263: return file.toURI().toString();
264: }
265: }
266:
267: private static File createTempDirectory(File baseDir)
268: throws IOException {
269: Random rand = new Random();
270: while (true) {
271: String dirName = String.valueOf(Math.abs(rand.nextInt()));
272: File dir = new File(baseDir, dirName);
273: if (!dir.exists()) {
274: if (!dir.mkdir()) {
275: throw new IOException(
276: "Failed to create temporary directory: "
277: + dir);
278: } else {
279: return dir;
280: }
281: }
282: }
283: }
284:
285: public String generateWsdl(Module module, String serviceClass,
286: DeploymentContext context, PortInfo portInfo)
287: throws DeploymentException {
288: //call wsgen tool to generate the wsdl file based on the bindingtype.
289: //let's set the outputDir as the module base directory in server repository.
290: File moduleBase = module.getEarContext().getBaseDir();
291: File moduleBaseDir = (moduleBase.isFile()) ? moduleBase
292: .getParentFile() : moduleBase;
293: File baseDir;
294:
295: try {
296: baseDir = createTempDirectory(moduleBaseDir);
297: } catch (IOException e) {
298: throw new DeploymentException(e);
299: }
300:
301: URL[] urls;
302: StringBuilder classPath = new StringBuilder();
303: //let's figure out the classpath for wsgen tools
304: try {
305: urls = getWsgenClasspath(context);
306: } catch (Exception e) {
307: throw new DeploymentException(
308: "Failed to generate the wsdl file using wsgen: unable to get the location of the required artifact(s).",
309: e);
310: }
311: //let's figure out the classpath string for the module and wsgen tools.
312: if (urls != null && urls.length > 0) {
313: for (URL url : urls) {
314: classPath.append(toFile(url).getAbsolutePath()).append(
315: File.pathSeparator);
316: }
317: }
318: getModuleClasspath(module, context, classPath);
319:
320: //create arguments;
321: String[] arguments = buildArguments(serviceClass, classPath
322: .toString(), baseDir, portInfo);
323:
324: try {
325: boolean result = false;
326:
327: if (this .forkWsgen) {
328: result = forkWsgen(classPath, arguments);
329: } else {
330: result = invokeWsgen(urls, arguments);
331: }
332:
333: if (result) {
334: //check to see if the file is created.
335: File wsdlFile = findWsdlFile(baseDir, portInfo);
336: if (wsdlFile == null) {
337: throw new DeploymentException(
338: "Unable to find the service wsdl file");
339: }
340: return getRelativeNameOrURL(moduleBase, wsdlFile);
341: } else {
342: throw new DeploymentException("WSDL generation failed");
343: }
344:
345: } catch (DeploymentException e) {
346: throw e;
347: } catch (Exception e) {
348: throw new DeploymentException(
349: "Unable to generate the wsdl file using wsgen.", e);
350: }
351: }
352:
353: private boolean invokeWsgen(URL[] jars, String[] arguments)
354: throws Exception {
355: ByteArrayOutputStream os = new ByteArrayOutputStream();
356: boolean rs = this .jaxwsTools.invokeWsgen(jars, os, arguments);
357: os.close();
358:
359: if (LOG.isDebugEnabled()) {
360: byte[] arr = os.toByteArray();
361: String wsgenOutput = new String(arr, 0, arr.length);
362: LOG.debug("wsgen output: " + wsgenOutput);
363: }
364:
365: return rs;
366: }
367:
368: private boolean forkWsgen(StringBuilder classPath,
369: String[] arguments) throws Exception {
370: List<String> cmd = new ArrayList<String>();
371: String javaHome = System.getProperty("java.home");
372: String java = javaHome + File.separator + "bin"
373: + File.separator + "java";
374: cmd.add(java);
375: cmd.add("-classpath");
376: cmd.add(classPath.toString());
377: cmd.add("com.sun.tools.ws.WsGen");
378: cmd.addAll(Arrays.asList(arguments));
379:
380: if (LOG.isDebugEnabled()) {
381: LOG.debug("Executing wsgen: " + cmd);
382: }
383:
384: ProcessBuilder builder = new ProcessBuilder(cmd);
385: builder.redirectErrorStream(true);
386:
387: Process process = builder.start();
388: return waitFor(process);
389: }
390:
391: private boolean waitFor(Process process) throws DeploymentException {
392: CaptureOutputThread outputThread = new CaptureOutputThread(
393: process.getInputStream());
394: outputThread.start();
395:
396: long sleepTime = 0;
397: while (sleepTime < this .forkTimeout) {
398: try {
399: int errorCode = process.exitValue();
400: if (errorCode == 0) {
401: if (LOG.isDebugEnabled()) {
402: LOG.debug("wsgen output: "
403: + outputThread.getOutput());
404: }
405: return true;
406: } else {
407: LOG.error("WSDL generation process failed");
408: LOG.error(outputThread.getOutput());
409: return false;
410: }
411: } catch (IllegalThreadStateException e) {
412: // still running
413: try {
414: Thread.sleep(FORK_POLL_FREQUENCY);
415: } catch (InterruptedException ee) {
416: // interrupted
417: process.destroy();
418: throw new DeploymentException(
419: "WSDL generation process was interrupted");
420: }
421: sleepTime += FORK_POLL_FREQUENCY;
422: }
423: }
424:
425: // timeout;
426: process.destroy();
427:
428: LOG.error("WSDL generation process timed out");
429: LOG.error(outputThread.getOutput());
430:
431: throw new DeploymentException(
432: "WSDL generation process timed out");
433: }
434:
435: private static class CaptureOutputThread extends Thread {
436:
437: private InputStream in;
438: private ByteArrayOutputStream out;
439:
440: public CaptureOutputThread(InputStream in) {
441: this .in = in;
442: this .out = new ByteArrayOutputStream();
443: }
444:
445: public String getOutput() {
446: // make sure the thread is done
447: try {
448: join(10 * 1000);
449:
450: // if it's still not done, interrupt it
451: if (isAlive()) {
452: interrupt();
453: }
454: } catch (InterruptedException e) {
455: // that's ok
456: }
457:
458: // get the output
459: byte[] arr = this .out.toByteArray();
460: String output = new String(arr, 0, arr.length);
461: return output;
462: }
463:
464: public void run() {
465: try {
466: copyAll(this .in, this .out);
467: } catch (IOException e) {
468: // ignore
469: } finally {
470: try {
471: this .out.close();
472: } catch (IOException ee) {
473: }
474: try {
475: this .in.close();
476: } catch (IOException ee) {
477: }
478: }
479: }
480:
481: private static void copyAll(InputStream in, OutputStream out)
482: throws IOException {
483: byte[] buffer = new byte[4096];
484: int count;
485: while ((count = in.read(buffer)) > 0) {
486: out.write(buffer, 0, count);
487: }
488: out.flush();
489: }
490: }
491: }
|