info.pancancer.arch3.worker.WorkerHeartbeat.java Source code

Java tutorial

Introduction

Here is the source code for info.pancancer.arch3.worker.WorkerHeartbeat.java

Source

/*
 *     Consonance - workflow software for multiple clouds
 *     Copyright (C) 2016 OICR
 *
 *     This program is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package info.pancancer.arch3.worker;

import com.rabbitmq.client.AlreadyClosedException;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.MessageProperties;

import info.pancancer.arch3.Base;
import info.pancancer.arch3.beans.Status;
import info.pancancer.arch3.beans.StatusState;
import info.pancancer.arch3.utils.Utilities;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.commons.configuration.HierarchicalINIConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This class will send a "heartbeat" message. How often it is sent can be configured via setSecondsDelay. The default delay is 2 seconds.
 * The destination queue for the messages, and the body of the messages can also be configured via setter methods.
 *
 * @author sshorser
 *
 */
public class WorkerHeartbeat implements Runnable {

    private String queueName;
    public static final double DEFAULT_DELAY = 30.0;
    private double secondsDelay = DEFAULT_DELAY;
    private final int stdoutSnipSize = DEFAULT_SNIP_SIZE;
    public static final int DEFAULT_SNIP_SIZE = 10;
    private static final int MILLISECONDS_PER_SECOND = 1000;
    private WorkflowRunner statusSource;
    private String networkID;
    private String vmUuid;
    private String jobUuid;
    private HierarchicalINIConfiguration settings;

    protected static final Logger LOG = LoggerFactory.getLogger(WorkerHeartbeat.class);

    @Override
    public void run() {

        Channel reportingChannel = null;
        try {
            try {
                reportingChannel = Utilities.setupExchange(settings, this.queueName);
            } catch (IOException | TimeoutException | AlreadyClosedException e) {
                LOG.error("Exception caught! Queue channel could not be opened, waiting. Exception is: "
                        + e.getMessage(), e);
                // retry after a minute, do not die simply because the launcher is unavailable, it may come back
                Thread.sleep(Base.ONE_MINUTE_IN_MILLISECONDS);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            LOG.info("Caught interrupt signal, heartbeat shutting down.", e);
            return;
        }

        LOG.info("starting heartbeat thread, will send heartbeat message ever " + secondsDelay + " seconds.");
        while (!Thread.interrupted()) {
            // byte[] stdOut = this.getMessageBody().getBytes(StandardCharsets.UTF_8);
            try {
                try {
                    Status heartbeatStatus = new Status();
                    heartbeatStatus.setJobUuid(this.jobUuid);
                    heartbeatStatus.setMessage("job is running; IP address: " + networkID);
                    heartbeatStatus.setState(StatusState.RUNNING);
                    heartbeatStatus.setType(Utilities.JOB_MESSAGE_TYPE);
                    heartbeatStatus.setVmUuid(this.vmUuid);
                    heartbeatStatus.setIpAddress(networkID);

                    // String stdOut = this.statusSource.getStdOut();
                    Lock lock = new ReentrantLock();
                    lock.lock();
                    String stdOut = this.statusSource.getStdOut(stdoutSnipSize);
                    String stdErr = this.statusSource.getStdErr();
                    lock.unlock();
                    heartbeatStatus.setStdout(stdOut);
                    heartbeatStatus.setStderr(stdErr);
                    String heartBeatMessage = heartbeatStatus.toJSON();
                    LOG.debug("Sending heartbeat message to " + queueName + ", with body: " + heartBeatMessage);
                    reportingChannel.basicPublish(queueName, queueName, MessageProperties.PERSISTENT_TEXT_PLAIN,
                            heartBeatMessage.getBytes(StandardCharsets.UTF_8));
                    reportingChannel.waitForConfirms();

                    Thread.sleep((long) (secondsDelay * MILLISECONDS_PER_SECOND));
                } catch (IOException | AlreadyClosedException e) {
                    LOG.error("IOException caught! Message may not have been published. Exception is: "
                            + e.getMessage(), e);
                    // retry after a minute, do not die simply because the launcher is unavailable, it may come back
                    Thread.sleep(Base.ONE_MINUTE_IN_MILLISECONDS);
                }
            } catch (InterruptedException e) {
                LOG.info("Heartbeat shutting down.");
                if (reportingChannel.getConnection().isOpen()) {
                    try {
                        reportingChannel.getConnection().close();
                    } catch (IOException e1) {
                        LOG.error("Error closing reportingChannel connection: " + e1.getMessage(), e1);
                    }
                }
                if (reportingChannel.isOpen()) {
                    try {
                        reportingChannel.close();
                    } catch (IOException e1) {
                        LOG.error("Error (IOException) closing reportingChannel: " + e1.getMessage(), e1);
                    } catch (TimeoutException e1) {
                        LOG.error("Error (TimeoutException) closing reportingChannel: " + e1.getMessage(), e1);
                    }
                }
                LOG.debug("reporting channel open: " + reportingChannel.isOpen());
                LOG.debug("reporting channel connection open: " + reportingChannel.getConnection().isOpen());

                Thread.currentThread().interrupt();
            }
        }
    }

    public void setStatusSource(WorkflowRunner runner) {
        this.statusSource = runner;
    }

    public String getQueueName() {
        return queueName;
    }

    public void setQueueName(String queueName) {
        this.queueName = queueName;
    }

    public double getSecondsDelay() {
        return secondsDelay;
    }

    public void setSecondsDelay(double secondsDelay) {
        if (secondsDelay == 0.0) {
            LOG.error("Sorry, you can't have a delay of 0. Resetting to the default: " + DEFAULT_DELAY);
            this.secondsDelay = WorkerHeartbeat.DEFAULT_DELAY;
        } else {
            this.secondsDelay = secondsDelay;
        }
    }

    public String getNetworkID() {
        return networkID;
    }

    public void setNetworkID(String networkID) {
        this.networkID = networkID;
    }

    public String getVmUuid() {
        return vmUuid;
    }

    public void setVmUuid(String vmUuid) {
        this.vmUuid = vmUuid;
    }

    public WorkflowRunner getStatusSource() {
        return statusSource;
    }

    public String getJobUuid() {
        return jobUuid;
    }

    public void setJobUuid(String jobUuid) {
        this.jobUuid = jobUuid;
    }

    /**
     * @return the settings
     */
    public HierarchicalINIConfiguration getSettings() {
        return settings;
    }

    /**
     * @param settings
     *            the settings to set
     */
    public void setSettings(HierarchicalINIConfiguration settings) {
        this.settings = settings;
    }

}