org.sonar.updatecenter.mavenplugin.SonarPluginMojo Maven / Gradle / Ivy
The newest version!
/*
* SonarSource :: Update Center :: Packaging Mojo
* Copyright (C) 2010 SonarSource
* [email protected]
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program 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 program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.updatecenter.mavenplugin;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.collect.Iterables;
import org.apache.commons.lang.StringUtils;
import org.apache.maven.archiver.MavenArchiveConfiguration;
import org.apache.maven.archiver.MavenArchiver;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
import org.apache.maven.model.Developer;
import org.apache.maven.model.License;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.shared.dependency.tree.DependencyNode;
import org.apache.maven.shared.dependency.tree.DependencyTreeBuilder;
import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException;
import org.codehaus.plexus.archiver.jar.JarArchiver;
import org.codehaus.plexus.util.FileUtils;
import org.sonar.updatecenter.common.FormatUtils;
import org.sonar.updatecenter.common.PluginKeyUtils;
import org.sonar.updatecenter.common.PluginManifest;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Build a Sonar Plugin from the current project.
*
* @author Evgeny Mandrikov
*/
@Mojo(name = "sonar-plugin", requiresDependencyResolution = ResolutionScope.RUNTIME, defaultPhase = LifecyclePhase.PACKAGE, threadSafe = true)
public class SonarPluginMojo extends AbstractSonarPluginMojo {
private static final String LIB_DIR = "META-INF/lib/";
private static final String[] DEFAULT_EXCLUDES = new String[] {"**/package.html"};
private static final String[] DEFAULT_INCLUDES = new String[] {"**/**"};
/**
* The Jar archiver.
*/
@Component(role = org.codehaus.plexus.archiver.Archiver.class, hint = "jar")
protected JarArchiver jarArchiver;
/**
* List of files to include. Specified as fileset patterns which are relative to the input directory whose contents
* is being packaged into the JAR.
*/
@Parameter
private String[] includes;
/**
* List of files to exclude. Specified as fileset patterns which are relative to the input directory whose contents
* is being packaged into the JAR.
*/
@Parameter
private String[] excludes;
/**
* The archive configuration to use.
* See Maven Archiver Reference.
*/
@Parameter
private MavenArchiveConfiguration archive = new MavenArchiveConfiguration();
@Component
private DependencyTreeBuilder dependencyTreeBuilder;
/**
* The artifact repository to use.
*/
@Parameter(defaultValue = "${localRepository}", readonly = true)
private ArtifactRepository localRepository;
@Parameter(property = "sonar.addMavenDescriptor")
private boolean addMavenDescriptor = true;
protected static File getJarFile(File basedir, String finalName, String classifier) {
String suffix;
if (StringUtils.isBlank(classifier)) {
suffix = "";
} else if (classifier.startsWith("-")) {
suffix = classifier;
} else {
suffix = "-" + classifier;
}
return new File(basedir, finalName + suffix + ".jar");
}
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
checkMandatoryAttributes();
File jarFile = createArchive();
String classifier = getClassifier();
if (classifier != null) {
projectHelper.attachArtifact(getProject(), "jar", classifier, jarFile);
} else {
getProject().getArtifact().setFile(jarFile);
}
}
private void checkMandatoryAttributes() throws MojoExecutionException {
checkPluginName();
checkPluginDescription();
checkPluginKey();
checkPluginClass();
}
private void checkPluginClass() throws MojoExecutionException {
if (!new File(getClassesDirectory(), getPluginClass().replace('.', '/') + ".class").exists()) {
throw new MojoExecutionException("Plugin class not found: '" + getPluginClass());
}
}
private void checkPluginKey() throws MojoExecutionException {
if (StringUtils.isNotBlank(getExplicitPluginKey()) && !PluginKeyUtils.isValid(getExplicitPluginKey())) {
throw new MojoExecutionException("Plugin key is badly formatted. Please use ascii letters and digits only: " + getExplicitPluginKey());
}
}
private void checkPluginDescription() throws MojoExecutionException {
if (StringUtils.isBlank(getPluginDescription())) {
throw new MojoExecutionException("Plugin description is missing. "
+ "Please add the field or the property sonar.pluginDescription.");
}
}
private void checkPluginName() throws MojoExecutionException {
// Maven 2 automatically sets the name as "Unnamed - " when the field is missing.
// Note that Maven 3 has a different behavior. Name is the artifact id by default.
if (StringUtils.isBlank(getPluginName()) || getPluginName().startsWith("Unnamed - ")) {
throw new MojoExecutionException("Plugin name is missing. "
+ "Please add the field or the property sonar.pluginName.");
}
}
public File createArchive() throws MojoExecutionException {
File jarFile = getJarFile(getOutputDirectory(), getFinalName(), getClassifier());
MavenArchiver archiver = new MavenArchiver();
archiver.setArchiver(jarArchiver);
archiver.setOutputFile(jarFile);
try {
archiver.getArchiver().addDirectory(getClassesDirectory(), getIncludes(), getExcludes());
archive.setAddMavenDescriptor(addMavenDescriptor);
String logLine = "-------------------------------------------------------";
getLog().info(logLine);
getLog().info("Plugin definition in update center");
addManifestProperty("Key", PluginManifest.KEY, getPluginKey());
addManifestProperty("Name", PluginManifest.NAME, getPluginName());
addManifestProperty("Description", PluginManifest.DESCRIPTION, getPluginDescription());
addManifestProperty("Version", PluginManifest.VERSION, getProject().getVersion());
addManifestProperty("Main class", PluginManifest.MAIN_CLASS, getPluginClass());
if (getRequirePlugins() != null) {
addManifestProperty("Require plugins", PluginManifest.REQUIRE_PLUGINS, getRequirePlugins());
}
if (isUseChildFirstClassLoader()) {
getLog().info(" Uses child-first class loading strategy");
archive.addManifestEntry(PluginManifest.USE_CHILD_FIRST_CLASSLOADER, "true");
}
if (StringUtils.isNotBlank(getBasePlugin())) {
getLog().info(" Base plugin: " + getBasePlugin());
archive.addManifestEntry(PluginManifest.BASE_PLUGIN, getBasePlugin());
}
addManifestProperty("Homepage", PluginManifest.HOMEPAGE, getPluginUrl());
addManifestProperty("Sonar version", PluginManifest.SONAR_VERSION, getSonarPluginApiArtifact().getVersion());
addManifestProperty("License", PluginManifest.LICENSE, getPluginLicense());
addManifestProperty("Organization", PluginManifest.ORGANIZATION, getPluginOrganization());
addManifestProperty("Organization URL", PluginManifest.ORGANIZATION_URL, getPluginOrganizationUrl());
addManifestProperty("Terms & Conditions URL", PluginManifest.TERMS_CONDITIONS_URL, getPluginTermsConditionsUrl());
addManifestProperty("Issue Tracker URL", PluginManifest.ISSUE_TRACKER_URL, getPluginIssueTrackerUrl());
addManifestProperty("Build date", PluginManifest.BUILD_DATE, FormatUtils.toString(new Date(), true));
addManifestProperty("Sources URL", PluginManifest.SOURCES_URL, getSourcesUrl());
addManifestProperty("Developers", PluginManifest.DEVELOPERS, getDevelopers());
getLog().info(logLine);
if (isSkipDependenciesPackaging()) {
getLog().info("Skip packaging of dependencies");
} else {
List libs = copyDependencies();
if (!libs.isEmpty()) {
archiver.getArchiver().addDirectory(getAppDirectory(), getIncludes(), getExcludes());
archive.addManifestEntry(PluginManifest.DEPENDENCIES, StringUtils.join(libs, " "));
}
}
archiver.createArchive(getSession(), getProject(), archive);
return jarFile;
} catch (Exception e) {
throw new MojoExecutionException("Error assembling Sonar-plugin", e);
}
}
private void addManifestProperty(String label, String key, String value) {
getLog().info(" " + label + ": " + StringUtils.defaultString(value));
archive.addManifestEntry(key, value);
}
private String getPluginLicense() {
List licenses = new ArrayList();
if (getProject().getLicenses() != null) {
for (Object license : getProject().getLicenses()) {
License l = (License) license;
if (l.getName() != null) {
licenses.add(l.getName());
}
}
}
return StringUtils.join(licenses, " ");
}
private String getPluginOrganization() {
if (getProject().getOrganization() != null) {
return getProject().getOrganization().getName();
}
return null;
}
private String getPluginOrganizationUrl() {
if (getProject().getOrganization() != null) {
return getProject().getOrganization().getUrl();
}
return null;
}
private String getPluginKey() {
if (StringUtils.isNotBlank(getExplicitPluginKey())) {
return getExplicitPluginKey();
}
return PluginKeyUtils.sanitize(getProject().getArtifactId());
}
private String getSourcesUrl() {
if (getProject().getScm() != null) {
return getProject().getScm().getUrl();
}
return null;
}
private String getDevelopers() {
if (getProject().getDevelopers() != null) {
return Joiner.on(",").join(
Iterables.transform(getProject().getDevelopers(), new Function() {
@Override
public String apply(Developer developer) {
return developer.getName();
}
}));
}
return null;
}
private List copyDependencies() throws IOException, DependencyTreeBuilderException {
List ids = new ArrayList();
List libs = new ArrayList();
File libDirectory = new File(getAppDirectory(), LIB_DIR);
Set artifacts = getNotProvidedDependencies();
for (Artifact artifact : artifacts) {
String targetFileName = getDefaultFinalName(artifact);
FileUtils.copyFileIfModified(artifact.getFile(), new File(libDirectory, targetFileName));
libs.add(LIB_DIR + targetFileName);
ids.add(artifact.getDependencyConflictId());
}
if (!ids.isEmpty()) {
getLog().info(getMessage("Following dependencies are packaged in the plugin:", ids));
getLog().info(new StringBuilder()
.append("See following page for more details about plugin dependencies:\n")
.append("\n\thttp://docs.codehaus.org/display/SONAR/Coding+a+plugin\n")
.toString());
}
return libs;
}
private String getDefaultFinalName(Artifact artifact) {
return artifact.getFile().getName();
}
private Set getNotProvidedDependencies() throws DependencyTreeBuilderException {
Set result = new HashSet();
Set providedArtifacts = getSonarProvidedArtifacts();
for (Artifact artifact : getIncludedArtifacts()) {
boolean include = true;
if (isSonarPlugin(artifact) || isScopeProvidedOrTest(artifact)) {
include = false;
}
if (containsArtifact(providedArtifacts, artifact)) {
getLog().warn(artifact + " is provided by SonarQube plugin API and will not be packaged in your plugin");
include = false;
}
if (include) {
result.add(artifact);
}
}
return result;
}
private boolean isScopeProvidedOrTest(Artifact artifact) {
return Artifact.SCOPE_PROVIDED.equals(artifact.getScope()) || Artifact.SCOPE_TEST.equals(artifact.getScope());
}
private boolean isSonarPlugin(Artifact artifact) {
return "sonar-plugin".equals(artifact.getType());
}
private boolean containsArtifact(Set artifacts, Artifact artifact) {
for (Artifact a : artifacts) {
if (StringUtils.equals(a.getGroupId(), artifact.getGroupId()) &&
StringUtils.equals(a.getArtifactId(), artifact.getArtifactId())) {
return true;
}
}
return false;
}
private Set getSonarProvidedArtifacts() throws DependencyTreeBuilderException {
Set result = new HashSet();
ArtifactFilter artifactFilter = new ScopeArtifactFilter(Artifact.SCOPE_RUNTIME);
// We need to use Maven 2 dependency tree in order to get omitted dependencies
DependencyNode rootNode = dependencyTreeBuilder.buildDependencyTree(getProject(),
localRepository,
artifactFilter);
searchForSonarProvidedArtifacts(rootNode, result, false);
return result;
}
private void searchForSonarProvidedArtifacts(DependencyNode dependency, Set sonarArtifacts, boolean isParentProvided) {
if (dependency != null) {
boolean provided;
if (dependency.getParent() != null) {
// skip check on root node - see SONAR-1815
provided = isParentProvided ||
("org.codehaus.sonar".equals(dependency.getArtifact().getGroupId()) && !Artifact.SCOPE_TEST.equals(dependency.getArtifact().getScope()));
} else {
provided = isParentProvided;
}
if (provided) {
sonarArtifacts.add(dependency.getArtifact());
}
if (!Artifact.SCOPE_TEST.equals(dependency.getArtifact().getScope())) {
for (Object childDep : dependency.getChildren()) {
searchForSonarProvidedArtifacts((DependencyNode) childDep, sonarArtifacts, provided);
}
}
}
}
private String[] getIncludes() {
if (includes != null && includes.length > 0) {
return includes;
}
return DEFAULT_INCLUDES;
}
private String[] getExcludes() {
if (excludes != null && excludes.length > 0) {
return excludes;
}
return DEFAULT_EXCLUDES;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy