/* * Copyright 2008 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.rioproject.impl.fdh; import net.jini.core.lookup.ServiceID; import net.jini.core.lookup.ServiceItem; import net.jini.lookup.ServiceItemFilter; import org.rioproject.impl.jmx.JMXUtil; import org.rioproject.impl.util.ThrowableUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXServiceURL; import java.io.IOException; import java.util.Properties; /** * The JMXFaultDetectionHandler is a fault detection handler that uses JMX remote * connectivity to determine if a peer JVM is active. The JMXFaultDetectionHandler * checks the liveness of a {@link javax.management.MBeanServerConnection} that * connects to a peer JVM's {@link javax.management.remote.JMXConnectorServer}. * <p> * If the peer JVM has not established a {@link javax.management.remote.JMXConnectorServer}, * the JMXFaultDetectionHandler will only create an event consumer for Lookup * Service TRANSITION_MATCH_NOMATCH transitions. * * <p> * <b><font size="+1">Configuring JMXFaultDetectionHandler</font> </b> * <p> * This implementation of <code>JMXFaultDetectionHandler</code> supports * the following configuration entries; where each configuration entry name is * associated with the component name * <code>org.rioproject.impl.fdh.JMXFaultDetectionHandler</code>. * <br> * <br> * <ul> * <li><span style="font-weight: bold; font-family: courier * new,courier,monospace;">invocationDelay</span> <table cellpadding="2" * cellspacing="2" border="0" style="text-align: left; width: 100%;"> <tbody> * <tr><td style="vertical-align: top; text-align: right; font-weight: * bold;">Type: <br> * </td> * <td style="vertical-align: top; font-family: monospace;">long</td> * </tr> * <tr><td style="vertical-align: top; text-align: right; font-weight: * bold;">Default: <br> * </td> * <td style="vertical-align: top;"><code>60*1000 (60 seconds)</code></td> * </tr> * <tr><td style="vertical-align: top; text-align: right; font-weight: * bold;">Description: <br> * </td> * <td style="vertical-align: top;">The amount of time in milliseconds to wait * between obtaining a {@link javax.management.MBeanServerConnection} to the service being monitored</td> * </tr> * </tbody> </table></li> * </ul> * <ul> * <li><span style="font-weight: bold; font-family: courier * new,courier,monospace;">retryCount </span> <table cellpadding="2" * cellspacing="2" border="0" style="text-align: left; width: 100%;"> <tbody> * <tr><td style="vertical-align: top; text-align: right; font-weight: * bold;">Type: <br> * </td> * <td style="vertical-align: top;"><code>int</code><br> * </td> * </tr> * <tr><td style="vertical-align: top; text-align: right; font-weight: * bold;">Default: <br> * </td> * <td style="vertical-align: top;"><code>3</code></td> * </tr> * <tr><td style="vertical-align: top; text-align: right; font-weight: * bold;">Description: <br> * </td> * <td style="vertical-align: top;">The number of times to retry connecting to * the service. If the service cannot be reached within the retry count specified * the service will be determined to be unreachable * </td> * </tr> * </tbody> </table></li> * </ul> * <ul> * <li><span style="font-weight: bold; font-family: courier * new,courier,monospace;">retryTimeout </span> <table cellpadding="2" * cellspacing="2" border="0" style="text-align: left; width: 100%;"> <tbody> * <tr><td style="vertical-align: top; text-align: right; font-weight: * bold;">Type: <br> * </td> * <td style="vertical-align: top;"><code>long</code></td> * </tr> * <tr><td style="vertical-align: top; text-align: right; font-weight: * bold;">Default: <br> * </td> * <td style="vertical-align: top;"><code>1000 (1 second)</code></td> * </tr> * <tr><td style="vertical-align: top; text-align: right; font-weight: * bold;">Description: <br> * </td> * <td style="vertical-align: top;">How long to wait between retries (in * milliseconds). This value will be used between retry attempts, waiting the * specified amount of time to retry <br> * </td> * </tr> * </tbody> </table></li> * </ul> * * @author Dennis Reedy */ public class JMXFaultDetectionHandler extends AbstractFaultDetectionHandler { private String jmxConnection; /** * A Logger */ private static Logger logger = LoggerFactory.getLogger(JMXFaultDetectionHandler.class); @Override public void monitor(Object proxy, final ServiceID id) { if(getLookupCache()!=null) { ServiceItem item = getLookupCache().lookup(new ServiceItemFilter() { public boolean check(ServiceItem item) { return item.serviceID.equals(id); } }); jmxConnection = JMXUtil.getJMXConnection(item.attributeSets); } super.monitor(proxy, id); } /** * Monitor the peer service * * @throws IllegalStateException If the <tt>jmxConnection</tt> property * has not been set * @throws Exception If there are problems creating the underlying service * monitor */ public void monitor() { getServiceMonitor(); } protected ServiceMonitor getServiceMonitor() { ServiceMonitor monitor = null; if (jmxConnection != null) { monitor = new MBeanServerConnectionMonitor(); } return (monitor); } public void setJMXConnection(String jmxConnection) { this.jmxConnection = jmxConnection; } @Override public void configure(Properties properties) { super.configure(properties); if(logger.isTraceEnabled()) { StringBuilder buffer = new StringBuilder(); buffer.append("JMXFaultDetectionHandler Properties : "); buffer.append("invocation delay=").append(invocationDelay).append(", "); buffer.append("retry count=").append(retryCount).append(", "); buffer.append("retry timeout=").append(retryTimeout); logger.trace(buffer.toString()); } } class MBeanServerConnectionMonitor extends Thread implements ServiceMonitor { boolean keepAlive = true; MBeanServerConnectionMonitor() { super("MBeanServerConnectionMonitor:"+ System.currentTimeMillis()); setDaemon(true); start(); } @Override public void interrupt() { if (logger.isTraceEnabled()) logger.trace("Terminating ServiceMonitor Thread"); keepAlive = false; super.interrupt(); } /** * Its all over */ public void drop() { interrupt(); } /** * Verify service can be reached. If the service cannot be reached * return false */ public boolean verify() { if (!keepAlive) return (false); boolean verified = false; JMXConnector connector = null; try { if (logger.isTraceEnabled()) logger.trace("Getting an MBeanServerConnection to {}", jmxConnection); connector = JMXConnectorFactory.connect(new JMXServiceURL(jmxConnection), null); connector.getMBeanServerConnection(); if (logger.isTraceEnabled()) logger.trace("MBeanServerConnection to {} succeeded", jmxConnection); verified = true; } catch (IOException e) { if (logger.isDebugEnabled()) logger.debug("Unable create MBeanServerConnection to {}, service cannot be reached", jmxConnection); keepAlive = false; } catch (Throwable t) { if (!ThrowableUtil.isRetryable(t)) { keepAlive = false; if (logger.isDebugEnabled()) logger.debug("Unrecoverable Exception getting MBeanServerConnection", t); } } finally { if(connector!=null) { try { connector.close(); } catch (IOException e) { logger.warn("Non-fatal error, unable to close JMXConnector to ["+jmxConnection+"]", e); } } } return (verified); } public void run() { while (!interrupted()) { if (!keepAlive) { return; } if (logger.isTraceEnabled()) logger.trace("Wait for [{}] millis to obtain MBeanServerConnection for {}", invocationDelay, jmxConnection); try { sleep(invocationDelay); } catch (InterruptedException ie) { /* should not happen */ } catch (IllegalArgumentException iae) { logger.warn("Sleep time is off : "+ invocationDelay); } /* * If we failed to invoke the method and the failure is * not fatal, retry */ if (!verify()) { if (logger.isTraceEnabled()) logger.trace("Unable create MBeanServerConnection to {}, retry [{}] times, waiting [{}] " + "millis between attempts", jmxConnection, retryCount, retryTimeout); boolean connected = false; for (int i = 0; i < retryCount; i++) { long t0 = System.currentTimeMillis(); connected = verify(); if (!connected) { long t1 = System.currentTimeMillis(); if (logger.isTraceEnabled()) logger.trace("Invocation attempt [{}] took [{}] millis to fail", i, (t1-t0)); if (retryTimeout > 0) { try { sleep(retryTimeout); } catch (InterruptedException ie) { /* should not happen */ } } } } if (!connected) { keepAlive = false; if (logger.isTraceEnabled()) { logger.trace( "Unable create MBeanServerConnection to {}, notify listeners and exit", jmxConnection); } notifyListeners(); break; } } //else { // notifyListeners(); // break; //} } terminate(); } } }