Java tutorial
/* * Copyright 2006-2008 Web Cohesion * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.codehaus.enunciate.modules.java_client; import com.sun.mirror.declaration.ClassDeclaration; import com.sun.mirror.declaration.TypeDeclaration; import com.sun.mirror.util.SourcePosition; import freemarker.template.*; import net.sf.jelly.apt.decorations.JavaDoc; import net.sf.jelly.apt.freemarker.FreemarkerJavaDoc; import org.apache.commons.digester.RuleSet; import org.codehaus.enunciate.EnunciateException; import org.codehaus.enunciate.apt.EnunciateClasspathListener; import org.codehaus.enunciate.apt.EnunciateFreemarkerModel; import org.codehaus.enunciate.config.SchemaInfo; import org.codehaus.enunciate.config.WsdlInfo; import org.codehaus.enunciate.contract.jaxb.QNameEnumTypeDefinition; import org.codehaus.enunciate.contract.jaxb.TypeDefinition; import org.codehaus.enunciate.contract.jaxb.Registry; import org.codehaus.enunciate.contract.jaxrs.ResourceMethod; import org.codehaus.enunciate.contract.jaxrs.RootResource; import org.codehaus.enunciate.contract.jaxws.*; import org.codehaus.enunciate.contract.validation.Validator; import org.codehaus.enunciate.main.ClientLibraryArtifact; import org.codehaus.enunciate.main.Enunciate; import org.codehaus.enunciate.main.NamedFileArtifact; import org.codehaus.enunciate.main.ArtifactType; import org.codehaus.enunciate.modules.FacetAware; import org.codehaus.enunciate.modules.FreemarkerDeploymentModule; import org.codehaus.enunciate.modules.DeploymentModule; import org.codehaus.enunciate.modules.ProjectExtensionModule; import org.codehaus.enunciate.modules.java_client.config.ClientPackageConversion; import org.codehaus.enunciate.modules.java_client.config.JavaClientRuleSet; import org.codehaus.enunciate.modules.xml.XMLDeploymentModule; import org.codehaus.enunciate.template.freemarker.*; import org.codehaus.enunciate.util.AntPatternMatcher; import org.codehaus.enunciate.util.FacetFilter; import java.io.*; import java.net.URL; import java.util.*; /** * <h1>Java Client Module</h1> * * <p>The Java client module generates the client-side libraries that will access the Web service API. For SOAP endpoints, * a client-side service interface will be generated that uses <a href="https://jax-ws.dev.java.net/">JAX-WS</a>. For REST endpoints, * the JAXB data model classes will be generated to access the XML endpoints. If there are any JSON endpoints, a set of data model * classes will be generated that can be used in conjunction with the <a href="http://jackson.codehaus.org/">Jackson</a> library to * access them.</p> * * <p>The order of the Java client module is 50, so as to allow the Java module to apply * metadata to the endpoints before processing the client.</p> * * <ul> * <li><a href="#steps">steps</a></li> * <li><a href="#config">configuration</a></li> * <li><a href="#artifacts">artifacts</a></li> * </ul> * * <h1><a name="steps">Steps</a></h1> * * <h3>generate</h3> * * <p>The "generate" step is by far the most intensive and complex step in the execution of the Java client * module. The "generate" step generates all source code for accessing the web service API.</p> * * <h3>compile</h3> * * <p>During the "compile" step, the Java client module compiles the code that was generated.</p> * * <h3>build</h3> * * <p>The "build" step assembles the classes that were assembled into a jar. If there are any JSON endpoints, the JSON client library * will be assembled into a separate jar. It also creates a source jar for each library.</p> * * <h1><a name="config">Configuration</a></h1> * * <p>The Java client module is configured by the "java-client" element under the "modules" element of the * enunciate configuration file. It supports the following attributes:</p> * * <ul> * <li>The "label" attribute is used to determine the name of the client-side artifact files. The default is the Enunciate project label.</li> * <li>The "jarName" attribute specifies the name of the jar file(s) that are to be created. If no jar name is specified, * the name will be calculated from the enunciate label, or a default will be supplied.</li> * <li>The "jsonJarName" attribute specifies the name of the jar file(s) that are to be created for the JSON client. If no jar name is specified, * the name will be calculated from the enunciate label, or a default will be supplied.</li> * <li>The "disableCompile" attribute prevents Enunciate from compiling its generated client source files.</li> * <li>The "bundleSourcesWithClasses" attribute indicates whether the sources and classes should be bundled together in a single jar.</li> * </ul> * * <h3>The "package-conversions" element</h3> * * <p>The "package-conversions" subelement of the "java-client" element is used to map packages from * the original API packages to different package names. This element supports an arbitrary number of * "convert" child elements that are used to specify the conversions. These "convert" elements support * the following attributes:</p> * * <ul> * <li>The "from" attribute specifies the package that is to be converted. This package will match * all classes in the package as well as any subpackages of the package. This means that if "org.enunciate" * were specified, it would match "org.enunciate", "org.enunciate.api", and "org.enunciate.api.impl".</li> * <li>The "to" attribute specifies what the package is to be converted to. Only the part of the package * that matches the "from" attribute will be converted.</li> * </ul> * * <h3>The "json-package-conversions" element</h3> * * <p>The "json-package-conversions" element has the same purpose and syntax as the "package-conversions" element above, but is instead applied * to the JSON java client. By default, ths JSON conversions will be the same as the "package-conversions" with the "json" subpackage appended.</p> * * <h3>The "server-side-type" element</h3> * * <p>An arbitrary number of "server-side-type" elements are allowed as child elements of the "java-client" element. The "server-side-type" element * can be used to specify a server-side type that is to be ported directly over to the client-side library (as opposed to <i>generating</i> the client-side type * from the server-side type). This can be useful to provide more useful client-side capabilities, but requires that there be no package conversions for types * and web faults.</p> * * <p>The "server-side-type" element supports one attribute, "pattern" that defines an ant-style pattern of the type(s) that are to be included (using a '.' * for separating the package name).</p> * * <h3>The "facets" element</h3> * * <p>The "facets" element is applicable to the Java Client module to configure which facets are to be included/excluded from the Java Client artifacts. For * more information, see <a href="http://docs.codehaus.org/display/ENUNCIATE/Enunciate+API+Facets">API Facets</a></p> * * <h1><a name="artifacts">Artifacts</a></h1> * * <p>The Java client module exports the following artifacts:</p> * * <ul> * <li>The libraries and sources are exported under the id "java.client.library". (Note that this is a * bundle, so if exporting to a directory multiple files will be exported. If exporting to a file, the bundle will * be zipped first.)</li> * <li>The libraries and sources for the json client library are exported under the id "java.json.client.library". (Note that this is a * bundle, so if exporting to a directory multiple files will be exported. If exporting to a file, the bundle will * be zipped first.)</li> * </ul> * * @author Ryan Heaton * @docFileName module_java_client.html */ public class JavaClientDeploymentModule extends FreemarkerDeploymentModule implements ProjectExtensionModule, EnunciateClasspathListener, FacetAware { private String jarName = null; private String jsonJarName = null; private final Map<String, String> clientPackageConversions; private final Map<String, String> jsonClientPackageConversions; private final JavaClientRuleSet configurationRules; private final Set<String> serverSideTypesToUse; private String label = null; private boolean disableCompile = false; private boolean forceGenerateJsonJar = false; private boolean disableJsonJar = false; private boolean jacksonXcAvailable = false; private boolean bundleSourcesWithClasses = false; private Set<String> facetIncludes = new TreeSet<String>(); private Set<String> facetExcludes = new TreeSet<String>(); public JavaClientDeploymentModule() { this.clientPackageConversions = new LinkedHashMap<String, String>(); this.jsonClientPackageConversions = new LinkedHashMap<String, String>(); this.configurationRules = new JavaClientRuleSet(); this.serverSideTypesToUse = new TreeSet<String>(); getAliases().add("jaxws-client"); } /** * @return "java-client" */ @Override public String getName() { return "java-client"; } /** * @return 50 */ @Override public int getOrder() { return 50; } public void onClassesFound(Set<String> classes) { jacksonXcAvailable |= classes.contains("org.codehaus.jackson.xc.JaxbAnnotationIntrospector"); } @Override public void doFreemarkerGenerate() throws IOException, TemplateException, EnunciateException { File generateDir = getGenerateDir(); boolean upToDate = getEnunciate().isUpToDateWithSources(generateDir); if (!upToDate) { //load the references to the templates.... URL eiTemplate = getTemplateURL("client-endpoint-interface.fmt"); URL soapImplTemplate = getTemplateURL("client-soap-endpoint-impl.fmt"); URL faultTemplate = getTemplateURL("client-web-fault.fmt"); URL faultBeanTemplate = getTemplateURL("client-fault-bean.fmt"); URL requestBeanTemplate = getTemplateURL("client-request-bean.fmt"); URL responseBeanTemplate = getTemplateURL("client-response-bean.fmt"); URL simpleTypeTemplate = getTemplateURL("client-simple-type.fmt"); URL complexTypeTemplate = getTemplateURL("client-complex-type.fmt"); URL enumTypeTemplate = getTemplateURL("client-enum-type.fmt"); URL qnameEnumTypeTemplate = getTemplateURL("client-qname-enum-type.fmt"); URL registryTemplate = getTemplateURL("client-registry.fmt"); URL jsonComplexTypeTemplate = getTemplateURL("json-complex-type.fmt"); URL jsonSimpleTypeTemplate = getTemplateURL("json-simple-type.fmt"); URL jsonEnumTypeTemplate = getTemplateURL("json-enum-type.fmt"); EnunciateFreemarkerModel model = getModel(); Map<String, String> conversions = getClientPackageConversions(); ClientClassnameForMethod classnameFor = new ClientClassnameForMethod(conversions); classnameFor.setJdk15(true); model.put("packageFor", new ClientPackageForMethod(conversions)); model.put("classnameFor", classnameFor); model.put("simpleNameFor", new SimpleNameWithParamsMethod(classnameFor)); debug("Generating the Java client classes..."); model.setFileOutputDirectory(getClientGenerateDir()); HashMap<String, WebFault> allFaults = new HashMap<String, WebFault>(); Set<String> seeAlsos = new TreeSet<String>(); // Process the annotations, the request/response beans, and gather the set of web faults // for each endpoint interface. for (WsdlInfo wsdlInfo : model.getNamespacesToWSDLs().values()) { for (EndpointInterface ei : wsdlInfo.getEndpointInterfaces()) { if (FacetFilter.accept(ei)) { for (WebMethod webMethod : ei.getWebMethods()) { if (FacetFilter.accept(webMethod)) { for (WebMessage webMessage : webMethod.getMessages()) { if (webMessage instanceof RequestWrapper) { model.put("message", webMessage); processTemplate(requestBeanTemplate, model); seeAlsos.add(getBeanName(classnameFor, ((RequestWrapper) webMessage).getRequestBeanName())); } else if (webMessage instanceof ResponseWrapper) { model.put("message", webMessage); processTemplate(responseBeanTemplate, model); seeAlsos.add(getBeanName(classnameFor, ((ResponseWrapper) webMessage).getResponseBeanName())); } else if (webMessage instanceof WebFault) { WebFault fault = (WebFault) webMessage; allFaults.put(fault.getQualifiedName(), fault); } } } } } } } //gather the annotation information and process the possible beans for each web fault. for (WebFault webFault : allFaults.values()) { boolean implicit = webFault.isImplicitSchemaElement(); String faultBean = implicit ? getBeanName(classnameFor, webFault.getImplicitFaultBeanQualifiedName()) : classnameFor.convert(webFault.getExplicitFaultBeanType()); seeAlsos.add(faultBean); if (implicit) { model.put("fault", webFault); processTemplate(faultBeanTemplate, model); } } model.put("seeAlsoBeans", seeAlsos); for (WsdlInfo wsdlInfo : model.getNamespacesToWSDLs().values()) { if (wsdlInfo.getProperty("filename") == null) { throw new EnunciateException("WSDL " + wsdlInfo.getId() + " doesn't have a filename."); } model.put("wsdlFileName", wsdlInfo.getProperty("filename")); for (EndpointInterface ei : wsdlInfo.getEndpointInterfaces()) { if (FacetFilter.accept(ei)) { model.put("endpointInterface", ei); processTemplate(eiTemplate, model); processTemplate(soapImplTemplate, model); } } } AntPatternMatcher matcher = new AntPatternMatcher(); matcher.setPathSeparator("."); for (WebFault webFault : allFaults.values()) { if (useServerSide(webFault, matcher)) { SourcePosition position = webFault.getPosition(); if (position == null || position.file() == null) { throw new IllegalStateException( "Unable to find source file for " + webFault.getQualifiedName()); } File sourceFile = position.file(); getEnunciate().copyFile(sourceFile, getServerSideDestFile(sourceFile, webFault)); } else { ClassDeclaration superFault = webFault.getSuperclass().getDeclaration(); if (superFault != null && allFaults.containsKey(superFault.getQualifiedName()) && allFaults.get(superFault.getQualifiedName()).isImplicitSchemaElement()) { model.put("superFault", allFaults.get(superFault.getQualifiedName())); } else { model.remove("superFault"); } model.put("fault", webFault); processTemplate(faultTemplate, model); } } final Set<String> uniquePackages = new TreeSet<String>(); for (SchemaInfo schemaInfo : model.getNamespacesToSchemas().values()) { for (TypeDefinition typeDefinition : schemaInfo.getTypeDefinitions()) { if (FacetFilter.accept(typeDefinition)) { if (useServerSide(typeDefinition, matcher)) { SourcePosition position = typeDefinition.getPosition(); if (position == null || position.file() == null) { throw new IllegalStateException( "Unable to find source file for " + typeDefinition.getQualifiedName()); } File sourceFile = position.file(); getEnunciate().copyFile(sourceFile, getServerSideDestFile(sourceFile, typeDefinition)); } else { model.put("rootEl", model.findRootElementDeclaration(typeDefinition)); model.put("type", typeDefinition); URL template = typeDefinition.isEnum() ? typeDefinition instanceof QNameEnumTypeDefinition ? qnameEnumTypeTemplate : enumTypeTemplate : typeDefinition.isSimple() ? simpleTypeTemplate : complexTypeTemplate; processTemplate(template, model); } if (typeDefinition.getPackage() != null) { uniquePackages.add(typeDefinition.getPackage().getQualifiedName()); } } } for (Registry registry : schemaInfo.getRegistries()) { model.put("registry", registry); processTemplate(registryTemplate, model); } } boolean generateJsonJar = isGenerateJsonJar(); model.put("generateJson", generateJsonJar); if (generateJsonJar) { //first set up the json client package conversions. Map<String, String> jsonConversions = getJsonPackageConversions(uniquePackages); model.setFileOutputDirectory(getJsonClientGenerateDir()); ClientClassnameForMethod jsonClassnameFor = new ClientClassnameForMethod(jsonConversions); jsonClassnameFor.setJdk15(true); model.put("packageFor", new ClientPackageForMethod(jsonConversions)); model.put("classnameFor", jsonClassnameFor); model.put("simpleNameFor", new SimpleNameWithParamsMethod(jsonClassnameFor)); debug("Generating the Java JSON client classes..."); for (SchemaInfo schemaInfo : model.getNamespacesToSchemas().values()) { for (TypeDefinition typeDefinition : schemaInfo.getTypeDefinitions()) { if (FacetFilter.accept(typeDefinition)) { model.put("type", typeDefinition); URL template = typeDefinition.isEnum() ? jsonEnumTypeTemplate : typeDefinition.isSimple() ? jsonSimpleTypeTemplate : jsonComplexTypeTemplate; processTemplate(template, model); } } } } } else { info("Skipping generation of Java client sources as everything appears up-to-date..."); } } /** * Get the list of json package conversions given the specified list of unique packages. * * @param uniquePackages The unique packages. * @return The package conversions. */ protected Map<String, String> getJsonPackageConversions(Set<String> uniquePackages) { HashMap<String, String> conversions = new HashMap<String, String>(); for (String serverSidePackage : uniquePackages) { boolean conversionFound = false; if (getJsonClientPackageConversions().containsKey(serverSidePackage)) { conversions.put(serverSidePackage, getJsonClientPackageConversions().get(serverSidePackage)); conversionFound = true; } else { for (String pkg : getJsonClientPackageConversions().keySet()) { if (serverSidePackage.startsWith(pkg)) { String conversion = getJsonClientPackageConversions().get(pkg); conversions.put(serverSidePackage, conversion + serverSidePackage.substring(pkg.length())); conversionFound = true; break; } } } if (!conversionFound) { if (getClientPackageConversions().containsKey(serverSidePackage)) { conversions.put(serverSidePackage, getClientPackageConversions().get(serverSidePackage) + ".json"); conversionFound = true; } else { for (String pkg : getClientPackageConversions().keySet()) { if (serverSidePackage.startsWith(pkg)) { String conversion = getClientPackageConversions().get(pkg) + ".json"; conversions.put(serverSidePackage, conversion + serverSidePackage.substring(pkg.length())); conversionFound = true; break; } } } } if (!conversionFound) { conversions.put(serverSidePackage, serverSidePackage + ".json"); } } return conversions; } /** * The generate directory for the java client classes. * * @return The generate directory for the java client classes. */ protected File getClientGenerateDir() { return new File(getGenerateDir(), "java"); } /** * The generate directory for the java json client classes. * * @return The generate directory for the java json client classes. */ protected File getJsonClientGenerateDir() { return new File(getGenerateDir(), "json"); } /** * Get the destination for the specified declaration if the server-side type is to be used. * * @param sourceFile The source file. * @param declaration The declaration. * @return The destination file. */ protected File getServerSideDestFile(File sourceFile, TypeDeclaration declaration) { File destDir = getGenerateDir(); String packageName = declaration.getPackage().getQualifiedName(); for (StringTokenizer packagePaths = new StringTokenizer(packageName, "."); packagePaths.hasMoreTokens();) { String packagePath = packagePaths.nextToken(); destDir = new File(destDir, packagePath); } destDir.mkdirs(); return new File(destDir, sourceFile.getName()); } /** * Whether to use the server-side declaration for this declaration. * * @param declaration The declaration. * @param matcher The matcher. * @return Whether to use the server-side declaration for this declaration. */ protected boolean useServerSide(TypeDeclaration declaration, AntPatternMatcher matcher) { boolean useServerSide = false; for (String pattern : serverSideTypesToUse) { if (matcher.match(pattern, declaration.getQualifiedName())) { useServerSide = true; break; } } return useServerSide; } /** * Get the bean name for a specified string. * * @param conversion The conversion to use. * @param preconvert The pre-converted fqn. * @return The converted fqn. */ protected String getBeanName(ClientClassnameForMethod conversion, String preconvert) { String pckg = conversion.convert(preconvert.substring(0, preconvert.lastIndexOf('.'))); String simpleName = preconvert.substring(preconvert.lastIndexOf('.') + 1); return pckg + "." + simpleName; } @Override protected void doCompile() throws EnunciateException, IOException { Enunciate enunciate = getEnunciate(); //Compile the java files. if (!enunciate.isUpToDateWithSources(getCompileDir())) { Collection<String> javaSourceFiles = enunciate.getJavaFiles(getClientGenerateDir()); String clientClasspath = enunciate.getEnunciateBuildClasspath(); //we use the build classpath for client-side jars so you don't have to include client-side dependencies on the server-side. if (!isDisableCompile()) { enunciate.invokeJavac(clientClasspath, "1.5", getClientCompileDir(), new ArrayList<String>(), javaSourceFiles.toArray(new String[javaSourceFiles.size()])); } else { info("Compilation of the java sources has been disabled."); } for (DeploymentModule module : enunciate.getConfig().getEnabledModules()) { if (module instanceof XMLDeploymentModule) { XMLDeploymentModule xmlModule = (XMLDeploymentModule) module; enunciate.copyDir(xmlModule.getGenerateDir(), getClientCompileDir()); } } if (isGenerateJsonJar()) { Collection<String> jsonSourceFiles = enunciate.getJavaFiles(getJsonClientGenerateDir()); clientClasspath = enunciate.getEnunciateBuildClasspath(); //we use the build classpath for client-side jars so you don't have to include client-side dependencies on the server-side. if (!isDisableCompile()) { enunciate.invokeJavac(clientClasspath, "1.5", getJsonClientCompileDir(), new ArrayList<String>(), jsonSourceFiles.toArray(new String[jsonSourceFiles.size()])); } else { info("Compilation of the java json sources has been disabled."); } } } else { info("Skipping compilation of Java client classes as everything appears up-to-date..."); } } /** * The generate directory for the java client classes. * * @return The generate directory for the java client classes. */ protected File getClientCompileDir() { return new File(getCompileDir(), "java"); } /** * The generate directory for the java json client classes. * * @return The generate directory for the java json client classes. */ protected File getJsonClientCompileDir() { return new File(getCompileDir(), "json"); } @Override protected void doBuild() throws EnunciateException, IOException { Enunciate enunciate = getEnunciate(); String jarName = getJarName(); String jsonJarName = getJsonJarName(); String label = "enunciate"; if (getLabel() != null) { label = getLabel(); } else if ((enunciate.getConfig() != null) && (enunciate.getConfig().getLabel() != null)) { label = enunciate.getConfig().getLabel(); } if (jarName == null) { jarName = label + "-client.jar"; } if (jsonJarName == null) { jsonJarName = label + "-json-client.jar"; } File clientJarFile = null; if (!isDisableCompile()) { clientJarFile = new File(getBuildDir(), jarName); if (!enunciate.isUpToDate(getClientCompileDir(), clientJarFile)) { if (isBundleSourcesWithClasses()) { enunciate.zip(clientJarFile, getClientCompileDir(), getClientGenerateDir()); } else { enunciate.zip(clientJarFile, getClientCompileDir()); } } else { info("Skipping creation of Java client jar as everything appears up-to-date..."); } } File clientSourcesJarFile = null; if (!isBundleSourcesWithClasses()) { clientSourcesJarFile = new File(getBuildDir(), jarName.replaceFirst("\\.jar", "-sources.jar")); if (!enunciate.isUpToDate(getClientGenerateDir(), clientSourcesJarFile)) { enunciate.zip(clientSourcesJarFile, getClientGenerateDir()); } else { info("Skipping creation of the Java client source jar as everything appears up-to-date..."); } } ClientLibraryArtifact artifactBundle = new ClientLibraryArtifact(getName(), "java.client.library", "Java Client Library"); artifactBundle.setPlatform("Java (Version 5+)"); artifactBundle.addAlias("jaxws.client.library"); //read in the description from file: artifactBundle.setDescription(readResource("library_description.fmt")); if (clientJarFile != null) { NamedFileArtifact binariesJar = new NamedFileArtifact(getName(), "java.client.library.binaries", clientJarFile); binariesJar.addAlias("jaxws.client.library.binaries"); binariesJar.setDescription("The binaries for the Java client library."); binariesJar.setPublic(false); binariesJar.setArtifactType(ArtifactType.binaries); artifactBundle.addArtifact(binariesJar); enunciate.addArtifact(binariesJar); } if (clientSourcesJarFile != null) { NamedFileArtifact sourcesJar = new NamedFileArtifact(getName(), "java.client.library.sources", clientSourcesJarFile); sourcesJar.addAlias("jaxws.client.library.sources"); sourcesJar.setDescription("The sources for the Java client library."); sourcesJar.setPublic(false); sourcesJar.setArtifactType(ArtifactType.sources); artifactBundle.addArtifact(sourcesJar); enunciate.addArtifact(sourcesJar); } if (clientJarFile != null || clientSourcesJarFile != null) { enunciate.addArtifact(artifactBundle); } if (isGenerateJsonJar()) { File jsonClientJarFile = null; if (!isDisableCompile()) { jsonClientJarFile = new File(getBuildDir(), jsonJarName); if (!enunciate.isUpToDate(getJsonClientCompileDir(), jsonClientJarFile)) { if (isBundleSourcesWithClasses()) { enunciate.zip(jsonClientJarFile, getJsonClientCompileDir(), getJsonClientGenerateDir()); } else { enunciate.zip(jsonClientJarFile, getJsonClientCompileDir()); } } else { info("Skipping creation of Java JSON client jar as everything appears up-to-date..."); } } File jsonClientSourcesJarFile = null; if (!isBundleSourcesWithClasses()) { jsonClientSourcesJarFile = new File(getBuildDir(), jsonJarName.replaceFirst("\\.jar", "-sources.jar")); if (!enunciate.isUpToDate(getJsonClientGenerateDir(), jsonClientSourcesJarFile)) { enunciate.zip(jsonClientSourcesJarFile, getJsonClientGenerateDir()); } else { info("Skipping creation of the Java JSON client source jar as everything appears up-to-date..."); } } artifactBundle = new ClientLibraryArtifact(getName(), "java.json.client.library", "Java JSON Client Library"); artifactBundle.setPlatform("Java (Version 5+)"); //read in the description from file: artifactBundle.setDescription(readResource("json_library_description.fmt")); if (jsonClientJarFile != null) { NamedFileArtifact binariesJar = new NamedFileArtifact(getName(), "java.json.client.library.binaries", jsonClientJarFile); binariesJar.setDescription("The binaries for the Java JSON client library."); binariesJar.setPublic(false); binariesJar.setArtifactType(ArtifactType.binaries); artifactBundle.addArtifact(binariesJar); enunciate.addArtifact(binariesJar); } if (jsonClientSourcesJarFile != null) { NamedFileArtifact sourcesJar = new NamedFileArtifact(getName(), "java.json.client.library.sources", jsonClientSourcesJarFile); sourcesJar.setDescription("The sources for the Java JSON client library."); sourcesJar.setPublic(false); sourcesJar.setArtifactType(ArtifactType.sources); artifactBundle.addArtifact(sourcesJar); enunciate.addArtifact(sourcesJar); } if (jsonClientJarFile != null || jsonClientSourcesJarFile != null) { enunciate.addArtifact(artifactBundle); } } } /** * Reads a resource into string form. * * @param resource The resource to read. * @return The string form of the resource. */ protected String readResource(String resource) throws IOException, EnunciateException { HashMap<String, Object> model = new HashMap<String, Object>(); model.put("sample_service_method", getModelInternal().findExampleWebMethod()); model.put("sample_resource", getModelInternal().findExampleResourceMethod()); URL res = JavaClientDeploymentModule.class.getResource(resource); ByteArrayOutputStream bytes = new ByteArrayOutputStream(); PrintStream out = new PrintStream(bytes); try { processTemplate(res, model, out); out.flush(); bytes.flush(); return bytes.toString("utf-8"); } catch (TemplateException e) { throw new EnunciateException(e); } } /** * Get a template URL for the template of the given name. * * @param template The specified template. * @return The URL to the specified template. */ protected URL getTemplateURL(String template) { return JavaClientDeploymentModule.class.getResource(template); } /** * The name of the jar. * * @return The name of the jar. */ public String getJarName() { return jarName; } /** * The name of the json client jar. * * @return The name of the json client jar. */ public String getJsonJarName() { return jsonJarName; } /** * The name of the jar. * * @param jarName The name of the jar. */ public void setJarName(String jarName) { this.jarName = jarName; } /** * The name of the json client jar. * * @param jarName The name of the json client jar. */ public void setJsonJarName(String jarName) { this.jsonJarName = jarName; } /** * An Java configuration rule set. * * @return An Java configuration rule set. */ @Override public RuleSet getConfigurationRules() { return this.configurationRules; } /** * An java-client validator. * * @return An java-client validator. */ @Override public Validator getValidator() { return new JavaClientValidator(getServerSideTypesToUse(), getClientPackageConversions()); } @Override protected ObjectWrapper getObjectWrapper() { return new DefaultObjectWrapper() { @Override public TemplateModel wrap(Object obj) throws TemplateModelException { if (obj instanceof JavaDoc) { return new FreemarkerJavaDoc((JavaDoc) obj); } return super.wrap(obj); } }; } /** * The client package conversions. * * @return The client package conversions. */ public Map<String, String> getClientPackageConversions() { return clientPackageConversions; } /** * Add a client package conversion. * * @param conversion The conversion to add. */ public void addClientPackageConversion(ClientPackageConversion conversion) { String from = conversion.getFrom(); String to = conversion.getTo(); if (from == null) { throw new IllegalArgumentException( "A 'from' attribute must be specified on a clientPackageConversion element."); } if (to == null) { throw new IllegalArgumentException( "A 'to' attribute must be specified on a clientPackageConversion element."); } this.clientPackageConversions.put(from, to); } /** * The json client package conversions. * * @return The json client package conversions. */ public Map<String, String> getJsonClientPackageConversions() { return jsonClientPackageConversions; } /** * Add a client package conversion. * * @param conversion The conversion to add. */ public void addJsonClientPackageConversion(ClientPackageConversion conversion) { String from = conversion.getFrom(); String to = conversion.getTo(); if (from == null) { throw new IllegalArgumentException( "A 'from' attribute must be specified on a clientPackageConversion element."); } if (to == null) { throw new IllegalArgumentException( "A 'to' attribute must be specified on a clientPackageConversion element."); } this.jsonClientPackageConversions.put(from, to); } /** * The server-side types that are to be used for the client-side libraries. * * @return The server-side types that are to be used for the client-side libraries. */ public Set<String> getServerSideTypesToUse() { return serverSideTypesToUse; } /** * Add a server-side type to use for the client-side library. * * @param serverSideTypeToUse The server-side type to use. */ public void addServerSideTypeToUse(String serverSideTypeToUse) { this.serverSideTypesToUse.add(serverSideTypeToUse); } /** * The label for the JAX-WS Client API. * * @return The label for the JAX-WS Client API. */ public String getLabel() { return label; } /** * The label for the JAX-WS Client API. * * @param label The label for the JAX-WS Client API. */ public void setLabel(String label) { this.label = label; } /** * Whether to bundle the sources and the classes together. * * @return Whether to bundle the sources and the classes together. */ public boolean isBundleSourcesWithClasses() { return bundleSourcesWithClasses; } /** * Whether to bundle the sources and the classes together. * * @param bundleSourcesWithClasses Whether to bundle the sources and the classes together. */ public void setBundleSourcesWithClasses(boolean bundleSourcesWithClasses) { this.bundleSourcesWithClasses = bundleSourcesWithClasses; } /** * Whether to generate the JSON client jar. * * @return Whether to generate the JSON client jar. */ public boolean isGenerateJsonJar() { return forceGenerateJsonJar || (!disableJsonJar && jacksonXcAvailable && existsAnyJsonResourceMethod(getModelInternal().getRootResources())); } /** * Whether any root resources exist that produce json. * * @param rootResources The root resources. * @return Whether any root resources exist that produce json. */ protected boolean existsAnyJsonResourceMethod(List<RootResource> rootResources) { for (RootResource rootResource : rootResources) { for (ResourceMethod resourceMethod : rootResource.getResourceMethods(true)) { for (String mime : resourceMethod.getProducesMime()) { if ("*/*".equals(mime)) { return true; } else if (mime.toLowerCase().contains("json")) { return true; } } for (String mime : resourceMethod.getConsumesMime()) { if ("*/*".equals(mime)) { return true; } else if (mime.toLowerCase().contains("json")) { return true; } } } } return false; } /** * Whether to generate the JSON client jar. * * @param generateJsonJar Whether to generate the JSON jar. */ public void setGenerateJsonJar(boolean generateJsonJar) { this.forceGenerateJsonJar = generateJsonJar; this.disableJsonJar = !generateJsonJar; } // Inherited. @Override public boolean isDisabled() { if (super.isDisabled()) { return true; } else if (getModelInternal() != null && getModelInternal().getNamespacesToWSDLs().isEmpty() && getModelInternal().getNamespacesToSchemas().isEmpty()) { debug("Java client module is disabled because there are no endpoint interfaces, nor any root schema types."); return true; } return false; } public List<File> getProjectSources() { return Collections.emptyList(); } public List<File> getProjectTestSources() { return Arrays.asList(getJsonClientGenerateDir(), getClientGenerateDir()); } public List<File> getProjectResourceDirectories() { return Collections.emptyList(); } public List<File> getProjectTestResourceDirectories() { ArrayList<File> testResources = new ArrayList<File>(); //the java-client requires the wsdl and schemas on the classpath. for (DeploymentModule enabledModule : getEnunciate().getConfig().getEnabledModules()) { if (enabledModule instanceof XMLDeploymentModule) { testResources.add(((XMLDeploymentModule) enabledModule).getGenerateDir()); } } return testResources; } /** * Whether to disable the compilation of the java sources (default: false). * * @return Whether to disable the compilation of the java sources (default: false). */ public boolean isDisableCompile() { return disableCompile; } /** * Whether to disable the compilation of the java sources (default: false). * * @param disableCompile Whether to disable the compilation of the java sources (default: false). */ public void setDisableCompile(boolean disableCompile) { this.disableCompile = disableCompile; } /** * The set of facets to include. * * @return The set of facets to include. */ public Set<String> getFacetIncludes() { return facetIncludes; } /** * Add a facet include. * * @param name The name. */ public void addFacetInclude(String name) { if (name != null) { this.facetIncludes.add(name); } } /** * The set of facets to exclude. * * @return The set of facets to exclude. */ public Set<String> getFacetExcludes() { return facetExcludes; } /** * Add a facet exclude. * * @param name The name. * @param value The value. */ public void addFacetExclude(String name, String value) { if (name != null) { this.facetExcludes.add(name); } } }