mirror of https://github.com/jenkinsci/jenkins.git
introduced more stories around Item/ItemGroup and full names.
updating the rest of the code to work with this. git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@1826 71c3de6d-444a-0410-be80-ed276b4c234a
This commit is contained in:
parent
95a7082efe
commit
db430b3896
|
|
@ -5,6 +5,7 @@ import hudson.model.ModelObject;
|
|||
import hudson.model.Node;
|
||||
import hudson.model.Project;
|
||||
import hudson.model.Run;
|
||||
import hudson.model.Items;
|
||||
import org.kohsuke.stapler.Ancestor;
|
||||
import org.kohsuke.stapler.StaplerRequest;
|
||||
|
||||
|
|
@ -162,7 +163,7 @@ public class Functions {
|
|||
}
|
||||
|
||||
public static String getProjectListString(List<Project> projects) {
|
||||
return Project.toNameList(projects);
|
||||
return Items.toNameList(projects);
|
||||
}
|
||||
|
||||
public static Object ifThenElse(boolean cond, Object thenValue, Object elseValue) {
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ package hudson;
|
|||
import hudson.model.ExternalJob;
|
||||
import hudson.model.ExternalRun;
|
||||
import hudson.model.Hudson;
|
||||
import hudson.model.Job;
|
||||
import hudson.model.Result;
|
||||
import hudson.model.TopLevelItem;
|
||||
import hudson.util.DualOutputStream;
|
||||
import hudson.util.EncodingStream;
|
||||
|
||||
|
|
@ -60,12 +60,12 @@ public class Main {
|
|||
public static int localPost(String[] args) throws Exception {
|
||||
Hudson app = new Hudson(new File(getHudsonHome()),null);
|
||||
|
||||
Job job = app.getJob(args[0]);
|
||||
if(!(job instanceof ExternalJob)) {
|
||||
TopLevelItem item = app.getItem(args[0]);
|
||||
if(!(item instanceof ExternalJob)) {
|
||||
System.err.println(args[0]+" is not a valid external job name in Hudson");
|
||||
return -1;
|
||||
}
|
||||
ExternalJob ejob = (ExternalJob) job;
|
||||
ExternalJob ejob = (ExternalJob) item;
|
||||
|
||||
ExternalRun run = ejob.newBuild();
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import hudson.model.AbstractBuild;
|
|||
import hudson.model.BuildListener;
|
||||
import hudson.model.Result;
|
||||
import hudson.model.Run;
|
||||
import hudson.model.AbstractProject;
|
||||
import hudson.model.Fingerprint.RangeSet;
|
||||
import hudson.remoting.VirtualChannel;
|
||||
import hudson.remoting.Channel;
|
||||
import hudson.util.IOException2;
|
||||
|
|
@ -52,6 +54,11 @@ public class MavenBuild extends AbstractBuild<MavenModule,MavenBuild> {
|
|||
run(new RunnerImpl());
|
||||
}
|
||||
|
||||
public RangeSet getDownstreamRelationship(AbstractProject that) {
|
||||
// TODO
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs Maven and builds the project.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -2,11 +2,10 @@ package hudson.maven;
|
|||
|
||||
import hudson.model.AbstractProject;
|
||||
import hudson.model.Descriptor;
|
||||
import hudson.model.Hudson;
|
||||
import hudson.model.Job;
|
||||
import hudson.model.TopLevelItemDescriptor;
|
||||
import hudson.model.ItemLoader;
|
||||
import hudson.model.Descriptor.FormException;
|
||||
import hudson.model.Hudson;
|
||||
import hudson.model.Items;
|
||||
import hudson.model.Job;
|
||||
import hudson.util.DescribableList;
|
||||
import org.kohsuke.stapler.StaplerRequest;
|
||||
import org.kohsuke.stapler.StaplerResponse;
|
||||
|
|
@ -14,6 +13,7 @@ import org.kohsuke.stapler.StaplerResponse;
|
|||
import javax.servlet.ServletException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@link Job} that builds projects based on Maven2.
|
||||
|
|
@ -48,6 +48,16 @@ public final class MavenModule extends AbstractProject<MavenModule,MavenBuild> i
|
|||
return new MavenBuild(this,dir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFingerprintConfigured() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<MavenModule> getDownstreamProjects() {
|
||||
// TODO
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* List of active {@link MavenReporter}s configured for this project.
|
||||
*/
|
||||
|
|
@ -68,6 +78,6 @@ public final class MavenModule extends AbstractProject<MavenModule,MavenBuild> i
|
|||
}
|
||||
|
||||
static {
|
||||
ItemLoader.XSTREAM.alias("maven2", MavenModule.class);
|
||||
Items.XSTREAM.alias("maven2", MavenModule.class);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,17 +3,16 @@ package hudson.maven;
|
|||
import hudson.model.AbstractItem;
|
||||
import hudson.model.Hudson;
|
||||
import hudson.model.ItemGroup;
|
||||
import hudson.model.ItemLoader;
|
||||
import hudson.model.TopLevelItem;
|
||||
import hudson.model.TopLevelItemDescriptor;
|
||||
import hudson.model.Items;
|
||||
import hudson.util.CopyOnWriteMap;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* Group of {@link MavenModule}s.
|
||||
|
|
@ -31,7 +30,7 @@ public class MavenModuleSet extends AbstractItem implements TopLevelItem, ItemGr
|
|||
/**
|
||||
* All {@link MavenModule}s.
|
||||
*/
|
||||
transient final Map<String,MavenModule> modules = new TreeMap<String,MavenModule>();
|
||||
transient final Map<String,MavenModule> modules = new CopyOnWriteMap.Tree<String,MavenModule>();
|
||||
|
||||
public MavenModuleSet(String name) {
|
||||
this.name = name;
|
||||
|
|
@ -53,12 +52,12 @@ public class MavenModuleSet extends AbstractItem implements TopLevelItem, ItemGr
|
|||
return Hudson.getInstance();
|
||||
}
|
||||
|
||||
public synchronized Collection<MavenModule> getItems() {
|
||||
return new ArrayList<MavenModule>(modules.values());
|
||||
public Collection<MavenModule> getItems() {
|
||||
return modules.values();
|
||||
}
|
||||
|
||||
public synchronized boolean contains(MavenModule item) {
|
||||
return modules.containsKey(item.getName());
|
||||
public MavenModule getItem(String name) {
|
||||
return modules.get(name);
|
||||
}
|
||||
|
||||
public Collection<MavenModule> getAllJobs() {
|
||||
|
|
@ -80,7 +79,7 @@ public class MavenModuleSet extends AbstractItem implements TopLevelItem, ItemGr
|
|||
modules.clear();
|
||||
for (File subdir : subdirs) {
|
||||
try {
|
||||
MavenModule item = (MavenModule)ItemLoader.load(subdir);
|
||||
MavenModule item = (MavenModule) Items.load(subdir);
|
||||
modules.put(item.getName(), item);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(); // TODO: logging
|
||||
|
|
@ -103,6 +102,6 @@ public class MavenModuleSet extends AbstractItem implements TopLevelItem, ItemGr
|
|||
};
|
||||
|
||||
static {
|
||||
ItemLoader.XSTREAM.alias("maven2-module-set", MavenModule.class);
|
||||
Items.XSTREAM.alias("maven2-module-set", MavenModule.class);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import hudson.Util;
|
|||
import hudson.maven.MavenBuild;
|
||||
import static hudson.model.Hudson.isWindows;
|
||||
import hudson.model.listeners.SCMListener;
|
||||
import hudson.model.Fingerprint.RangeSet;
|
||||
import hudson.scm.CVSChangeLogParser;
|
||||
import hudson.scm.ChangeLogParser;
|
||||
import hudson.scm.ChangeLogSet;
|
||||
|
|
@ -21,6 +22,7 @@ import java.io.IOException;
|
|||
import java.io.PrintStream;
|
||||
import java.util.Calendar;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Base implementation of {@link Run}s that build software.
|
||||
|
|
@ -200,6 +202,61 @@ public abstract class AbstractBuild<P extends AbstractProject<P,R>,R extends Abs
|
|||
*/
|
||||
public abstract void run();
|
||||
|
||||
//
|
||||
//
|
||||
// fingerprint related stuff
|
||||
//
|
||||
//
|
||||
/**
|
||||
* Gets the downstream builds of this build, which are the builds of the
|
||||
* downstream projects that use artifacts of this build.
|
||||
*
|
||||
* @return
|
||||
* For each project with fingerprinting enabled, returns the range
|
||||
* of builds (which can be empty if no build uses the artifact from this build.)
|
||||
*/
|
||||
public Map<AbstractProject,RangeSet> getDownstreamBuilds() {
|
||||
Map<AbstractProject,RangeSet> r = new HashMap<AbstractProject,RangeSet>();
|
||||
for (AbstractProject p : getParent().getDownstreamProjects()) {
|
||||
if(p.isFingerprintConfigured())
|
||||
r.put(p,getDownstreamRelationship(p));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWhyKeepLog() {
|
||||
// if any of the downstream project is configured with 'keep dependency component',
|
||||
// we need to keep this log
|
||||
for (Map.Entry<AbstractProject, RangeSet> e : getDownstreamBuilds().entrySet()) {
|
||||
AbstractProject<?,?> p = e.getKey();
|
||||
if(!p.isKeepDependencies()) continue;
|
||||
|
||||
// is there any active build that depends on us?
|
||||
for (AbstractBuild build : p.getBuilds()) {
|
||||
if(e.getValue().includes(build.getNumber()))
|
||||
return "kept because of "+build;
|
||||
}
|
||||
}
|
||||
|
||||
return super.getWhyKeepLog();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the dependency relationship from this build (as the source)
|
||||
* and that project (as the sink.)
|
||||
*
|
||||
* @return
|
||||
* range of build numbers that represent which downstream builds are using this build.
|
||||
* The range will be empty if no build of that project matches this.
|
||||
*/
|
||||
public abstract RangeSet getDownstreamRelationship(AbstractProject that);
|
||||
|
||||
//
|
||||
//
|
||||
// web methods
|
||||
//
|
||||
//
|
||||
/**
|
||||
* Stops this build if it's still going.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -38,6 +38,13 @@ public abstract class AbstractItem extends Actionable implements Item {
|
|||
*/
|
||||
public abstract String getName();
|
||||
|
||||
|
||||
public final String getFullName() {
|
||||
String n = getParent().getFullName();
|
||||
if(n.length()==0) return getName();
|
||||
else return n+'/'+getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called right after when a {@link Item} is loaded from disk.
|
||||
* This is an opporunity to do a post load processing.
|
||||
|
|
@ -66,6 +73,6 @@ public abstract class AbstractItem extends Actionable implements Item {
|
|||
}
|
||||
|
||||
protected final XmlFile getConfigFile() {
|
||||
return ItemLoader.getConfigFile(this);
|
||||
return Items.getConfigFile(this);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package hudson.model;
|
|||
|
||||
import hudson.FilePath;
|
||||
import hudson.Launcher;
|
||||
import hudson.util.EditDistance;
|
||||
import hudson.model.RunMap.Constructor;
|
||||
import hudson.model.Descriptor.FormException;
|
||||
import hudson.triggers.Trigger;
|
||||
|
|
@ -227,7 +228,6 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
|
|||
*/
|
||||
protected abstract R loadBuild(File dir) throws IOException;
|
||||
|
||||
|
||||
/**
|
||||
* Gets the {@link Node} where this project was last built on.
|
||||
*
|
||||
|
|
@ -342,6 +342,22 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
|
|||
return Descriptor.toMap(triggers);
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
// fingerprint related
|
||||
//
|
||||
//
|
||||
/**
|
||||
* True if the builds of this project produces {@link Fingerprint} records.
|
||||
*/
|
||||
public abstract boolean isFingerprintConfigured();
|
||||
|
||||
/**
|
||||
* Gets the other {@link AbstractProject}s that should be built
|
||||
* when a build of this project is completed.
|
||||
*/
|
||||
public abstract List<? extends AbstractProject> getDownstreamProjects();
|
||||
|
||||
//
|
||||
//
|
||||
// actions
|
||||
|
|
@ -450,4 +466,17 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
|
|||
new DirectoryBrowserSupport(this).serveFile(req, rsp, ws, "folder.gif", true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a {@link AbstractProject} that has the name closest to the given name.
|
||||
*/
|
||||
public static AbstractProject findNearest(String name) {
|
||||
List<AbstractProject> projects = Hudson.getInstance().getAllItems(AbstractProject.class);
|
||||
String[] names = new String[projects.size()];
|
||||
for( int i=0; i<projects.size(); i++ )
|
||||
names[i] = projects.get(i).getName();
|
||||
|
||||
String nearest = EditDistance.findNearest(name, names);
|
||||
return (AbstractProject)Hudson.getInstance().getItem(nearest);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,24 +41,6 @@ public final class Build extends AbstractBuild<Project,Build> {
|
|||
super(project,buildDir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWhyKeepLog() {
|
||||
// if any of the downstream project is configured with 'keep dependency component',
|
||||
// we need to keep this log
|
||||
for (Map.Entry<Project, RangeSet> e : getDownstreamBuilds().entrySet()) {
|
||||
Project p = e.getKey();
|
||||
if(!p.isKeepDependencies()) continue;
|
||||
|
||||
// is there any active build that depends on us?
|
||||
for (Build build : p.getBuilds()) {
|
||||
if(e.getValue().includes(build.getNumber()))
|
||||
return "kept because of "+build;
|
||||
}
|
||||
}
|
||||
|
||||
return super.getWhyKeepLog();
|
||||
}
|
||||
|
||||
public Calendar due() {
|
||||
return timestamp;
|
||||
}
|
||||
|
|
@ -70,15 +52,8 @@ public final class Build extends AbstractBuild<Project,Build> {
|
|||
return getAction(AbstractTestResultAction.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the dependency relationship from this build (as the source)
|
||||
* and that project (as the sink.)
|
||||
*
|
||||
* @return
|
||||
* range of build numbers that represent which downstream builds are using this build.
|
||||
* The range will be empty if no build of that project matches this.
|
||||
*/
|
||||
public RangeSet getDownstreamRelationship(Project that) {
|
||||
@Override
|
||||
public RangeSet getDownstreamRelationship(AbstractProject that) {
|
||||
RangeSet rs = new RangeSet();
|
||||
|
||||
FingerprintAction f = getAction(FingerprintAction.class);
|
||||
|
|
@ -94,23 +69,6 @@ public final class Build extends AbstractBuild<Project,Build> {
|
|||
return rs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the downstream builds of this build, which are the builds of the
|
||||
* downstream projects that use artifacts of this build.
|
||||
*
|
||||
* @return
|
||||
* For each project with fingerprinting enabled, returns the range
|
||||
* of builds (which can be empty if no build uses the artifact from this build.)
|
||||
*/
|
||||
public Map<Project,RangeSet> getDownstreamBuilds() {
|
||||
Map<Project,RangeSet> r = new HashMap<Project,RangeSet>();
|
||||
for (Project p : getParent().getDownstreamProjects()) {
|
||||
if(p.isFingerprintConfigured())
|
||||
r.put(p,getDownstreamRelationship(p));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the dependency relationship from this build (as the sink)
|
||||
* and that project (as the source.)
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ public class Fingerprint implements ModelObject {
|
|||
* or null if such a job no longer exists.
|
||||
*/
|
||||
public Job getJob() {
|
||||
return Hudson.getInstance().getJob(name);
|
||||
return Hudson.getInstance().getItemByFullName(name,Job.class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -461,7 +461,7 @@ public class Fingerprint implements ModelObject {
|
|||
return true;
|
||||
|
||||
for (Entry<String,RangeSet> e : usages.entrySet()) {
|
||||
Job j = Hudson.getInstance().getJob(e.getKey());
|
||||
Job j = Hudson.getInstance().getItemByFullName(e.getKey(),Job.class);
|
||||
if(j==null)
|
||||
continue;
|
||||
|
||||
|
|
|
|||
|
|
@ -68,6 +68,8 @@ import java.util.Map.Entry;
|
|||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.Vector;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.Stack;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogRecord;
|
||||
|
||||
|
|
@ -364,10 +366,37 @@ public final class Hudson extends View implements ItemGroup<TopLevelItem>, Node
|
|||
return Util.createSubList(items.values(),Job.class);
|
||||
}
|
||||
|
||||
public String getFullName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public List<TopLevelItem> getItems() {
|
||||
return new ArrayList<TopLevelItem>(items.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the {@link Item}s recursively in the {@link ItemGroup} tree
|
||||
* and filter them by the given type.
|
||||
*/
|
||||
public <T extends Item> List<T> getAllItems(Class<T> type) {
|
||||
List<T> r = new ArrayList<T>();
|
||||
|
||||
Stack<ItemGroup> q = new Stack<ItemGroup>();
|
||||
q.push(this);
|
||||
|
||||
while(!q.isEmpty()) {
|
||||
ItemGroup<?> parent = q.pop();
|
||||
for (Item i : parent.getItems()) {
|
||||
if(type.isInstance(i))
|
||||
r.add(type.cast(r));
|
||||
if(i instanceof ItemGroup)
|
||||
q.push((ItemGroup)i);
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the snapshot of all the projects.
|
||||
*/
|
||||
|
|
@ -576,17 +605,12 @@ public final class Hudson extends View implements ItemGroup<TopLevelItem>, Node
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets the job of the given name.
|
||||
*
|
||||
* @return null
|
||||
* if such a project doesn't exist.
|
||||
* @deprecated
|
||||
* Left only for the compatibility of URLs.
|
||||
* Should not be invoked for any other purpose.
|
||||
*/
|
||||
public Job getJob(String name) {
|
||||
TopLevelItem item = items.get(name);
|
||||
if(item instanceof Job)
|
||||
return (Job)item;
|
||||
else
|
||||
return null;
|
||||
public TopLevelItem getJob(String name) {
|
||||
return getItem(name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -596,6 +620,35 @@ public final class Hudson extends View implements ItemGroup<TopLevelItem>, Node
|
|||
return items.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link Item} object by its full name.
|
||||
* Full names are like path names, where each name of {@link Item} is
|
||||
* combined by '/'.
|
||||
*
|
||||
* @return
|
||||
* null if either such {@link Item} doesn't exist under the given full name,
|
||||
* or it exists but it's no an instance of the given type.
|
||||
*/
|
||||
public <T extends Item> T getItemByFullName(String fullName, Class<T> type) {
|
||||
StringTokenizer tokens = new StringTokenizer(fullName,"/");
|
||||
ItemGroup parent = this;
|
||||
|
||||
while(true) {
|
||||
Item item = parent.getItem(tokens.nextToken());
|
||||
if(!tokens.hasMoreTokens()) {
|
||||
if(type.isInstance(item))
|
||||
return type.cast(item);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
if(!(item instanceof ItemGroup))
|
||||
return null; // this item can't have any children
|
||||
|
||||
parent = (ItemGroup) item;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user of the given name.
|
||||
*
|
||||
|
|
@ -722,7 +775,7 @@ public final class Hudson extends View implements ItemGroup<TopLevelItem>, Node
|
|||
items.clear();
|
||||
for (File subdir : subdirs) {
|
||||
try {
|
||||
TopLevelItem item = (TopLevelItem)ItemLoader.load(subdir);
|
||||
TopLevelItem item = (TopLevelItem)Items.load(subdir);
|
||||
items.put(item.getName(), item);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(); // TODO: logging
|
||||
|
|
@ -908,10 +961,10 @@ public final class Hudson extends View implements ItemGroup<TopLevelItem>, Node
|
|||
result = createProject(src.getDescriptor(),name);
|
||||
|
||||
// copy config
|
||||
Util.copyFile(ItemLoader.getConfigFile(src).getFile(),ItemLoader.getConfigFile(result).getFile());
|
||||
Util.copyFile(Items.getConfigFile(src).getFile(),Items.getConfigFile(result).getFile());
|
||||
|
||||
// reload from the new config
|
||||
result = (TopLevelItem)ItemLoader.load(result.getRootDir());
|
||||
result = (TopLevelItem)Items.load(result.getRootDir());
|
||||
result.onCopiedFrom(src);
|
||||
items.put(name,result);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,15 +4,32 @@ import java.io.IOException;
|
|||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Object that can be displayed in a {@link ListView}.
|
||||
* Basic configuration unit in Hudson.
|
||||
*
|
||||
* {@link Item}s are allowed to show up in multiple {@link View}s,
|
||||
* so they need to have unique names among all {@link Item}s.
|
||||
* This uniqueness is also used for allocating file system storage
|
||||
* for each {@link Item}.
|
||||
* <p>
|
||||
* Every {@link Item} is hosted in an {@link ItemGroup} called "parent",
|
||||
* and some {@link Item}s are {@link ItemGroup}s. This form a tree
|
||||
* structure, which is rooted at {@link Hudson}.
|
||||
*
|
||||
* <p>
|
||||
* Unlike file systems, where a file can be moved from one directory
|
||||
* to another, {@link Item} inherently belongs to a single {@link ItemGroup}
|
||||
* and that relationship will not change.
|
||||
* Think of
|
||||
* <a href="http://images.google.com/images?q=Windows%20device%20manager">Windows device manager</a>
|
||||
* — an HDD always show up under 'Disk drives' and it can never be moved to another parent.
|
||||
*
|
||||
* Similarly, {@link ItemGroup} is not a generic container. Each subclass
|
||||
* of {@link ItemGroup} can usually only host a certain limited kinds of
|
||||
* {@link Item}s.
|
||||
*
|
||||
* <p>
|
||||
* {@link Item}s have unique {@link #getName() name}s that distinguish themselves
|
||||
* among their siblings uniquely. The names can be combined by '/' to form an
|
||||
* item full name, which uniquely identifies an {@link Item} inside the whole {@link Hudson}.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
* @see ItemLoader
|
||||
* @see Items
|
||||
*/
|
||||
public interface Item extends PersistenceRoot {
|
||||
/**
|
||||
|
|
@ -29,16 +46,32 @@ public interface Item extends PersistenceRoot {
|
|||
* Gets the name of the item.
|
||||
*
|
||||
* <p>
|
||||
* The name must be unique among all {@link Item}s in this Hudson,
|
||||
* because we allow a single {@link Item} to show up in multiple
|
||||
* {@link View}s.
|
||||
* The name must be unique among other {@link Item}s that belong
|
||||
* to the same parent.
|
||||
*
|
||||
* <p>
|
||||
* This name is also used for directory name, so it cannot contain
|
||||
* any character that's not allowed on the file system.
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Gets the full name of this item, like "abc/def/ghi".
|
||||
*
|
||||
* <p>
|
||||
* Full name consists of {@link #getName() name}s of {@link Item}s
|
||||
* that lead from the root {@link Hudson} to this {@link Item},
|
||||
* separated by '/'. This is the unique name that identifies this
|
||||
* {@link Item} inside the whole {@link Hudson}.
|
||||
*
|
||||
* @see Hudson#getItemByFullName(String,Class)
|
||||
*/
|
||||
String getFullName();
|
||||
|
||||
/**
|
||||
* Returns the URL of this project relative to the context root of the application.
|
||||
*
|
||||
* @see AbstractItem#getUrl() for how to implement this
|
||||
* @see AbstractItem#getUrl() for how to implement this.
|
||||
*/
|
||||
String getUrl();
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,12 @@ import java.util.Collection;
|
|||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public interface ItemGroup<T extends Item> extends PersistenceRoot, ModelObject {
|
||||
/**
|
||||
* Gets the full name of this {@link ItemGroup}.
|
||||
*
|
||||
* @see Item#getFullName()
|
||||
*/
|
||||
String getFullName();
|
||||
|
||||
/**
|
||||
* Gets all the items in this collection in a read-only view.
|
||||
|
|
@ -25,4 +31,9 @@ public interface ItemGroup<T extends Item> extends PersistenceRoot, ModelObject
|
|||
* Like "job", "item", etc.
|
||||
*/
|
||||
String getUrlChildPrefix();
|
||||
|
||||
/**
|
||||
* Gets the {@link Item} inside this group that has a given name.
|
||||
*/
|
||||
T getItem(String name);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,53 @@
|
|||
package hudson.model;
|
||||
|
||||
import com.thoughtworks.xstream.XStream;
|
||||
import hudson.util.XStream2;
|
||||
import hudson.XmlFile;
|
||||
import hudson.util.XStream2;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.StringTokenizer;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import com.thoughtworks.xstream.XStream;
|
||||
|
||||
/**
|
||||
* Used to load {@link Item} implementation.
|
||||
*
|
||||
* Convenience methods related to {@link Item}.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public final class ItemLoader {
|
||||
public class Items {
|
||||
/**
|
||||
* Converts a list of items into a camma-separated full names.
|
||||
*/
|
||||
public static String toNameList(Collection<? extends Item> items) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (Item item : items) {
|
||||
if(buf.length()>0)
|
||||
buf.append(", ");
|
||||
buf.append(item.getFullName());
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the opposite of {@link #toNameList(Collection)}.
|
||||
*/
|
||||
public static <T extends Item> List<T> fromNameList(String list,Class<T> type) {
|
||||
Hudson hudson = Hudson.getInstance();
|
||||
|
||||
List<T> r = new ArrayList<T>();
|
||||
StringTokenizer tokens = new StringTokenizer(list,",");
|
||||
while(tokens.hasMoreTokens()) {
|
||||
String fullName = tokens.nextToken().trim();
|
||||
T item = hudson.getItemByFullName(fullName,type);
|
||||
if(item!=null)
|
||||
r.add(item);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a {@link Item} from a config file.
|
||||
*
|
||||
|
|
@ -237,7 +237,7 @@ public abstract class Job<JobT extends Job<JobT,RunT>, RunT extends Run<JobT,Run
|
|||
// sanity check
|
||||
if(newName==null)
|
||||
throw new IllegalArgumentException("New name is not given");
|
||||
if(parent.getJob(newName)!=null)
|
||||
if(parent.getItem(newName)!=null)
|
||||
throw new IllegalArgumentException("Job "+newName+" already exists");
|
||||
|
||||
// noop?
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import hudson.tasks.Builder;
|
|||
import hudson.tasks.Fingerprinter;
|
||||
import hudson.tasks.Publisher;
|
||||
import hudson.triggers.Trigger;
|
||||
import hudson.util.EditDistance;
|
||||
import org.kohsuke.stapler.StaplerRequest;
|
||||
import org.kohsuke.stapler.StaplerResponse;
|
||||
|
||||
|
|
@ -26,7 +25,6 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.TreeMap;
|
||||
import java.util.Vector;
|
||||
|
||||
|
|
@ -142,10 +140,10 @@ public class Project extends AbstractProject<Project,Build> implements TopLevelI
|
|||
return r;
|
||||
}
|
||||
|
||||
public List<Project> getDownstreamProjects() {
|
||||
public List<AbstractProject> getDownstreamProjects() {
|
||||
BuildTrigger buildTrigger = (BuildTrigger) getPublishers().get(BuildTrigger.DESCRIPTOR);
|
||||
if(buildTrigger==null)
|
||||
return new ArrayList<Project>();
|
||||
return new ArrayList<AbstractProject>();
|
||||
else
|
||||
return buildTrigger.getChildProjects();
|
||||
}
|
||||
|
|
@ -187,9 +185,7 @@ public class Project extends AbstractProject<Project,Build> implements TopLevelI
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the fingerprint record is configured in this project.
|
||||
*/
|
||||
@Override
|
||||
public boolean isFingerprintConfigured() {
|
||||
synchronized(publishers) {
|
||||
for (Publisher p : publishers) {
|
||||
|
|
@ -212,7 +208,7 @@ public class Project extends AbstractProject<Project,Build> implements TopLevelI
|
|||
*/
|
||||
public void doConfigSubmit( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
|
||||
|
||||
Set<Project> upstream = Collections.emptySet();
|
||||
Set<AbstractProject> upstream = Collections.emptySet();
|
||||
|
||||
synchronized(this) {
|
||||
try {
|
||||
|
|
@ -234,7 +230,7 @@ public class Project extends AbstractProject<Project,Build> implements TopLevelI
|
|||
}
|
||||
|
||||
if(req.getParameter("pseudoUpstreamTrigger")!=null) {
|
||||
upstream = new HashSet<Project>(Project.fromNameList(req.getParameter("upstreamProjects")));
|
||||
upstream = new HashSet<AbstractProject>(Items.fromNameList(req.getParameter("upstreamProjects"),AbstractProject.class));
|
||||
}
|
||||
|
||||
// this needs to be done after we release the lock on this,
|
||||
|
|
@ -242,7 +238,7 @@ public class Project extends AbstractProject<Project,Build> implements TopLevelI
|
|||
for (Project p : Hudson.getInstance().getProjects()) {
|
||||
boolean isUpstream = upstream.contains(p);
|
||||
synchronized(p) {
|
||||
List<Project> newChildProjects = p.getDownstreamProjects();
|
||||
List<AbstractProject> newChildProjects = p.getDownstreamProjects();
|
||||
|
||||
if(isUpstream) {
|
||||
if(!newChildProjects.contains(this))
|
||||
|
|
@ -312,51 +308,6 @@ public class Project extends AbstractProject<Project,Build> implements TopLevelI
|
|||
@Deprecated
|
||||
private transient String slave;
|
||||
|
||||
/**
|
||||
* Converts a list of projects into a camma-separated names.
|
||||
*/
|
||||
public static String toNameList(Collection<? extends Project> projects) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (Project project : projects) {
|
||||
if(buf.length()>0)
|
||||
buf.append(", ");
|
||||
buf.append(project.getName());
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the opposite of {@link #toNameList(Collection)}.
|
||||
*/
|
||||
public static List<Project> fromNameList(String list) {
|
||||
Hudson hudson = Hudson.getInstance();
|
||||
|
||||
List<Project> r = new ArrayList<Project>();
|
||||
StringTokenizer tokens = new StringTokenizer(list,",");
|
||||
while(tokens.hasMoreTokens()) {
|
||||
String projectName = tokens.nextToken().trim();
|
||||
Job job = hudson.getJob(projectName);
|
||||
if(!(job instanceof Project)) {
|
||||
continue; // ignore this token
|
||||
}
|
||||
r.add((Project) job);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a {@link Project} that has the name closest to the given name.
|
||||
*/
|
||||
public static Project findNearest(String name) {
|
||||
List<Project> projects = Hudson.getInstance().getProjects();
|
||||
String[] names = new String[projects.size()];
|
||||
for( int i=0; i<projects.size(); i++ )
|
||||
names[i] = projects.get(i).getName();
|
||||
|
||||
String nearest = EditDistance.findNearest(name, names);
|
||||
return (Project)Hudson.getInstance().getJob(nearest);
|
||||
}
|
||||
|
||||
private static final Comparator<Integer> REVERSE_INTEGER_COMPARATOR = new Comparator<Integer>() {
|
||||
public int compare(Integer o1, Integer o2) {
|
||||
return o2-o1;
|
||||
|
|
|
|||
|
|
@ -124,9 +124,9 @@ public class Queue {
|
|||
BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(queueFile)));
|
||||
String line;
|
||||
while((line=in.readLine())!=null) {
|
||||
Job j = Hudson.getInstance().getJob(line);
|
||||
if(j instanceof Project)
|
||||
((Project)j).scheduleBuild();
|
||||
AbstractProject j = Hudson.getInstance().getItemByFullName(line,AbstractProject.class);
|
||||
if(j!=null)
|
||||
j.scheduleBuild();
|
||||
}
|
||||
in.close();
|
||||
// discard the queue file now that we are done
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import hudson.ExtensionPoint;
|
|||
*
|
||||
* <p>
|
||||
* To register a custom {@link TopLevelItem} class from a plugin, add it to
|
||||
* {@link TopLevelItems#LIST}. Also see {@link ItemLoader#XSTREAM}.
|
||||
* {@link TopLevelItems#LIST}. Also see {@link Items#XSTREAM}.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -70,16 +70,16 @@ public class WorkspaceCleanupThread extends PeriodicWork {
|
|||
private boolean shouldBeDeleted(String jobName, FilePath dir, Node n) throws IOException, InterruptedException {
|
||||
// TODO: the use of remoting is not optimal.
|
||||
// One remoting can execute "exists", "lastModified", and "delete" all at once.
|
||||
Job job = Hudson.getInstance().getJob(jobName);
|
||||
if(job==null)
|
||||
TopLevelItem item = Hudson.getInstance().getItem(jobName);
|
||||
if(item==null)
|
||||
// no such project anymore
|
||||
return true;
|
||||
|
||||
if(!dir.exists())
|
||||
return false;
|
||||
|
||||
if (job instanceof Project) {
|
||||
Project p = (Project) job;
|
||||
if (item instanceof Project) {
|
||||
Project p = (Project) item;
|
||||
Node lb = p.getLastBuiltOn();
|
||||
if(lb!=null && lb.equals(n))
|
||||
// this is the active workspace. keep it.
|
||||
|
|
|
|||
|
|
@ -1049,7 +1049,7 @@ public class CVSSCM extends AbstractCVSFamilySCM implements Serializable {
|
|||
}
|
||||
|
||||
upName = upName.substring(9); // trim off 'upstream.'
|
||||
Job p = Hudson.getInstance().getJob(upName);
|
||||
Job p = Hudson.getInstance().getItemByFullName(upName,Job.class);
|
||||
|
||||
Run build = p.getBuildByNumber(upstreams.get(p));
|
||||
tagSet.put((AbstractBuild) build,tag);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
package hudson.tasks;
|
||||
|
||||
import hudson.Launcher;
|
||||
import hudson.model.AbstractProject;
|
||||
import hudson.model.Build;
|
||||
import hudson.model.BuildListener;
|
||||
import hudson.model.Descriptor;
|
||||
import hudson.model.Hudson;
|
||||
import hudson.model.Item;
|
||||
import hudson.model.Items;
|
||||
import hudson.model.Job;
|
||||
import hudson.model.Project;
|
||||
import hudson.model.Result;
|
||||
import hudson.util.FormFieldValidator;
|
||||
import org.kohsuke.stapler.StaplerRequest;
|
||||
|
|
@ -33,21 +35,21 @@ public class BuildTrigger extends Publisher {
|
|||
this.childProjects = childProjects;
|
||||
}
|
||||
|
||||
public BuildTrigger(List<Project> childProjects) {
|
||||
this(Project.toNameList(childProjects));
|
||||
public BuildTrigger(List<AbstractProject> childProjects) {
|
||||
this(Items.toNameList(childProjects));
|
||||
}
|
||||
|
||||
public String getChildProjectsValue() {
|
||||
return childProjects;
|
||||
}
|
||||
|
||||
public List<Project> getChildProjects() {
|
||||
return Project.fromNameList(childProjects);
|
||||
public List<AbstractProject> getChildProjects() {
|
||||
return Items.fromNameList(childProjects,AbstractProject.class);
|
||||
}
|
||||
|
||||
public boolean perform(Build build, Launcher launcher, BuildListener listener) {
|
||||
if(build.getResult()== Result.SUCCESS) {
|
||||
for (Project p : getChildProjects()) {
|
||||
for (AbstractProject p : getChildProjects()) {
|
||||
listener.getLogger().println("Triggering a new build of "+p.getName());
|
||||
p.scheduleBuild();
|
||||
}
|
||||
|
|
@ -121,14 +123,14 @@ public class BuildTrigger extends Publisher {
|
|||
StringTokenizer tokens = new StringTokenizer(list,",");
|
||||
while(tokens.hasMoreTokens()) {
|
||||
String projectName = tokens.nextToken().trim();
|
||||
Job job = Hudson.getInstance().getJob(projectName);
|
||||
if(job==null) {
|
||||
Item item = Hudson.getInstance().getItemByFullName(projectName,Item.class);
|
||||
if(item==null) {
|
||||
error("No such project '"+projectName+"'. Did you mean '"+
|
||||
Project.findNearest(projectName).getName()+"'?");
|
||||
AbstractProject.findNearest(projectName).getName()+"'?");
|
||||
return;
|
||||
}
|
||||
if(!(job instanceof Project)) {
|
||||
error(projectName+" is not a software build");
|
||||
if(!(item instanceof AbstractProject)) {
|
||||
error(projectName+" is not buildable");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue