001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *      Copyright 2015 ForgeRock AS
024 */
025package org.forgerock.maven;
026
027import java.io.File;
028import java.util.LinkedList;
029import java.util.List;
030
031import org.apache.maven.plugin.AbstractMojo;
032import org.apache.maven.plugin.MojoExecutionException;
033import org.apache.maven.plugin.MojoFailureException;
034import org.apache.maven.plugins.annotations.LifecyclePhase;
035import org.apache.maven.plugins.annotations.Mojo;
036import org.apache.maven.plugins.annotations.Parameter;
037import org.apache.maven.project.MavenProject;
038import org.tmatesoft.svn.core.SVNDepth;
039import org.tmatesoft.svn.core.SVNException;
040import org.tmatesoft.svn.core.wc.ISVNStatusHandler;
041import org.tmatesoft.svn.core.wc.SVNClientManager;
042import org.tmatesoft.svn.core.wc.SVNPropertyData;
043import org.tmatesoft.svn.core.wc.SVNRevision;
044
045/**
046 * This be used to make sure that the file has the correct "svn:eol-style"
047 * property value.
048 */
049@Mojo(name = "check-svn-property", defaultPhase = LifecyclePhase.VALIDATE)
050public class CheckSVNPropertyMojo extends AbstractMojo implements ISVNStatusHandler {
051
052    /** The Maven Project. */
053    @Parameter(property = "project", required = true, readonly = true)
054    private MavenProject project;
055
056    /** The path to the root of the Subversion workspace to check. */
057    @Parameter(required = true, defaultValue = "${basedir}")
058    private String svnWorkspaceRoot;
059
060    @Parameter(required = true)
061    private String svnPropertyName;
062
063    @Parameter(required = true)
064    private String svnPropertyExpectedValue;
065
066    @Parameter(property = "skipSvnPropCheck", required = true, defaultValue = "false")
067    private boolean checkDisabled;
068
069    /**
070     * The name of the system property that may be used to prevent eol-style
071     * problems from failing the build.
072     */
073    @Parameter(property = "ignoreSvnPropertyCheckErrors", required = true, defaultValue = "false")
074    private boolean ignoreErrors;
075
076    /** The overall SVN Client Manager. */
077    private final SVNClientManager svnClientManager = SVNClientManager.newInstance();
078
079    private final List<String> errorFilePaths = new LinkedList<>();
080
081    /** {@inheritDoc} **/
082    public void execute() throws MojoExecutionException, MojoFailureException {
083        if (checkDisabled) {
084            getLog().info("Check svn property " + svnPropertyName + " is disabled");
085            return;
086        }
087
088        try {
089            svnClientManager.getStatusClient().doStatus(new File(svnWorkspaceRoot), SVNRevision.WORKING,
090                    SVNDepth.INFINITY, false, false, false, false, this, null);
091        } catch (Exception e) {
092            throw new MojoExecutionException("Encountered an error while examining subversion status: "
093                    + e.getMessage() + "No further checks will be performed.", e);
094        }
095
096        if (!errorFilePaths.isEmpty()) {
097            logWithAppropriateLevel(" Potential " + svnPropertyName + " updates needed " + "for the following files:");
098            for (String fileName : errorFilePaths) {
099                logWithAppropriateLevel("     " + fileName);
100            }
101
102            if (!ignoreErrors) {
103                throw new MojoExecutionException("Fix " + svnPropertyName + " problems before proceeding, or "
104                        + "use '-DignoreSvnPropertyCheckErrors=true' to ignore these warnings warnings.");
105            }
106        }
107    }
108
109    private void logWithAppropriateLevel(final String message) {
110        if (!ignoreErrors) {
111            getLog().error(message);
112        } else {
113            getLog().warn(message);
114        }
115    }
116
117    /**
118     * Examines the provided status item to determine whether the associated
119     * file is acceptable.
120     *
121     * @param status
122     *            The SVN status information for the file of interest.
123     */
124    public void handleStatus(org.tmatesoft.svn.core.wc.SVNStatus status) {
125        File changedFile = status.getFile();
126        if (!changedFile.exists() || !changedFile.isFile()) {
127            return;
128        }
129
130        try {
131            SVNPropertyData propertyData = SVNClientManager.newInstance().getWCClient()
132                    .doGetProperty(changedFile, svnPropertyName, SVNRevision.BASE, SVNRevision.WORKING);
133            if (propertyData == null || !svnPropertyExpectedValue.equals(propertyData.getValue().getString())) {
134                errorFilePaths.add(changedFile.getPath());
135            }
136        } catch (SVNException se) {
137            // This could happen if the file isn't under version control.
138            getLog().warn("Impossible to check svn:eol-style for the file " + changedFile.getAbsolutePath()
139                            + " you might need to add it to svn.");
140        }
141    }
142}