org.jenkinsci.plugins.registry.notification.webhook.JSONWebHook.java Source code

Java tutorial

Introduction

Here is the source code for org.jenkinsci.plugins.registry.notification.webhook.JSONWebHook.java

Source

/**
 * The MIT License
 *
 * Copyright (c) 2015, 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
 * 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.
 */
package org.jenkinsci.plugins.registry.notification.webhook;

import hudson.model.*;
import hudson.model.Queue;
import hudson.security.ACL;
import jenkins.model.Jenkins;
import jenkins.model.ParameterizedJobMixIn;
import net.sf.json.JSONObject;
import org.apache.commons.io.IOUtils;
import org.jenkinsci.plugins.registry.notification.Coordinator;
import org.jenkinsci.plugins.registry.notification.DockerHubTrigger;
import org.jenkinsci.plugins.registry.notification.TriggerStore;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.interceptor.RequirePOST;
import org.kohsuke.stapler.interceptor.RespondSuccess;

import javax.annotation.Nonnull;
import java.io.IOException;
import java.net.URLDecoder;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

public abstract class JSONWebHook implements UnprotectedRootAction {
    private static final Logger logger = Logger.getLogger(JSONWebHook.class.getName());

    public String getIconFileName() {
        return null;
    }

    public String getDisplayName() {
        return "DockerHub web hook";
    }

    @RequirePOST
    @RespondSuccess
    public void doNotify(@QueryParameter(required = false) String payload, StaplerRequest request,
            StaplerResponse response) throws IOException {

        WebHookPayload hookPayload = null;
        if (payload != null) {
            try {
                hookPayload = createPushNotification(JSONObject.fromObject(payload));
            } catch (Exception e) {
                logger.log(Level.SEVERE, "Could not parse the web hook payload!", e);
            }
        } else {
            hookPayload = parse(request);
        }
        if (hookPayload != null) {
            for (PushNotification pushNotification : hookPayload.getPushNotifications()) {
                try {
                    trigger(response, pushNotification);
                } catch (Exception e) {
                    logger.log(Level.SEVERE, "Could not trigger a job!", e);
                }
            }
        }
    }

    /**
     * Stapler entry for the multi build result page
     * @param sha the id of the trigger data.
     */
    @Nonnull
    public ResultPage getDetails(@Nonnull final String sha) throws IOException, InterruptedException {
        TriggerStore.TriggerEntry entry = TriggerStore.getInstance().getEntry(sha);
        if (entry != null) {
            return new ResultPage(entry);
        } else {
            return ResultPage.NO_RESULT;
        }
    }

    protected void trigger(StaplerResponse response, final PushNotification pushNotification) throws IOException {
        final Jenkins jenkins = Jenkins.getInstance();
        if (jenkins == null) {
            return;
        }
        ACL.impersonate(ACL.SYSTEM, new Runnable() {
            @Override
            public void run() {
                // search all jobs for DockerHubTrigger
                for (ParameterizedJobMixIn.ParameterizedJob p : jenkins
                        .getAllItems(ParameterizedJobMixIn.ParameterizedJob.class)) {
                    DockerHubTrigger trigger = DockerHubTrigger.getTrigger(p);
                    if (trigger == null) {
                        logger.log(Level.FINER, "job {0} doesn't have DockerHubTrigger set", p.getName());
                        continue;
                    }
                    logger.log(Level.FINER, "Inspecting candidate job {0}", p.getName());
                    Set<String> allRepoNames = trigger.getAllRepoNames();
                    String repoName = pushNotification.getRepoName();
                    if (allRepoNames.contains(repoName)) {
                        schedule((Job) p, pushNotification);
                    }
                }
            }
        });
    }

    private void schedule(@Nonnull final Job job, @Nonnull final PushNotification pushNotification) {
        if (new JobbMixIn(job).schedule(pushNotification.getCause(), pushNotification.getJobParamerers())) {
            logger.info(pushNotification.getCauseMessage());
            Coordinator coordinator = Coordinator.getInstance();
            if (coordinator != null) {
                coordinator.onTriggered(job, pushNotification);
            }
        }
    }

    /**
     * If someone wanders in to the index page, redirect to Jenkins root.
     *
     * @param response the response object
     * @throws IOException if so
     */
    public void doIndex(StaplerRequest request, StaplerResponse response) throws IOException {
        response.sendRedirect(request.getContextPath() + "/");
    }

    protected abstract WebHookPayload createPushNotification(JSONObject data);

    private WebHookPayload parse(StaplerRequest req) throws IOException {
        //TODO Actually test what duckerhub is really sending
        String body = IOUtils.toString(req.getInputStream(), req.getCharacterEncoding());
        String contentType = req.getContentType();
        if (contentType != null && contentType.startsWith("application/x-www-form-urlencoded")) {
            body = URLDecoder.decode(body, req.getCharacterEncoding());
        }
        logger.log(Level.FINER, "Received commit hook notification : {0}", body);
        try {
            JSONObject payload = JSONObject.fromObject(body);
            return createPushNotification(payload);
        } catch (Exception e) {
            logger.log(Level.SEVERE, "Could not parse the web hook payload!", e);
            return null;
        }
    }

    /**
     * Workaround until {@link ParameterizedJobMixIn#getDefaultParametersValues()} gets public.
     */
    static class JobbMixIn<JobT extends Job<JobT, RunT> & ParameterizedJobMixIn.ParameterizedJob & Queue.Task, RunT extends Run<JobT, RunT> & Queue.Executable>
            extends ParameterizedJobMixIn<JobT, RunT> {

        /**
         * Some breathing room to iterate through most/all of the jobs before the first triggered build starts.
         */
        public static final int MIN_QUIET = 3;

        private JobT the;

        public JobbMixIn(JobT the) {
            this.the = the;
        }

        @Override
        protected JobT asJob() {
            return the;
        }

        public boolean schedule(Cause cause, Set<ParameterValue> parameters) {
            if (!asJob().isBuildable()) {
                return false;
            }
            List<Action> queueActions = new LinkedList<Action>();

            queueActions.add(new ParametersAction(getParameterValues(parameters)));
            queueActions.add(new CauseAction(cause));

            int quiet = Math.max(MIN_QUIET, asJob().getQuietPeriod());

            final Jenkins jenkins = Jenkins.getInstance();
            if (jenkins == null) {
                logger.log(Level.WARNING, "Tried to schedule a build while Jenkins was gone.");
                return false;
            }
            final Queue queue = jenkins.getQueue();
            if (queue == null) {
                throw new IllegalStateException("The queue is not initialized?!");
            }
            Queue.Item i = queue.schedule2(asJob(), quiet, queueActions).getItem();
            return i != null && i.getFuture() != null;
        }

        private List<ParameterValue> getParameterValues(Set<ParameterValue> parameters) {
            Set<ParameterValue> result = new HashSet<ParameterValue>();
            if (isParameterized()) {
                result.addAll(getDefaultParametersValues());
            }
            result.addAll(parameters);
            return Collections.unmodifiableList(new LinkedList<ParameterValue>(result));
        }

        /**
         * Direct copy from {@link ParameterizedJobMixIn#getDefaultParametersValues()} (version 1.580).
         *
         * @return the configured parameters with their default values.
         */
        private List<ParameterValue> getDefaultParametersValues() {
            ParametersDefinitionProperty paramDefProp = asJob().getProperty(ParametersDefinitionProperty.class);
            ArrayList<ParameterValue> defValues = new ArrayList<ParameterValue>();

            /*
             * This check is made ONLY if someone will call this method even if isParametrized() is false.
             */
            if (paramDefProp == null)
                return defValues;

            /* Scan for all parameter with an associated default values */
            for (ParameterDefinition paramDefinition : paramDefProp.getParameterDefinitions()) {
                ParameterValue defaultValue = paramDefinition.getDefaultParameterValue();

                if (defaultValue != null)
                    defValues.add(defaultValue);
            }

            return defValues;
        }

    }
}