com.evolveum.midpoint.tools.schemadist.SchemaDistMojo.java Source code

Java tutorial

Introduction

Here is the source code for com.evolveum.midpoint.tools.schemadist.SchemaDistMojo.java

Source

/*
 * Copyright (c) 2014 Evolveum
 *
 * 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.
 * 
 * This file is inspired and uses minor parts of the maven-dependency-plugin by Brian Fox. 
 */

package com.evolveum.midpoint.tools.schemadist;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactoryConfigurationError;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.factory.ArtifactFactory;
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.artifact.resolver.ArtifactResolver;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.artifact.versioning.VersionRange;
import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProjectHelper;
import org.apache.xml.resolver.Catalog;
import org.apache.xml.resolver.CatalogManager;
import org.codehaus.plexus.archiver.ArchiverException;
import org.codehaus.plexus.archiver.UnArchiver;
import org.codehaus.plexus.archiver.manager.ArchiverManager;
import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
import org.codehaus.plexus.components.io.fileselectors.IncludeExcludeFileSelector;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.evolveum.midpoint.util.DOMUtil;

/**
 * @goal schemadist
 * @requiresDependencyResolution compile
 * @phase package
 */
public class SchemaDistMojo extends AbstractMojo {

    /**
    * @parameter
    */
    private String includes;

    /**
     * @parameter
     */
    private String excludes;

    /**
     * @parameter default-value="${project.build.directory}/schemadist" required=true
     */
    private File outputDirectory;

    /**
     * @parameter default-value="${project.build.directory}/schemadist-work" required=true
     */
    private File workDirectory;

    /**
     * @parameter
     */
    private List<ArtifactItem> artifactItems;

    /**
    * @parameter default-value="true" required=true
    */
    private boolean translateSchemaLocation;

    /**
     * @parameter default-value="src/main/schemadoc/resources"
     */
    private File resourcesDir;

    /** @parameter default-value="${project}" */
    private org.apache.maven.project.MavenProject project;

    /** @parameter default-value="${localRepository}" */
    private ArtifactRepository local;

    /** @parameter default-value="${project.remoteArtifactRepositories}" */
    protected List<ArtifactRepository> remoteRepos;

    /**
     * @component
     */
    private ArtifactFactory factory;

    /**
     * @component
     */
    private ArtifactResolver resolver;

    /**
     * @component
     */
    private ArchiverManager archiverManager;

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

    private void processArtifactItems() throws MojoExecutionException, InvalidVersionSpecificationException {
        for (ArtifactItem artifactItem : artifactItems) {
            if (StringUtils.isEmpty(artifactItem.getVersion())) {
                fillMissingArtifactVersion(artifactItem);
            }
            artifactItem.setArtifact(getArtifact(artifactItem));
        }
    }

    private void fillMissingArtifactVersion(ArtifactItem artifactItem) throws MojoExecutionException {
        List<Dependency> deps = project.getDependencies();
        List<Dependency> depMngt = project.getDependencyManagement() == null ? Collections.<Dependency>emptyList()
                : project.getDependencyManagement().getDependencies();

        if (!findDependencyVersion(artifactItem, deps, false)
                && (project.getDependencyManagement() == null
                        || !findDependencyVersion(artifactItem, depMngt, false))
                && !findDependencyVersion(artifactItem, deps, true) && (project.getDependencyManagement() == null
                        || !findDependencyVersion(artifactItem, depMngt, true))) {
            throw new MojoExecutionException("Unable to find artifact version of " + artifactItem.getGroupId() + ":"
                    + artifactItem.getArtifactId()
                    + " in either dependency list or in project's dependency management.");
        }
    }

    private boolean findDependencyVersion(ArtifactItem artifact, List<Dependency> dependencies,
            boolean looseMatch) {
        for (Dependency dependency : dependencies) {
            if (StringUtils.equals(dependency.getArtifactId(), artifact.getArtifactId())
                    && StringUtils.equals(dependency.getGroupId(), artifact.getGroupId())
                    && (looseMatch || StringUtils.equals(dependency.getClassifier(), artifact.getClassifier()))
                    && (looseMatch || StringUtils.equals(dependency.getType(), artifact.getType()))) {
                artifact.setVersion(dependency.getVersion());
                return true;
            }
        }

        return false;
    }

    protected Artifact getArtifact(ArtifactItem artifactItem)
            throws MojoExecutionException, InvalidVersionSpecificationException {
        Artifact artifact;

        VersionRange vr = VersionRange.createFromVersionSpec(artifactItem.getVersion());

        if (StringUtils.isEmpty(artifactItem.getClassifier())) {
            artifact = factory.createDependencyArtifact(artifactItem.getGroupId(), artifactItem.getArtifactId(), vr,
                    artifactItem.getType(), null, Artifact.SCOPE_COMPILE);
        } else {
            artifact = factory.createDependencyArtifact(artifactItem.getGroupId(), artifactItem.getArtifactId(), vr,
                    artifactItem.getType(), artifactItem.getClassifier(), Artifact.SCOPE_COMPILE);
        }

        try {
            resolver.resolve(artifact, remoteRepos, local);
        } catch (ArtifactResolutionException | ArtifactNotFoundException e) {
            throw new MojoExecutionException("Error resolving artifact " + artifact, e);
        }

        return artifact;
    }

    public void execute() throws MojoExecutionException, MojoFailureException {
        getLog().info("SchemaDist plugin started");

        try {
            processArtifactItems();
        } catch (InvalidVersionSpecificationException e) {
            handleFailure(e);
        }
        final File outDir = initializeOutDir(outputDirectory);

        CatalogManager catalogManager = new CatalogManager();
        catalogManager.setVerbosity(999);

        for (ArtifactItem artifactItem : artifactItems) {
            Artifact artifact = artifactItem.getArtifact();
            getLog().info("SchemaDist unpacking artifact " + artifact);
            File workDir = new File(workDirectory, artifact.getArtifactId());
            initializeOutDir(workDir);
            artifactItem.setWorkDir(workDir);
            unpack(artifactItem, workDir);

            if (translateSchemaLocation) {
                String catalogPath = artifactItem.getCatalog();
                File catalogFile = new File(workDir, catalogPath);
                if (!catalogFile.exists()) {
                    throw new MojoExecutionException("No catalog file " + catalogPath + " in artifact " + artifact);
                }
                Catalog catalog = new Catalog(catalogManager);
                catalog.setupReaders();
                try {
                    // UGLY HACK. On Windows, file names like d:\abc\def\catalog.xml eventually get treated very strangely
                    // (resulting in names like "file:<current-working-dir>d:\abc\def\catalog.xml" that are obviously wrong)
                    // Prefixing such names with "file:/" helps.
                    String prefix;
                    if (catalogFile.isAbsolute() && !catalogFile.getPath().startsWith("/")) {
                        prefix = "/";
                    } else {
                        prefix = "";
                    }
                    String fileName = "file:" + prefix + catalogFile.getPath();
                    getLog().debug("Calling parseCatalog with: " + fileName);
                    catalog.parseCatalog(fileName);
                } catch (MalformedURLException e) {
                    throw new MojoExecutionException(
                            "Error parsing catalog file " + catalogPath + " in artifact " + artifact, e);
                } catch (IOException e) {
                    throw new MojoExecutionException(
                            "Error parsing catalog file " + catalogPath + " in artifact " + artifact, e);
                }
                artifactItem.setResolveCatalog(catalog);
            }
        }

        for (ArtifactItem artifactItem : artifactItems) {
            Artifact artifact = artifactItem.getArtifact();
            getLog().info("SchemaDist processing artifact " + artifact);
            final File workDir = artifactItem.getWorkDir();
            FileVisitor<Path> fileVisitor = new FileVisitor<Path>() {
                @Override
                public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                    // nothing to do
                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult visitFile(Path filePath, BasicFileAttributes attrs) throws IOException {
                    String fileName = filePath.getFileName().toString();
                    if (fileName.endsWith(".xsd")) {
                        getLog().debug("Processing file " + filePath);
                        try {
                            processXsd(filePath, workDir, outDir);
                        } catch (MojoExecutionException | MojoFailureException e) {
                            throw new RuntimeException(e.getMessage(), e);
                        }
                    } else if (fileName.endsWith(".wsdl")) {
                        getLog().debug("Processing file " + filePath);
                        try {
                            processWsdl(filePath, workDir, outDir);
                        } catch (MojoExecutionException | MojoFailureException e) {
                            throw new RuntimeException(e.getMessage(), e);
                        }
                    } else {
                        getLog().debug("Skipping file " + filePath);
                    }
                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
                    return FileVisitResult.TERMINATE;
                }

                @Override
                public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                    // nothing to do
                    return FileVisitResult.CONTINUE;
                }
            };
            try {
                Files.walkFileTree(workDir.toPath(), fileVisitor);
            } catch (IOException e) {
                throw new MojoExecutionException("Error processing files of artifact " + artifact, e);
            }

        }
        getLog().info("SchemaDist plugin finished");
    }

    private void processXsd(Path filePath, File workDir, File outDir)
            throws MojoExecutionException, MojoFailureException {
        Document dom = DOMUtil.parseFile(filePath.toFile());
        Element rootElement = DOMUtil.getFirstChildElement(dom);
        if (translateSchemaLocation) {
            processXsdElement(rootElement, filePath, workDir, outDir);
        }
        serializeXml(dom, filePath, workDir, outDir);
    }

    private void serializeXml(Document dom, Path filePath, File workDir, File outDir)
            throws MojoFailureException, MojoExecutionException {
        Path fileRelPath = workDir.toPath().relativize(filePath);
        File outFile = new File(outDir, fileRelPath.toString());
        initializeOutDir(outFile.getParentFile());
        try {
            DOMUtil.serializeDOMToFile(dom, outFile);
        } catch (TransformerFactoryConfigurationError | TransformerException e) {
            throw new MojoExecutionException(
                    "Error serializing modified file " + fileRelPath + " to XML: " + e.getMessage(), e);
        }
    }

    private void processXsdElement(Element rootElement, Path filePath, File workDir, File outDir)
            throws MojoExecutionException, MojoFailureException {
        List<Element> importElements = DOMUtil.getChildElements(rootElement, DOMUtil.XSD_IMPORT_ELEMENT);
        for (Element importElement : importElements) {
            String namespace = DOMUtil.getAttribute(importElement, DOMUtil.XSD_ATTR_NAMESPACE);
            String schemaLocation;
            try {
                schemaLocation = resolveSchemaLocation(namespace, filePath, workDir);
            } catch (IOException e) {
                throw new MojoExecutionException(
                        "Error resolving namespace " + namespace + " in file " + filePath + ": " + e.getMessage(),
                        e);
            }
            importElement.setAttribute(DOMUtil.XSD_ATTR_SCHEMA_LOCATION.getLocalPart(), schemaLocation);
        }
    }

    private void processWsdl(Path filePath, File workDir, File outDir)
            throws MojoExecutionException, MojoFailureException {
        Document dom = DOMUtil.parseFile(filePath.toFile());

        if (translateSchemaLocation) {
            Element rootElement = DOMUtil.getFirstChildElement(dom);
            List<Element> importElements = DOMUtil.getChildElements(rootElement, DOMUtil.WSDL_IMPORT_ELEMENT);
            for (Element importElement : importElements) {
                String namespace = DOMUtil.getAttribute(importElement, DOMUtil.WSDL_ATTR_NAMESPACE);
                String schemaLocation;
                try {
                    schemaLocation = resolveSchemaLocation(namespace, filePath, workDir);
                } catch (IOException e) {
                    throw new MojoExecutionException("Error resolving namespace " + namespace + " in file "
                            + filePath + ": " + e.getMessage(), e);
                }
                importElement.setAttribute(DOMUtil.WSDL_ATTR_SCHEMA_LOCATION.getLocalPart(), schemaLocation);
            }

            List<Element> typesElements = DOMUtil.getChildElements(rootElement, DOMUtil.WSDL_TYPES_ELEMENT);
            for (Element typesElement : typesElements) {
                processXsdElement(DOMUtil.getFirstChildElement(typesElement), filePath, workDir, outDir);
            }
        }

        serializeXml(dom, filePath, workDir, outDir);
    }

    private String resolveSchemaLocation(String namespace, Path filePath, File workDir)
            throws MojoExecutionException, IOException {
        for (ArtifactItem artifactItem : artifactItems) {
            Catalog catalog = artifactItem.getResolveCatalog();
            String publicId = namespace;
            if (publicId.endsWith("#")) {
                publicId = publicId.substring(0, publicId.length() - 1);
            }
            String resolvedString = catalog.resolveEntity(filePath.toString(), publicId, publicId);
            if (resolvedString != null) {
                getLog().debug("-------------------");
                getLog().debug(
                        "Resolved namespace " + namespace + " to " + resolvedString + " using catalog " + catalog);
                URL resolvedUrl = new URL(resolvedString);
                String resolvedPathString = resolvedUrl.getPath();
                Path resolvedPath = new File(resolvedPathString).toPath();
                Path workDirPath = workDir.toPath();

                Path resolvedRelativeToCatalogWorkdir = artifactItem.getWorkDir().toPath().relativize(resolvedPath);
                Path fileRelativeToWorkdir = workDirPath.relativize(filePath);

                getLog().debug("workDirPath: " + workDirPath);
                getLog().debug("resolvedRelativeToCatalogWorkdir: " + resolvedRelativeToCatalogWorkdir
                        + ",  fileRelativeToWorkdir: " + fileRelativeToWorkdir);

                Path relativePath = fileRelativeToWorkdir.getParent().relativize(resolvedRelativeToCatalogWorkdir);
                getLog().debug("Rel: " + relativePath);
                return relativePath.toString();
            }
        }
        throw new MojoExecutionException(
                "Cannot resolve namespace " + namespace + " in file " + filePath + " using any of the catalogs");
    }

    private File initializeOutDir(File dir) throws MojoFailureException {
        getLog().info("Output dir: " + dir);
        if (dir.exists() && !dir.isDirectory()) {
            throw new MojoFailureException("Output directory is not a directory: " + dir);
        }
        if (dir.exists() && !dir.canWrite()) {
            throw new MojoFailureException("Output directory is not writable: " + dir);
        }
        dir.mkdirs();
        return dir;
    }

    private void unpack(ArtifactItem artifactItem, File destDir) throws MojoExecutionException {
        Artifact artifact = artifactItem.getArtifact();
        File file = artifact.getFile();
        if (file == null) {
            throw new MojoExecutionException("No file for artifact " + artifact);
        }
        if (file.isDirectory()) {
            try {
                FileUtils.copyDirectory(file, destDir);
            } catch (IOException e) {
                throw new MojoExecutionException(
                        "Error copying directory " + file + " to " + destDir + ": " + e.getMessage(), e);
            }
        } else {
            try {
                UnArchiver unArchiver = archiverManager.getUnArchiver(artifact.getType());
                unArchiver.setSourceFile(file);
                unArchiver.setDestDirectory(destDir);

                if (StringUtils.isNotEmpty(excludes) || StringUtils.isNotEmpty(includes)) {
                    // Create the selectors that will filter
                    // based on include/exclude parameters
                    // MDEP-47
                    IncludeExcludeFileSelector[] selectors = new IncludeExcludeFileSelector[] {
                            new IncludeExcludeFileSelector() };

                    if (StringUtils.isNotEmpty(excludes)) {
                        selectors[0].setExcludes(excludes.split(","));
                    }

                    if (StringUtils.isNotEmpty(includes)) {
                        selectors[0].setIncludes(includes.split(","));
                    }

                    unArchiver.setFileSelectors(selectors);
                }

                unArchiver.extract();
            } catch (ArchiverException | NoSuchArchiverException e) {
                throw new MojoExecutionException(
                        "Error unpacking file: " + file + " to: " + destDir + "\r\n" + e.toString(), e);
            }
        }
    }

    private void handleFailure(Exception e) throws MojoFailureException {
        e.printStackTrace();
        throw new MojoFailureException(e.getMessage());
    }

}