001/*
002 * #%L
003 * Netarchivesuite - monitor
004 * %%
005 * Copyright (C) 2005 - 2014 The Royal Danish Library, the Danish State and University Library,
006 *             the National Library of France and the Austrian National Library.
007 * %%
008 * This program is free software: you can redistribute it and/or modify
009 * it under the terms of the GNU Lesser General Public License as
010 * published by the Free Software Foundation, either version 2.1 of the
011 * License, or (at your option) any later version.
012 * 
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Lesser Public License for more details.
017 * 
018 * You should have received a copy of the GNU General Lesser Public
019 * License along with this program.  If not, see
020 * <http://www.gnu.org/licenses/lgpl-2.1.html>.
021 * #L%
022 */
023package dk.netarkivet.monitor.distribute;
024
025import java.util.Timer;
026import java.util.TimerTask;
027
028import org.slf4j.Logger;
029import org.slf4j.LoggerFactory;
030
031import dk.netarkivet.common.CommonSettings;
032import dk.netarkivet.common.distribute.JMSConnectionFactory;
033import dk.netarkivet.common.distribute.monitorregistry.MonitorRegistryClient;
034import dk.netarkivet.common.exceptions.ArgumentNotValid;
035import dk.netarkivet.common.exceptions.NetarkivetException;
036import dk.netarkivet.common.utils.CleanupHook;
037import dk.netarkivet.common.utils.CleanupIF;
038import dk.netarkivet.common.utils.Settings;
039import dk.netarkivet.monitor.MonitorSettings;
040import dk.netarkivet.monitor.registry.distribute.RegisterHostMessage;
041
042/**
043 * The monitor registry client sends messages with JMS to register the host for JMX monitoring.
044 */
045public final class JMSMonitorRegistryClient implements MonitorRegistryClient, CleanupIF {
046    /** The singleton instance of this class. */
047    private static JMSMonitorRegistryClient instance;
048    /** The logger for this class. */
049    private static final Logger log = LoggerFactory.getLogger(JMSMonitorRegistryClient.class);
050    /** The cleanup hook that will clean up this client on VM shutdown. */
051    private CleanupHook hook;
052    /** The timer that sends messages. */
053    private Timer registryTimer;
054    /**
055     * One minute in milliseconds. Used for control of timer task that sends messages.
056     */
057    private static final long MINUTE_IN_MILLISECONDS = 60000L;
058    /**
059     * Zero milliseconds from now. Used for control of timer task that sends messages.
060     */
061    private static final long NOW = 0L;
062
063    /**
064     * Intialises the client.
065     */
066    private JMSMonitorRegistryClient() {
067        hook = new CleanupHook(this);
068        Runtime.getRuntime().addShutdownHook(hook);
069    }
070
071    /**
072     * Get the registry client singleton.
073     *
074     * @return The registry client.
075     */
076    public static synchronized JMSMonitorRegistryClient getInstance() {
077        if (instance == null) {
078            instance = new JMSMonitorRegistryClient();
079        }
080        return instance;
081    }
082
083    /**
084     * Register this host for monitoring. Once this method is called it will reregister for monitoring every minute, to
085     * ensure the scheduling is done. If called again, it will restart the timer that registers the host.
086     *
087     * @param localHostName The name of the host.
088     * @param jmxPort The port for JMX connections to the host.
089     * @param rmiPort The port for RMI connections for JMX communication.
090     * @throws ArgumentNotValid on null or empty localHostName, or negative port numbers.
091     */
092    public synchronized void register(final String localHostName, final int jmxPort, final int rmiPort) {
093        ArgumentNotValid.checkNotNullOrEmpty(localHostName, "String localHostName");
094        ArgumentNotValid.checkNotNegative(jmxPort, "int jmxPort");
095        ArgumentNotValid.checkNotNegative(rmiPort, "int rmiPort");
096        if (registryTimer != null) {
097            log.info("Cancelling old registryTimer instance");
098            registryTimer.cancel();
099        }
100        registryTimer = new Timer("Monitor-registry-client", true);
101        TimerTask timerTask = new TimerTask() {
102            /** The action to be performed by this timer task. */
103            public void run() {
104                log.trace("Registering this client for monitoring, using hostname '{}' and JMX/RMI ports {}/{}",
105                        localHostName, jmxPort, rmiPort);
106                JMSConnectionFactory.getInstance().send(new RegisterHostMessage(localHostName, jmxPort, rmiPort));
107            }
108        };
109
110        long reregisterDelay = Settings.getLong(MonitorSettings.DEFAULT_REREGISTER_DELAY);
111        try {
112            reregisterDelay = Long.parseLong(Settings.get(CommonSettings.MONITOR_REGISTRY_CLIENT_REREGISTERDELAY));
113        } catch (NumberFormatException e1) {
114            log.warn("Couldn't parse setting {}. Only numbers are allowed. Using defaultvalue {}",
115                    CommonSettings.MONITOR_REGISTRY_CLIENT_REREGISTERDELAY, MonitorSettings.DEFAULT_REREGISTER_DELAY);
116        } catch (NetarkivetException e2) {
117            log.warn("Couldn't find setting {}. Using defaultvalue {}",
118                    CommonSettings.MONITOR_REGISTRY_CLIENT_REREGISTERDELAY, MonitorSettings.DEFAULT_REREGISTER_DELAY);
119        }
120
121        log.info("Registering this client for monitoring every {} minutes, using hostname '{}' and JMX/RMI ports {}/{}",
122                reregisterDelay, localHostName, jmxPort, rmiPort);
123        registryTimer.scheduleAtFixedRate(timerTask, NOW, reregisterDelay * MINUTE_IN_MILLISECONDS);
124    }
125
126    /**
127     * Used to clean up a class from within a shutdown hook. Must not do any logging. Program defensively, please.
128     */
129    public synchronized void cleanup() {
130        if (registryTimer != null) {
131            registryTimer.cancel();
132            registryTimer = null;
133        }
134        try {
135            Runtime.getRuntime().removeShutdownHook(hook);
136        } catch (IllegalStateException e) {
137            // Okay, it just means we are already shutting down.
138        }
139        hook = null;
140    }
141
142}