com.redhat.jenkins.plugins.ci.messaging.FedMsgMessagingWorker.java Source code

Java tutorial

Introduction

Here is the source code for com.redhat.jenkins.plugins.ci.messaging.FedMsgMessagingWorker.java

Source

package com.redhat.jenkins.plugins.ci.messaging;

import hudson.EnvVars;
import hudson.model.Result;
import hudson.model.TaskListener;
import hudson.model.Run;

import java.io.StringReader;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;

import net.sf.json.JSONObject;

import org.apache.commons.lang.text.StrSubstitutor;
import org.apache.commons.lang3.StringUtils;
import org.zeromq.ZMQ;
import org.zeromq.ZMsg;
import org.zeromq.jms.selector.ZmqMessageSelector;
import org.zeromq.jms.selector.ZmqSimpleMessageSelector;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.redhat.jenkins.plugins.ci.CIEnvironmentContributingAction;
import com.redhat.jenkins.plugins.ci.messaging.checks.MsgCheck;
import com.redhat.jenkins.plugins.ci.messaging.data.FedmsgMessage;
import com.redhat.utils.MessageUtils;

/*
 * 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 FedMsgMessagingWorker extends JMSMessagingWorker {

    private static final Logger log = Logger.getLogger(FedMsgMessagingWorker.class.getName());
    private final FedMsgMessagingProvider provider;
    private final MessagingProviderOverrides overrides;
    public static final String DEFAULT_PREFIX = "org.fedoraproject";

    private ZMQ.Context context;
    private ZMQ.Poller poller;
    private ZMQ.Socket socket;
    private boolean interrupt = false;
    private String topic;
    private String selector;

    public FedMsgMessagingWorker(FedMsgMessagingProvider fedMsgMessagingProvider,
            MessagingProviderOverrides overrides, String jobname) {
        this.provider = fedMsgMessagingProvider;
        this.overrides = overrides;
        this.jobname = jobname;
    }

    @Override
    public boolean subscribe(String jobname, String selector) {
        this.topic = getTopic();
        this.selector = selector;
        if (this.topic != null) {
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    if (!isConnected()) {
                        if (!connect()) {
                            return false;
                        }
                    }
                    if (socket == null) {
                        socket = context.socket(ZMQ.SUB);
                        log.info("Subscribing job '" + jobname + "' to " + this.topic + " topic.");
                        socket.subscribe(this.topic.getBytes());
                        socket.setLinger(0);
                        socket.connect(provider.getHubAddr());
                        poller.register(socket, ZMQ.Poller.POLLIN);
                        log.info("Successfully subscribed job '" + jobname + "' to " + this.topic
                                + " topic with selector: " + selector);
                    } else {
                        log.info("Already subscribed to " + this.topic + " topic with selector: " + selector
                                + " for job '" + jobname);
                    }
                    return true;
                } catch (Exception ex) {

                    // Either we were interrupted, or something else went
                    // wrong. If we were interrupted, then we will jump ship
                    // on the next iteration. If something else happened,
                    // then we just unsubscribe here, sleep, so that we may
                    // try again on the next iteration.

                    log.log(Level.SEVERE, "Eexception raised while subscribing job '" + jobname + "', retrying in "
                            + RETRY_MINUTES + " minutes.", ex);
                    if (!Thread.currentThread().isInterrupted()) {

                        unsubscribe(jobname);

                        try {
                            Thread.sleep(RETRY_MINUTES * 60 * 1000);
                        } catch (InterruptedException ie) {
                            // We were interrupted while waiting to retry.
                            // We will jump ship on the next iteration.

                            // NB: The interrupt flag was cleared when
                            // InterruptedException was thrown. We have to
                            // re-install it to make sure we eventually
                            // leave this thread.
                            Thread.currentThread().interrupt();
                        }
                    }
                }
            }
        }
        return false;
    }

    @Override
    public void unsubscribe(String jobname) {
        try {
            if (poller != null) {
                for (Integer i = 0; i < poller.getSize(); i++) {
                    ZMQ.Socket s = poller.getSocket(i);
                    poller.unregister(s);
                    s.disconnect(provider.getHubAddr());
                    log.info("Un-subscribing job '" + jobname + "' from " + this.topic + " topic.");
                    socket.unsubscribe(this.topic.getBytes());
                }
                socket.close();
            }
            if (context != null) {
                context.term();
            }
        } catch (Exception e) {
            log.fine(e.getMessage());
        }
        poller = null;
        context = null;
        socket = null;
    }

    private String formatMessage(FedmsgMessage data) {
        return data.getMsg().toString();
    }

    private void process(FedmsgMessage data) {
        Map<String, String> params = new HashMap<String, String>();
        params.put("CI_MESSAGE", getMessageBody(data));

        Iterator<String> it = data.getMsg().keySet().iterator();
        while (it.hasNext()) {
            String key = it.next();
            Object obj = data.getMsg().get(key);
            if (obj instanceof String) {
                params.put(key, (String) obj);
            }
            if (obj instanceof Integer) {
                params.put(key, ((Integer) obj).toString());
            }
        }
        trigger(jobname, formatMessage(data), params);
    }

    private String getMessageBody(FedmsgMessage data) {
        return JSONObject.fromObject(data.getMsg()).toString();
    }

    @Override
    public void receive(String jobname, List<MsgCheck> checks, long timeoutInMs) {
        ObjectMapper mapper = new ObjectMapper();
        long start = new Date().getTime();
        try {
            while ((new Date().getTime() - start) < timeoutInMs) {
                if (poller.poll(1000) > 0) {
                    for (Integer i = 0; i < poller.getSize(); i++) {
                        if (poller.pollin(i)) {
                            ZMsg z = ZMsg.recvMsg(poller.getSocket(i));
                            String json = z.getLast().toString();
                            FedmsgMessage data = mapper.readValue(json, FedmsgMessage.class);
                            data.getMsg().put("topic", data.getTopic());
                            ZmqMessageSelector selectorObj = ZmqSimpleMessageSelector.parse(selector);
                            log.info("Evaluating selector: " + selectorObj.toString());
                            if (!selectorObj.evaluate(data.getMsg())) {
                                log.info("false");
                                continue;
                            }
                            //check checks here
                            boolean allPassed = true;
                            for (MsgCheck check : checks) {
                                if (!verify(data, check)) {
                                    allPassed = false;
                                    log.fine("msg check: " + check.toString() + " failed against: "
                                            + formatMessage(data));
                                    break;
                                }
                            }
                            if (allPassed) {
                                if (checks.size() > 0) {
                                    log.info("All msg checks have passed.");
                                }
                                process(data);
                            } else {
                                log.info("Some msg checks did not pass.");
                            }
                        }
                    }
                } else {
                    if (interrupt) {
                        log.info("We have been interrupted...");
                        break;
                    }
                }
            }
            if (!interrupt) {
                log.info("No message received for the past " + timeoutInMs + " ms, re-subscribing for job '"
                        + jobname + "'.");
                unsubscribe(jobname);
            } else {
                interrupt = false;
            }
        } catch (Exception e) {
            if (!Thread.currentThread().isInterrupted()) {
                // Something other than an interrupt causes this.
                // Unsubscribe, but stay in our loop and try to reconnect..
                log.log(Level.WARNING, "JMS exception raised, going to re-subscribe for job '" + jobname + "'.", e);
                log.log(Level.SEVERE, org.apache.commons.lang.exception.ExceptionUtils.getStackTrace(e));
                unsubscribe(jobname); // Try again next time.
            }
        }

    }

    private boolean verify(FedmsgMessage message, MsgCheck check) {
        Map<String, Object> msg = message.getMsg();
        if (msg == null) {
            return false;
        }

        Object val = msg.get(check.getField());
        String sVal = "";
        if (val != null) {
            sVal = val.toString();
        }
        String eVal = "";
        if (check.getExpectedValue() != null) {
            eVal = check.getExpectedValue();
        }
        if (Pattern.compile(eVal).matcher(sVal).find()) {
            return true;
        }
        return false;
    }

    @Override
    public boolean connect() {
        context = ZMQ.context(1);
        poller = new ZMQ.Poller(1);
        return true;
    }

    @Override
    public boolean isConnected() {
        return poller != null;
    }

    @Override
    public void disconnect() {
    }

    @Override
    public boolean sendMessage(Run<?, ?> build, TaskListener listener, MessageUtils.MESSAGE_TYPE type, String props,
            String content) {
        ZMQ.Context context = ZMQ.context(1);
        ZMQ.Socket sock = context.socket(ZMQ.PUB);
        sock.setLinger(0);
        log.fine("pub address: " + provider.getPubAddr());
        sock.connect(provider.getPubAddr());
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        HashMap<String, Object> message = new HashMap<String, Object>();
        message.put("CI_NAME", build.getParent().getName());
        message.put("CI_TYPE", type.getMessage());
        if (!build.isBuilding()) {
            message.put("CI_STATUS", (build.getResult() == Result.SUCCESS ? "passed" : "failed"));
        }

        StrSubstitutor sub = null;
        try {
            sub = new StrSubstitutor(build.getEnvironment(listener));
            if (props != null && !props.trim().equals("")) {
                Properties p = new Properties();
                p.load(new StringReader(props));
                @SuppressWarnings("unchecked")
                Enumeration<String> e = (Enumeration<String>) p.propertyNames();
                while (e.hasMoreElements()) {
                    String key = e.nextElement();
                    message.put(key, sub.replace(p.getProperty(key)));
                }
            }

            message.put(MESSAGECONTENTFIELD, sub.replace(content));

            FedmsgMessage blob = new FedmsgMessage();
            blob.setMsg(message);
            blob.setTopic(getTopic());
            blob.setTimestamp((new java.util.Date()).getTime() / 1000);

            sock.sendMore(blob.getTopic());
            sock.send(blob.toJson().toString());
            log.fine(blob.toJson().toString());

        } catch (Exception e) {
            log.log(Level.SEVERE, "Unhandled exception: ", e);
            return false;
        } finally {
            sock.close();
            context.term();
        }

        return true;
    }

    @Override
    public String waitForMessage(Run<?, ?> build, String selector, String variable, Integer timeout) {
        log.info("Waiting for message with selector: " + selector);
        ZMQ.Context lcontext = ZMQ.context(1);
        ZMQ.Poller lpoller = new ZMQ.Poller(1);
        ZMQ.Socket lsocket = lcontext.socket(ZMQ.SUB);

        lsocket.subscribe(getTopic().getBytes());
        lsocket.setLinger(0);
        lsocket.connect(provider.getHubAddr());
        lpoller.register(lsocket, ZMQ.Poller.POLLIN);

        ObjectMapper mapper = new ObjectMapper();
        long start = new Date().getTime();

        int timeoutInMs = timeout * 60 * 1000;
        try {
            while ((new Date().getTime() - start) < timeoutInMs) {
                if (lpoller.poll(1000) > 0) {
                    for (Integer i = 0; i < lpoller.getSize(); i++) {
                        if (lpoller.pollin(i)) {
                            ZMsg z = ZMsg.recvMsg(lpoller.getSocket(i));
                            String json = z.getLast().toString();
                            FedmsgMessage data = mapper.readValue(json, FedmsgMessage.class);
                            data.getMsg().put("topic", data.getTopic());
                            ZmqMessageSelector selectorObj = ZmqSimpleMessageSelector.parse(selector);
                            log.info("Evaluating selector: " + selectorObj.toString());
                            if (!selectorObj.evaluate(data.getMsg())) {
                                log.info("false");
                                continue;
                            }
                            String value = getMessageBody(data);
                            if (build != null) {
                                if (StringUtils.isNotEmpty(variable)) {
                                    EnvVars vars = new EnvVars();
                                    vars.put(variable, value);
                                    build.addAction(new CIEnvironmentContributingAction(vars));
                                }
                            }
                            return value;
                        }
                    }
                }
            }
            log.severe("Timed out waiting for message!");
        } catch (Exception e) {
            log.log(Level.SEVERE, "Unhandled exception waiting for message.", e);
        } finally {
            for (Integer i = 0; i < lpoller.getSize(); i++) {
                ZMQ.Socket s = lpoller.getSocket(i);
                lpoller.unregister(s);
                s.disconnect(provider.getHubAddr());
                if (provider.getTopic() == null || provider.getTopic().equals("")) {
                    lsocket.unsubscribe(DEFAULT_PREFIX.getBytes());
                } else {

                    lsocket.unsubscribe(provider.getTopic().getBytes());
                }
            }
            lsocket.close();
            lcontext.term();
        }
        return null;
    }

    @Override
    public void prepareForInterrupt() {
        interrupt = true;
    }

    private String getTopic() {
        if (overrides != null && overrides.getTopic() != null && !overrides.getTopic().isEmpty()) {
            return overrides.getTopic();
        } else if (provider.getTopic() != null && !provider.getTopic().isEmpty()) {
            return provider.getTopic();
        } else {
            return DEFAULT_PREFIX;
        }
    }
}