hudson.maven.MavenModule Maven / Gradle / Ivy
Show all versions of maven-plugin Show documentation
/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, id:cactusman
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.maven;
import hudson.CopyOnWrite;
import hudson.Util;
import hudson.Functions;
import hudson.maven.reporters.MavenMailer;
import hudson.model.AbstractProject;
import hudson.model.Action;
import hudson.model.DependencyGraph;
import hudson.model.Descriptor;
import hudson.model.Descriptor.FormException;
import hudson.model.Hudson;
import hudson.model.Item;
import hudson.model.ItemGroup;
import hudson.model.JDK;
import hudson.model.Job;
import hudson.model.Label;
import hudson.model.Node;
import hudson.model.Resource;
import hudson.model.Saveable;
import hudson.tasks.LogRotator;
import hudson.tasks.Publisher;
import hudson.tasks.Maven.MavenInstallation;
import hudson.util.DescribableList;
import org.apache.maven.project.MavenProject;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.export.Exported;
import javax.servlet.ServletException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.HashSet;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* {@link Job} that builds projects based on Maven2.
*
* @author Kohsuke Kawaguchi
*/
public final class MavenModule extends AbstractMavenProject implements Saveable {
private DescribableList> reporters =
new DescribableList>(this);
/**
* Name taken from {@link MavenProject#getName()}.
*/
private String displayName;
/**
* Version number of this module as of fhe last build, taken from {@link MavenProject#getVersion()}.
*
* This field can be null if Hudson loaded old data
* that didn't record this information, so that situation
* needs to be handled gracefully.
*/
private String version;
private transient ModuleName moduleName;
/**
* @see documentation in {@link PomInfo#relativePath}
*/
private String relativePath;
/**
* If this module has goals specified by itself.
* Otherwise leave it null to use the default goals specified in the parent.
*/
private String goals;
/**
* List of modules that this module declares direct dependencies on.
*/
@CopyOnWrite
private volatile Set dependencies;
/**
* List of child modules as defined by <module> POM element.
* Used to determine parent/child relationship of modules.
*
* For compatibility reason, this field may be null when loading data from old hudson.
*/
@CopyOnWrite
private volatile List children;
/**
* Nest level used to display this module in the module list.
* The root module and orphaned module gets 0.
*/
/*package*/ volatile transient int nestLevel;
/*package*/ MavenModule(MavenModuleSet parent, PomInfo pom, int firstBuildNumber) throws IOException {
super(parent, pom.name.toFileSystemName());
reconfigure(pom);
updateNextBuildNumber(firstBuildNumber);
}
/**
* {@link MavenModule} follows the same log rotation schedule as its parent.
*/
@Override
public LogRotator getLogRotator() {
return getParent().getLogRotator();
}
/**
* @deprecated
* Not allowed to configure log rotation per module.
*/
@Override
public void setLogRotator(LogRotator logRotator) {
throw new UnsupportedOperationException();
}
@Override
public boolean supportsLogRotator() {
return false;
}
@Override
public boolean isBuildable() {
// not buildable if the parent project is disabled
return super.isBuildable() && getParent().isBuildable();
}
/**
* Computes the list of {@link MavenModule}s that are 'under' this POM filesystem-wise. The list doens't include
* this module itself.
*
*
* Note that this doesn't necessary has anything to do with the module inheritance structure or parent/child
* relationship of the POM.
*/
public List getSubsidiaries() {
List r = new ArrayList();
for (MavenModule mm : getParent().getModules())
if(mm!=this && mm.getRelativePath().startsWith(getRelativePath()))
r.add(mm);
return r;
}
/**
* Called to update the module with the new POM.
*
* This method is invoked on {@link MavenModule} that has the matching
* {@link ModuleName}.
*/
/*package*/ void reconfigure(PomInfo pom) {
this.displayName = pom.displayName;
this.version = pom.version;
this.relativePath = pom.relativePath;
this.dependencies = pom.dependencies;
this.children = pom.children;
this.nestLevel = pom.getNestLevel();
disabled = false;
if (pom.mailNotifier != null) {
MavenReporter reporter = getReporters().get(MavenMailer.class);
if (reporter != null) {
MavenMailer mailer = (MavenMailer) reporter;
mailer.dontNotifyEveryUnstableBuild = !pom.mailNotifier.isSendOnFailure();
String recipients = pom.mailNotifier.getConfiguration().getProperty("recipients");
if (recipients != null) {
mailer.recipients = recipients;
}
}
}
}
@Override
protected void doSetName(String name) {
moduleName = ModuleName.fromFileSystemName(name);
super.doSetName(moduleName.toString());
}
@Override
public void onLoad(ItemGroup extends Item> parent, String name) throws IOException {
super.onLoad(parent,name);
if(reporters==null)
reporters = new DescribableList>(this);
reporters.setOwner(this);
if(dependencies==null)
dependencies = Collections.emptySet();
else {
// Until 1.207, we used to have ModuleName in dependencies. So convert.
Set deps = new HashSet(dependencies.size());
for (Object d : (Set)dependencies) {
if (d instanceof ModuleDependency) {
deps.add((ModuleDependency) d);
} else {
deps.add(new ModuleDependency((ModuleName)d, ModuleDependency.UNKNOWN));
}
}
dependencies = deps;
}
}
/**
* Relative path to this module's root directory
* from the workspace of a {@link MavenModuleSet}.
*
* The path separator is normalized to '/'.
*/
public String getRelativePath() {
return relativePath;
}
/**
* Gets the version number in Maven POM as of the last build.
*
* @return
* This method can return null if Hudson loaded old data
* that didn't record this information, so that situation
* needs to be handled gracefully.
*/
public String getVersion() {
return version;
}
/**
* Gets the list of goals to execute for this module.
*/
public String getGoals() {
if(goals!=null) return goals;
return getParent().getGoals();
}
/**
* Gets the list of goals specified by the user,
* without taking inheritance and POM default goals
* into account.
*
*
* This is only used to present the UI screen, and in
* all the other cases {@link #getGoals()} should be used.
*/
public String getUserConfiguredGoals() {
return goals;
}
public DescribableList> getPublishersList() {
// TODO
return new DescribableList>(this);
}
@Override
public JDK getJDK() {
// share one setting for the whole module set.
return getParent().getJDK();
}
@Override
protected Class getBuildClass() {
return MavenBuild.class;
}
@Override
protected MavenBuild newBuild() throws IOException {
return super.newBuild();
}
public ModuleName getModuleName() {
return moduleName;
}
/**
* Gets groupId+artifactId+version as {@link ModuleDependency}.
*/
public ModuleDependency asDependency() {
return new ModuleDependency(moduleName,Functions.defaulted(version,ModuleDependency.UNKNOWN));
}
@Override
public String getShortUrl() {
return moduleName.toFileSystemName()+'/';
}
@Exported(visibility=2)
@Override
public String getDisplayName() {
return displayName;
}
@Override
public String getPronoun() {
return Messages.MavenModule_Pronoun();
}
@Override
public boolean isNameEditable() {
return false;
}
@Override
public MavenModuleSet getParent() {
return (MavenModuleSet)super.getParent();
}
/**
* Gets all the child modules (that are listed in the <module> element in our POM.)
*
* This method returns null if this information is not recorded. This happens
* for compatibility reason.
*/
public List getChildren() {
List l = children; // take a snapshot
if(l==null) return null;
List modules = new ArrayList(l.size());
for (ModuleName n : l) {
MavenModule m = getParent().modules.get(n);
if(m!=null)
modules.add(m);
}
return modules;
}
/**
* {@link MavenModule} uses the workspace of the {@link MavenModuleSet},
* so it always needs to be built on the same slave as the parent.
*/
@Override
public Label getAssignedLabel() {
Node n = getParent().getLastBuiltOn();
if(n==null) return null;
return n.getSelfLabel();
}
/**
* Workspace of a {@link MavenModule} is a part of the parent's workspace.
*
* That is, {@Link MavenModuleSet} builds are incompatible with any {@link MavenModule}
* builds, whereas {@link MavenModule} builds are compatible with each other.
*
* @deprecated as of 1.319 in {@link AbstractProject}.
*/
@Override
public Resource getWorkspaceResource() {
return new Resource(getParent().getWorkspaceResource(),getDisplayName()+" workspace");
}
@Override
public boolean isFingerprintConfigured() {
return true;
}
@Override // to make this accessible to MavenModuleSet
protected void updateTransientActions() {
super.updateTransientActions();
}
protected void buildDependencyGraph(DependencyGraph graph) {
if(isDisabled() || getParent().ignoreUpstremChanges()) return;
Map modules = new HashMap();
// when we load old data that doesn't record version in dependency, we'd like
// to emulate the old behavior that it tries to identify the upstream by ignoring the version.
// do this by always putting groupId:artifactId:UNKNOWN to the modules list.
for (MavenModule m : Hudson.getInstance().getAllItems(MavenModule.class)) {
if(m.isDisabled()) continue;
modules.put(m.asDependency(),m);
modules.put(m.asDependency().withUnknownVersion(),m);
}
// in case two modules with the same name is defined, modules in the same MavenModuleSet
// takes precedence.
for (MavenModule m : getParent().getModules()) {
if(m.isDisabled()) continue;
modules.put(m.asDependency(),m);
modules.put(m.asDependency().withUnknownVersion(),m);
}
// if the build style is the aggregator build, define dependencies against project,
// not module.
AbstractProject dest = getParent().isAggregatorStyleBuild() ? getParent() : this;
for (ModuleDependency d : dependencies) {
MavenModule src = modules.get(d);
if(src!=null) {
DependencyGraph.Dependency dep = new MavenModuleDependency(
src.getParent().isAggregatorStyleBuild() ? src.getParent() : src,dest);
if (!dep.pointsItself())
graph.addDependency(dep);
}
}
}
@Override
protected void addTransientActionsFromBuild(MavenBuild build, List collection, Set added) {
if(build==null) return;
List list = build.projectActionReporters;
if(list==null) return;
for (MavenProjectActionBuilder step : list) {
if(!added.add(step.getClass())) continue; // already added
try {
collection.addAll(step.getProjectActions(this));
} catch (Exception e) {
LOGGER.log(Level.WARNING, "Failed to getProjectAction from " + step
+ ". Report issue to plugin developers.", e);
}
}
}
public MavenInstallation inferMavenInstallation() {
return getParent().inferMavenInstallation();
}
/**
* List of active {@link MavenReporter}s configured for this module.
*/
public DescribableList> getReporters() {
return reporters;
}
@Override
protected void submit(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException, FormException {
super.submit(req, rsp);
reporters.rebuild(req, req.getSubmittedForm(),MavenReporters.getConfigurableList());
goals = Util.fixEmpty(req.getParameter("goals").trim());
// dependency setting might have been changed by the user, so rebuild.
Hudson.getInstance().rebuildDependencyGraph();
}
@Override
protected void performDelete() throws IOException, InterruptedException {
super.performDelete();
getParent().onModuleDeleted(this);
}
/**
* Creates a list of {@link MavenReporter}s to be used for a build of this project.
*/
protected List createReporters() {
List reporterList = new ArrayList();
getReporters().addAllTo(reporterList);
getParent().getReporters().addAllTo(reporterList);
for (MavenReporterDescriptor d : MavenReporterDescriptor.all()) {
if(getReporters().contains(d))
continue; // already configured
MavenReporter auto = d.newAutoInstance(this);
if(auto!=null)
reporterList.add(auto);
}
return reporterList;
}
/**
* for debug purpose
*/
public String toString() {
return super.toString()+'['+getFullName()+']'+"[relativePath:"+getRelativePath()+']';
}
private static final Logger LOGGER = Logger.getLogger(MavenModule.class.getName());
}