org.novelang.outfit.shell.JmxBeanPool.java Source code

Java tutorial

Introduction

Here is the source code for org.novelang.outfit.shell.JmxBeanPool.java

Source

/*
 * Copyright (C) 2011 Laurent Caillette
 *
 * This program 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 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 org.novelang.outfit.shell;

import java.io.IOException;
import java.util.Map;
import javax.management.InstanceNotFoundException;
import javax.management.JMX;
import javax.management.MBeanRegistrationException;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;

import com.google.common.collect.Maps;
import org.apache.commons.lang.StringUtils;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

/**
 * For a given host and port, takes care of every JMX connections and beans and maps them to {@link JmxKit} instances.
 * <p>
 * Inside the Novelang project, {@link org.novelang.outfit.shell.JavaShell} doesn't need more that one {@link JmxKit}
 * instance. But one (commercial) project the author is working on needs this kind of mapping. 
 * <p>
 * This implementation is not thread-safe.
 *
 * @author Laurent Caillette
 */
public class JmxBeanPool {

    private final String host;
    private final int port;

    public JmxBeanPool(final String host, final int port) {
        checkArgument(!StringUtils.isBlank(host));
        checkArgument(port > 0);
        this.host = host;
        this.port = port;
    }

    private final Map<JmxKit, JmxConnectionBundle> connectionBundles = Maps.newHashMap();

    /**
     * This is a {@code Map} between a ({@code ObjectName}, {@link org.novelang.outfit.shell.JmxKit}) pair,
     * and a ({@link ObjectName}, JMX bean proxy, and {@link JmxConnectionBundle}) triplet.
     */
    private final Map<JmxBeanKey, JmxBeanValue> managedBeans = Maps.newHashMap();

    private JmxConnectionBundle getOrCreateConnectionBundle(final JmxKit someJmxKit)
            throws IOException, InterruptedException {
        final JmxConnectionBundle connectionBundle;
        final JmxConnectionBundle maybeConnectionBundle = connectionBundles.get(someJmxKit);
        if (maybeConnectionBundle == null) {
            final JMXConnector jmxConnector = someJmxKit.createJmxConnector(host, port);
            connectionBundle = new JmxConnectionBundle(jmxConnector, jmxConnector.getMBeanServerConnection());
            connectionBundles.put(someJmxKit, connectionBundle);
        } else {
            connectionBundle = maybeConnectionBundle;
        }
        return connectionBundle;
    }

    public <BEAN> BEAN getManagedBean(final Class<BEAN> beanClass, final ObjectName beanName, final JmxKit jmxKit)
            throws IOException, InterruptedException {
        checkNotNull(beanClass);
        checkNotNull(beanName);
        checkNotNull(jmxKit);

        final JmxBeanKey key = new JmxBeanKey(beanName, jmxKit);

        final JmxBeanValue value = managedBeans.get(key);
        final BEAN bean;
        if (value == null) {
            final JmxConnectionBundle connectionBundle = getOrCreateConnectionBundle(jmxKit);
            bean = JMX.newMBeanProxy(connectionBundle.connection, beanName, beanClass, true);
            final JmxBeanValue newValue = new JmxBeanValue(connectionBundle, beanName, bean);
            managedBeans.put(key, newValue);
        } else {
            //noinspection unchecked
            bean = (BEAN) value.getJmxBean();
        }
        return bean;
    }

    /**
     * Unregisters JMX Beans and closes the {@link javax.management.remote.JMXConnector}s.
     * This method should close the default JMX connector o {@link org.novelang.outfit.shell.JavaShell} because, when there is one, there is always
     * a registered {@link org.novelang.outfit.shell.insider.Insider} at startup so it should appear inside the
     * {@code Map}.
     *
     */
    public void disconnectAll() {

        // First, unregister all beans.
        for (final JmxBeanValue value : managedBeans.values()) {
            try {
                value.getConnectionBundle().connection.unregisterMBean(value.getObjectName());
            } catch (InstanceNotFoundException e) {
                logCouldntUnregister(value.getObjectName(), e);
            } catch (MBeanRegistrationException e) {
                logCouldntUnregister(value.getObjectName(), e);
            } catch (IOException e) {
                logCouldntUnregister(value.getObjectName(), e);
            }
        }

        // Now we can close safely. The JMXConnector#close() method has no effect when called more than once.
        for (final JmxBeanValue value : managedBeans.values()) {
            try {
                value.getConnectionBundle().connector.close();
            } catch (IOException e) {
                logCouldntUnregister(value.getConnectionBundle().connector, e);
            }
        }
    }

    protected void logCouldntUnregister(final Object culprit, final Exception e) {
        /*
            LOG.debug( "Couldn't disconnect or unregister " + culprit + ", cause: " + e.getClass() +
                " (may be normal if other VM terminated)." ) ;
        */
    }

}