View Javadoc

1   /*
2    * Copyright 2006 Guillaume Nodet.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.codehaus.mojo.jaxws;
18  
19  import java.io.BufferedReader;
20  import java.io.BufferedWriter;
21  import java.io.File;
22  import java.io.FileInputStream;
23  import java.io.FileWriter;
24  import java.io.InputStreamReader;
25  import java.lang.annotation.Annotation;
26  import java.net.URL;
27  import java.net.URLClassLoader;
28  import java.util.ArrayList;
29  import java.util.LinkedList;
30  import java.util.List;
31  import java.util.Set;
32  
33  import org.apache.maven.artifact.Artifact;
34  import org.apache.maven.plugin.MojoExecutionException;
35  import org.apache.maven.plugin.MojoFailureException;
36  
37  import com.sun.tools.ws.WsGen;
38  
39  
40  /**
41   * @svnLink $Revision: 3872 $;$Date: 2009-08-04 13:41:09 +0200 (Di, 04. Aug 2009) $;$Author: swismer $;$URL: https://el4j.svn.sourceforge.net/svnroot/el4j/branches/el4j_3_1/el4j/maven/plugins/maven-jaxws-plugin/src/main/java/org/codehaus/mojo/jaxws/AbstractWsGenMojo.java $
42   *
43   * @author gnodet <gnodet@apache.org>
44   * @author dantran <dantran@apache.org>
45   * @author Stefan Wismer (SWI)
46   *
47   * @version $Id: AbstractWsGenMojo.java 3872 2009-08-04 11:41:09Z swismer $
48   */
49  abstract class AbstractWsGenMojo extends AbstractJaxwsMojo {
50  
51  	/**
52  	 * Specify that a WSDL file should be generated in ${resourceDestDir}
53  	 *
54  	 * @parameter default-value="false"
55  	 */
56  	private boolean genWsdl;
57  
58  	
59  	/**
60  	 * Directory containing the generated wsdl files.
61  	 *
62  	 * @parameter default-value="${project.build.directory}/jaxws/wsgen/wsdl"
63  	 */
64  	
65  	private File resourceDestDir;
66  
67  	/**
68  	 * service endpoint implementation class name.
69  	 *
70  	 * @parameter  default-value="*"
71  	 * @required
72  	 */
73  	private String sei;
74  	
75  	/**
76  	 * Project test classpath.
77  	 *
78  	 * @parameter expression="${project.testClasspathElements}"
79  	 * @required
80  	 * @readonly
81  	 */
82  	private List classpathElements;
83  	
84  	/**
85  	 * The source directories containing the sources to be compiled.
86  	 *
87  	 * @parameter expression="${project.compileSourceRoots}"
88  	 * @required
89  	 * @readonly
90  	 */
91  	private List compileSourceRoots;
92  	
93  	/**
94  	 * The directory where compiled classes go.
95  	 *
96  	 * @parameter expression="${project.build.outputDirectory}"
97  	 * @required
98  	 * @readonly
99  	 */
100 	private File classDirectory;
101 
102 	/**
103 	 * Used in conjunction with genWsdl to specify the protocol to use in the
104 	 * wsdl:binding.  Value values are "soap1.1" or "Xsoap1.2", default is "soap1.1".
105 	 * "Xsoap1.2" is not standard and can only be used in conjunction with the
106 	 * -extensions option
107 	 *
108 	 * @parameter
109 	 */
110 	private String protocol;
111 	
112 	/**
113 	 * The host part of the URL (like http://localhost:8080).
114 	 * @parameter
115 	 */
116 	private String hostURL;
117 	
118 	/**
119 	 * The context part of the URL (this becomes the directory).
120 	 * @parameter
121 	 */
122 	private String contextURL;
123 	
124 	/**
125 	 * The service part of the URL. The star charcter (*) gets replaced by
126 	 * the service interface name.
127 	 * @parameter
128 	 */
129 	private String serviceURL;
130 
131 	
132 	/**
133 	 * Specify where to place generated source files, keep is turned on with this option.
134 	 *
135 	 * @parameter
136 	 */
137 	private File sourceDestDir;
138 	//default-value="${project.build.directory}/jaxws/java"
139 	
140 	private URLClassLoader classLoader = null;
141 
142 	public void execute()
143 		throws MojoExecutionException, MojoFailureException {
144 		init();
145 
146 		// Need to build a URLClassloader since Maven removed it form the chain
147 		ClassLoader parent = this.getClass().getClassLoader();
148 		String orginalSystemClasspath = this.initClassLoader(parent);
149 
150 		try {
151 			List<String> classesToProcess = new LinkedList<String>();
152 			if (sei.equals("*")) {
153 				// init classloader
154 				ArrayList<URL> urls = new ArrayList<URL>();
155 				URL url = classDirectory.toURL();
156 				urls.add(url);
157 				
158 				for (Object cp : classpathElements) {
159 					url = new File((String) cp).toURL();
160 					urls.add(url);
161 				}
162 				classLoader = new URLClassLoader(urls.toArray(new URL[0]));
163 				
164 				// find class files to process
165 				addClassFiles(classesToProcess, classDirectory);
166 			} else {
167 				classesToProcess.add(sei);
168 			}
169 			
170 			// prepare hostURL and contextURL
171 			if (hostURL != null && contextURL != null) {
172 				if (!hostURL.endsWith("/")) {
173 					hostURL = hostURL + "/";
174 				}
175 				if (!contextURL.endsWith("/")) {
176 					contextURL = contextURL + "/";
177 				}
178 			}
179 			
180 			for (String classToProcess : classesToProcess) {
181 				sei = classToProcess;
182 				ArrayList<String> args = getWsGenArgs();
183 
184 				if (WsGen.doMain(args.toArray(new String[args.size()])) != 0)
185 					throw new MojoExecutionException("Error executing: wsgen " + args);
186 				
187 				
188 				if (genWsdl && hostURL != null && contextURL != null && serviceURL != null) {
189 					
190 					String[] nameAndServiceName = getNameAndServiceName(classToProcess);
191 					String name = nameAndServiceName[0];
192 					String serviceName = nameAndServiceName[1];
193 					
194 					String actualServiceURL = serviceURL.replaceAll("\\*", name);
195 					replaceURLinWSDL(serviceName, hostURL + contextURL + actualServiceURL);
196 				}
197 			}
198 		} catch (MojoExecutionException e) {
199 			throw e;
200 		} catch (Throwable e) {
201 			throw new MojoExecutionException("Failed to execute wsgen",e);
202 		} finally {
203 			// Set back the old classloader
204 			Thread.currentThread().setContextClassLoader(parent);
205 			System.setProperty("java.class.path", orginalSystemClasspath);
206 		}
207 	}
208 
209 	private void init() throws MojoExecutionException, MojoFailureException {
210 		if (!getDestDir().exists())
211 			getDestDir().mkdirs();
212 	}
213 
214 	/**
215 	 * Construct wsgen arguments
216 	 * @return a list of arguments
217 	 * @throws MojoExecutionException
218 	 */
219 	private ArrayList<String> getWsGenArgs()
220 		throws MojoExecutionException {
221 		ArrayList<String> args = new ArrayList<String>();
222 
223 		if (verbose) {
224 			args.add("-verbose");
225 		}
226 
227 		if (keep || this.sourceDestDir!=null) {
228 			args.add("-keep");
229 		}
230 
231 		if (this.sourceDestDir != null) {
232 			args.add("-s");
233 			args.add(this.sourceDestDir.getAbsolutePath());
234 			this.sourceDestDir.mkdirs();
235 		}
236 
237 		args.add("-d");
238 		args.add(getDestDir().getAbsolutePath());
239 
240 		args.add("-cp");
241 		StringBuilder buf = new StringBuilder();
242 		buf.append(getDestDir().getAbsolutePath());
243 		for (Artifact a : (Set<Artifact>)project.getArtifacts()) {
244 			buf.append(File.pathSeparatorChar);
245 			buf.append(a.getFile().getAbsolutePath());
246 		}
247 		args.add(buf.toString());
248 
249 		if (this.genWsdl) {
250 			if (this.protocol != null) {
251 				args.add("-wsdl:" + this.protocol);
252 			} else {
253 				args.add("-wsdl");
254 			}
255 
256 			args.add("-r");
257 			args.add(this.resourceDestDir.getAbsolutePath());
258 			this.resourceDestDir.mkdirs();
259 
260 		}
261 
262 		args.add(sei);
263 
264 		getLog().debug("jaxws:wsgen args: " + args);
265 
266 		return args;
267 	}
268 	
269 	/**
270 	 * Adds all the class files located in the folder to the list.
271 	 * @param list      the list to add the class names
272 	 * @param folder    the folder to examine
273 	 */
274 	@SuppressWarnings("unchecked")
275 	private void addClassFiles(List<String> list, File folder) {
276 		for (File file : folder.listFiles()) {
277 			if (file.isDirectory()) {
278 				addClassFiles(list, file);
279 			} else {
280 				if (!file.getName().endsWith(".class")) {
281 					continue;
282 				}
283 				
284 				// cut classDirectory part away
285 				String path = file.toString().substring(
286 					classDirectory.toString().length() + 1);
287 				
288 				// only take non-generated files (they must have source files)
289 				boolean found = false;
290 				for (Object srcDir : compileSourceRoots) {
291 					String dirReplaced = ((String) srcDir)
292 						+ File.separatorChar + path;
293 					dirReplaced = dirReplaced.substring(0,
294 						dirReplaced.length() - "class".length());
295 					if (new File(dirReplaced + "java").exists()) {
296 						found = true;
297 					}
298 				}
299 				if (!found) {
300 					continue;
301 				}
302 				
303 				String className = path.replace(File.separatorChar, '.');
304 				className = className.substring(0,
305 					className.length() - ".class".length());
306 
307 				try {
308 					Class c = classLoader.loadClass(className);
309 					
310 					// ignore interfaces
311 					if (c.isInterface()) {
312 						continue;
313 					}
314 					
315 					Annotation[] annots = c.getAnnotations();
316 					// search for @WebService annotations
317 					for (Annotation annotation : annots) {
318 						if (annotation.annotationType().getName()
319 							.equals("javax.jws.WebService")) {
320 							
321 							list.add(className);
322 							break;
323 						}
324 					}
325 				} catch (Exception e) {
326 					// ignore class
327 				}
328 			}
329 		}
330 	}
331 	
332 	/**
333 	 * Replaces REPLACE_WITH_ACTUAL_URL by actual url in WSDL file.
334 	 * @param serviceName    the service name
335 	 * @param url            the url which replaces REPLACE_WITH_ACTUAL_URL
336 	 * @throws MojoExecutionException
337 	 */
338 	private void replaceURLinWSDL(String serviceName, String url)
339 		throws MojoExecutionException {
340 		
341 		File file = new File(resourceDestDir, serviceName + ".wsdl");
342 		
343 		if (file.exists()) {
344 			String line;
345 			StringBuilder sb = new StringBuilder();
346 			try {
347 				// read file and replace
348 				FileInputStream fis = new FileInputStream(file);
349 				BufferedReader reader = new BufferedReader(
350 					new InputStreamReader(fis));
351 				while ((line = reader.readLine()) != null) {
352 					line = line.replaceAll("REPLACE_WITH_ACTUAL_URL", url);
353 					sb.append(line + "\n");
354 				}
355 				reader.close();
356 				
357 				// write file
358 				BufferedWriter out = new BufferedWriter(new FileWriter(file));
359 				out.write(sb.toString());
360 				out.close();
361 			} catch (Throwable e) {
362 				throw new MojoExecutionException(
363 					"Could not modify WSDL file for service " + serviceName);
364 			}
365 		} else {
366 			getLog().warn("Could not find generated WSDL for service '" + serviceName + "'");
367 		}
368 	}
369 	
370 	/**
371 	 * @param classToProcess   the web service class to process
372 	 * @return                 the service name derived from the annotated class
373 	 * @throws MojoExecutionException
374 	 */
375 	@SuppressWarnings("unchecked")
376 	private String[] getNameAndServiceName(String classToProcess)
377 		throws MojoExecutionException {
378 		
379 		String name = null;
380 		String serviceName = null;
381 		try {
382 			Class c = classLoader.loadClass(classToProcess);
383 			
384 			Annotation[] annots = c.getAnnotations();
385 			// search for @WebService annotations
386 			for (Annotation annotation : annots) {
387 				if (annotation.annotationType().getName()
388 					.equals("javax.jws.WebService")) {
389 					
390 					name = (String) annotation
391 						.annotationType().getMethod("name")
392 						.invoke(annotation);
393 					
394 					serviceName = (String) annotation
395 						.annotationType().getMethod("serviceName")
396 						.invoke(annotation);
397 					
398 					break;
399 				}
400 			}
401 			
402 			if (name == null) {
403 				// JAX-WS default value
404 				name = c.getSimpleName();
405 			}
406 			if (serviceName == null) {
407 				// JAX-WS default value
408 				serviceName = c.getSimpleName() + "Service";
409 			}
410 		} catch (Exception e) {
411 			throw new MojoExecutionException(
412 				"Could not get serviceName from " + classToProcess);
413 		}
414 		if (serviceName == null) {
415 			throw new MojoExecutionException(
416 				"Could not get serviceName from " + classToProcess);
417 		}
418 		
419 		return new String[] {name, serviceName};
420 	}
421 }