提交 8d150d57 编写于 作者: K Kohsuke Kawaguchi

pushing ItemGroupParent implementations outside to facilitate different implemenations.

上级 43c17872
......@@ -126,7 +126,6 @@ import hudson.util.TextFile;
import hudson.util.VersionNumber;
import hudson.util.XStream2;
import hudson.util.Service;
import hudson.util.IOUtils;
import hudson.views.DefaultMyViewsTabBar;
import hudson.views.DefaultViewsTabBar;
import hudson.views.MyViewsTabBar;
......@@ -155,7 +154,6 @@ import org.jvnet.hudson.reactor.ReactorListener;
import org.jvnet.hudson.reactor.TaskGraphBuilder.Handle;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
import org.kohsuke.stapler.Ancestor;
import org.kohsuke.stapler.HttpRedirect;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.HttpResponses;
......@@ -539,6 +537,21 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
*/
private transient final AdjunctManager adjuncts;
/**
* Code that handles {@link ItemGroup} work.
*/
private transient final ItemGroupMixIn itemGroupMixIn = new ItemGroupMixIn(this,this) {
@Override
protected void add(TopLevelItem item) {
items.put(item.getName(),item);
}
@Override
protected File getRootDirFor(String name) {
return Hudson.this.getRootDirFor(name);
}
};
@CLIResolver
public static Hudson getInstance() {
return theInstance;
......@@ -2085,25 +2098,8 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
* @throws IllegalArgumentException
* if a project of the give name already exists.
*/
public synchronized TopLevelItem createProject( TopLevelItemDescriptor type, String name, boolean notify )
throws IOException {
if(items.containsKey(name))
throw new IllegalArgumentException("Project of the name "+name+" already exists");
TopLevelItem item;
try {
item = type.newInstance(name);
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
item.onCreatedFromScratch();
item.save();
items.put(name,item);
if (notify)
ItemListener.fireOnCreated(item);
return item;
public synchronized TopLevelItem createProject( TopLevelItemDescriptor type, String name, boolean notify ) throws IOException {
return itemGroupMixIn.createProject(type,name,notify);
}
/**
......@@ -2613,67 +2609,7 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
}
public synchronized Item doCreateItem( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
checkPermission(Job.CREATE);
TopLevelItem result;
String requestContentType = req.getContentType();
if(requestContentType==null) {
rsp.sendError(HttpServletResponse.SC_BAD_REQUEST,"No Content-Type header set");
return null;
}
boolean isXmlSubmission = requestContentType.startsWith("application/xml") || requestContentType.startsWith("text/xml");
String name = req.getParameter("name");
if(name==null) {
rsp.sendError(HttpServletResponse.SC_BAD_REQUEST,"Query parameter 'name' is required");
return null;
}
name = checkJobName(name);
String mode = req.getParameter("mode");
if(mode!=null && mode.equals("copy")) {
String from = req.getParameter("from");
TopLevelItem src = getItem(from);
if(src==null) {
rsp.setStatus(SC_BAD_REQUEST);
if(Util.fixEmpty(from)==null)
sendError("Specify which job to copy",req,rsp);
else
sendError("No such job: "+from,req,rsp);
return null;
}
result = copy(src,name);
} else {
if(isXmlSubmission) {
result = createProjectFromXML(name, req.getInputStream());
rsp.setStatus(HttpServletResponse.SC_OK);
return result;
} else {
if(mode==null) {
rsp.sendError(SC_BAD_REQUEST);
return null;
}
// create empty job and redirect to the project config screen
result = createProject(Items.getDescriptor(mode), name);
}
}
// send the browser to the config page
// use View to trim view/{default-view} from URL if possible
String redirect = result.getUrl()+"configure";
List<Ancestor> ancestors = req.getAncestors();
for (int i = ancestors.size() - 1; i >= 0; i--) {
Object o = ancestors.get(i).getObject();
if (o instanceof View) {
redirect = req.getContextPath() + '/' + ((View)o).getUrl() + redirect;
break;
}
}
rsp.sendRedirect2(redirect);
return result;
return itemGroupMixIn.createTopLevelItem(req, rsp);
}
/**
......@@ -2682,25 +2618,7 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
* @since 1.319
*/
public TopLevelItem createProjectFromXML(String name, InputStream xml) throws IOException {
// place it as config.xml
File configXml = Items.getConfigFile(getRootDirFor(name)).getFile();
configXml.getParentFile().mkdirs();
try {
IOUtils.copy(xml,configXml);
// load it
TopLevelItem result = (TopLevelItem)Items.load(this,configXml.getParentFile());
items.put(name,result);
ItemListener.fireOnCreated(result);
rebuildDependencyGraph();
return result;
} catch (IOException e) {
// if anything fails, delete the config file to avoid further confusion
Util.deleteRecursive(configXml.getParentFile());
throw e;
}
return itemGroupMixIn.createProjectFromXML(name,xml);
}
/**
......@@ -2715,19 +2633,7 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
*/
@SuppressWarnings({"unchecked"})
public <T extends TopLevelItem> T copy(T src, String name) throws IOException {
T result = (T)createProject(src.getDescriptor(),name,false);
// copy config
Util.copyFile(Items.getConfigFile(src).getFile(),Items.getConfigFile(result).getFile());
// reload from the new config
result = (T)Items.load(this,result.getRootDir());
result.onCopiedFrom(src);
items.put(name,result);
ItemListener.fireOnCopied(src,result);
return result;
return itemGroupMixIn.copy(src,name);
}
// a little more convenient overloading that assumes the caller gives us the right type
......
/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
......@@ -23,21 +23,62 @@
*/
package hudson.model;
import hudson.Util;
import hudson.model.listeners.ItemListener;
import hudson.security.AccessControlled;
import hudson.util.CopyOnWriteMap;
import hudson.util.Function1;
import hudson.util.IOUtils;
import org.kohsuke.stapler.Ancestor;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
/**
* Defines a bunch of static methods to be used as a "mix-in" for {@link ItemGroup}
* implementations.
* implementations. Not meant for a consumption from outside {@link ItemGroup}s.
*
* @author Kohsuke Kawaguchi
*/
public class ItemGroupMixIn {
public abstract class ItemGroupMixIn {
/**
* {@link ItemGroup} for which we are working.
*/
private final ItemGroup parent;
private final AccessControlled acl;
protected ItemGroupMixIn(ItemGroup parent, AccessControlled acl) {
this.parent = parent;
this.acl = acl;
}
/*
* Callback methods to be implemented by the ItemGroup implementation.
*/
/**
* Adds a newly created item to the parent.
*/
protected abstract void add(TopLevelItem item);
/**
* Assigns the root directory for a prospective item.
*/
protected abstract File getRootDirFor(String name);
/*
* The rest is the methods that provide meat.
*/
/**
* Loads all the child {@link Item}s.
*
......@@ -73,4 +114,145 @@ public class ItemGroupMixIn {
return item.getName();
}
};
/**
* Creates a {@link TopLevelItem} from the submission of the '/lib/hudson/newFromList/formList'
*/
public synchronized TopLevelItem createTopLevelItem( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
acl.checkPermission(Job.CREATE);
TopLevelItem result;
String requestContentType = req.getContentType();
if(requestContentType==null)
throw new Failure("No Content-Type header set");
boolean isXmlSubmission = requestContentType.startsWith("application/xml") || requestContentType.startsWith("text/xml");
String name = req.getParameter("name");
if(name==null)
throw new Failure("Query parameter 'name' is required");
{// check if the name looks good
Hudson.checkGoodName(name);
name = name.trim();
if(parent.getItem(name)!=null)
throw new Failure(Messages.Hudson_JobAlreadyExists(name));
}
String mode = req.getParameter("mode");
if(mode!=null && mode.equals("copy")) {
String from = req.getParameter("from");
Item src = parent.getItem(from);
if(src==null) {
if(Util.fixEmpty(from)==null)
throw new Failure("Specify which job to copy");
else
throw new Failure("No such job: "+from);
}
if (!(src instanceof TopLevelItem)) {
throw new Failure(from+" cannot be copied");
}
result = copy((TopLevelItem) src,name);
} else {
if(isXmlSubmission) {
result = createProjectFromXML(name, req.getInputStream());
rsp.setStatus(HttpServletResponse.SC_OK);
return result;
} else {
if(mode==null)
throw new Failure("No mode given");
// create empty job and redirect to the project config screen
result = createProject(Items.getDescriptor(mode), name, true);
}
}
// send the browser to the config page
// use View to trim view/{default-view} from URL if possible
String redirect = result.getUrl()+"configure";
List<Ancestor> ancestors = req.getAncestors();
for (int i = ancestors.size() - 1; i >= 0; i--) {
Object o = ancestors.get(i).getObject();
if (o instanceof View) {
redirect = req.getContextPath() + '/' + ((View)o).getUrl() + redirect;
break;
}
}
rsp.sendRedirect2(redirect);
return result;
}
/**
* Copies an existing {@link TopLevelItem} to a new name.
*
* The caller is responsible for calling {@link ItemListener#fireOnCopied(Item, Item)}. This method
* cannot do that because it doesn't know how to make the newly added item reachable from the parent.
*/
@SuppressWarnings({"unchecked"})
public synchronized <T extends TopLevelItem> T copy(T src, String name) throws IOException {
acl.checkPermission(Job.CREATE);
T result = (T)createProject(src.getDescriptor(),name,false);
// copy config
Util.copyFile(Items.getConfigFile(src).getFile(),Items.getConfigFile(result).getFile());
// reload from the new config
result = (T)Items.load(parent,result.getRootDir());
result.onCopiedFrom(src);
add(result);
ItemListener.fireOnCopied(src,result);
return result;
}
public synchronized TopLevelItem createProjectFromXML(String name, InputStream xml) throws IOException {
acl.checkPermission(Job.CREATE);
// place it as config.xml
File configXml = Items.getConfigFile(getRootDirFor(name)).getFile();
configXml.getParentFile().mkdirs();
try {
IOUtils.copy(xml,configXml);
// load it
TopLevelItem result = (TopLevelItem)Items.load(parent,configXml.getParentFile());
add(result);
ItemListener.fireOnCreated(result);
Hudson.getInstance().rebuildDependencyGraph();
return result;
} catch (IOException e) {
// if anything fails, delete the config file to avoid further confusion
Util.deleteRecursive(configXml.getParentFile());
throw e;
}
}
public synchronized TopLevelItem createProject( TopLevelItemDescriptor type, String name, boolean notify )
throws IOException {
acl.checkPermission(Job.CREATE);
if(parent.getItem(name)!=null)
throw new IllegalArgumentException("Project of the name "+name+" already exists");
TopLevelItem item;
try {
item = type.newInstance(parent,name);
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
item.onCreatedFromScratch();
item.save();
add(item);
if (notify)
ItemListener.fireOnCreated(item);
return item;
}
}
......@@ -24,15 +24,17 @@
package hudson.model;
import com.thoughtworks.xstream.XStream;
import hudson.XmlFile;
import hudson.DescriptorExtensionList;
import hudson.Extension;
import hudson.scm.RepositoryBrowser;
import hudson.matrix.MatrixProject;
import hudson.matrix.MatrixConfiguration;
import hudson.XmlFile;
import hudson.matrix.Axis;
import hudson.util.XStream2;
import hudson.matrix.MatrixConfiguration;
import hudson.matrix.MatrixProject;
import hudson.util.DescriptorList;
import hudson.util.XStream2;
import java.io.File;
import java.io.IOException;
......
......@@ -25,9 +25,12 @@ package hudson.model;
import hudson.Extension;
import hudson.ExtensionPoint;
import hudson.matrix.MatrixConfiguration;
/**
* {@link Item} that can be directly displayed under {@link Hudson}.
* {@link Item} that can be directly displayed under {@link Hudson} or other containers.
* Ones that don't need to be under specific parent (say, unlike {@link MatrixConfiguration}),
* and thus can be freely moved, copied, etc.
*
* <p>
* To register a custom {@link TopLevelItem} class from a plugin, put {@link Extension} on your
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册