Commit 6adf37c5 authored by Andre Freyssinet's avatar Andre Freyssinet
Browse files

1st version of JMS healthcheck component.

parent c49be272
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>joram-tools-jmscheck</artifactId>
<packaging>bundle</packaging>
<name>JORAM :: joram :: tools :: jmscheck</name>
<description>JMS Health-Check bundle.</description>
<parent>
<groupId>org.ow2.joram</groupId>
<artifactId>joram-tools</artifactId>
<version>5.17.6-SNAPSHOT</version>
</parent>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>${maven.bundle.plugin.version}</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
<Bundle-Activator>org.ow2.joram.tools.jmscheck.Activator</Bundle-Activator>
<Import-Package>
fr.dyade.aaa.common,
fr.dyade.aaa.util.management,
fr.dyade.aaa.agent,
javax.jms,
javax.naming,
fr.dyade.aaa.jndi2.client,
javax.transaction.xa,
org.osgi.framework,
org.objectweb.util.monolog,
org.objectweb.util.monolog.api,
org.objectweb.joram.client.jms,
org.objectweb.joram.client.jms.tcp,
org.objectweb.joram.client.jms.local
</Import-Package>
<!-- TODO remove DynamicImport-Package -->
<DynamicImport-Package>*</DynamicImport-Package>
</instructions>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>attached</goal>
</goals>
<phase>package</phase>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>org.ow2.joram.tools.jmscheck.JMSCheck</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.ow2.spec.ee</groupId>
<artifactId>ow2-jms-2.0-spec</artifactId>
</dependency>
<dependency>
<groupId>org.ow2.joram</groupId>
<artifactId>a3-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.ow2.joram</groupId>
<artifactId>a3-rt</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.ow2.joram</groupId>
<artifactId>joram-client-jms</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.ow2.joram</groupId>
<artifactId>jndi-client</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<modules>
</modules>
</project>
/*
* Copyright (C) 2020 ScalAgent Distributed Technologies
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*
* Initial developer(s): ScalAgent Distributed Technologies
* Contributor(s):
*/
package org.ow2.joram.tools.jmscheck;
import javax.naming.InitialContext;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import fr.dyade.aaa.agent.AgentServer;
import fr.dyade.aaa.common.Debug;
public class Activator implements BundleActivator {
public static final Logger logger = Debug.getLogger(Activator.class.getName());
public final static String CHECK_PREFIX = "org.ow2.joram.check.";
public final static String GLOBAL_PERIOD = CHECK_PREFIX + "period";
public final static String GLOBAL_TIMEOUT = CHECK_PREFIX + "timeout";
public final static String DUMP_FILE = CHECK_PREFIX + "dump.file";
public final static String DUMP_THRESHOLD = CHECK_PREFIX + "dump.threshold";
public final static String DUMP_DELAY = CHECK_PREFIX + "dump.delay";
public final static String JNDI_FILE = CHECK_PREFIX + "jndi.file";
public final static String JNDI_FACTORY = CHECK_PREFIX + "jndi.factory";
public final static String JNDI_HOST = CHECK_PREFIX + "jndi.host";
public final static String JNDI_PORT = CHECK_PREFIX + "jndi.port";
public static final String CF_NAME = CHECK_PREFIX + "cf";
public static final String USER_NAME = CHECK_PREFIX + "user";
public static final String PASSWORD = CHECK_PREFIX + "password";
public static final String QUEUE_NAME = CHECK_PREFIX + "queue";
private static BundleContext context;
public Activator() {
}
int period, timeout;
JMSStatus jmsStatus;
@Override
public void start(BundleContext context) throws Exception {
Activator.context = context;
// Gets the global period.
period = JMSStatus.DFLT_PERIOD;
String value = context.getProperty(GLOBAL_PERIOD);
if (value != null) {
try {
period = Integer.parseInt(value);
} catch (NumberFormatException exc) {
logger.log(BasicLevel.WARN,
"MqttCheckActivator.start: bad value for property " + GLOBAL_PERIOD + ", set to default.");
period = JMSStatus.DFLT_PERIOD;
}
}
if (period <= 0) return;
// Gets the global timeout.
timeout = JMSStatus.DFLT_TIMEOUT;
value = context.getProperty(GLOBAL_TIMEOUT);
if (value != null) {
try {
timeout = Integer.parseInt(value);
} catch (NumberFormatException exc) {
logger.log(BasicLevel.WARN,
"MqttCheckActivator.start: bad value for property " + GLOBAL_TIMEOUT + ", set to default.");
timeout = JMSStatus.DFLT_TIMEOUT;
}
}
// Gets the pathname of the dump file.
String dumpFilePath = context.getProperty(DUMP_FILE);
if (dumpFilePath == null) {
dumpFilePath = JMSStatus.DFLT_DUMP_FILE;
}
// Gets the threshold to generate a dump file.
int threshold = JMSStatus.DFLT_THRESHOLD;
value = context.getProperty(DUMP_THRESHOLD);
if (value != null) {
try {
threshold = Integer.parseInt(value);
} catch (NumberFormatException exc) {
logger.log(BasicLevel.WARN,
"MqttCheckActivator.start: bad value for property " + DUMP_THRESHOLD + ", set to default.");
threshold = JMSStatus.DFLT_THRESHOLD;
}
}
// Gets the minimal delay before to generate anew a dump file.
int delay = JMSStatus.DFLT_DELAY;
value = context.getProperty(DUMP_DELAY);
if (value != null) {
try {
delay = Integer.parseInt(value);
} catch (NumberFormatException exc) {
logger.log(BasicLevel.WARN,
"MqttCheckActivator.start: bad value for property " + DUMP_DELAY + ", set to default.");
delay = JMSStatus.DFLT_DELAY;
}
}
jmsStatus = new JMSStatus("Joram#" + AgentServer.getServerId(), period, timeout, dumpFilePath, threshold, delay);
String jndiFile = context.getProperty(JNDI_FILE);
String jndiFactory = context.getProperty(JNDI_FACTORY);
String jndiHost = context.getProperty(JNDI_HOST);
String jndiPort = context.getProperty(JNDI_PORT);
InitialContext ictx = jmsStatus.getInitialContext(jndiFile, jndiFactory, jndiHost, jndiPort);
createConnector(jmsStatus, "", ictx);
for (int i=1;; i++) {
if (!createConnector(jmsStatus, "." + i, ictx)) break;
}
jmsStatus.start();
}
// public synchronized Object lookup(String name) throws NamingException {
// if (logger.isLoggable(BasicLevel.DEBUG))
// logger.log(BasicLevel.DEBUG, "Helper.lookup " + name);
//
// ClassLoader originalContextClassLoader = Thread.currentThread().getContextClassLoader();
// try {
// Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
//
// if (ictx == null)
// ictx = new InitialContext(jndiProps);
// return ictx.lookup(name);
// } finally {
// Thread.currentThread().setContextClassLoader(originalContextClassLoader);
// }
// }
/**
* Adds a new check component using the related properties.
*
* @param check The root component.
* @param suffix The property suffix.
* @param ictx The InitialContext to use.
* @return True if the component is successively created.
*/
private boolean createConnector(JMSStatus check, String suffix, InitialContext ictx) {
String cfname = context.getProperty(CF_NAME + suffix);
if (cfname != null) {
String user = context.getProperty(USER_NAME + suffix);
String pass = context.getProperty(PASSWORD + suffix);
String qname = context.getProperty(QUEUE_NAME + suffix);
// TODO (AF): Allow to override period and timeout.
check.addConnectorStatus(cfname, ictx, user, pass, qname, period, timeout);
return true;
}
return false;
}
@Override
public void stop(BundleContext context) throws Exception {
context = null;
jmsStatus.stop();
}
}
package org.ow2.joram.tools.jmscheck;
import javax.naming.InitialContext;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
import fr.dyade.aaa.common.Debug;
public class JMSCheck {
public static final Logger logger = Debug.getLogger(JMSCheck.class.getName());
public final static String GLOBAL_PERIOD = "period";
public final static String GLOBAL_TIMEOUT = "timeout";
public final static String JNDI_FILE = "jndi.file";
public final static String JNDI_FACTORY = "jndi.factory";
public final static String JNDI_HOST = "jndi.host";
public final static String JNDI_PORT = "jndi.port";
public static final String CF_NAME = "cf";
public static final String USER_NAME = "user";
public static final String PASSWORD = "password";
public static final String QUEUE_NAME = "queue";
static int period, timeout;
static JMSStatus jmsStatus;
public static void main(String[] args) throws Exception {
// Gets the global period.
period = JMSStatus.DFLT_PERIOD;
String value = System.getProperty(GLOBAL_PERIOD);
if (value != null) {
try {
period = Integer.parseInt(value);
} catch (NumberFormatException exc) {
logger.log(BasicLevel.WARN,
"MqttCheckActivator.start: bad value for property " + GLOBAL_PERIOD + ", set to default.");
period = JMSStatus.DFLT_PERIOD;
}
}
if (period <= 0) return;
// Gets the global timeout.
timeout = JMSStatus.DFLT_TIMEOUT;
value = System.getProperty(GLOBAL_TIMEOUT);
if (value != null) {
try {
timeout = Integer.parseInt(value);
} catch (NumberFormatException exc) {
logger.log(BasicLevel.WARN,
"MqttCheckActivator.start: bad value for property " + GLOBAL_TIMEOUT + ", set to default.");
timeout = JMSStatus.DFLT_TIMEOUT;
}
}
jmsStatus = new JMSStatus("Joram", period, timeout, null, 0, 0);
String jndiFile = System.getProperty(JNDI_FILE);
String jndiFactory = System.getProperty(JNDI_FACTORY);
String jndiHost = System.getProperty(JNDI_HOST);
String jndiPort = System.getProperty(JNDI_PORT);
InitialContext ictx = jmsStatus.getInitialContext(jndiFile, jndiFactory, jndiHost, jndiPort);
createConnector(jmsStatus, "", ictx);
for (int i=1;; i++) {
if (!createConnector(jmsStatus, "." + i, ictx)) break;
}
jmsStatus.start();
}
/**
* Adds a new check component using the related properties.
*
* @param check The root component.
* @param suffix The property suffix.
* @param ictx The InitialContext to use.
* @return True if the component is successively created.
*/
private static boolean createConnector(JMSStatus jmsStatus, String suffix, InitialContext ictx) {
String cfname = System.getProperty(CF_NAME + suffix);
if (cfname != null) {
String user = System.getProperty(USER_NAME + suffix);
String pass = System.getProperty(PASSWORD + suffix);
String qname = System.getProperty(QUEUE_NAME + suffix);
// TODO (AF): Allow to override period and timeout.
jmsStatus.addConnectorStatus(cfname, ictx, user, pass, qname, period, timeout);
return true;
}
return false;
}
}
/*
* Copyright (C) 2020 ScalAgent Distributed Technologies
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*
* Initial developer(s): ScalAgent Distributed Technologies
* Contributor(s):
*/
package org.ow2.joram.tools.jmscheck;
import java.text.DateFormat;
import java.util.Date;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.objectweb.joram.client.jms.ConnectionFactory;
import org.objectweb.joram.client.jms.Queue;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
import fr.dyade.aaa.common.Debug;
import fr.dyade.aaa.util.management.MXWrapper;
public class JMSConnectorStatus implements JMSConnectorStatusMBean {
public static Logger logger = Debug.getLogger(JMSConnectorStatus.class.getName());
public static final int RUNNING = 0;
public static final int UNREACHABLE = 1;
public static String[] info = { "RUNNING", "UNREACHABLE" };
/** retryStatusCount == 0 => RUNNING, retryStatusCount > 0 => UNREACHABLE */
private int retryStatusCount = UNREACHABLE;
@Override
public int getStatus() {
return retryStatusCount;
}
@Override
public String getStatusInfo() {
if (retryStatusCount > 0)
return info[1] + '(' + retryStatusCount + ')';
return info[0];
}
void setStatus(int retry) {
JMSStatus.checkDump(cfname, retry);
this.retryStatusCount = retry;
}
private int nbtry = 0;
@Override
public int getNbTry() {
return nbtry;
}
private transient int failures = 0;
@Override
public int getNbFailures() {
return failures;
}
private String errorMsg;
@Override
public String getErrorMsg() {
return errorMsg;
}
private long latencyConnect = -1;
@Override
public long getLatencyConnect() {
return latencyConnect;
}
private void setLatencyConnect(long latencyConnect) {
this.latencyConnect = latencyConnect;
}
private String lastConnectTime = "-";
@Override
public String getLastConnectTime() {
return lastConnectTime;
}
private long latencyPubSub = -1;
@Override
public long getLatencyPubSub() {
return latencyPubSub;
}
private void setLatencyPubSub(long latencyPubSub) {
this.latencyPubSub = latencyPubSub;
}
/** The period of time between 2 checks. */
private int period = 60; // in seconds
@Override
public int getPeriod() {
return period;
}
@Override
public void setPeriod(int period) {
try {
if (period < 5)
period = 5;
if (period < (2* timeout))
period = 2* timeout;
this.period = period;
schedule();
} catch (Exception e) {
e.printStackTrace();
}
}
/** The maximum time waiting for connection. */
private int timeout = 10; // in seconds
@Override
public int getTimeOut() {
return timeout;
}
@Override
public void setTimeOut(int timeOut) {
if (timeOut < 5)
timeOut = 5;
if (period < (2* timeOut))
period = 2* timeOut;
this.timeout = timeOut;
schedule();
}
/** Root component */
JMSStatus jmsStatus;
/** JNDI Name of the ConnectionFactory to use. */
private String cfname;
@Override
public String getName() {
return cfname;
}
/* User name and password for authentication */
private String user;
private String pass;
/* Internal name of the queue, be careful this name is not a JNDI name. */
private String qname;
private InitialContext ictx;
private ConnectionFactory cf;
private Queue queue;
ScheduledFuture<?> callableHandle;
/**
*
* @param jmsStatus Root component.