提交 db430b38 编写于 作者: K kohsuke

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
上级 95a7082e
......@@ -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.Descriptor.FormException;
import hudson.model.Hudson;
import hudson.model.Items;
import hudson.model.Job;
import hudson.model.TopLevelItemDescriptor;
import hudson.model.ItemLoader;
import hudson.model.Descriptor.FormException;
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>
* &mdash; 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);
}
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);
......
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;
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册