/******************************************************************************* * * Copyright (c) 2004-2013 Oracle Corporation. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * * Roy Varghese * * *******************************************************************************/ package hudson.util; import com.google.common.base.Function; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import hudson.model.BuildHistory; import hudson.model.BuildHistory.Record; import hudson.model.Hudson; import hudson.model.Item; import hudson.model.Job; import hudson.model.Node; import hudson.model.Result; import hudson.model.Run; import hudson.model.View; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.GregorianCalendar; import java.util.Iterator; import java.util.List; /** * The equivalent of {@link RunList} that is based on {@link BuildHistory.Record}s * instead of the more heavy-weight {@link Run} object. * * @author Roy Varghese */ public class BuildHistoryList<J extends Job<J,R>, R extends Run<J,R>> extends AbstractRunList<BuildHistory.Record<J,R>> { private static class DateComparator<JobT extends Job<JobT,RunT>, RunT extends Run<JobT,RunT>> implements Comparator<Record<JobT,RunT>> { public int compare(Record<JobT,RunT> lhs, Record<JobT,RunT> rhs) { long lt = lhs.getTimeInMillis(); long rt = rhs.getTimeInMillis(); if (lt > rt) { return -1; } if (lt < rt) { return 1; } return 0; } }; private BuildHistoryList(List<BuildHistory.Record<J,R>> records) { super(records); } /** * Creates a {@code BuildHistoryList} from a single Job. */ public static <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,RunT>> BuildHistoryList<JobT,RunT> newBuildHistoryList(BuildHistory<JobT,RunT> history) { return new BuildHistoryList(history.allRecords()); } /** * Create a {@code BuildHistoryList} for a collection of Jobs. */ public static <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,RunT>> BuildHistoryList<JobT,RunT> newBuildHistoryList(Collection<JobT> jobs) { ArrayList<Record<JobT,RunT>> list = new ArrayList<Record<JobT,RunT>>(); for (JobT job: jobs) { BuildHistory<JobT,RunT> bh = job.getBuildHistoryData(); list.addAll(bh.allRecords()); } Collections.sort(list, new DateComparator<JobT,RunT>()); return new BuildHistoryList(list); } /** * Create a {@code BuildHistoryList} for a view. */ public static BuildHistoryList newBuildHistoryList(View view) { ArrayList list = new ArrayList(); for (Item item : view.getItems()) { for (Job<?, ?> j : item.getAllJobs()) { list.addAll(j.getBuildHistoryData().allRecords()); } } Collections.sort(list, new DateComparator()); return new BuildHistoryList(list); } @Override public BuildHistory.Record<J,R> getFirstBuild() { return size() > 0? get(0): null; } @Override public BuildHistory.Record<J,R> getLastBuild() { return size() > 0? get(size()-1): null; } /** * Filter the list to non-successful builds only. */ public BuildHistoryList<J,R> failureOnly() { Iterator<BuildHistory.Record<J,R>> iter = new Iterators.FilterIterator<Record<J,R>>(this.iterator()) { @Override protected boolean filter(Record<J, R> record) { return !Result.SUCCESS.equals(record.getResult()); } }; return new BuildHistoryList<J,R>( ImmutableList.copyOf(iter)); } /** * Filter the list to regression builds only. */ @Override public BuildHistoryList<J,R> regressionOnly() { Iterator<BuildHistory.Record<J,R>> iter = new Iterators.FilterIterator<Record<J,R>>(this.iterator()) { @Override protected boolean filter(Record<J, R> record) { return record.getBuildStatusSummary().isWorse; } }; return new BuildHistoryList<J,R>( ImmutableList.copyOf(iter)); } /** * Filter the list by timestamp. * * {@code s<=;e}. */ @Override public BuildHistoryList<J,R> byTimestamp(long start, long end) { Comparator<Long> DESCENDING_ORDER = new Comparator<Long>() { public int compare(Long o1, Long o2) { if (o1 > o2) { return -1; } if (o1 < o2) { return +1; } return 0; } }; Function<Record<J,R>,Long> TRANSFORMER = new Function<Record<J,R>,Long>() { @Override public Long apply(Record<J, R> input) { return input.getTimeInMillis(); } }; int s = Collections.binarySearch(Lists.transform(this, TRANSFORMER), start, DESCENDING_ORDER); if (s < 0) { s = -(s + 1); // min is inclusive } int e = Collections.binarySearch(Lists.transform(this, TRANSFORMER), end, DESCENDING_ORDER); if (e < 0) { e = -(e + 1); } else { e++;// max is exclusive } return new BuildHistoryList<J,R>(subList(e,s)); } /** * Reduce the size of the list by only leaving relatively new ones. This * also removes on-going builds, as RSS cannot be used to publish * information if it changes. */ @Override public BuildHistoryList<J,R> newBuilds() { GregorianCalendar threshold = new GregorianCalendar(); threshold.add(Calendar.DAY_OF_YEAR, -7); final long timeInMillis = threshold.getTimeInMillis(); Iterator<Record<J,R>> iter = new Iterators.FilterIterator<Record<J,R>>(this.iterator()) { int count = 0; @Override protected boolean filter(Record<J, R> record) { boolean result = ( !record.isBuilding() && (count < 10 || record.getTimeInMillis() > timeInMillis)); if ( result ) { count++; } return result; } }; return new BuildHistoryList<J,R>(Lists.newArrayList(iter)); } @Override public BuildHistoryList<J,R> node(final Node node) { Iterator<Record<J,R>> iter = new Iterators.FilterIterator<Record<J,R>>(this.iterator()) { int count = 0; @Override protected boolean filter(Record<J, R> record) { String nodeName = record.getBuiltOnNodeName(); return (nodeName == null && node == Hudson.getInstance()) || node.getNodeName().equals(nodeName); } }; return new BuildHistoryList(Lists.newArrayList(iter)); } }