Package org.lilyproject.runtime.tools.plugin.genclassloader

Source Code of org.lilyproject.runtime.tools.plugin.genclassloader.ClassloaderMojo

/*
* Copyright 2013 NGDATA nv
* Copyright 2008 Outerthought bvba and Schaubroeck nv
*
* 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.lilyproject.runtime.tools.plugin.genclassloader;

import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper;
import org.apache.maven.shared.artifact.filter.collection.ArtifactFilterException;
import org.apache.maven.shared.artifact.filter.collection.ArtifactIdFilter;
import org.apache.maven.shared.artifact.filter.collection.ClassifierFilter;
import org.apache.maven.shared.artifact.filter.collection.FilterArtifacts;
import org.apache.maven.shared.artifact.filter.collection.GroupIdFilter;
import org.apache.maven.shared.artifact.filter.collection.ScopeFilter;
import org.apache.maven.shared.artifact.filter.collection.TransitivityFilter;
import org.apache.maven.shared.artifact.filter.collection.TypeFilter;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;

/**
* Writes a listing of dependencies in a specific format to a predefined file.
*
* @goal generate
* @requiresDependencyResolution runtime
* @description Genenerate a Lily Runtime classloader file for a module.
*/
public class ClassloaderMojo extends AbstractMojo {

    /**
     * @parameter default-value="${project}"
     * @required
     * @readonly
     */
    private MavenProject project;

    /**
     * Helper class to assist in attaching artifacts to the project instance. project-helper instance, used to
     * make addition of resources simpler.
     *
     * @component
     */
    private MavenProjectHelper projectHelper;

    /**
     * @parameter default-value="src/main/lily"
     */
    private String templateDirectory;

    /**
     * @parameter default-value="classloader-template.xml"
     */
    private String templateFileName;

    /**
     * Where should the file end up?
     *
     * @parameter default-value="${project.build.directory}/classes/LILY-INF"
     */
    private String targetDirectory;

    /**
     * Filename to use.
     *
     * @parameter default-value="classloader.xml"
     */
    private String targetFileName;

    /**
     * Indicates whether the project artifact itself should also be included
     *
     * @parameter default-value="false"
     * @optional
     */
    protected boolean includeSelf;

    /**
     * If we should exclude transitive dependencies
     *
     * @since 2.0
     * @optional
     * @parameter property="excludeTransitive" default-value="false"
     */
    protected boolean excludeTransitive;

    /**
     * Comma Separated list of Types to include. Empty String indicates include everything (default).
     *
     * @since 2.0
     * @parameter property="includeTypes" default-value=""
     * @optional
     */
    protected String includeTypes;

    /**
     * Comma Separated list of Types to exclude. Empty String indicates don't exclude anything (default).
     * Ignored if includeTypes is used.
     *
     * @since 2.0
     * @parameter property="excludeTypes" default-value=""
     * @optional
     */
    protected String excludeTypes;

    /**
     * Scope to include. An Empty string indicates all scopes (default).
     *
     * @since 2.0
     * @parameter property="includeScope" default-value="runtime"
     * @optional
     */
    protected String includeScope;

    /**
     * Scope to exclude. An Empty string indicates no scopes (default). Ignored if includeScope is used.
     *
     * @since 2.0
     * @parameter property="excludeScope" default-value="provided"
     * @optional
     */
    protected String excludeScope;

    /**
     * Comma Separated list of Classifiers to include. Empty String indicates include everything (default).
     *
     * @since 2.0
     * @parameter property="includeClassifiers" default-value=""
     * @optional
     */
    protected String includeClassifiers;

    /**
     * Comma Separated list of Classifiers to exclude. Empty String indicates don't exclude anything
     * (default). Ignored if includeClassifiers is used.
     *
     * @since 2.0
     * @parameter property="excludeClassifiers" default-value=""
     * @optional
     */
    protected String excludeClassifiers;

    /**
     * Comma Seperated list of Artifact names too exclude. Ignored if includeArtifacts is used.
     *
     * @since 2.0
     * @optional
     * @parameter property="excludeArtifactIds" default-value=""
     */
    protected String excludeArtifactIds;

    /**
     * Comma Seperated list of Artifact names to include.
     *
     * @since 2.0
     * @optional
     * @parameter property="includeArtifactIds" default-value=""
     */
    protected String includeArtifactIds;

    /**
     * Comma Seperated list of GroupId Names to exclude. Ignored if includeGroupsIds is used.
     *
     * @since 2.0
     * @optional
     * @parameter property="excludeGroupIds" default-value=""
     */
    protected String excludeGroupIds;

    /**
     * Comma Seperated list of GroupIds to include.
     *
     * @since 2.0
     * @optional
     * @parameter property="includeGroupIds" default-value=""
     */
    protected String includeGroupIds;

    /**
     * The dependencies to list in file.
     */
    private Collection<Artifact> dependenciesToList;

    /**
     * Destination file.
     */
    private File projectDescriptorFile;

    /**
     * Mapping of artifacts with their corresponding dom-element.
     */
    private Map<Entry, Element> entryMap = new HashMap<Entry, Element>();

    /**
     * The value of the share-self attribute in the classloader template, if specified.
     */
    private String shareSelf = null;

    public void execute() throws MojoExecutionException, MojoFailureException {
        try {
            getLog().info("Creating Dependency List...");
            doDependencyResolution();
            if (dependenciesToList.size() > 0) {
                createDestinationFile();
                // lookup template file
                File file = new File(project.getBasedir(), templateDirectory + "/" + templateFileName);
                getLog().debug("Looking for classloader template in location '" + file + "'.");
                createDependencyListing(file);
                getLog().info(
                        "Wrote " + dependenciesToList.size() + " dependencies to file "
                                + projectDescriptorFile.getPath());
                projectHelper.attachArtifact(project, "xml", "dependencylist", projectDescriptorFile);
            }
        } catch (Exception ex) {
            throw new MojoExecutionException("Error while creating dependency list.", ex);
        }
    }

    private void createDestinationFile() throws IOException {
        File targetDirectoryFile = new File(targetDirectory);
        if (!targetDirectoryFile.exists()) {
            targetDirectoryFile.mkdirs();
        }

        projectDescriptorFile = new File(targetDirectoryFile, targetFileName);
        if (!projectDescriptorFile.exists()) {
            projectDescriptorFile.createNewFile();
        }
    }

    /**
     * This method uses a Filtering technique as showed by the maven-dependency-plugin. It allows for
     * including/excluding artifacts in a number of ways.
     *
     * @throws MojoExecutionException
     */
    @SuppressWarnings("unchecked")
    private void doDependencyResolution() throws ArtifactFilterException {
        FilterArtifacts filter = new FilterArtifacts();

        filter.addFilter(new TransitivityFilter(project.getDependencyArtifacts(), this.excludeTransitive));
        filter.addFilter(new ScopeFilter(this.includeScope, this.excludeScope));
        filter.addFilter(new TypeFilter(this.includeTypes, this.excludeTypes));
        filter.addFilter(new ClassifierFilter(this.includeClassifiers, this.excludeClassifiers));
        filter.addFilter(new GroupIdFilter(this.includeGroupIds, this.excludeGroupIds));
        filter.addFilter(new ArtifactIdFilter(this.includeArtifactIds, this.excludeArtifactIds));

        // start with all artifacts.
        Set<Artifact> artifacts = project.getArtifacts();

        if (includeSelf) {
            artifacts.add(project.getArtifact());
        }

        // perform filtering
        dependenciesToList = filter.filter(artifacts);
    }

    /**
     * Create a project file containing the dependencies.
     *
     * @throws IOException
     * @throws SAXException
     */
    private void createDependencyListing(File classloaderTemplate) throws IOException, SAXException {
        if(classloaderTemplate != null && classloaderTemplate.exists()) {
            getLog().info("Found classloader template, trying to parse it...");
            parseClassloaderTemplate(classloaderTemplate);
        }
        final boolean hasEntries = entryMap.size() > 0;
        // fill in file with all dependencies
        FileOutputStream fos = new FileOutputStream(projectDescriptorFile);
        TransformerHandler ch = null;
        TransformerFactory factory = TransformerFactory.newInstance();
        if (factory.getFeature(SAXTransformerFactory.FEATURE)) {
            try {
                ch = ((SAXTransformerFactory) factory).newTransformerHandler();
                // set properties
                Transformer serializer = ch.getTransformer();
                serializer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
                serializer.setOutputProperty(OutputKeys.INDENT, "yes");
                serializer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
                // set result
                Result result = new StreamResult(fos);
                ch.setResult(result);
                // start file generation
                ch.startDocument();
                AttributesImpl atts = new AttributesImpl();
                if (this.shareSelf != null) {
                    atts.addAttribute("", "share-self", "share-self", "CDATA", this.shareSelf);
                }
                ch.startElement("", "classloader", "classloader", atts);
                atts.clear();
                ch.startElement("", "classpath", "classpath", atts);
                SortedSet<Artifact> sortedArtifacts = new TreeSet<Artifact>(dependenciesToList);
                Entry entry;
                String entryShare;
                String entryVersion;
                for (Artifact artifact : sortedArtifacts) {
                    atts.addAttribute("", "groupId", "groupId", "CDATA", artifact.getGroupId());
                    atts.addAttribute("", "artifactId", "artifactId", "CDATA", artifact.getArtifactId());
                    if (artifact.getClassifier() != null) {
                        atts.addAttribute("", "classifier", "classifier", "CDATA", artifact.getClassifier());
                    }
                    if (!artifact.getGroupId().equals("org.lilyproject")) {
                        atts.addAttribute("", "version", "version", "CDATA", artifact.getBaseVersion());
                    }
                    entry = new Entry(artifact.getGroupId(), artifact.getArtifactId(), artifact.getClassifier());
                    if (hasEntries && entryMap.containsKey(entry)) {
                        entryVersion = extractAttVal(entryMap.get(entry).getAttributes(), "version");
                        entryShare = extractAttVal(entryMap.get(entry).getAttributes(), "share");
                        entryMap.remove(entry);
                        if (entryVersion != null && !entryVersion.equals("") && !entryVersion.equals(artifact.getBaseVersion())) {
                            getLog().warn("version conflict between entry in template and artifact on classpath for " + entry);
                        }
                        if(entryShare != null) {
                            atts.addAttribute("", "", "share", "CDATA", entryShare);
                        }
                    } else {
                        // atts.addAttribute("", "", "share", "CDATA", SHARE_DEFAULT);
                    }
                    ch.startElement("", "artifact", "artifact", atts);
                    ch.endElement("", "artifact", "artifact");
                    atts.clear();
                }
                ch.endElement("", "classpath", "classpath");
                ch.endElement("", "classloader", "classloader");
                ch.endDocument();
                fos.close();
                if(entryMap.size() > 0) {
                    getLog().warn("Classloader template contains entries that could not be resolved.");
                }
            } catch (TransformerConfigurationException ex) {
                ex.printStackTrace();
                throw new SAXException("Unable to get a TransformerHandler.");
            }
        } else {
            throw new RuntimeException("Could not load SAXTransformerFactory.");
        }
    }

    private void parseClassloaderTemplate(File file) {
        DOMResult domResult = null;
        Transformer transformer = null;
        try {
            Source xmlSource = new StreamSource(file);
            TransformerFactory tfFactory = TransformerFactory.newInstance();
            if (tfFactory.getFeature(DOMResult.FEATURE)) {
                transformer = tfFactory.newTransformer();
                domResult = new DOMResult();
                transformer.transform(xmlSource, domResult);
            }
        } catch (TransformerException ex) {
            throw new RuntimeException("Error parsing file '" + file + "'.");
        }
        if (domResult != null && transformer != null) {
            Document docu = (Document) domResult.getNode();
            Element classpath;
            NodeList list;

            String shareSelf = docu.getDocumentElement().getAttribute("share-self").trim();
            if (shareSelf.length() > 0) {
                this.shareSelf = shareSelf;
            }

            try {
                classpath = (Element) docu.getElementsByTagName("classpath").item(0);
                list = classpath.getElementsByTagName("artifact");
            } catch (NullPointerException npex) {
                throw new RuntimeException("Classloader template is invalid.");
            }
            NamedNodeMap map;
            Entry entry;
            String groupId;
            String classifier;
            String artifactId;
            Element domArtifact;
            for (int i = 0; i < list.getLength(); i++) {
                domArtifact = (Element) list.item(i);
                map = domArtifact.getAttributes();
                groupId = extractAttVal(map, "groupId");
                artifactId = extractAttVal(map, "artifactId");
                classifier = extractAttVal(map, "classifier");
                entry = new Entry(groupId, artifactId, classifier);
                entryMap.put(entry, domArtifact);
            }
        }

    }

    private String extractAttVal(NamedNodeMap map, String name) {
        Node node = map.getNamedItem(name);
        return (node != null) ? node.getNodeValue() : null;
    }

    private class Entry {
        protected String groupId;
        protected String artifactId;
        protected String classifier;

        public Entry(String groupId, String artifactId, String classifier) {
            this.groupId = groupId;
            this.artifactId = artifactId;
            this.classifier = classifier;
        }

        /**
         * @see java.lang.Object#hashCode()
         */
        public int hashCode() {
            return new HashCodeBuilder(715542779, 122039963).append(this.groupId).append(this.artifactId)
                    .append(this.classifier).toHashCode();
        }

        /**
         * @see java.lang.Object#equals(Object)
         */
        public boolean equals(Object object) {
            if (!(object instanceof Entry)) {
                return false;
            }
            Entry rhs = (Entry) object;
            boolean equal = true;
            if (!this.groupId.equals(rhs.groupId)) {
                equal = false;
            } else if (!this.artifactId.equals(rhs.artifactId)) {
                equal = false;
            } else if (!ObjectUtils.equals(this.classifier, rhs.classifier)) {
                equal = false;
            }

            return equal;
        }

        @Override
        public String toString() {
            return "artifact(" + groupId + "," + artifactId + "," + classifier + ")";
        }

    }

}
TOP

Related Classes of org.lilyproject.runtime.tools.plugin.genclassloader.ClassloaderMojo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.