Java tutorial
package com.redhat.jenkins.plugins.ci; import hudson.Extension; import hudson.Util; import hudson.model.BuildableItem; import hudson.model.Item; import hudson.model.ParameterValue; import hudson.model.AbstractProject; import hudson.model.CauseAction; import hudson.model.Job; import hudson.model.ParameterDefinition; import hudson.model.ParametersAction; import hudson.model.ParametersDefinitionProperty; import hudson.model.StringParameterValue; import hudson.triggers.Trigger; import hudson.triggers.TriggerDescriptor; import hudson.util.FormValidation; import hudson.util.ListBoxModel; import java.io.IOException; import java.io.ObjectStreamException; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.WeakHashMap; import java.util.logging.Level; import java.util.logging.Logger; import jenkins.model.Jenkins; import jenkins.model.ParameterizedJobMixIn; import org.apache.commons.lang.StringUtils; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.DataBoundSetter; import org.kohsuke.stapler.QueryParameter; import com.redhat.jenkins.plugins.ci.messaging.JMSMessagingProvider; import com.redhat.jenkins.plugins.ci.messaging.MessagingProviderOverrides; import com.redhat.jenkins.plugins.ci.messaging.checks.MsgCheck; /* * The MIT License * * Copyright (c) Red Hat, 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 * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ public class CIBuildTrigger extends Trigger<BuildableItem> { private static final Logger log = Logger.getLogger(CIBuildTrigger.class.getName()); private String selector; private String providerName; private List<MsgCheck> checks = new ArrayList<MsgCheck>(); private MessagingProviderOverrides overrides; public static final transient WeakHashMap<String, CITriggerThread> triggerInfo = new WeakHashMap<String, CITriggerThread>(); private transient boolean providerUpdated; @DataBoundConstructor public CIBuildTrigger(String selector, String providerName, MessagingProviderOverrides overrides, List<MsgCheck> checks) { super(); this.selector = StringUtils.stripToNull(selector); this.providerName = providerName; this.overrides = overrides; if (checks == null) { checks = new ArrayList<MsgCheck>(); } this.checks = checks; } @DataBoundSetter public void setProviderName(String providerName) { this.providerName = providerName; } public MessagingProviderOverrides getOverrides() { return overrides; } @DataBoundSetter public void setOverrides(MessagingProviderOverrides overrides) { this.overrides = overrides; } public List<MsgCheck> getChecks() { if (checks == null) { checks = new ArrayList<MsgCheck>(); } return Collections.unmodifiableList(checks); } public static CIBuildTrigger findTrigger(String fullname) { Jenkins jenkins = Jenkins.getInstance(); final Job p = jenkins.getItemByFullName(fullname, Job.class); if (p != null) { return ParameterizedJobMixIn.getTrigger(p, CIBuildTrigger.class); } return null; } @Override protected Object readResolve() throws ObjectStreamException { if (providerName == null && GlobalCIConfiguration.get().isMigrationInProgress()) { log.info("Provider is null and migration is in progress for providers..."); JMSMessagingProvider provider = GlobalCIConfiguration.get().getConfigs().get(0); if (provider != null) { providerName = provider.getName(); providerUpdated = true; try { if (job != null) { job.save(); } } catch (IOException e) { log.warning("Exception while trying to save job: " + e.getMessage()); } } } return this; } @Override public void start(BuildableItem project, boolean newInstance) { super.start(project, newInstance); startTriggerThread(); } @Override public void stop() { super.stop(); stopTriggerThread(); } public void rename(String oldFullName) { stopTriggerThread(oldFullName); startTriggerThread(); } private void startTriggerThread() { if (providerUpdated) { log.info("Saving job since messaging provider was migrated..."); try { job.save(); } catch (IOException e) { log.warning("Exception while trying to save job: " + e.getMessage()); } } if (job instanceof AbstractProject) { AbstractProject aJob = (AbstractProject) job; if (aJob.isDisabled()) { log.info("Job '" + job.getFullName() + "' is disabled, not subscribing."); return; } } try { stopTriggerThread(); JMSMessagingProvider provider = GlobalCIConfiguration.get().getProvider(providerName); if (provider == null) { log.log(Level.SEVERE, "Failed to locate JMSMessagingProvider with name " + providerName + ". You must update the job configuration. Trigger not started."); return; } CITriggerThread trigger = new CITriggerThread(provider, overrides, job.getFullName(), selector, getChecks()); trigger.setName("CIBuildTrigger-" + job.getFullName() + "-" + provider.getClass().getSimpleName()); trigger.setDaemon(true); trigger.start(); triggerInfo.put(job.getFullName(), trigger); } catch (Exception e) { log.log(Level.SEVERE, "Unhandled exception in trigger start.", e); } } private void stopTriggerThread() { if (job != null) { stopTriggerThread(job.getFullName()); } } private void stopTriggerThread(String fullName) { CITriggerThread thread = triggerInfo.get(fullName); if (thread != null) { try { thread.sendInterrupt(); } catch (Exception e) { log.log(Level.SEVERE, "Unhandled exception in trigger stop.", e); } } triggerInfo.remove(fullName); } public String getSelector() { return selector; } public void setSelector(String selector) { this.selector = selector; } /** * Inspects {@link ParametersAction} to see what kind of capabilities it has in regards to SECURITY-170. * Assuming the safeParameters constructor could not be found. * * @return the inspection result */ private static synchronized ParametersActionInspection getParametersInspection() { if (parametersInspectionCache == null) { parametersInspectionCache = new ParametersActionInspection(); } return parametersInspectionCache; } /** * Stored cache of the inspection. * @see #getParametersInspection() */ private static volatile ParametersActionInspection parametersInspectionCache = null; /** * Data structure with information regarding what kind of capabilities {@link ParametersAction} has. */ private static class ParametersActionInspection { private static final Class<ParametersAction> KLASS = ParametersAction.class; private boolean inspectionFailure; private boolean safeParametersSet = false; private boolean keepUndefinedParameters = false; private boolean hasSafeParameterConfig = false; /** * Constructor that performs the inspection. */ ParametersActionInspection() { try { for (Field field : KLASS.getDeclaredFields()) { if (Modifier.isStatic(field.getModifiers()) && (field.getName().equals("KEEP_UNDEFINED_PARAMETERS_SYSTEM_PROPERTY_NAME") || field.getName().equals("SAFE_PARAMETERS_SYSTEM_PROPERTY_NAME"))) { this.hasSafeParameterConfig = true; break; } } if (hasSafeParameterConfig) { if (Boolean.getBoolean(KLASS.getName() + ".keepUndefinedParameters")) { this.keepUndefinedParameters = true; } this.safeParametersSet = false; } this.inspectionFailure = false; } catch (Exception e) { this.inspectionFailure = true; } } /** * If the system property .keepUndefinedParameters is set and set to true. * * @return true if so. */ boolean isKeepUndefinedParameters() { return keepUndefinedParameters; } /** * If any of the constant fields regarding safeParameters are declared in {@link ParametersAction}. * * @return true if so. */ boolean isHasSafeParameterConfig() { return hasSafeParameterConfig; } /** * If there was an exception when inspecting the class. * * @return true if so. */ public boolean isInspectionFailure() { return inspectionFailure; } } protected ParametersAction createParameters(BuildableItem project, Map<String, String> messageParams) { List<ParameterValue> definedParameters = getDefinedParameters(project); List<ParameterValue> parameters = getUpdatedParameters(messageParams, definedParameters); try { Constructor<ParametersAction> constructor = ParametersAction.class.getConstructor(List.class); return constructor.newInstance(parameters); } catch (NoSuchMethodException e) { ParametersActionInspection inspection = getParametersInspection(); if (inspection.isInspectionFailure()) { log.log(Level.WARNING, "Failed to inspect ParametersAction to determine " + "if we can behave normally around SECURITY-170.\nSee " + "https://wiki.jenkins-ci.org/display/SECURITY/Jenkins+Security+Advisory+2016-05-11" + " for information."); } else if (inspection.isHasSafeParameterConfig()) { StringBuilder txt = new StringBuilder( "Running on a core with SECURITY-170 fixed but no direct way for Gerrit Trigger" + " to self-specify safe parameters."); txt.append(" You should consider upgrading to a new Jenkins core version.\n"); if (inspection.isKeepUndefinedParameters()) { txt.append(".keepUndefinedParameters is set so the trigger should behave normally."); } else { txt.append("No overriding system properties appears to be set,"); txt.append(" your builds might not work as expected.\n"); txt.append( "See https://wiki.jenkins-ci.org/display/SECURITY/Jenkins+Security+Advisory+2016-05-11"); txt.append(" for information."); } log.log(Level.WARNING, txt.toString()); } else { log.log(Level.FINE, "Running on an old core before safe parameters, we should be safe."); } } catch (IllegalAccessException e) { log.log(Level.WARNING, "Running on a core with safe parameters fix available, but not allowed to specify them"); } catch (Exception e) { log.log(Level.WARNING, "Running on a core with safe parameters fix available, but failed to provide them"); } return new ParametersAction(parameters); } public void scheduleBuild(Map<String, String> messageParams) { ParametersAction parameters = createParameters(job, messageParams); List<ParameterValue> definedParameters = getDefinedParameters(job); List<ParameterValue> buildParameters = getUpdatedParameters(messageParams, definedParameters); ParameterizedJobMixIn jobMixIn = new ParameterizedJobMixIn() { @Override protected Job asJob() { return (Job) job; } }; jobMixIn.scheduleBuild2(0, new CauseAction(new CIBuildCause()), parameters, new CIEnvironmentContributingAction(messageParams, buildParameters)); } private List<ParameterValue> getUpdatedParameters(Map<String, String> messageParams, List<ParameterValue> definedParams) { // Update any build parameters that may have values from the triggering message. HashMap<String, ParameterValue> newParams = new HashMap<String, ParameterValue>(); for (ParameterValue def : definedParams) { newParams.put(def.getName(), def); } for (String key : messageParams.keySet()) { if (newParams.containsKey(key)) { StringParameterValue spv = new StringParameterValue(key, messageParams.get(key)); newParams.put(key, spv); } } return new ArrayList<ParameterValue>(newParams.values()); } private List<ParameterValue> getDefinedParameters(BuildableItem project) { List<ParameterValue> parameters = new ArrayList<ParameterValue>(); ParametersDefinitionProperty properties = (ParametersDefinitionProperty) ((Job) project) .getProperty(ParametersDefinitionProperty.class); if (properties != null && properties.getParameterDefinitions() != null) { for (ParameterDefinition paramDef : properties.getParameterDefinitions()) { ParameterValue param = paramDef.getDefaultParameterValue(); if (param != null) { parameters.add(param); } } } return parameters; } @Override public CIBuildTriggerDescriptor getDescriptor() { return (CIBuildTriggerDescriptor) Jenkins.getInstance().getDescriptor(getClass()); } public String getProviderName() { return providerName; } @Extension public static class CIBuildTriggerDescriptor extends TriggerDescriptor { public ListBoxModel doFillProviderNameItems() { ListBoxModel items = new ListBoxModel(); for (JMSMessagingProvider provider : GlobalCIConfiguration.get().getConfigs()) { items.add(provider.getName()); } return items; } public FormValidation doCheckField(@QueryParameter String value) { String field = Util.fixEmptyAndTrim(value); if (field == null) { return FormValidation.error("Field cannot be empty"); } return FormValidation.ok(); } public CIBuildTriggerDescriptor() { super(CIBuildTrigger.class); } @Override public boolean isApplicable(Item item) { return true; } @Override public String getDisplayName() { return Messages.PluginName(); } } }