/* * #%L restdoc-plugin %% Copyright (C) 2012 IG Group %% 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. #L% */ package com.iggroup.oss.restdoclet.plugin.mojo; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import org.apache.commons.collections.CollectionUtils; import org.apache.log4j.Logger; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.jibx.runtime.JiBXException; import com.iggroup.oss.restdoclet.doclet.type.Controller; import com.iggroup.oss.restdoclet.doclet.type.ControllerSummary; import com.iggroup.oss.restdoclet.doclet.type.Method; import com.iggroup.oss.restdoclet.doclet.type.Service; import com.iggroup.oss.restdoclet.doclet.type.Services; import com.iggroup.oss.restdoclet.doclet.type.Uri; import com.iggroup.oss.restdoclet.doclet.util.ControllerTypePredicate; import com.iggroup.oss.restdoclet.doclet.util.DocletUtils; import com.iggroup.oss.restdoclet.doclet.util.JiBXUtils; import com.iggroup.oss.restdoclet.plugin.io.ConfigCopier; import com.iggroup.oss.restdoclet.plugin.io.DirectoryBuilder; import com.iggroup.oss.restdoclet.plugin.io.FileUploader; import com.iggroup.oss.restdoclet.plugin.io.JarBuilder; import com.iggroup.oss.restdoclet.plugin.util.JavadocNotFoundException; import com.iggroup.oss.restdoclet.plugin.util.MavenUtils; import com.iggroup.oss.restdoclet.plugin.util.ServiceUtils; /** * Mojo for generating services from Java documentation created by XmlDoclet. * * @goal restdoclet * @phase package * @requiresProject */ public class RestDocumentationMojo extends AbstractMojo { private static final Logger LOG = Logger .getLogger(RestDocumentationMojo.class); /** * The artifact identifier of the module this Mojo is running on. * * @parameter expression="${project.artifactId}" * @readonly * @required */ private transient String artifactId; /** * The version of the module this Mojo is running on. * * @parameter expression="${project.version}" * @readonly * @required */ private transient String version; /** * The packaging of the module this Mojo is running on. * * @parameter expression="${project.packaging}" * @readonly * @required */ private transient String packaging; /** * The build-name of the module this Mojo is running on. * * @parameter expression="${project.build.finalName}" * @readonly * @required */ private transient String finalName; /** * The base-directory of the module this Mojo is running on. * * @parameter expression="${basedir}" * @readonly * @optional */ private transient File baseDirectory; /** * The classifier used by documentation. It is set to * <code>restdoclet</code> by default. * * @parameter default-value="restdoclet" * @readonly */ private transient String classifier; /** * The name of the the directory this Mojo's output is generated. * * @parameter expression="${outputDirectory}" default-value="restdoclet" * @readonly */ private transient String outputDirectory; /** * The scm url of the module this Mojo is running on. * * @parameter expression="${project.scm.url}" * @readonly * @required */ private transient String scmUrl; /** * The list of parameters that have to be excluded while matching methods in * controllers. * * @parameter */ private transient List<String> excludes; /** * The restdoclet deployment url of the module this Mojo is running on. * * @parameter expression="${deployUrl}" */ private transient String deployUrl; /** * The restdoclet deployment dir of the module this Mojo is running on. * * @parameter expression="${deployDir}" * @required */ private transient String deployDir; /** * The documentation of controllers generated by XmlDoclet. */ private final transient Collection<Controller> controllers = new ArrayList<Controller>(); /** * Collects documentations of controllers and data-binders. * * @throws CloneNotSupportedException if a data-binder's documentation can't * be cloned. * @throws FileNotFoundException if the file containing documentation can't * be found. * @throws JiBXException if a JiBX exception occurs. */ private void javadocs() throws CloneNotSupportedException, FileNotFoundException, JiBXException { LOG.info("Collecting controller javadocs"); /* root directory */ File root = baseDirectory; while (root.getParentFile() != null && new File(root.getParentFile(), MavenUtils.POM_FILE).exists()) { root = root.getParentFile(); } /* collect controller javadocs */ LOG.info("Collecting Controller javadocs"); final Collection<File> cfiles = ServiceUtils.collectControllerJavadocs(root); if (cfiles.size() == 0) { throw new IllegalArgumentException( "No controller javadoc found. Is the javadoc plugin configured correctly?"); } for (final File file : cfiles) { LOG.debug(file.getAbsolutePath() + File.separatorChar + file.getName()); final Controller cntrl = JiBXUtils.unmarshallController(file); LOG.info(cntrl.getType()); for (Method m : cntrl.getMethods()) { LOG.info(m.toString()); } if (!controllers.contains(cntrl)) { controllers.add(cntrl); } } } /** * Generates services from the documentation of controllers and * data-binders. * * @throws BeansNotFoundException if a bean with an identifier or Java type * can't be found. * @throws IOException if services can't be marshaled. * @throws JavadocNotFoundException if a controller's documentation can't be * found. * @throws JiBXException if a JiBX exception occurs. */ private void services() throws IOException, JavadocNotFoundException, JiBXException { LOG.info("Generating services"); DirectoryBuilder dirs = new DirectoryBuilder(baseDirectory, outputDirectory); int identifier = 1; List<Service> services = new ArrayList<Service>(); LOG.info("Looking for mappings"); HashMap<String, ArrayList<Method>> uriMethodMappings = new HashMap<String, ArrayList<Method>>(); HashMap<String, Controller> uriControllerMappings = new HashMap<String, Controller>(); HashMap<String, Collection<Uri>> multiUriMappings = new HashMap<String, Collection<Uri>>(); for (Controller controller : controllers) { LOG.info(new StringBuilder().append("- Controller ") .append(controller.getType()).toString()); for (Method method : controller.getMethods()) { LOG.info(new StringBuilder().append("... for Method ").append( method.toString())); if (excludeMethod(method)) { continue; } // Collate multiple uris into one string key. Collection<Uri> uris = method.getUris(); if (!uris.isEmpty()) { String multiUri = ""; for (Uri uri : uris) { multiUri = multiUri + ", " + uri; } multiUriMappings.put(multiUri, uris); ArrayList<Method> methodList = uriMethodMappings.get(multiUri); if (methodList == null) { methodList = new ArrayList<Method>(); uriMethodMappings.put(multiUri, methodList); } methodList.add(method); uriControllerMappings.put(multiUri, controller); } } } LOG.info("Processing controllers..."); for (String uri : uriControllerMappings.keySet()) { LOG.info(new StringBuilder().append("Processing controllers for ") .append(uri).toString()); Controller controller = uriControllerMappings.get(uri); LOG.info(new StringBuilder().append("Found controller ") .append(uriControllerMappings.get(uri).getType()).toString()); ArrayList<Method> matches = uriMethodMappings.get(uri); LOG.info(new StringBuilder().append("Found methods ") .append(matches.toString()).append(" ").append(matches.size()) .toString()); Service service = new Service(identifier, multiUriMappings.get(uri), new Controller( controller.getType(), controller.getJavadoc(), matches)); services.add(service); service.assertValid(); JiBXUtils.marshallService(service, ServiceUtils.serviceFile(dirs, identifier)); identifier++; } LOG.info("Processing services..."); Services list = new Services(); for (Service service : services) { org.apache.commons.collections.Predicate predicate = new ControllerTypePredicate(service.getController().getType()); if (CollectionUtils.exists(list.getControllers(), predicate)) { ControllerSummary controller = (ControllerSummary) CollectionUtils.find(list.getControllers(), predicate); controller.addService(service); } else { ControllerSummary controller = new ControllerSummary(service.getController().getType(), service.getController().getJavadoc()); controller.addService(service); list.addController(controller); } } LOG.info("Marshalling services..."); list.assertValid(); JiBXUtils.marshallServices(list, ServiceUtils.servicesFile(dirs)); } /** * Packages and deploys the web-application. * * @throws IOException if an input-output exception occurs. */ private void deploy() throws IOException { LOG.info("Generating jar-archive"); final DirectoryBuilder dirs = new DirectoryBuilder(baseDirectory, outputDirectory); final ConfigCopier cc = new ConfigCopier(dirs); cc.copy(); LOG.debug("Creating properties: " + artifactId + ", " + version + ", " + finalName + ", " + classifier + ", " + scmUrl); cc.createProperties(artifactId, version, finalName, classifier, scmUrl); LOG.debug("Building jar: " + finalName + '-' + classifier); File jar = new JarBuilder(dirs, finalName + '-' + classifier).build(); LOG.info("Deploying " + jar.getName()); if (deployUrl != null && deployUrl.toLowerCase().startsWith("http")) { FileUploader.upload(deployUrl, deployDir, jar); } } /** * Facade method for generating services and the web-application. * * @throws BeansNotFoundException if a bean with an identifier or Java type * can't be found. * @throws CloneNotSupportedException if a data-binder's documentation can't * be cloned. * @throws IOException if an input-output exception occurs. * @throws JavadocNotFoundException if a controller's documentation can't be * found. * @throws JiBXException if a JiBX exception occurs. */ private void build() throws CloneNotSupportedException, IOException, JavadocNotFoundException, JiBXException { javadocs(); services(); deploy(); } /** * {@inheritDoc} */ @Override public void execute() throws MojoExecutionException { try { DocletUtils.initialiseLogging(); if (MavenUtils.WAR_PACKAGING.equalsIgnoreCase(packaging)) { build(); } } catch (final IOException e) { throw new MojoExecutionException(e.getClass().getName() + ": " + e.getMessage(), e); } catch (final JiBXException e) { throw new MojoExecutionException(e.getClass().getName() + ": " + e.getMessage(), e); } catch (final JavadocNotFoundException e) { throw new MojoExecutionException(e.getClass().getName() + ": " + e.getMessage(), e); } catch (final CloneNotSupportedException e) { throw new MojoExecutionException(e.getClass().getName() + ": " + e.getMessage(), e); } catch (final Exception e) { throw new MojoExecutionException(e.getClass().getName() + ": " + e.getMessage(), e); } } /** * Return true if the method name is on the exclude list * * @param method * @return true if the method name is on the exclude list */ private boolean excludeMethod(Method method) { for (String exclude : excludes) { if (method.getName().equalsIgnoreCase(exclude)) { LOG.info("Excluding : " + method); return true; } } return false; } }