package hudson.model;
import hudson.DescriptorExtensionList;
import hudson.Util;
import hudson.Extension;
import hudson.views.BuildButtonColumn;
import hudson.views.JobColumn;
import hudson.views.LastDurationColumn;
import hudson.views.LastFailureColumn;
import hudson.views.LastSuccessColumn;
import hudson.views.ListViewColumn;
import hudson.views.ListViewColumnDescriptor;
import hudson.views.StatusColumn;
import hudson.views.WeatherColumn;
import hudson.views.StatusColumn.DescriptorImpl;
import hudson.model.Descriptor.FormException;
import hudson.tasks.test.AbstractTestResultAction;
import hudson.triggers.Messages;
import hudson.triggers.SCMTrigger.SCMTriggerCause;
import hudson.util.CaseInsensitiveComparator;
import hudson.util.DescribableList;
import hudson.util.FormValidation;
import hudson.model.Cause.UserCause;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
import javax.servlet.ServletException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import javax.servlet.ServletException;
/**
* Displays {@link Job}s in a flat list view.
*
* @author Kohsuke Kawaguchi
*/
public class OneAndOneView extends View implements Saveable {
/**
* List of job names. This is what gets serialized.
*/
/* package */final SortedSet<String> jobNames = new TreeSet<String>(
CaseInsensitiveComparator.INSTANCE);
private DescribableList<ListViewColumn, Descriptor<ListViewColumn>> columns;
/**
* Include regex string.
*/
private String includeRegex;
/**
* Compiled include pattern from the includeRegex string.
*/
private transient Pattern includePattern;
private String showType;
public String getShowType() {
return showType;
}
private static final String TYPEALL = "ALL";
private static final String TYPESUCCESS = "Successes";
private static final String TYPEFAILURE = "Failures";
private static final String TYPEUNSTABLE = "Unstables";
private static final String TYPEDISABLED = "Disabled";
private static final String TYPETESTERRORS = "TestErrors";
public String getTYPEALL() {
return TYPEALL;
}
public String getTYPESUCCESS() {
return TYPESUCCESS;
}
public String getTYPEFAILURE() {
return TYPEFAILURE;
}
public String getTYPEUNSTABLE() {
return TYPEUNSTABLE;
}
public String getTYPEDISABLED() {
return TYPEDISABLED;
}
public String getTYPETESTERRORS() {
return TYPETESTERRORS;
}
private String viewType;
public String getViewType() {
return viewType;
}
public static final String VIEWTYPEGLOBAL = "Global";
public static final String VIEWTYPEADMIN = "Admin";
public static final String VIEWTYPEUSER = "User";
public String getVIEWTYPEGLOBAL() {
return VIEWTYPEGLOBAL;
}
public String getVIEWTYPEADMIN() {
return VIEWTYPEADMIN;
}
public String getVIEWTYPEUSER() {
return VIEWTYPEUSER;
}
private String viewUserName;
public String getViewUserName() {
if (viewUserName == null)
viewUserName = "";
return viewUserName;
}
@DataBoundConstructor
public OneAndOneView(String name) {
super(name);
initColumns();
}
public OneAndOneView(String name, ViewGroup owner) {
this(name);
this.owner = owner;
}
public void save() throws IOException {
// persistence is a part of the owner.
// due to the initialization timing issue, it can be null when this method is called.
if (owner!=null)
owner.save();
}
private Object readResolve() {
if (includeRegex != null)
includePattern = Pattern.compile(includeRegex);
initColumns();
return this;
}
protected void initColumns() {
if (columns != null) {
// already persisted
return;
}
// OK, set up default list of columns:
// create all instances
ArrayList<ListViewColumn> r = new ArrayList<ListViewColumn>();
DescriptorExtensionList<ListViewColumn, Descriptor<ListViewColumn>> all = ListViewColumn.all();
ArrayList<Descriptor<ListViewColumn>> left = new ArrayList<Descriptor<ListViewColumn>>(all);
for (Class<? extends ListViewColumn> d: DEFAULT_COLUMNS) {
Descriptor<ListViewColumn> des = all.find(d);
if (des != null) {
try {
r.add(des.newInstance(null, null));
left.remove(des);
} catch (FormException e) {
LOGGER.log(Level.WARNING, "Failed to instantiate "+des.clazz,e);
}
}
}
for (Descriptor<ListViewColumn> d : left)
try {
if (d instanceof ListViewColumnDescriptor) {
ListViewColumnDescriptor ld = (ListViewColumnDescriptor) d;
if (!ld.shownByDefault()) continue; // skip this
}
ListViewColumn lvc = d.newInstance(null, null);
if (!lvc.shownByDefault()) continue; // skip this
r.add(lvc);
} catch (FormException e) {
LOGGER.log(Level.WARNING, "Failed to instantiate "+d.clazz,e);
}
columns = new DescribableList<ListViewColumn, Descriptor<ListViewColumn>>(this,r);
}
/**
* Returns the transient {@link Action}s associated with the top page.
*
* @see Hudson#getActions()
*/
public List<Action> getActions() {
return Hudson.getInstance().getActions();
}
public Iterable<ListViewColumn> getColumns() {
return columns;
}
/**
* Returns a read-only view of all {@link Job}s in this view.
*
* <p>
* This method returns a separate copy each time to avoid concurrent
* modification issue.
*/
public synchronized List<TopLevelItem> getItems() {
SortedSet<String> names = new TreeSet<String>(jobNames);
if (includePattern != null) {
for (TopLevelItem item : Hudson.getInstance().getItems()) {
String itemName = item.getName();
if (includePattern.matcher(itemName).matches()) {
names.add(itemName);
}
}
}
for (Iterator<String> iterator = names.iterator(); iterator.hasNext();) {
String name = iterator.next();
Result result = Result.NOT_BUILT;
AbstractTestResultAction testResultAction = null;
if (Hudson.getInstance().getItem(name) instanceof AbstractProject){
if (((AbstractProject<?, ?>) Hudson.getInstance().getItem(name))
.getLastBuild() != null) {
testResultAction = ((AbstractProject<?, ?>) Hudson
.getInstance().getItem(name)).getLastBuild()
.getTestResultAction();
result = ((AbstractProject<?, ?>) Hudson.getInstance().getItem(
name)).getLastBuild().getResult();
}
boolean isDisabled = ((AbstractProject<?, ?>) Hudson.getInstance()
.getItem(name)).isDisabled();
int failedTestCount = 0;
if (testResultAction != null)
failedTestCount = testResultAction.getFailCount();
if (showType != null) {
if ((showType.equals(TYPEFAILURE))
&& ((result != Result.FAILURE) || isDisabled)) {
iterator.remove();
} else if ((showType.equals(TYPESUCCESS))
&& ((result != Result.SUCCESS) || isDisabled)) {
iterator.remove();
} else if ((showType.equals(TYPEUNSTABLE))
&& ((result != Result.UNSTABLE) || isDisabled)) {
iterator.remove();
} else if ((showType.equals(TYPEDISABLED)) && (!isDisabled)) {
iterator.remove();
} else if ((showType.equals(TYPETESTERRORS))
&& (failedTestCount == 0)) {
iterator.remove();
}
}
}
else{
iterator.remove();
}
}
List<TopLevelItem> items = new ArrayList<TopLevelItem>(names.size());
for (String name : names) {
TopLevelItem item = Hudson.getInstance().getItem(name);
if (item != null)
items.add(item);
}
return items;
}
public boolean contains(TopLevelItem item) {
return jobNames.contains(item.getName());
}
public String getIncludeRegex() {
return includeRegex;
}
public Item doCreateItem(StaplerRequest req, StaplerResponse rsp)
throws IOException, ServletException {
Item item = Hudson.getInstance().doCreateItem(req, rsp);
if (item != null) {
jobNames.add(item.getName());
owner.save();
}
return item;
}
@Override
public synchronized void onJobRenamed(Item item, String oldName,
String newName) {
if (jobNames.remove(oldName) && newName != null)
jobNames.add(newName);
}
/**
* Build all jobs of this view.
*
* @throws ServletException
*/
public synchronized void doBuild(StaplerRequest req, StaplerResponse rsp)
throws IOException, ServletException {
for (TopLevelItem item : this.getItems()) {
((AbstractProject<?, ?>) item).scheduleBuild(new UserCause());
}
rsp.sendRedirect(".");
}
/**
* Handles the configuration submission.
*
* Load view-specific properties here.
*/
@Override
protected void submit(StaplerRequest req) throws IOException, ServletException,
FormException {
jobNames.clear();
for (TopLevelItem item : Hudson.getInstance().getItems()) {
if (req.getParameter(item.getName()) != null)
jobNames.add(item.getName());
}
if (req.getParameter("showType") != null)
showType = req.getParameter("showType");
else
showType = TYPEALL;
if (req.getParameter("viewType") != null)
viewType = req.getParameter("viewType");
else
viewType = VIEWTYPEGLOBAL;
if (req.getParameter("viewUserName") != null)
viewUserName = req.getParameter("viewUserName");
else
viewUserName = "";
if (req.getParameter("useincluderegex") != null) {
includeRegex = Util.nullify(req.getParameter("includeRegex"));
includePattern = Pattern.compile(includeRegex);
} else {
includeRegex = null;
includePattern = null;
}
if (columns == null) {
columns = new DescribableList<ListViewColumn, Descriptor<ListViewColumn>>(
Saveable.NOOP);
}
columns.rebuildHetero(req, req.getSubmittedForm(), Hudson.getInstance()
.getDescriptorList(ListViewColumn.class), "columns");
}
@Extension
public static final class DescriptorImpl extends ViewDescriptor {
public String getDisplayName() {
return "1&1 View";
}
/**
* Checks if the include regular expression is valid.
*/
public FormValidation doCheckIncludeRegex(@QueryParameter String value)
throws IOException, ServletException, InterruptedException {
String v = Util.fixEmpty(value);
if (v != null) {
try {
Pattern.compile(v);
} catch (PatternSyntaxException pse) {
return FormValidation.error(pse.getMessage());
}
}
return FormValidation.ok();
}
}
public static List<ListViewColumn> getDefaultColumns() {
ArrayList<ListViewColumn> r = new ArrayList<ListViewColumn>();
DescriptorExtensionList<ListViewColumn, Descriptor<ListViewColumn>> all = ListViewColumn.all();
for (Class<? extends ListViewColumn> t : DEFAULT_COLUMNS) {
Descriptor<ListViewColumn> d = all.find(t);
if (d != null) {
try {
r.add (d.newInstance(null, null));
} catch (FormException e) {
LOGGER.log(Level.WARNING, "Failed to instantiate "+d.clazz,e);
}
}
}
return Collections.unmodifiableList(r);
}
public static class ViewCause extends Cause {
@Override
public String getShortDescription() {
if (Hudson.getAuthentication().getName() != null) {
return Hudson.getAuthentication().getName()
+ " triggered this build via complete view build of ";
//+ "View"
} else {
return "Unknown triggered this build via complete view build of ";
//+ getDisplayName();
}
}
@Override
public boolean equals(Object o) {
return o instanceof ViewCause;
}
@Override
public int hashCode() {
return 7;
}
}
private static final Logger LOGGER = Logger.getLogger(ListView.class.getName());
/**
* Traditional column layout before the {@link ListViewColumn} becomes extensible.
*/
private static final List<Class<? extends ListViewColumn>> DEFAULT_COLUMNS = Arrays.asList(
StatusColumn.class,
WeatherColumn.class,
BuildButtonColumn.class,
JobColumn.class,
LastSuccessColumn.class,
LastFailureColumn.class,
LastDurationColumn.class
);
}