package com.sun.jersey.wadl; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.OutputStream; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.xml.bind.JAXBContext; import javax.xml.bind.Marshaller; import org.apache.tools.ant.AntClassLoader; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Task; import org.apache.tools.ant.types.Path; import org.apache.tools.ant.types.Reference; import org.apache.xml.serialize.OutputFormat; import org.apache.xml.serialize.XMLSerializer; import com.sun.jersey.api.core.PackagesResourceConfig; import com.sun.jersey.api.core.ResourceConfig; import com.sun.jersey.api.model.AbstractResource; import com.sun.jersey.server.impl.modelapi.annotation.IntrospectionModeller; import com.sun.jersey.server.wadl.WadlBuilder; import com.sun.jersey.server.wadl.WadlGenerator; import com.sun.jersey.server.wadl.WadlGeneratorImpl; import com.sun.jersey.wadl.resourcedoc.ResourceDoclet; import com.sun.research.ws.wadl.Application; /** * This ant task generates a wadl file, using an option resourcedoc file created * by the {@link ResourceDoclet} and an applicationdoc file.
* Created on: Jun 18, 2008
* * @author Andrew Ochsner * @version $Id$ * */ public class GenerateWadlTask extends Task { public class Package { private String name; public Package() { } public String getName() { return name; } public void setName(String name) { this.name = name; } } public class Packages { public Packages() { packages = new ArrayList(); } public Package createPackage() { Package _package = new Package(); packages.add(_package); return _package; } public List getPackages() { return packages; } private List packages; } public class WadlGeneratorInfoParam { private String name; private String value; public WadlGeneratorInfoParam() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } } public class WadlGeneratorInfo { private String name; private List params; public WadlGeneratorInfo() { params = new ArrayList(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public List getParams() { return params; } public WadlGeneratorInfoParam createParam() { WadlGeneratorInfoParam param = new WadlGeneratorInfoParam(); params.add(param); return param; } } public class WadlGenerators { private List wadlGeneratorDescriptions; public WadlGenerators() { wadlGeneratorDescriptions = new ArrayList(); } public WadlGeneratorInfo createWadlGeneratorDescription() { WadlGeneratorInfo info = new WadlGeneratorInfo(); wadlGeneratorDescriptions.add(info); return info; } public List getWadlGeneratorDescriptions() { return wadlGeneratorDescriptions; } } /** * Location of the wadl file to create. * * @parameter property="wadlFile" * expression="${project.build.directory}/application.wadl" * @required */ private File _wadlFile; /** * Specifies, if the generated wadl file shall contain formatted xml or not. * The default value is true. * * @parameter property="formatWadlFile" */ private boolean _formatWadlFile = true; /** * The base-uri to use. * * @parameter property="baseUri" * @required */ private String _baseUri; /** * A list of packages that is searched for resource classes * * @paramter property="packages" * @required */ private Packages _packages; /** * A list of wadl generators to run * * @parameter property="wadlGenerators" * @required */ private WadlGenerators _wadlGenerators; /** * @parameter property="classpathref" * @required */ private Path _classpath; @Override public void execute() throws BuildException { if (_packages == null || _packages.getPackages().size() == 0) { throw new BuildException( "A fileset of resources is required but not defined."); } if (_wadlFile == null) { throw new BuildException( "The wadlFile attribute is required but not defined."); } if (_baseUri == null || _baseUri.length() == 0) { throw new BuildException( "The baseUri attribute is required but not defined."); } AntClassLoader classLoader = null; try { classLoader = getProject().createClassLoader(_classpath); classLoader.setParent(this.getClass().getClassLoader()); classLoader.setThreadContextLoader(); com.sun.jersey.server.wadl.WadlGenerator wadlGenerator = new WadlGeneratorImpl(); if (_wadlGenerators != null) { for (WadlGeneratorInfo wadlGeneratorInfo : _wadlGenerators .getWadlGeneratorDescriptions()) { wadlGenerator = loadWadlGenerator(wadlGeneratorInfo, wadlGenerator); } } wadlGenerator.init(); final Application a = createApplication(wadlGenerator); a.getResources().setBase(_baseUri); final JAXBContext c = JAXBContext.newInstance(wadlGenerator .getRequiredJaxbContextPath(), Thread.currentThread() .getContextClassLoader()); final Marshaller m = c.createMarshaller(); m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, _formatWadlFile); final OutputStream out = new BufferedOutputStream( new FileOutputStream(_wadlFile)); final XMLSerializer serializer = getXMLSerializer(out); m.marshal(a, serializer); out.close(); log("Wrote " + _wadlFile); } catch (Exception e) { if (classLoader != null) { classLoader.resetThreadContextLoader(); classLoader.cleanup(); } throw new BuildException("Could not write wadl file", e); } } private XMLSerializer getXMLSerializer(OutputStream out) throws FileNotFoundException { // configure an OutputFormat to handle CDATA OutputFormat of = new OutputFormat(); // specify which of your elements you want to be handled as CDATA. // The use of the '^' between the namespaceURI and the localname // seems to be an implementation detail of the xerces code. // When processing xml that doesn't use namespaces, simply omit the // namespace prefix as shown in the third CDataElement below. of.setCDataElements(new String[] { "http://research.sun.com/wadl/2006/10^doc", // "ns2^doc", // "^doc" /* * , "ns2:doc", "doc" */}); // // set any other options you'd like of.setPreserveSpace(true); of.setIndenting(true); // of.setLineWidth( 120 ); // of.setNonEscapingElements( new String[] { // "http://www.w3.org/1999/xhtml^br", "http://www.w3.org/1999/xhtml^br" // } ); // create the serializer XMLSerializer serializer = new XMLSerializer(of); serializer.setOutputByteStream(out); return serializer; } private WadlGenerator loadWadlGenerator( WadlGeneratorInfo wadlGeneratorInfo, com.sun.jersey.server.wadl.WadlGenerator wadlGeneratorDelegate) throws Exception { log("Loading wadlGeneratorInfo " + wadlGeneratorInfo.getName()); final Class clazz = Class.forName(wadlGeneratorInfo.getName(), true, Thread.currentThread().getContextClassLoader()); final WadlGenerator generator = clazz.asSubclass(WadlGenerator.class) .newInstance(); generator.setWadlGeneratorDelegate(wadlGeneratorDelegate); if (!wadlGeneratorInfo.getParams().isEmpty()) { for (WadlGeneratorInfoParam param : wadlGeneratorInfo.getParams()) { setProperty(generator, param.getName(), param.getValue()); } } return generator; } private void setProperty(final Object object, final String propertyName, final Object propertyValue) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException, InstantiationException { final String methodName = "set" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); final Method method = getMethodByName(methodName, object.getClass()); if (method.getParameterTypes().length != 1) { throw new RuntimeException( "Method " + methodName + " is no setter, it does not expect exactly one parameter, but " + method.getParameterTypes().length); } final Class paramClazz = method.getParameterTypes()[0]; if (paramClazz == propertyValue.getClass()) { method.invoke(object, propertyValue); } else { /* * does the param class provide a constructor for string? */ final Constructor paramTypeConstructor = paramClazz .getConstructor(propertyValue.getClass()); if (paramTypeConstructor != null) { final Object typedPropertyValue = paramTypeConstructor .newInstance(propertyValue); method.invoke(object, typedPropertyValue); } else { throw new RuntimeException( "The property '" + propertyName + "' could not be set" + " because the expected parameter is neither of type " + propertyValue.getClass() + " nor of any type that provides a constructor expecting a " + propertyValue.getClass() + "." + " The expected parameter is of type " + paramClazz.getName()); } } } private Method getMethodByName(final String methodName, final Class clazz) { for (Method method : clazz.getMethods()) { if (method.getName().equals(methodName)) { return method; } } throw new RuntimeException("Method '" + methodName + "' not found for class " + clazz.getName()); } private Application createApplication(WadlGenerator wadlGenerator) { final Map map = new HashMap(); List packageNames = new ArrayList(); for (Package p : _packages.getPackages()) { packageNames.add(p.getName()); } map.put(PackagesResourceConfig.PROPERTY_PACKAGES, packageNames .toArray(new String[] {})); final ResourceConfig rc = new PackagesResourceConfig(map); final Set s = new HashSet(); for (Class c : rc.getRootResourceClasses()) { log("Adding class " + c.getName()); s.add(IntrospectionModeller.createResource(c)); } return new WadlBuilder(wadlGenerator).generate(s); } public void setWadlFile(File file) { _wadlFile = file; } public void setFormatWadlFile(boolean wadlFile) { _formatWadlFile = wadlFile; } public void setBaseUri(String uri) { _baseUri = uri; } public void setClasspath(Path path) { if (_classpath == null) { _classpath = path; } else { _classpath.append(path); } } public Path createClasspath() { if (_classpath == null) { _classpath = new Path(getProject()); } return _classpath.createPath(); } public void setClasspathRef(Reference r) { createClasspath().setRefid(r); } public Packages createPackages() { if (_packages == null) { _packages = new Packages(); } return _packages; } public WadlGenerators createWadlGenerators() { if (_wadlGenerators == null) { _wadlGenerators = new WadlGenerators(); } return _wadlGenerators; } }