org.ajax4jsf.builder.mojo.AssemblyAttachedLibraryMojo.java Source code

Java tutorial

Introduction

Here is the source code for org.ajax4jsf.builder.mojo.AssemblyAttachedLibraryMojo.java

Source

/**
 * License Agreement.
 *
 * Rich Faces - Natural Ajax for Java Server Faces (JSF)
 *
 * Copyright (C) 2007 Exadel, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 */

package org.ajax4jsf.builder.mojo;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.ajax4jsf.builder.config.ParsingException;
import org.ajax4jsf.builder.xml.NamesListComparator;
import org.ajax4jsf.builder.xml.XMLBody;
import org.ajax4jsf.builder.xml.XMLBodyMerge;
import org.ajax4jsf.builder.xml.XPathComparator;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Model;
import org.apache.maven.model.Parent;
import org.apache.maven.model.Resource;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectBuilder;
import org.apache.maven.project.MavenProjectHelper;
import org.apache.maven.project.ProjectBuildingException;
import org.apache.velocity.VelocityContext;
import org.codehaus.plexus.archiver.UnArchiver;
import org.codehaus.plexus.archiver.manager.ArchiverManager;
import org.codehaus.plexus.util.DirectoryScanner;
import org.w3c.dom.Node;

/**
 * This plugin assembly full components library from modules, included in parent
 * project. Steps to create library : 1. Got parent project, and check modules
 * included in them. 2. For every module , build project and got it properties.
 * 3. Resolve module artifact, check for "jar" type and include/exclude
 * criteria. for accepted modules, unpack it to classes directory, and put
 * config files in separate directories. 4. Append unpacked directory to
 * resources, included in result jar. 5. Merge all META-INF/faces-config.xml ,
 * *.tld, *.taglib.xml , resources-config.xml into ones. 6. append dependencies
 * of included projects to this.
 * 
 * @author shura
 * @goal attached
 * @phase package
 * @aggregator
 */
public class AssemblyAttachedLibraryMojo extends AbstractCDKMojo {

    private static final String TEMPLATES_PREFIX = "/templates/";

    private static final String TEMPLATES12_PREFIX = "/templates12/";

    private static final String FACES_CONFIG_TEMPLATE = "faces-config.vm";

    private static final String RESOURCES_CONFIG_TEMPLATE = "resources-config.vm";

    private static final String TLD_TEMPLATE = "tld.vm";

    private static final String TAGLIB_TEMPLATE = "taglib.vm";

    private static final String[] TLD_TAG_NAMES = new String[] { "description", "display-name", "icon",
            "tlib-version", "short-name", "uri", "validator", "listener", "tag", "tag-file", "function",
            "taglib-extension" };

    private static final String[] TAGLIB_TAG_NAMES = new String[] { "library-class", "namespace", "tag",
            "function" };

    /**
     * Used to look up Artifacts in the remote repository.
     * 
     * @component
     */
    private org.apache.maven.artifact.factory.ArtifactFactory factory;

    /**
     * Used to look up Artifacts in the remote repository.
     * 
     * @component
     */
    private org.apache.maven.artifact.resolver.ArtifactResolver resolver;

    /**
     * The local repository.
     * 
     * @parameter expression="${localRepository}"
     */
    private ArtifactRepository localRepository;

    /**
     * To look up Archiver/UnArchiver implementations
     * 
     * @component
     */
    private ArchiverManager archiverManager;

    /**
     * Project builder
     * 
     * @component
     */
    private MavenProjectBuilder mavenProjectBuilder;

    /**
     * Maven ProjectHelper
     * 
     * @component
     */
    private MavenProjectHelper projectHelper;

    /**
     * The reactor projects.
     * 
     * @parameter expression="${reactorProjects}"
     * @required
     * @readonly
     */
    private List reactorProjects;

    /**
     * The reactor projects.
     * 
     * @parameter expression="${project.parent}"
     * @readonly
     */
    private MavenProject parentProject;

    /**
     * The directory for compiled classes.
     * 
     * @parameter expression="${project.build.directory}"
     * @required
     * @readonly
     */
    private File buildDirectory;

    /**
     * The directory for compiled classes.
     * 
     * @parameter expression="${project.build.directory}/pom.xml"
     * @required
     * @readonly
     */
    private File generatedPom;

    /**
     * The directory for compiled classes.
     * 
     * @parameter expression="${project.build.directory}/src"
     * @required
     * @readonly
     */
    private File modulesSrc;

    /**
     * @parameter
     */
    private String templates;

    /**
     * @parameter
     */
    private String includeTld = "META-INF/*.tld";

    /**
     * @parameter
     */
    private String includeTaglib = "META-INF/*.taglib.xml";

    /**
     * @parameter
     */
    private Taglib[] taglibs;

    /**
     * 
     */
    public AssemblyAttachedLibraryMojo() {
        // used for plexus init.
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.apache.maven.plugin.Mojo#execute()
     */
    public void execute() throws MojoExecutionException, MojoFailureException {
        if (null != reactorProjects) {
            getLog().info("Reactor projects");
            for (Iterator iterator = reactorProjects.iterator(); iterator.hasNext();) {
                MavenProject reactor = (MavenProject) iterator.next();
                getLog().info("Project " + reactor.getGroupId() + ":" + reactor.getArtifactId());
            }
        }
        //      assemblyProjects();

    }

    /**
     * @throws MojoFailureException
     * @throws MojoExecutionException
     */
    protected void assemblyProjects() throws MojoFailureException, MojoExecutionException {
        // Parent project not set for a custom lifecycles. Try to load artifact.
        if (null == parentProject || null == parentProject.getFile()) {
            Parent parentModel = project.getModel().getParent();
            if (null != parentModel) {
                String relativePath = parentModel.getRelativePath();
                File parentPom = new File(project.getFile().getAbsoluteFile().getParentFile(), relativePath);
                if (parentPom.isDirectory()) {
                    parentPom = new File(parentPom, "pom.xml");
                }
                if (parentPom.exists()) {
                    try {
                        parentProject = mavenProjectBuilder.build(parentPom, localRepository, null);
                    } catch (ProjectBuildingException e) {
                        throw new MojoFailureException("Error get parent project for a components library");
                    }
                } else {
                    throw new MojoFailureException("Parent project pom file not found for a components library");
                }
            } else {
                throw new MojoFailureException(
                        "Components library project must have parent pom with components modules");
            }

        }
        if (null != parentProject) {
            this.checkLibraryConfig();
            if (null == templates) {
                templates = Library.JSF12.equals(library.getJsfVersion()) ? TEMPLATES12_PREFIX : TEMPLATES_PREFIX;
            }
            Model generatedProject;
            try {
                MavenXpp3Reader reader = new MavenXpp3Reader();
                generatedProject = reader.read(new FileReader(project.getFile()));
            } catch (Exception e1) {
                throw new MojoExecutionException("Unable to read local POM", e1);
            }
            List modules = parentProject.getModules();
            List models = new ArrayList(modules.size());
            Map<String, Dependency> projectsDependencies = new HashMap<String, Dependency>();
            for (Iterator iter = modules.iterator(); iter.hasNext();) {
                String moduleName = (String) iter.next();
                getLog().info("Parent project have module " + moduleName);
                Model model;
                File f = new File(parentProject.getBasedir(), moduleName + "/pom.xml");
                if (f.exists()) {
                    try {
                        model = mavenProjectBuilder.build(f, localRepository, null).getModel();
                    } catch (ProjectBuildingException e) {
                        throw new MojoExecutionException("Unable to read local module-POM", e);
                    }
                } else {
                    getLog().warn("No filesystem module-POM available");

                    model = new Model();
                    model.setName(moduleName);
                    model.setUrl(moduleName);
                }
                if (project.getGroupId().equals(model.getGroupId()) && "jar".equals(model.getPackaging())
                        && (!project.getArtifactId().equals(model.getArtifactId()))) {
                    // TODO - check include/exclude
                    getLog().debug("Project " + model.getName() + " included to library set");
                    List dependencies = model.getDependencies();
                    for (Iterator iterator = dependencies.iterator(); iterator.hasNext();) {
                        Dependency dependency = (Dependency) iterator.next();
                        getLog().debug(dependency.getClass().getName() + " : " + dependency + " with key: "
                                + dependency.getManagementKey());
                        if (!"test".equals(dependency.getScope())) {
                            projectsDependencies.put(dependency.getManagementKey(), dependency);

                        }
                    }
                    models.add(model);
                }
            }
            // Remove modules projects from dependencise
            Set<String> unwanted = new HashSet<String>(projectsDependencies.size());
            for (Iterator iter = models.iterator(); iter.hasNext();) {
                Model model = (Model) iter.next();
                for (Iterator iterator = projectsDependencies.values().iterator(); iterator.hasNext();) {
                    Dependency dependency = (Dependency) iterator.next();
                    if (model.getGroupId().equals(dependency.getGroupId())
                            && model.getArtifactId().equals(dependency.getArtifactId())) {
                        getLog().debug("Remove dependency of library module " + dependency.getManagementKey());
                        unwanted.add(dependency.getManagementKey());
                    }
                }
                // Got module and unpack it to target directory.
                Artifact artifact = factory.createBuildArtifact(model.getGroupId(), model.getArtifactId(),
                        model.getVersion(), model.getPackaging());
                File moduleDir = new File(buildDirectory, model.getArtifactId());
                unpackArtifact(artifact, moduleDir, true);
                artifact = factory.createArtifactWithClassifier(model.getGroupId(), model.getArtifactId(),
                        model.getVersion(), "jar", "sources");
                unpackArtifact(artifact, modulesSrc, false);
            }
            // Add projects dependencies to this project
            projectsDependencies.keySet().removeAll(unwanted);
            generatedProject.getDependencies().addAll(projectsDependencies.values());
            writePom(generatedProject);
            mergeFacesConfig(models);
            File resourcesConfig = new File(outputDirectory, "META-INF/resources-config.xml");
            mergeXML(models, "META-INF/resources-config.xml", RESOURCES_CONFIG_TEMPLATE,
                    "/resource-config/resource", "name/text()", new VelocityContext(), resourcesConfig, null);
            File tld = new File(outputDirectory, "META-INF/" + library.getTaglib().getShortName() + ".tld");
            mergeXML(models, includeTld, TLD_TEMPLATE, "/taglib/tag | /taglib/listener", null,
                    new VelocityContext(), tld, new NamesListComparator(
                            new XPathComparator("name/text()", "listener-class/text()"), TLD_TAG_NAMES));
            File taglib = new File(outputDirectory,
                    "META-INF/" + library.getTaglib().getShortName() + ".taglib.xml");
            mergeXML(models, includeTaglib, TAGLIB_TEMPLATE, "/facelet-taglib/tag | /facelet-taglib/function", null,
                    new VelocityContext(), taglib, new NamesListComparator(
                            new XPathComparator("tag-name/text()", "function-name/text()"), TAGLIB_TAG_NAMES));
        } else {
            throw new MojoFailureException(
                    "Components library project must have parent pom with components modules");
        }
    }

    /**
     * @param models
     * @throws MojoExecutionException
     */
    private void mergeFacesConfig(List models) throws MojoExecutionException {
        StringBuffer config = new StringBuffer();
        for (int i = 0; i < library.getRenderkits().length; i++) {
            Renderkit kit = library.getRenderkits()[i];
            kit.setContent(new StringBuffer());
        }
        for (Iterator iter = models.iterator(); iter.hasNext();) {
            Model model = (Model) iter.next();
            File moduleFacesConfig = new File(buildDirectory, model.getArtifactId() + "/META-INF/faces-config.xml");
            if (moduleFacesConfig.exists()) {
                getLog().info("Process faces-config.xml for module " + model.getArtifactId());
                XMLBody configBody = new XMLBody();
                try {
                    configBody.loadXML(new FileInputStream(moduleFacesConfig));
                    config.append(configBody.getContent("/faces-config/*[name()!=\'render-kit\']"));
                    for (int i = 0; i < library.getRenderkits().length; i++) {
                        Renderkit kit = library.getRenderkits()[i];
                        kit.getContent().append(configBody.getContent(
                                "/faces-config/render-kit[child::render-kit-id='" + kit.getName() + "']/renderer"));
                    }
                } catch (FileNotFoundException e) {
                    throw new MojoExecutionException("Could't read faces-config file", e);
                } catch (ParsingException e) {
                    throw new MojoExecutionException("Error parsing faces-config file", e);
                }
            }
        }
        VelocityContext context = new VelocityContext();
        context.put("content", config.toString());
        context.put("library", library);
        context.put("renderkits", Arrays.asList(library.getRenderkits()));
        try {
            writeParsedTemplate(templates + FACES_CONFIG_TEMPLATE, context,
                    new File(outputDirectory, "META-INF/faces-config.xml"));
        } catch (Exception e) {
            throw new MojoExecutionException("Error to process faces-config template", e);
        }
    }

    /**
     * Merge XML files from extracted models to one in build directory.
     * 
     * @param models
     *            models collected in library.
     * @param filename
     *            relative path to config file in models/output.
     * @param templateName -
     *            name of velocity template for result file.
     * @param commonXpath -
     *            XPath expression fof common part of result file.
     * @param keyXPath - XPath expression for key part of common parts
     * @param keySet - {@link Set} to check for duplicate keys. Must not be null
     * @param context -
     *            Velocity context for template processing.
     * @throws MojoExecutionException
     */
    private void mergeXML(List<Model> models, String filename, String templateName, String commonXpath,
            String keyXPath, VelocityContext context, File target, Comparator<Node> comparator)
            throws MojoExecutionException {
        StringBuilder content = new StringBuilder();
        XMLBodyMerge bodyMerge = new XMLBodyMerge(commonXpath, keyXPath);
        List<XMLBody> xmls = new ArrayList<XMLBody>(models.size());
        String[] split = filename.split(",");
        for (Iterator<Model> iter = models.iterator(); iter.hasNext();) {
            Model model = iter.next();
            File moduleDir = new File(buildDirectory, model.getArtifactId());
            DirectoryScanner ds = new DirectoryScanner();
            ds.setFollowSymlinks(true);
            ds.setBasedir(moduleDir);
            ds.setIncludes(split);
            ds.addDefaultExcludes();
            ds.scan();
            String[] files = ds.getIncludedFiles();
            for (int i = 0; i < files.length; i++) {
                File moduleFacesConfig = new File(moduleDir, files[i]);
                getLog().info("Process " + files[i] + " for module " + model.getArtifactId());
                XMLBody configBody = new XMLBody();
                try {
                    configBody.loadXML(new FileInputStream(moduleFacesConfig));
                    xmls.add(configBody);
                    bodyMerge.add(configBody);
                } catch (FileNotFoundException e) {
                    throw new MojoExecutionException("Could't read  file " + moduleFacesConfig.getPath(), e);
                } catch (ParsingException e) {
                    throw new MojoExecutionException("Error parsing config file " + moduleFacesConfig.getPath(), e);
                }

            }
        }
        if (comparator != null) {
            bodyMerge.sort(comparator);
        }

        try {
            content.append(bodyMerge.getContent());
        } catch (Exception e) {
            // TODO: handle exception
        }

        if (xmls.size() > 0) {
            context.put("content", content);
            context.put("library", library);
            context.put("models", models);
            context.put("xmls", xmls);
            try {
                writeParsedTemplate(templates + templateName, context, target);
            } catch (Exception e) {
                throw new MojoExecutionException(
                        "Error to process template " + templateName + " for files " + filename, e);
            }

        }
    }

    private void unpackArtifact(Artifact artifact, File moduleDir, boolean isResource)
            throws MojoExecutionException {
        try {
            resolver.resolve(artifact, Collections.EMPTY_LIST, localRepository);
            unpack(artifact.getFile(), moduleDir);
            if (isResource) {
                Resource resource = new Resource();
                resource.setDirectory(moduleDir.getPath());
                resource.addExclude("META-INF/faces-config.xml");
                resource.addExclude("META-INF/resources-config.xml");
                resource.addExclude("META-INF/*.taglib.xml");
                resource.addExclude("META-INF/*.tld");
                project.addResource(resource);

            }
        } catch (ArtifactResolutionException e) {
            getLog().error("Error with resolve artifact " + artifact, e);
        } catch (ArtifactNotFoundException e) {
            getLog().error("Not found artifact " + artifact, e);
        }
    }

    private List<MavenProject> populateReactorProjects() {
        List<MavenProject> projects = new ArrayList<MavenProject>();
        if (reactorProjects != null && reactorProjects.size() > 1) {
            Iterator reactorItr = reactorProjects.iterator();

            while (reactorItr.hasNext()) {
                MavenProject reactorProject = (MavenProject) reactorItr.next();

                if (reactorProject != null && reactorProject.getParent() != null
                        && project.getArtifactId().equals(reactorProject.getParent().getArtifactId())) {
                    String name = reactorProject.getGroupId() + ":" + reactorProject.getArtifactId();
                    getLog().info("Have reactor project with name " + name);
                    projects.add(reactorProject);
                }
            }
        }
        return projects;
    }

    /**
     * Unpacks the archive file.
     * 
     * @param file
     *            File to be unpacked.
     * @param location
     *            Location where to put the unpacked files.
     */
    private void unpack(File file, File location) throws MojoExecutionException {

        getLog().debug("Unpack file " + file.getAbsolutePath() + " to: " + location.getAbsolutePath());
        try {
            location.mkdirs();

            UnArchiver unArchiver;

            unArchiver = archiverManager.getUnArchiver(file);

            unArchiver.setSourceFile(file);

            unArchiver.setDestDirectory(location);

            unArchiver.setOverwrite(true);

            unArchiver.extract();

        } catch (Exception e) {
            throw new MojoExecutionException(
                    "Error unpacking file: " + file + " to: " + location + "\r\n" + e.toString(), e);
        }
    }

    private void writePom(Model pom) throws MojoExecutionException {
        MavenXpp3Writer pomWriter = new MavenXpp3Writer();
        try {
            FileWriter out = new FileWriter(generatedPom);
            pomWriter.write(out, pom);
        } catch (IOException e) {
            throw new MojoExecutionException("Error for write generated pom", e);
        }

    }

    /**
     * @return the archiverManager
     */
    public ArchiverManager getArchiverManager() {
        return this.archiverManager;
    }

    /**
     * @param archiverManager
     *            the archiverManager to set
     */
    public void setArchiverManager(ArchiverManager archiverManager) {
        this.archiverManager = archiverManager;
    }

    /**
     * @return the buildDirectory
     */
    public File getBuildDirectory() {
        return this.buildDirectory;
    }

    /**
     * @param buildDirectory
     *            the buildDirectory to set
     */
    public void setBuildDirectory(File buildDirectory) {
        this.buildDirectory = buildDirectory;
    }

    /**
     * @return the factory
     */
    public org.apache.maven.artifact.factory.ArtifactFactory getFactory() {
        return this.factory;
    }

    /**
     * @param factory
     *            the factory to set
     */
    public void setFactory(org.apache.maven.artifact.factory.ArtifactFactory factory) {
        this.factory = factory;
    }

    /**
     * @return the localRepository
     */
    public ArtifactRepository getLocalRepository() {
        return this.localRepository;
    }

    /**
     * @param localRepository
     *            the localRepository to set
     */
    public void setLocalRepository(ArtifactRepository localRepository) {
        this.localRepository = localRepository;
    }

    /**
     * @return the mavenProjectBuilder
     */
    public MavenProjectBuilder getMavenProjectBuilder() {
        return this.mavenProjectBuilder;
    }

    /**
     * @param mavenProjectBuilder
     *            the mavenProjectBuilder to set
     */
    public void setMavenProjectBuilder(MavenProjectBuilder mavenProjectBuilder) {
        this.mavenProjectBuilder = mavenProjectBuilder;
    }

    /**
     * @return the parentProject
     */
    public MavenProject getParentProject() {
        return this.parentProject;
    }

    /**
     * @param parentProject
     *            the parentProject to set
     */
    public void setParentProject(MavenProject parentProject) {
        this.parentProject = parentProject;
    }

    /**
     * @return the reactorProjects
     */
    public List getReactorProjects() {
        return this.reactorProjects;
    }

    /**
     * @param reactorProjects
     *            the reactorProjects to set
     */
    public void setReactorProjects(List reactorProjects) {
        this.reactorProjects = reactorProjects;
    }

    /**
     * @return the resolver
     */
    public org.apache.maven.artifact.resolver.ArtifactResolver getResolver() {
        return this.resolver;
    }

    /**
     * @param resolver
     *            the resolver to set
     */
    public void setResolver(org.apache.maven.artifact.resolver.ArtifactResolver resolver) {
        this.resolver = resolver;
    }

}