package hudson.plugins.accurev; import hudson.Extension; import hudson.Util; import hudson.console.AnnotatedLargeText; import hudson.model.*; import hudson.model.listeners.ItemListener; import hudson.plugins.accurev.AccurevSCM.AccurevSCMDescriptor; import hudson.plugins.accurev.AccurevSCM.AccurevServer; import hudson.triggers.Trigger; import hudson.triggers.TriggerDescriptor; import hudson.util.StreamTaskListener; import jenkins.model.Jenkins; import org.apache.commons.jelly.XMLOutput; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.math.NumberUtils; import org.kohsuke.stapler.DataBoundConstructor; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; import java.io.*; import java.nio.file.Files; import java.util.*; import java.util.logging.Logger; import static java.nio.charset.StandardCharsets.UTF_8; /** * Initialized by josp on 16/08/16. */ public class AccurevPromoteTrigger extends Trigger<AbstractProject<?, ?>> { private static final Logger LOGGER = Logger.getLogger(AccurevPromoteTrigger.class.getName()); private static final HashMap<String, AccurevPromoteListener> listeners = new HashMap<>(); private static final String ACCUREVLASTTRANSFILENAME = "AccurevLastTrans.txt"; @DataBoundConstructor public AccurevPromoteTrigger() { super(); } public synchronized static void initServer(String host, AccurevServer server) { if (!listeners.containsKey(host) && server.isUsePromoteListen()) { listeners.put(host, new AccurevPromoteListener(server)); } } public synchronized static void validateListeners() { AccurevSCMDescriptor descriptor = Jenkins.getInstance().getDescriptorByType(AccurevSCMDescriptor.class); for (AccurevServer server : descriptor.getServers()) { initServer(server.getHost(), server); } for (Project<?, ?> p : Jenkins.getInstance().getAllItems(Project.class)) { AccurevPromoteTrigger t = p.getTrigger(AccurevPromoteTrigger.class); if (t != null) { if (t.getServer().isUsePromoteListen()) { String host = t.getServer().getHost(); if (StringUtils.isNotEmpty(host)) { AccurevPromoteListener listener = listeners.get(host); if (null != listener) { listener.addTrigger(t); } } } } } } public static void setLastTransaction(Job<?, ?> job, String previous) throws IOException { if (job == null) throw new IOException("Job is null"); File f = new File(job.getRootDir(), ACCUREVLASTTRANSFILENAME); try (BufferedWriter br = Files.newBufferedWriter(f.toPath(), UTF_8)) { br.write(previous); } } private String getLastTransaction(Job<?, ?> job) throws IOException { if (job == null) throw new IOException("Job is null"); File f = new File(job.getRootDir(), ACCUREVLASTTRANSFILENAME); if (!f.exists()) { if (f.createNewFile()) return ""; else throw new IOException("Failed to create file"); } try (BufferedReader br = Files.newBufferedReader(f.toPath(), UTF_8)) { return br.readLine(); } } @Override public void start(AbstractProject<?, ?> project, boolean newInstance) { super.start(project, newInstance); AccurevServer server = getServer(); String host = server.getHost(); if (StringUtils.isNotEmpty(host)) { initServer(host, server); AccurevPromoteListener listener = listeners.get(host); if (listener != null) { listener.addTrigger(this); removeDuplicatedTriggers(listener.getTriggers()); } } } private void removeDuplicatedTriggers(HashSet<AccurevPromoteTrigger> triggers) { Map<String, AccurevPromoteTrigger> temp = new HashMap<>(); for (AccurevPromoteTrigger trigger : triggers) { temp.put(trigger.getProjectName(), trigger); } triggers.clear(); triggers.addAll(temp.values()); } @Override public void stop() { AccurevServer server = getServer(); if (server != null) { String host = server.getHost(); if (StringUtils.isNotEmpty(host)) { AccurevPromoteListener listener = listeners.get(host); if (listener != null) { listener.removeTrigger(this); } } } super.stop(); } public String getProjectName() { return job == null ? "" : job.getName(); } public String getDepot() { AccurevSCM scm = getScm(); return scm == null ? "" : scm.getDepot(); } public String getStream() { AccurevSCM scm = getScm(); return scm == null ? "" : scm.getStream(); } public AccurevServer getServer() { AccurevSCM scm = getScm(); if (null != scm) { return scm.getServer(); } return null; } public void scheduleBuild(String author, String stream) { LOGGER.fine("schedule build: " + getProjectName()); if (job != null) job.scheduleBuild2(10, new AccurevPromoteCause(author, stream)); else LOGGER.warning("Job is null"); } @Override public DescriptorImpl getDescriptor() { return (DescriptorImpl) super.getDescriptor(); } @CheckForNull public AccurevSCM getScm() { if (job != null && job.getScm() instanceof AccurevSCM) { return (AccurevSCM) job.getScm(); } return null; } public boolean checkForChanges(String promoteDepot, String promoteStream, int promoteTrans, Map<String, AccurevStream> streams) { try (StreamTaskListener listener = new StreamTaskListener(getLogFile())) { PrintStream logger = listener.getLogger(); AccurevSCM scm = getScm(); if (scm == null || job == null) return false; if (getStream().equals(promoteStream)) { logger.println("Matching stream: " + promoteStream); int lastTrans = NumberUtils.toInt(getLastTransaction(job), 0); logger.println("Last build Transaction: " + lastTrans + ", promote transaction: " + promoteTrans); if (promoteTrans > lastTrans) { setLastTransaction(job, String.valueOf(promoteTrans)); return true; } } else if (promoteDepot.equals(getDepot()) && !scm.isIgnoreStreamParent()) { String localStream; try { localStream = scm.getPollingStream(job, listener); } catch (IllegalArgumentException ex) { listener.getLogger().println(ex.getMessage()); return false; } if (streams == null) { listener.fatalError("streams EMPTY"); return false; } if (!streams.containsKey(localStream)) { listener.fatalError("The specified stream, '" + localStream + "' does not appear to exist!"); return false; } AccurevStream stream = streams.get(localStream); do { if (stream.getName().equals(promoteStream)) { logger.println("Found matching parent stream: " + promoteStream); int lastTrans = NumberUtils.toInt(getLastTransaction(job), 0); logger.println("Last build Transaction: " + lastTrans + ", promote transaction: " + promoteTrans); if (promoteTrans > lastTrans) { setLastTransaction(job, String.valueOf(promoteTrans)); return true; } } stream = stream.getParent(); } while (stream != null && stream.isReceivingChangesFromParent()); logger.println("No matching parent stream found"); } } catch (IllegalArgumentException | IOException ex) { LOGGER.warning(ex.getMessage()); } return false; } private File getLogFile() throws IOException { if (job == null) throw new IOException("No job"); return new File(job.getRootDir(), "accurev-promote-trigger.log"); } public Collection<? extends Action> getProjectActions() { if (job == null) { return Collections.emptyList(); } return Collections.singleton(new AccurevPromoteAction()); } @Extension public static class DescriptorImpl extends TriggerDescriptor { @Override public boolean isApplicable(Item item) { return true; } @Nonnull @Override public String getDisplayName() { return "Build when a change is promoted to AccuRev"; } @Extension public static class ItemListenerImpl extends ItemListener { @Override public void onLoaded() { validateListeners(); } } } public final class AccurevPromoteAction implements Action { public Job<?, ?> getOwner() { return job; } @Override public String getIconFileName() { return "clipboard.png"; } @Override public String getDisplayName() { return "Accurev Promote Log"; } @Override public String getUrlName() { return "AccurevPromoteLog"; } public String getLog() throws IOException { return Util.loadFile(getLogFile()); } @SuppressWarnings("unused") // Used by Jetty public void writeLogTo(XMLOutput out) throws IOException { new AnnotatedLargeText<>(getLogFile(), UTF_8, true, this) .writeHtmlTo(0, out.asWriter()); } } }