org.helios.collector.jmx.connection.AbstractMBeanServerConnectionFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.helios.collector.jmx.connection.AbstractMBeanServerConnectionFactory.java

Source

/**
 * Helios, OpenSource Monitoring
 * Brought to you by the Helios Development Group
 *
 * Copyright 2007, Helios Development Group and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This 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 (at your option) any later version.
 *
 * This software 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 software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 
 *
 */
package org.helios.collector.jmx.connection;

import org.apache.commons.collections.ListUtils;
import org.apache.log4j.Logger;
import org.helios.apmrouter.jmx.JMXHelper;
import org.helios.collector.jmx.HSPProtocol;
import org.helios.collector.jmx.connection.triggers.ConnectTrigger;
import org.helios.collector.jmx.connection.triggers.OnFirstConnectTrigger;
import org.helios.collector.jmx.identifiers.AbstractMBeanServerIdentifier;
import org.helios.collector.jmx.identifiers.DefaultMBeanServerIdentifier;
import org.helios.collector.jmx.identifiers.IMBeanServerIdentifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;

import javax.management.*;
import javax.management.remote.JMXServiceURL;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicLong;

/**
 * <p>Title: AbstractMBeanServerConnectionFactory</p>
 * <p>Description: Abstract base class for concrete MBeanServerConnectionFactories</p> 
 * <p>Company: Helios Development Group LLC</p>
 * @author Whitehead (nwhitehead AT heliosdev DOT org)
 * @version $LastChangedRevision$
 * <p><code>org.helios.collectors.jmx.AbstractMBeanServerConnectionFactory</code></p>
 */
@ManagedResource
//@JMXNotifications(notifications={
//        @JMXNotification(description="Notification indicating error connecting to a remote MBeanServer", types={
//                @JMXNotificationType(type="org.helios.remote.mbean.state.notification")
//        }),
//        @JMXNotification(description="Notification indicating change in active domains on remote MBeanServer", types={
//                @JMXNotificationType(type="org.helios.remote.mbean.domain.notification")
//        })
//})
public abstract class AbstractMBeanServerConnectionFactory implements IMBeanServerConnectionFactory,
        ApplicationContextAware, AbstractMBeanServerConnectionFactoryMXBean, MBeanServerConnection {
    /**  */
    private static final long serialVersionUID = 2931250432886706976L;
    /** A serial number factory for designating IDs for unidentifiable VMs */
    protected static final AtomicLong UNKNOWN_SERIAL = new AtomicLong();
    /** A connection pool of MBeanServerConnections */
    protected MBeanServerConnectionPool connectionPool;
    /** The bean name of the mbean server connection pool prototype factory. Defaults to <b><code>MBeanServerConnectionPoolFactory</code></b>. */
    protected String factoryName = DEFAULT_FACTORY_NAME;
    /** The application context */
    protected ApplicationContext applicationContext = null;
    /** The default prototype bean name providing an MBeanServerConnection Pool */
    public static final String DEFAULT_FACTORY_NAME = "MBeanServerConnectionPoolFactory";

    /** The configured class name of the server identifier for this connection factory */
    protected volatile String serverIdentifierClassName = null;

    /** The server identifier for this connection factory */
    protected volatile IMBeanServerIdentifier serverIdentifier = null;

    /** the host Id that this factory connects to  */
    protected String hostId = null;
    /** the vm Id that this factory connects to  */
    protected String vmId = null;
    /** the JMX default domain name of the MBeanServer that this factory connects to  */
    protected String defaultDomain = null;
    /** The permanent MBeanServer Connection */
    protected volatile MBeanServerConnection mBeanConnection = null;
    /** The HSPProtocol definition */
    protected final HSPProtocol hspProtocol;

    /** A set of connection triggers */
    protected final Set<ConnectTrigger> triggers = new CopyOnWriteArraySet<ConnectTrigger>();

    /** The JMXServiceURL template for local in-vm proxy URLs */
    public static final String LOCAL_JMX_SERVICE_TEMPLATE = "service:jmx:hsp://{0}/{1}/{2}?shared={3}";

    // service:jmx:rmi://njwub810/jndi/rmi://localhost:8003/jmxrmi?host=njwub810&vmid=default&defaultdomain=jboss
    /** Maximum time (milliseconds) to wait for a remote MBean Server for a new connection */
    protected long timeout = 3000l;

    /**  Indicate whether detailed stack trace needs to be printed out in logs*/
    protected boolean printStackTrace = false;

    protected Logger log = null;

    protected boolean connected = false;
    protected int retrialsSinceOffline = 0;
    protected List<String> liveDomains = null;
    private boolean everConnected = false;
    /**   The MBean's object name */
    protected ObjectName objectName = null;

    /**
     * Creates a new AbstractMBeanServerConnectionFactory
     * @param hspProtocol The HSP protocol used to communicate with this MBeanServer proxy
     */
    public AbstractMBeanServerConnectionFactory(HSPProtocol hspProtocol) {
        super();
        this.hspProtocol = hspProtocol;
    }

    /**
     * Registers a management interface and a JMX Server Proxy for this connection factory
     * @throws Exception
     */
    public void start() throws Exception {
        try {
            getConnection();
        } catch (MBeanServerConnectionFactoryException e) {
            // The remote JMX server to be monitored is offline 
            // so don't register it to Helios MBean Server yet!
            // Subsequent requests to getConnection would take care
            // of it when that instance will come online
        }
    }

    /**
     * 
     */
    private void registerToHeliosServer() {
        String[] ids = getIds();
        hostId = ids[0];
        vmId = ids[1];
        defaultDomain = ids[2];
        createPool();
        //reflectObject(this);
        //reflectObject(connectionPool);
        objectName = JMXHelper.objectName(new StringBuilder(CONNECTION_MBEAN_DOMAIN + ":host=").append(hostId)
                .append(",vm=").append(vmId).append(",domain=").append(defaultDomain));
        try {
            if (JMXHelper.getHeliosMBeanServer().isRegistered(objectName)) {
                JMXHelper.getHeliosMBeanServer().unregisterMBean(objectName);
            }
            JMXHelper.getHeliosMBeanServer().registerMBean(this, objectName);
        } catch (Exception e) {
            e.printStackTrace(System.err);
        }
    }

    /**
     * Unregisters the management interface and the JMX Server Proxy for this connection factory
     */
    public void stop() {
        try {
            connectionPool.close();
        } catch (Exception e) {
            log.debug(e.getMessage());
        }
        try {
            JMXHelper.getHeliosMBeanServer().unregisterMBean(objectName);
        } catch (Exception e) {
        }
    }

    /**
     * Returns a default (shared connection) JMXServiceURL for a proxy connection to this MBeanServer proxy. 
     * @return a JMXServiceURL
     */
    @ManagedAttribute
    public String getJMXServiceURL() {
        return getServiceURL(true).toString();
    }

    /**
     * Returns a dedicated connection JMXServiceURL for a proxy connection to this MBeanServer proxy. 
     * @return a JMXServiceURL
     */
    @ManagedAttribute
    public String getDedicatedJMXServiceURL() {
        return getServiceURL(false).toString();
    }

    /**
     * Creates and returns a JMXServiceURL for a proxy connection to this proxy MBeanServer
     * @param shared true for shared physical connections, false for a dedicated connection
     * @return JMXServiceURL
     */
    protected JMXServiceURL getServiceURL(boolean shared) {
        String url = null;
        try {
            //url = MessageFormat.format(LOCAL_JMX_SERVICE_TEMPLATE, hostId, vmId, defaultDomain, shared);
            url = hspProtocol.formatServiceURL(objectName, shared);
            return new JMXServiceURL(url);
        } catch (Exception e) {
            throw new RuntimeException("Failed to create LocalServiceURL for [" + url + "]", e);
        }
    }

    /**
     * Creates the configured pool. If the config is not set or an exception is thrown 
     * creating the configured pool, the default pool is created.
     */
    protected void createPool() {
        try {
            connectionPool = (MBeanServerConnectionPool) applicationContext.getBean(factoryName, this);
        } catch (Exception e) {
            connectionPool = new MBeanServerConnectionPool(this);
        }
    }

    /**
     * Returns the permanent connection to the target MBeanServer
     * @return MBeanServerConnection
     * @throws MBeanServerConnectionFactoryException
     */
    @ManagedOperation
    public MBeanServerConnection getConnection() throws MBeanServerConnectionFactoryException {
        try {
            //If the connection is already created, return that to the caller
            synchronized (this) {
                if (mBeanConnection == null) {
                    mBeanConnection = _getConnection();
                    if (!everConnected) {
                        everConnected = true;
                        registerToHeliosServer();
                    }
                }
            }
            setConnectionStatus(true);
            // test the connection if we have registered triggers
            if (!triggers.isEmpty()) {
                mBeanConnection.getDefaultDomain();
                for (ConnectTrigger trigger : triggers) {
                    if (trigger instanceof OnFirstConnectTrigger) {
                        OnFirstConnectTrigger ofct = (OnFirstConnectTrigger) trigger;
                        if (ofct.isFirstConnect()) {
                            ofct.onFirstConnect(mBeanConnection);
                        }
                    }
                }
            }
            return mBeanConnection;
        } catch (Exception e) {
            setConnectionStatus(false);
            if (printStackTrace)
                log.error(e.getMessage(), e);
            for (ConnectTrigger trigger : triggers) {
                trigger.onConnectionFailed();
            }
            throw new MBeanServerConnectionFactoryException("Failed to acquire permanent MBeanServer Connection",
                    e);
        }
    }

    @ManagedOperation
    public synchronized void setConnectionStatus(boolean isConnected) {
        //reporting a status that we already know - so return right away
        if (isConnected == connected) {
            if (!this.connected)
                retrialsSinceOffline++;
            return;
        }
        if (isConnected) {
            // Connection to remote MBean server is back online now.  
            if (!connected) {
                connected = true;
                retrialsSinceOffline = 0;
                //this.sendNotification(new Notification("org.helios.remote.mbean.connection.state",this,notificationSequence.incrementAndGet(),System.currentTimeMillis(),"Connection created to remote MBean server with object name: " + this.objectName));
            }
        } else {
            retrialsSinceOffline++;
            if (connected) {
                connected = false;
                //this.sendNotification(new Notification("org.helios.remote.mbean.connection.state",this,notificationSequence.incrementAndGet(),System.currentTimeMillis(),"Connection lost to remote MBean server with object name: " + this.objectName));
            }
        }
    }

    /**
     * Returns a new connection
     * @return An MBeanConnection.
     * @throws MBeanServerConnectionFactoryException
     * @throws IOException 
     */
    @ManagedOperation
    public MBeanServerConnection getNewConnection() throws MBeanServerConnectionFactoryException {
        try {
            MBeanServerConnection connection = _getConnection();
            setConnectionStatus(true);
            return connection;
        } catch (Exception e) {
            setConnectionStatus(false);
            throw new MBeanServerConnectionFactoryException("Failed to create a new connection", e);
        }
    }

    /**
     * Detects any domain changes from the previous poll and send notification if there are any changes
     * 
     * @param domains
     */
    @ManagedOperation
    public void processDomains(ArrayList<String> domains) {
        if (liveDomains == null) {
            liveDomains = domains;
        } else {
            List<String> offlineDomains = (List<String>) ListUtils.subtract(liveDomains, domains);
            List<String> newDomains = (List<String>) ListUtils.subtract(domains, liveDomains);
            if (!(offlineDomains.isEmpty()) || !(newDomains.isEmpty())) {
                log.debug("There are domain changes for this remote MBean Server: " + objectName);
                if (!(newDomains.isEmpty())) {
                    // Some domain(s) are new so send out notification for each one of 'em
                    for (String newD : newDomains) {
                        //this.sendNotification(new Notification("org.helios.remote.mbean.domain.notification", this, notificationSequence.incrementAndGet(),System.currentTimeMillis(), "Domain came online: " + newD));
                        log.info("Notification sent out for new Domain: " + newD);
                    }
                }

                if (!(offlineDomains.isEmpty())) {
                    // Some domain(s) have gone offline so send out notification for each one of 'em
                    for (Object offlineD : offlineDomains) {
                        //this.sendNotification(new Notification("org.helios.remote.mbean.domain.notification", this, notificationSequence.incrementAndGet(),System.currentTimeMillis(), "Domain went offline: " + offlineD));
                        log.info("Notification sent out for dead Domain: " + offlineD);
                    }
                }
                liveDomains = domains;
            } else {
                log.debug("There are no domain changes for this remote MBean Server: " + objectName);
            }
        }
    }

    /**
     * Returns a pooled connection
     * @return An MBeanConnection.
     * @throws MBeanServerConnectionFactoryException
     */
    @ManagedOperation
    public MBeanServerConnection getPooledConnection() throws MBeanServerConnectionFactoryException {
        try {
            return connectionPool.borrowObject();
        } catch (Exception e) {
            throw new MBeanServerConnectionFactoryException("Failed to create new connection", e);
        }
    }

    /**
     * Returns a pooled connection to the pool
     * @param connection the MBeanServerConnection to return to the pool
     * @throws Exception
     */
    @ManagedOperation
    public void returnPooledConnection(MBeanServerConnection connection) throws Exception {
        connectionPool.returnObject(connection);
    }

    /**
     * Resets the permanent connection
     * @return the permanent connection
     * @throws MBeanServerConnectionFactoryException
     */
    @ManagedOperation
    public MBeanServerConnection resetConnection() throws MBeanServerConnectionFactoryException {
        mBeanConnection = null;
        return getConnection();
    }

    /**
     * The internal getConnection implementation
     * @return an MBeanServerConnection
     * @throws IOException
     */
    protected abstract MBeanServerConnection _getConnection() throws Exception;

    /**
     * Returns the host Id that this factory connects to 
     * @return the host Id that this factory connects to
     */
    @ManagedAttribute
    public String getHostId() {
        return hostId;
    }

    /**
     * Returns the vm Id that this factory connects to 
     * @return the vm Id that this factory connects to
     */
    @ManagedAttribute
    public String getVMId() {
        return vmId;
    }

    /**
     * Returns the JMX default domain name of the MBeanServer that this factory connects to
     * @return the JMX default domain name of the MBeanServer that this factory connects to
     */
    @ManagedAttribute
    public String getDefaultDomain() {
        return defaultDomain;
    }

    /**
     * Returns the host and vm ids for the VM that this connection factory connects to
     * @return A String array with the host id in [0] and the VM id in [1].
     */
    public synchronized String[] getIds() {
        if (serverIdentifier == null) {
            if (serverIdentifierClassName == null) {
                serverIdentifier = new DefaultMBeanServerIdentifier();
            } else {
                serverIdentifier = AbstractMBeanServerIdentifier.getInstance(serverIdentifierClassName);
            }
        }
        MBeanServerConnection connection = null;
        try {
            connection = this.getConnection();
            return serverIdentifier.getHostVMId(connection);
        } catch (Exception e) {
            return new String[] { "UnknownHost#" + UNKNOWN_SERIAL.incrementAndGet(),
                    "UnknownVM#" + UNKNOWN_SERIAL.incrementAndGet(),
                    "UnknownDomain#" + UNKNOWN_SERIAL.incrementAndGet() };
        } finally {
            // How do we close the connection ?
        }
    }

    /**
     * Returns the configured class name of the server identifier for this connection factory 
     * @return the serverIdentifierClassName
     */
    public String getServerIdentifierClassName() {
        return serverIdentifierClassName;
    }

    /**
     * Sets the configured class name of the server identifier for this connection factory
     * @param serverIdentifierClassName the serverIdentifierClassName to set
     */
    public void setServerIdentifierClassName(String serverIdentifierClassName) {
        this.serverIdentifierClassName = serverIdentifierClassName;
    }

    //   /**
    //    * No Op
    //    * @param obj
    //    * @throws Exception
    //    */
    //   @Override
    //   public void activateObject(Object obj) throws Exception {
    //      
    //   }
    //
    //   /**
    //    * Destroys an MBeanServerConnection
    //    * @param obj an MBeanServerConnection
    //    * @throws Exception
    //    */
    //   @Override
    //   public void destroyObject(Object obj) throws Exception {
    //      if(obj!=null && obj instanceof Closeable) {
    //         try { ((Closeable)obj).close(); } catch (Exception e) {}
    //      }
    //   }
    //
    //   /**
    //    * Creates a new MBeanServerConnection
    //    * @return a new MBeanServerConnection
    //    * @throws Exception
    //    */
    //   @Override
    //   public MBeanServerConnection makeObject() throws Exception {
    //      return getNewConnection();
    //   }
    //
    //   /**
    //    * No Op
    //    * @param obj
    //    * @throws Exception
    //    */
    //   @Override
    //   public void passivateObject(Object obj) throws Exception {
    //      
    //   }
    //
    //   /**
    //    * Validates an MBeanServerConnection
    //    * @param connection
    //    * @return
    //    */
    //   @Override
    //   public boolean validateObject(Object connection) {
    //      if(connection==null || !(connection instanceof MBeanServerConnection)) return false;
    //      try { 
    //         ((MBeanServerConnection)connection).getMBeanCount(); 
    //         return true; 
    //      } catch (Exception e) {
    //         return false;
    //      }      
    //   }

    /**
     * Sets the application context
     * @param applicationContext the applicationContext to set
     */
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    /**
     * Returns the bean name of the mbean server connection pool prototype factory
     * @return the factoryName
     */
    public String getFactoryName() {
        return factoryName;
    }

    /**
     * Sets the bean name of the mbean server connection pool prototype factory
     * @param factoryName the factoryName to set
     */
    public void setFactoryName(String factoryName) {
        this.factoryName = factoryName;
    }

    /**
     * Indicates if the permanent MBeanServer connection is connected
     * @return true if the permanent MBeanServer connection is connected
     */
    @ManagedOperation
    public boolean getConnected() {
        return connected;
    }

    /**
     * Checks the permanent connection and acquires it if it is null.
     */
    protected void validateConn() {
        if (mBeanConnection == null) {
            try {
                getConnection();
            } catch (Exception e) {
                throw new RuntimeException("Failed to invoke against Permanent MBeanServerConnection", e);
            }
        }
    }

    /**
     * Adds a notification listener to the permanent MBeanServer connection
     * @param objectName
     * @param listener
     * @param filter
     * @param handback
     * @throws InstanceNotFoundException
     * @throws IOException
     */
    @Override
    @ManagedOperation
    public void addNotificationListener(ObjectName objectName, NotificationListener listener,
            NotificationFilter filter, Object handback) throws InstanceNotFoundException, IOException {
        validateConn();
        mBeanConnection.addNotificationListener(objectName, listener, filter, handback);
    }

    /**
     * Adds a notification listener to the permanent MBeanServer connection
     * @param objectName
     * @param listener
     * @param filter
     * @param handback
     * @throws InstanceNotFoundException
     * @throws IOException
     */
    @Override
    @ManagedOperation
    public void addNotificationListener(ObjectName objectName, ObjectName listener, NotificationFilter filter,
            Object handback) throws InstanceNotFoundException, IOException {
        validateConn();
        mBeanConnection.addNotificationListener(objectName, listener, filter, handback);
    }

    /**
     * Instantiates and registers an MBean in the MBean server.
     * @param className
     * @param objectName
     * @return the ObjectInstance representing the created MBean
     * @throws ReflectionException
     * @throws InstanceAlreadyExistsException
     * @throws MBeanRegistrationException
     * @throws MBeanException
     * @throws NotCompliantMBeanException
     * @throws IOException
     */
    @Override
    @ManagedOperation
    public ObjectInstance createMBean(String className, ObjectName objectName)
            throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException,
            NotCompliantMBeanException, IOException {
        validateConn();
        MBeanServerConnection conn = null;
        try {
            conn = getPooledConnection();
            return conn.createMBean(className, objectName);
        } catch (MBeanServerConnectionFactoryException e) {
            throw new RuntimeException("Failed to get pooled connection", e);
        } finally {
            try {
                this.returnPooledConnection(conn);
            } catch (Exception e) {
                log.debug(e.getMessage());
            }
        }
    }

    /**
     * Instantiates and registers an MBean in the MBean server.
     * @param className
     * @param objectName
     * @param loaderName
     * @return the ObjectInstance representing the created MBean
     * @throws ReflectionException
     * @throws InstanceAlreadyExistsException
     * @throws MBeanRegistrationException
     * @throws MBeanException
     * @throws NotCompliantMBeanException
     * @throws IOException
     * @throws InstanceNotFoundException
     */
    @Override
    @ManagedOperation
    public ObjectInstance createMBean(String className, ObjectName objectName, ObjectName loaderName)
            throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException,
            NotCompliantMBeanException, IOException, InstanceNotFoundException {
        validateConn();
        MBeanServerConnection conn = null;
        try {
            conn = getPooledConnection();
            return conn.createMBean(className, objectName, loaderName);
        } catch (MBeanServerConnectionFactoryException e) {
            throw new RuntimeException("Failed to get pooled connection", e);
        } finally {
            try {
                this.returnPooledConnection(conn);
            } catch (Exception e) {
                log.debug(e.getMessage());
            }
        }
    }

    /**
     * Instantiates and registers an MBean in the MBean server.
     * @param className
     * @param objectName
     * @param param 
     * @param signature
     * @return the ObjectInstance representing the created MBean
     * @throws ReflectionException
     * @throws InstanceAlreadyExistsException
     * @throws MBeanRegistrationException
     * @throws MBeanException
     * @throws NotCompliantMBeanException
     * @throws IOException
     */
    @Override
    @ManagedOperation
    public ObjectInstance createMBean(String className, ObjectName objectName, Object[] params, String[] signature)
            throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException,
            NotCompliantMBeanException, IOException {
        validateConn();
        MBeanServerConnection conn = null;
        try {
            conn = getPooledConnection();
            return conn.createMBean(className, objectName, params, signature);
        } catch (MBeanServerConnectionFactoryException e) {
            throw new RuntimeException("Failed to get pooled connection", e);
        } finally {
            try {
                this.returnPooledConnection(conn);
            } catch (Exception e) {
                log.debug(e.getMessage());
            }
        }
    }

    /**
     * Instantiates and registers an MBean in the MBean server.
     * @param className
     * @param objectName
     * @param loaderName
     * @param param 
     * @param signature
     * @return the ObjectInstance representing the created MBean
     * @throws ReflectionException
     * @throws InstanceAlreadyExistsException
     * @throws MBeanRegistrationException
     * @throws MBeanException
     * @throws NotCompliantMBeanException
     * @throws IOException
     * @throws InstanceNotFoundException
     */
    @Override
    @ManagedOperation
    public ObjectInstance createMBean(String className, ObjectName objectName, ObjectName loaderName,
            Object[] params, String[] signature)
            throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException,
            NotCompliantMBeanException, IOException, InstanceNotFoundException {
        validateConn();
        MBeanServerConnection conn = null;
        try {
            conn = getPooledConnection();
            return conn.createMBean(className, objectName, loaderName, params, signature);
        } catch (MBeanServerConnectionFactoryException e) {
            throw new RuntimeException("Failed to get pooled connection", e);
        } finally {
            try {
                this.returnPooledConnection(conn);
            } catch (Exception e) {
                log.debug(e.getMessage());
            }
        }
    }

    /**
     * Returns the value of an attribute
     * @param objectName
     * @param attributeName
     * @return
     * @throws MBeanException
     * @throws AttributeNotFoundException
     * @throws InstanceNotFoundException
     * @throws ReflectionException
     * @throws IOException
     */
    @Override
    @ManagedOperation
    public Object getAttribute(ObjectName objectName, String attributeName) throws MBeanException,
            AttributeNotFoundException, InstanceNotFoundException, ReflectionException, IOException {
        validateConn();
        MBeanServerConnection conn = null;
        try {
            conn = getPooledConnection();
            return conn.getAttribute(objectName, attributeName);
        } catch (MBeanServerConnectionFactoryException e) {
            throw new RuntimeException("Failed to get pooled connection", e);
        } finally {
            try {
                this.returnPooledConnection(conn);
            } catch (Exception e) {
                log.debug(e.getMessage());
            }
        }
    }

    /**
     * Returns the values of the passed attribute names
     * @param objectName
     * @param attributeNames
     * @return
     * @throws InstanceNotFoundException
     * @throws ReflectionException
     * @throws IOException
     */
    @Override
    @ManagedOperation
    public AttributeList getAttributes(ObjectName objectName, String[] attributeNames)
            throws InstanceNotFoundException, ReflectionException, IOException {
        validateConn();
        MBeanServerConnection conn = null;
        try {
            conn = getPooledConnection();
            return conn.getAttributes(objectName, attributeNames);
        } catch (MBeanServerConnectionFactoryException e) {
            throw new RuntimeException("Failed to get pooled connection", e);
        } finally {
            try {
                this.returnPooledConnection(conn);
            } catch (Exception e) {
                log.debug(e.getMessage());
            }
        }
    }

    /**
     * Returns the list of domains for target MBeanServer that was retrieved during 
     * last connection check poll for this server
     * @return an array of domain names
     * @throws IOException
     */
    @Override
    @ManagedOperation
    public String[] getDomains() {
        try {
            if (liveDomains == null)
                return getConnection().getDomains();
            else
                return liveDomains.toArray(new String[0]);
        } catch (Exception ex) {
            log.error(ex.getMessage());
            return null;
        }
    }

    /**
     * Returns the number of MBeans registered in the target MBeanServer
     * @return
     * @throws IOException
     */
    @Override
    @ManagedOperation
    public Integer getMBeanCount() {
        try {
            return getConnection().getMBeanCount();
        } catch (Exception ex) {
            return -1;
        }
    }

    /**
     * Returns the MBeanInfo for the passed ObjectName
     * @param objectName
     * @return
     * @throws InstanceNotFoundException
     * @throws IntrospectionException
     * @throws ReflectionException
     * @throws IOException
     */
    @Override
    @ManagedOperation
    public MBeanInfo getMBeanInfo(ObjectName objectName)
            throws InstanceNotFoundException, IntrospectionException, ReflectionException, IOException {
        validateConn();
        MBeanServerConnection conn = null;
        try {
            conn = getPooledConnection();
            return conn.getMBeanInfo(objectName);
        } catch (MBeanServerConnectionFactoryException e) {
            throw new RuntimeException("Failed to get pooled connection", e);
        } finally {
            try {
                this.returnPooledConnection(conn);
            } catch (Exception e) {
                log.debug(e.getMessage());
            }
        }
    }

    /**
     * Returns the ObjectInstance for the passed ObjectName
     * @param objectName
     * @return
     * @throws InstanceNotFoundException
     * @throws IOException
     */
    @Override
    @ManagedOperation
    public ObjectInstance getObjectInstance(ObjectName objectName) throws InstanceNotFoundException, IOException {
        validateConn();
        MBeanServerConnection conn = null;
        try {
            conn = getPooledConnection();
            return conn.getObjectInstance(objectName);
        } catch (MBeanServerConnectionFactoryException e) {
            throw new RuntimeException("Failed to get pooled connection", e);
        } finally {
            try {
                this.returnPooledConnection(conn);
            } catch (Exception e) {
                log.debug(e.getMessage());
            }
        }
    }

    /**
     * Invokes the named operation on the named MBean
     * @param objectName
     * @param opName
     * @param params
     * @param signature
     * @return
     * @throws InstanceNotFoundException
     * @throws MBeanException
     * @throws ReflectionException
     * @throws IOException
     */
    @Override
    @ManagedOperation
    public Object invoke(ObjectName objectName, String opName, Object[] params, String[] signature)
            throws InstanceNotFoundException, MBeanException, ReflectionException, IOException {
        validateConn();
        MBeanServerConnection conn = null;
        try {
            conn = getPooledConnection();
            return conn.invoke(objectName, opName, params, signature);
        } catch (MBeanServerConnectionFactoryException e) {
            throw new RuntimeException("Failed to get pooled connection", e);
        } finally {
            try {
                this.returnPooledConnection(conn);
            } catch (Exception e) {
                log.debug(e.getMessage());
            }
        }
    }

    /**
     * Determines if the named MBean is an instance of the passed class name
     * @param objectName
     * @param className
     * @return
     * @throws InstanceNotFoundException
     * @throws IOException
     */
    @Override
    @ManagedOperation
    public boolean isInstanceOf(ObjectName objectName, String className)
            throws InstanceNotFoundException, IOException {
        validateConn();
        MBeanServerConnection conn = null;
        try {
            conn = getPooledConnection();
            return conn.isInstanceOf(objectName, className);
        } catch (MBeanServerConnectionFactoryException e) {
            throw new RuntimeException("Failed to get pooled connection", e);
        } finally {
            try {
                this.returnPooledConnection(conn);
            } catch (Exception e) {
                log.debug(e.getMessage());
            }
        }
    }

    /**
     * Determines if the passed ObjectName is registered
     * @param objectName
     * @return
     * @throws IOException
     */
    @Override
    @ManagedOperation
    public boolean isRegistered(ObjectName objectName) throws IOException {
        validateConn();
        MBeanServerConnection conn = null;
        try {
            conn = getPooledConnection();
            return conn.isRegistered(objectName);
        } catch (MBeanServerConnectionFactoryException e) {
            throw new RuntimeException("Failed to get pooled connection", e);
        } finally {
            try {
                this.returnPooledConnection(conn);
            } catch (Exception e) {
                log.debug(e.getMessage());
            }
        }
    }

    /**
     * Queries a set of MBean ObjectInstances from the passed ObjectName and Query expression
     * @param objectNaNames 
     * @param query
     * @return
     * @throws IOException
     */
    @Override
    @ManagedOperation
    public Set<ObjectInstance> queryMBeans(ObjectName objectName, QueryExp query) throws IOException {
        validateConn();
        MBeanServerConnection conn = null;
        try {
            conn = getPooledConnection();
            return conn.queryMBeans(objectName, query);
        } catch (MBeanServerConnectionFactoryException e) {
            throw new RuntimeException("Failed to get pooled connection", e);
        } finally {
            try {
                this.returnPooledConnection(conn);
            } catch (Exception e) {
                log.debug(e.getMessage());
            }
        }
    }

    /**
     * Queries a set of MBean ObjectNames from the passed ObjectName and Query expression
     * @param objectName
     * @param query
     * @return
     * @throws IOException
     */
    @Override
    @ManagedOperation
    public Set<ObjectName> queryNames(ObjectName objectName, QueryExp query) throws IOException {
        validateConn();
        MBeanServerConnection conn = null;
        try {
            conn = getPooledConnection();
            return conn.queryNames(objectName, query);
        } catch (MBeanServerConnectionFactoryException e) {
            throw new RuntimeException("Failed to get pooled connection", e);
        } finally {
            try {
                this.returnPooledConnection(conn);
            } catch (Exception e) {
                log.debug(e.getMessage());
            }
        }
    }

    /**
     * Removes a registered notification listener
     * @param objectName
     * @param listener
     * @throws InstanceNotFoundException
     * @throws ListenerNotFoundException
     * @throws IOException
     */
    @Override
    @ManagedOperation
    public void removeNotificationListener(ObjectName objectName, ObjectName listener)
            throws InstanceNotFoundException, ListenerNotFoundException, IOException {
        validateConn();
        MBeanServerConnection conn = null;
        try {
            conn = getPooledConnection();
            conn.removeNotificationListener(objectName, listener);
        } catch (MBeanServerConnectionFactoryException e) {
            throw new RuntimeException("Failed to get pooled connection", e);
        } finally {
            try {
                this.returnPooledConnection(conn);
            } catch (Exception e) {
                log.debug(e.getMessage());
            }
        }
    }

    /**
     * Removes a registered notification listener
     * @param objectName
     * @param listener
     * @throws InstanceNotFoundException
     * @throws ListenerNotFoundException
     * @throws IOException
     */
    @Override
    @ManagedOperation
    public void removeNotificationListener(ObjectName objectName, NotificationListener listener)
            throws InstanceNotFoundException, ListenerNotFoundException, IOException {
        validateConn();
        MBeanServerConnection conn = null;
        try {
            conn = getPooledConnection();
            conn.removeNotificationListener(objectName, listener);
        } catch (MBeanServerConnectionFactoryException e) {
            throw new RuntimeException("Failed to get pooled connection", e);
        } finally {
            try {
                this.returnPooledConnection(conn);
            } catch (Exception e) {
                log.debug(e.getMessage());
            }
        }
    }

    /**
     * Removes a registered notification listener
     * @param objectName
     * @param listener
     * @param filter
     * @param handback
     * @throws InstanceNotFoundException
     * @throws ListenerNotFoundException
     * @throws IOException
     */
    @Override
    @ManagedOperation
    public void removeNotificationListener(ObjectName objectName, ObjectName listener, NotificationFilter filter,
            Object handback) throws InstanceNotFoundException, ListenerNotFoundException, IOException {
        validateConn();
        MBeanServerConnection conn = null;
        try {
            conn = getPooledConnection();
            conn.removeNotificationListener(objectName, listener, filter, handback);
        } catch (MBeanServerConnectionFactoryException e) {
            throw new RuntimeException("Failed to get pooled connection", e);
        } finally {
            try {
                this.returnPooledConnection(conn);
            } catch (Exception e) {
                log.debug(e.getMessage());
            }
        }
    }

    /**
     * Removes a registered notification listener
     * @param objectName
     * @param listener
     * @param filter
     * @param handback
     * @throws InstanceNotFoundException
     * @throws ListenerNotFoundException
     * @throws IOException
     */
    @Override
    @ManagedOperation
    public void removeNotificationListener(ObjectName objectName, NotificationListener listener,
            NotificationFilter filter, Object handback)
            throws InstanceNotFoundException, ListenerNotFoundException, IOException {
        validateConn();
        MBeanServerConnection conn = null;
        try {
            conn = getPooledConnection();
            conn.removeNotificationListener(objectName, listener, filter, handback);
        } catch (MBeanServerConnectionFactoryException e) {
            throw new RuntimeException("Failed to get pooled connection", e);
        } finally {
            try {
                this.returnPooledConnection(conn);
            } catch (Exception e) {
                log.debug(e.getMessage());
            }
        }
    }

    /**
     * Sets the value of an attribute
     * @param objectName
     * @param attribute
     * @throws InstanceNotFoundException
     * @throws AttributeNotFoundException
     * @throws InvalidAttributeValueException
     * @throws MBeanException
     * @throws ReflectionException
     * @throws IOException
     */
    @Override
    @ManagedOperation
    public void setAttribute(ObjectName objectName, Attribute attribute)
            throws InstanceNotFoundException, AttributeNotFoundException, InvalidAttributeValueException,
            MBeanException, ReflectionException, IOException {
        validateConn();
        MBeanServerConnection conn = null;
        try {
            conn = getPooledConnection();
            conn.setAttribute(objectName, attribute);
        } catch (MBeanServerConnectionFactoryException e) {
            throw new RuntimeException("Failed to get pooled connection", e);
        } finally {
            try {
                this.returnPooledConnection(conn);
            } catch (Exception e) {
                log.debug(e.getMessage());
            }
        }

    }

    /**
     * Sets the value of a list of attributes
     * @param objectName
     * @param attributeList
     * @return
     * @throws InstanceNotFoundException
     * @throws ReflectionException
     * @throws IOException
     */
    @Override
    @ManagedOperation
    public AttributeList setAttributes(ObjectName objectName, AttributeList attributeList)
            throws InstanceNotFoundException, ReflectionException, IOException {
        validateConn();
        MBeanServerConnection conn = null;
        try {
            conn = getPooledConnection();
            return conn.setAttributes(objectName, attributeList);
        } catch (MBeanServerConnectionFactoryException e) {
            throw new RuntimeException("Failed to get pooled connection", e);
        } finally {
            try {
                this.returnPooledConnection(conn);
            } catch (Exception e) {
                log.debug(e.getMessage());
            }
        }
    }

    /**
     * Unregisters an MBean identified by the passed ObjectName
     * @param objectName
     * @throws InstanceNotFoundException
     * @throws MBeanRegistrationException
     * @throws IOException
     */
    @Override
    @ManagedOperation
    public void unregisterMBean(ObjectName objectName)
            throws InstanceNotFoundException, MBeanRegistrationException, IOException {
        validateConn();
        MBeanServerConnection conn = null;
        try {
            conn = getPooledConnection();
            conn.unregisterMBean(objectName);
        } catch (MBeanServerConnectionFactoryException e) {
            throw new RuntimeException("Failed to get pooled connection", e);
        } finally {
            try {
                this.returnPooledConnection(conn);
            } catch (Exception e) {
                log.debug(e.getMessage());
            }
        }

    }

    /** Return timeout currently set (milliseconds)*/
    public long getTimeout() {
        return timeout;
    }

    /**
     * Set timeout (milliseconds
     * @param timeout
     */
    @ManagedOperation
    public void setTimeout(long timeout) {
        this.timeout = timeout;
    }

    @ManagedAttribute
    public boolean getPrintStackTrace() {
        return printStackTrace;
    }

    /**
     * Dictates whether detailed stack traces for exceptions should be printed out in logs
     * @param logStackTrace
     */
    public void setPrintStackTrace(boolean printStackTrace) {
        this.printStackTrace = printStackTrace;
    }

    /**
     * @return the retrialsSinceOffline
     */
    @ManagedAttribute
    public int getRetrialsSinceOffline() {
        return retrialsSinceOffline;
    }

    @Override
    public Object makeObject() throws Exception {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void destroyObject(Object obj) throws Exception {
        // TODO Auto-generated method stub

    }

    @Override
    public boolean validateObject(Object obj) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public void activateObject(Object obj) throws Exception {
        // TODO Auto-generated method stub

    }

    @Override
    public void passivateObject(Object obj) throws Exception {
        // TODO Auto-generated method stub

    }

    /**
     * Returns a set of the names of the registered connect triggers
     * @return a set of the names of the registered connect triggers
     */
    @ManagedAttribute(description = "The names of the registered connect triggers")
    public Set<String> getTriggerNames() {
        Set<String> triggerNames = new HashSet<String>(triggers.size());
        for (ConnectTrigger trigger : triggers) {
            triggerNames.add(trigger.getClass().getName());
        }
        return triggerNames;
    }

    /**
     * Adds a set of triggers to this connection factory
     * @param triggers a set of triggers to add to this connection factory
     */
    public void setTriggers(Set<ConnectTrigger> triggers) {
        if (triggers != null) {
            this.triggers.addAll(triggers);
        }
    }

}