All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.rhq.enterprise.communications.command.client.ServerPollingThread Maven / Gradle / Ivy

The newest version!
/*
 * RHQ Management Platform
 * Copyright (C) 2005-2008 Red Hat, Inc.
 * All rights reserved.
 *
 * 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 version 2 of the License.
 *
 * 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, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
package org.rhq.enterprise.communications.command.client;

import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

import mazz.i18n.Logger;

import org.rhq.core.util.exception.ThrowableUtil;
import org.rhq.enterprise.communications.command.CommandResponse;
import org.rhq.enterprise.communications.command.impl.identify.IdentifyCommand;
import org.rhq.enterprise.communications.i18n.CommI18NFactory;
import org.rhq.enterprise.communications.i18n.CommI18NResourceKeys;
import org.rhq.enterprise.communications.util.CommUtils;

/**
 * An object that runs in a thread whose sole job is to poll the given remote server. When the server's status changes
 * (that is, goes down or comes back up), the client sender that owns this thread will be told to stop or start sending
 * as appropriate.
 *
 * @author John Mazzitelli
 */
class ServerPollingThread extends Thread {
    /**
     * Logger
     */
    private static final Logger LOG = CommI18NFactory.getLogger(ServerPollingThread.class);

    /**
     * The client sender that asked us to poll the server.
     */
    private final ClientCommandSender m_clientSender;

    /**
     * The amount of time in milliseconds that this thread will sleep in between polling the server.
     */
    private final long m_interval;

    /**
     * This is simply a flag to eliminate a flood of log messages that would get dumped each time the server failed to
     * be communicated with. We want to warn once in the case the failure is due to a misconfiguration (the log message
     * should help diagnose the misconfiguration), but we don't want to continually log warnings in the normal case when
     * the server just isn't up yet.
     */
    private boolean m_warnedAboutConnectionFailure;

    /**
     * The list of polling listeners that will be notified when the sender's polling thread polls the server.
     */
    private final Set m_pollingListeners = new CopyOnWriteArraySet();

    /**
     * Constructor for {@link ServerPollingThread} making this thread a daemon thread.
     *
     * @param client           the client sender on whose behalf we are polling
     * @param polling_interval the amount of time in milliseconds that the thread will pause in between polls
     */
    public ServerPollingThread(ClientCommandSender client, long polling_interval) {
        super("RHQ Server Polling Thread");
        setDaemon(true);

        m_clientSender = client;
        m_interval = polling_interval;
        m_warnedAboutConnectionFailure = false;
    }

    /**
     * @see java.lang.Thread#run()
     */
    @Override
    public void run() {
        LOG.debug(CommI18NResourceKeys.SERVER_POLLING_THREAD_STARTED, m_interval);

        // Chapter 7 - Java Concurrency in Practice
        // Use interruption to do thread cancellation
        try {
            while (!isInterrupted()) {
                try {
                    // Send a small, simple identify command to the server and if it succeeds, tell the client
                    // sender that it is OK to start sending messages, if it is not already sending
                    IdentifyCommand id_cmd = new IdentifyCommand();
                    m_clientSender.preprocessCommand(id_cmd);
                    CommandResponse response = m_clientSender.send(id_cmd);

                    // let all our listeners know what the results of the poll was
                    for (PollingListener listener : m_pollingListeners) {
                        try {
                            listener.pollResponse(response);
                        } catch (Throwable t) {
                            // should never happen, but I'm paranoid
                        }
                    }

                    // there are special cases when we might get a response back but it should be considered "server down".
                    // 1) when the server replies with a NotProcessedException response
                    // 2) when our failover mechanism runs out of retries and it can't find a server to process our request,
                    //    the comm layer will reply with a failoverable exception.
                    // In both cases, our CommUtils will detect this.
                    if (CommUtils.isExceptionFailoverable(response.getException())) {
                        throw response.getException();
                    }

                    if (m_clientSender.startSending()) {
                        LOG.info(CommI18NResourceKeys.SERVER_POLLING_THREAD_SERVER_ONLINE);
                        m_warnedAboutConnectionFailure = false; // if we detect the server is down again, lets log the exception again
                    }
                } catch (InterruptedException e) {
                    throw e;
                } catch (Throwable t) {
                    // This probably just means that the server isn't online yet
                    // However, we want to log a warning at least once in case this is a configuration error (in which case
                    // the connection will never succeed - without this log message, it will be hard to debug the misconfiguration).
                    if (!m_warnedAboutConnectionFailure) {
                        m_warnedAboutConnectionFailure = true;
                        LOG.debug(CommI18NResourceKeys.SERVER_POLL_FAILURE, ThrowableUtil.getAllMessages(t));
                    }

                    // Failed to send the command for some reason, make sure the client sender isn't trying to send the server messages.
                    // Since the server is down, no sense processing commands currently in the queue.
                    if (m_clientSender.stopSending(false)) {
                        LOG.warn(CommI18NResourceKeys.SERVER_POLLING_THREAD_SERVER_OFFLINE);
                    }
                }

                synchronized (this) {
                    wait(m_interval); // go to sleep before we poll again
                }
            }
        } catch (InterruptedException e) {
            // Thread exiting
        }

        LOG.debug(CommI18NResourceKeys.SERVER_POLLING_THREAD_STOPPED);
    }

    /**
     * Tells this thread to stop polling. This will block and wait for the thread to die.
     */
    public void stopPolling() {
        interrupt();
        try {
            // Not sure why the thread wouldn't die immediately, but you never know
            join(m_interval * 2);
        } catch (InterruptedException e) {
            interrupt();
        }
        m_pollingListeners.clear();
    }

    public void addPollingListener(PollingListener listener) {
        if (m_pollingListeners.add(listener)) {
            LOG.debug(CommI18NResourceKeys.SERVER_POLLING_THREAD_ADDED_POLLING_LISTENER, listener);
        }
    }

    public void removePollingListener(PollingListener listener) {
        if (m_pollingListeners.remove(listener)) {
            LOG.debug(CommI18NResourceKeys.SERVER_POLLING_THREAD_REMOVED_POLLING_LISTENER, listener);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy