Package org.jboss.mx.remote.connector.socket

Source Code of org.jboss.mx.remote.connector.socket.SocketConnector$RemoteListener

/*
* JBoss, the OpenSource J2EE webOS
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.mx.remote.connector.socket;

import java.net.InetAddress;
import java.util.*;
import javax.management.*;
import javax.management.loading.ClassLoaderRepository;

import org.jboss.mx.remote.RemoteMethodInvocation;
import org.jboss.mx.remote.JMXUtil;
import org.jboss.mx.remote.notification.PriorityNotificationListener;
import org.jboss.mx.remote.connector.AbstractConnector;
import org.jboss.mx.remote.connector.classloader.*;
import org.jboss.mx.util.SerializationHelper;

/**
* SocketConnector is an MBean that uses Sockets to connect remote JMX Clients to the local
* MBeanServer.    <P>
*
* NOTE: we should refactor this with an AbstractConnector when we start implementing the
* other connectors since a lot of logic inside this class can be generalized. -JGH
*
* @author <a href="mailto:jhaynie@vocalocity.net">Jeff Haynie</a>
* @author <a href="mailto:telrod@e2technologies.net">Tom Elrod</a>
* @version $Revision: 1.15 $
*/
public class SocketConnector extends AbstractConnector
        implements SocketConnectorMBean, SocketExecutor
{

    protected SocketAcceptor acceptor;

    protected final Map connections = Collections.synchronizedMap(new HashMap());
    private final Object lock = new Object();

    private String ipaddr = null;
    private int port = -1;
    private Timer timer;

    /**
     * constructor
     *
     */
    public SocketConnector()
    {
        super();
    }

    /**
     * return a string representation of this object
     */
    public String toString ()
    {
        return "SocketConnector [ip="+ipaddr+",port="+port+"]";
    }

    /**
     * get the type of transport for this connector
     *
     * @return transport type string
     */
    public String getTransportType()
    {
        return "socket";
    }

    /**
     * get the transport properties for configuring this
     * transport
     *
     * @return connector transport properties
     */
    public Map getTransportProperties()
    {
        Map p = new HashMap(2);
        String addr = getAddress();
        p.put("ipaddress", (addr == null) ? "0.0.0.0" : addr);
        p.put("port", String.valueOf(getPort()));
        return p;
    }

    /**
     * set the transport properties for the connector
     *
     * @param p
     */
    public void setTransportProperties(Map p)
    {
        String ip = (String)p.get("ipaddress");
        String port =(String)p.get("port");
        if (ip == null)
        {
            throw new IllegalArgumentException("Couldn't find required 'ipaddress' property");
        }
        if (port == null)
        {
            throw new IllegalArgumentException("Couldn't find required 'port' property");
        }
        // set
        setAddress(ip);
        setPort(Integer.parseInt(port));
    }

    /**
     * stops the connector
     *
     * @throws java.lang.Exception
     */
    protected void destroyService() throws Exception
    {
        stopService();
    }


    /**
     * start the connector
     *
     * @throws java.lang.Exception
     */
    protected void startService() throws Exception
    {
        acceptor = new SocketAcceptor(this);
        boolean update = false;
        if (ipaddr == null)
        {
            acceptor.setAddress(null);
            update = true;
        }
        if (port <= 0)
        {
            acceptor.setPort(0);
            update = true;
        }

        acceptor.start();

        if (update)
        {
            InetAddress ap = acceptor.getAddress();
            setAddress(ap == null?null:ap.getHostAddress());
            setPort(acceptor.getPort());
        }
    }

    /**
     * stop the connector
     *
     * @throws java.lang.Exception
     */
    protected void stopService() throws Exception
    {
        if (acceptor!=null)
        {
            try
            {
                acceptor.stop();
            }
            finally
            {
                acceptor = null;
                port = -1;
            }
        }
    }

    /**
     * get the port that the SocketConnector is running on
     *
     * @return port number
     */
    public int getPort()
    {
        return port;
    }

    /**
     * set the port the connector should listen on
     *
     * @param port
     */
    public void setPort(int port)
    {
        int old = this.port;
        this.port = port;
        if (old != this.port)
        {
            if (log.isDebugEnabled())
            {
                log.debug("port changed to: " + port);
            }
            if (acceptor != null)
            {
                acceptor.setPort(port);
            }
            if (getState() == STARTED)
            {
                sendNotification(
                        new AttributeChangeNotification(
                                this,
                                getNextNotificationSequenceNumber(),
                                System.currentTimeMillis(),
                                "connector.socket.port.changed",
                                "Port",
                                Integer.class.getName(),
                                new Integer(old),
                                new Integer(port)));
            }
        }
    }

    /**
     * get the address the SocketConnector is listening on
     *
     * @return address
     */
    public String getAddress()
    {
        return ipaddr;
    }

    /**
     * set IP Address for the SocketConnector to listen on
     *
     * @param addr
     */
    public void setAddress(String addr)
    {
        String old = this.ipaddr;
        this.ipaddr = addr;

        if ((old == null && this.ipaddr != null) ||
                (old != null && this.ipaddr == null) ||
                (old != null && old.equals(this.ipaddr) == false))
        {
            if (acceptor != null)
            {
                if (log.isDebugEnabled())
                {
                    log.debug("IP Address changed to: " + addr);
                }
                try
                {
                    acceptor.setAddress(addr == null ? null : InetAddress.getByName(addr));
                }
                catch (Exception ex)
                {
                    throw new IllegalArgumentException("Invalid IP Address: " + addr + ". Exception message: " + ex.getMessage());
                }
            }
            if (getState() == STARTED)
            {
                sendNotification(
                        new AttributeChangeNotification(
                                this,
                                getNextNotificationSequenceNumber(),
                                System.currentTimeMillis(),
                                "connector.socket.address.changed",
                                "Address",
                                String.class.getName(),
                                old,
                                this.ipaddr));
            }
        }
    }

    /**
     * return the notification info for the connector
     *
     * @return notification metadata
     */
    public MBeanNotificationInfo[] getNotificationInfo()
    {
        MBeanNotificationInfo ni[] = new MBeanNotificationInfo[1];
        ni[0] = new MBeanNotificationInfo(new String[]{"connector.socket.address.changed", "connector.socket.address.changed"}, AttributeChangeNotification.class.getName(), "attribute change notification");
        return ni;
    }

    /**
     * internal class that maps a connection and a set of listeners for a given connection
     */
    protected final class Connection extends TimerTask implements RemoteClassHandler
    {
        final String key;
        final SocketClient client;
        ClassByteClassLoader classloader;
        long lastUsed;
        int hits=0;
        Map listeners=new HashMap();

        Connection(String key, InetAddress addr, int port)
                throws Exception
        {
            this.key = key;
            this.client = new SocketClient(addr, port);
            this.client.start();
            if (log.isDebugEnabled())
            {
                log.debug("start called on SocketClient - "+addr+":"+port+" ["+client+"]");
            }
            if (SocketConnector.this.timer==null)
            {
                SocketConnector.this.timer=new Timer();
                SocketConnector.this.timer.scheduleAtFixedRate(this,360000,360000);
            }
            classloader = new ClassByteClassLoader(new ClassLoader[]{this.getClass().getClassLoader()},this);
        }
        /**
         * return the remote classloader of remotely loaded classes - note, don't call this
         * classloader directly to load classes since it will only expose local classes
         *
         * @return classloader
         */
        public ClassLoader getRemoteClassLoader ()
        {
            return classloader;
        }
        synchronized void hit ()
        {
            lastUsed=System.currentTimeMillis();
            hits++;
        }
        public void run ()
        {
            // check connection and make sure that the remote connection is still connected
            synchronized(this)
            {
                if (System.currentTimeMillis()-lastUsed<=120000L)
                {
                    // if used in last 2 min let it go
                    return;
                }
                try
                {
                    Boolean state = (Boolean)this.client.invoke("SocketConnector_Ping",null,null,null,null);
                    //log.debug("state returned = "+state);
                    if (state != null && state.booleanValue())
                    {
                        return;
                    }
                    destroy();
                    return;
                }
                catch (Throwable t)
                {
                    if (log.isDebugEnabled())
                    {
                        log.debug("error calling getState",t);
                    }
                }
                if (this.client.isStopped() ||
                    this.client.isBound() == false ||
                    this.client.isClosed() ||
                    this.client.isConnected() == false ||
                    this.client.isInputShutdown() ||
                    this.client.isOutputShutdown())
                {
                    destroy();
                    return;
                }
            }
        }
        /**
         * load the Class using the classname
         *
         * @param className
         * @return loaded class
         * @throws ClassNotFoundException
         */
        public Class loadClass(String className)
                throws ClassNotFoundException
        {
            if (className==null||className.equals("null class"))
            {
                if (log.isDebugEnabled())
                {
                    log.debug("loadClass called with null class");
                }
                throw new ClassNotFoundException();
            }
            if (classloader.hasClass(className)) {
                return classloader.loadClass(className);
            }
            try
            {
                if (logMethod && log.isDebugEnabled())
                {
                    log.debug("attempting to load remote class: "+className+" from: "+client);
                }
                Map map=new HashMap(2);
                map.put("ip",ipaddr);
                map.put("port",new Integer(port));
                ClassBytes bytes = (ClassBytes) client.invoke("SocketConnector_GetClass", new Object[]{className}, new String[]{String.class.getName()},map,null);
                if (bytes.isClassFound())
                {
                    classloader.add(bytes);
                    return classloader.loadClass(className);
                }
            }
            catch (Throwable ex)
            {
                if (logMethod && log.isDebugEnabled()) {
                    log.debug("Error adding class bytes for classname: " + className, ex);
                }
            }
            throw new ClassNotFoundException(className);
        }

        private void destroy ()
        {
            if (log.isDebugEnabled())
            {
                log.debug("destroy");
            }
            try
            {
                client.stop();
            }
            catch (Exception ex)
            {
            }
            try
            {
                cancel();
                timer.cancel();
            }
            catch (Exception ex)
            {

            }
            finally
            {
                timer=null;
            }
            synchronized(listeners)
            {
                Iterator iter=listeners.values().iterator();
                while(iter.hasNext())
                {
                    RemoteListener listener=(RemoteListener)iter.next();
                    try
                    {
                        listener.removeListener();
                    }
                    catch (Exception ex) { }
                    iter.remove();
                    listener=null;
                }
            }
            connections.remove(key);
        }

    }

    /**
     * a remote listener class
     */
    private final class RemoteListener implements PriorityNotificationListener, NotificationFilter
    {
        final String key;
        final Connection connection;
        final ObjectName obj;
        final int priority;

        RemoteListener(String key, Connection connection, NotificationFilter filter, ObjectName obj, int priority)
                throws Exception
        {
            this.key = key;
            this.connection = connection;
            this.obj = obj;
            this.priority = priority;
            getServer().addNotificationListener(JMXUtil.getMBeanServerObjectName(),this,this,this);
            if (logMethod && log.isDebugEnabled())
            {
                log.debug("adding remote notification listener: "+this+" with key: "+key);
            }
            getServer().addNotificationListener(obj, this, filter, key);
        }

        public int getServicePriority ()
        {
            return priority;
        }

        private void removeListener ()
        {
            try
            {
                if (logMethod&&log.isDebugEnabled())
                {
                    log.debug("removing failed connection listener: "+this+" from: "+obj);
                }
                // remove our local notification listener
                getServer().removeNotificationListener(obj,this);
            }
            catch (Exception ig) { }
            try
            {
                // remove our local notification listener
                getServer().removeNotificationListener(JMXUtil.getMBeanServerObjectName(),this);
            }
            catch (Exception ig) { }
        }
        private void stop ()
        {
            try
            {
                connection.destroy();
            }
            catch (Exception ignore) {}
        }

        /**
         * This method is called before a notification is sent to see whether
         * the listener wants the notification.
         *
         * @param notification the notification to be sent.
         * @return true if the listener wants the notification, false otherwise
         */
        public boolean isNotificationEnabled (Notification notification)
        {
            return (notification instanceof MBeanServerNotification &&
                    notification.getType().equals(MBeanServerNotification.UNREGISTRATION_NOTIFICATION) &&
                    ((MBeanServerNotification)notification).getMBeanName().equals(obj));
        }

        public synchronized void handleNotification(Notification notification, Object o)
        {
            if (logMethod&&log.isDebugEnabled())
            {
                log.debug("RemoteListener ["+key+"] received local notification: "+notification);
            }
            if (o!=null && o.equals(this) && notification instanceof MBeanServerNotification)
            {
                // remove ourself since we can no longer receive events
                connection.listeners.remove(key);
                return;
            }
            try
            {
                if (this.connection.client.isClosed() ||
                    this.connection.client.isStopped())
                {
                    // we're dead already
                    stop();
                    return;
                }
                if (logMethod && log.isDebugEnabled())
                {
                    log.debug("sending remote notification: "+notification+" to: "+key);
                }
                Map params=new HashMap(2);
                params.put("ip",ipaddr);
                params.put("port",new Integer(port));
                // invoke the notification back on the remote client
                this.connection.client.invoke("handleNotification", new Object[]{notification, o}, new String[]{Notification.class.getName(), Object.class.getName()},params,null);
            }
            catch (Throwable ex)
            {
                synchronized (lock)
                {
                    // determine what the problem was
                    if (this.connection.client.isStopped() ||
                        this.connection.client.isBound() == false ||
                        this.connection.client.isClosed() ||
                        this.connection.client.isConnected() == false ||
                        this.connection.client.isInputShutdown() ||
                        this.connection.client.isOutputShutdown())
                    {
                        if (log.isDebugEnabled())
                        {
                            log.debug("removing bad connection: " + this.connection.client+" on handleNotification",ex);
                        }
                        stop();
                    }
                    else
                    {
                        log.warn("Error in handleNotification", ex);
                    }
                }
            }
        }
    }

    /**
     * handle the add notification listener
     *
     */
    protected void handleAddNotificationListener (RemoteMethodInvocation invocation, ObjectName objName, NotificationListener listener, NotificationFilter filter, Object handback)
            throws Throwable
    {
        InetAddress addr = (InetAddress)invocation.getParameter("ip");
        Integer port = (Integer)invocation.getParameter("port");
        String filterKey = (String)invocation.getParameter("key");
        Integer priority = (Integer)invocation.getParameter("priority");
        synchronized (lock)
        {
            Connection conn = getOrMakeConnection(addr,port);
            if (conn.listeners.containsKey(filterKey))
            {
                if (log.isDebugEnabled())
                {
                    log.debug("Ignoring addNotificationListener since we already have one with that key: "+filterKey);
                }
                // we already have it registered
                return;
            }
            listener = new RemoteListener(filterKey, conn, filter, objName, priority.intValue());
            conn.listeners.put(filterKey,listener);
        }
    }

    /**
     * handle the remove notification listener
     *
     */
    protected void handleRemoveNotificationListener (RemoteMethodInvocation invocation, ObjectName objName, NotificationListener listener)
            throws Throwable
    {
        InetAddress addr = (InetAddress)invocation.getParameter("ip");
        Integer port = (Integer)invocation.getParameter("port");
        String filterKey = (String)invocation.getParameter("key");
        String ipKey = addr.toString() + port.toString();
        Connection conn = (Connection) connections.get(ipKey);
        if (conn!=null)
        {
           remove (conn, filterKey, objName, false);
        }
    }
    protected void deserialize (InetAddress from, RemoteMethodInvocation mi)
        throws Exception
    {
        if (logMethod && log.isDebugEnabled())
        {
            log.debug("deserializing ... "+mi);
        }
        String sig[]=mi.getSig();
        Connection conn=null;
        String name = mi.getName();
        Class cl[]=new Class[sig.length];
        Object args[]=new Object[sig.length];
        int start = 0;

        // no point in deserializing arguments and worring about class loading
        // if there are no arguments in the first place
        if(sig.length > 0)
        {

            if (name.equals("addNotificationListener") ||
                name.equals("removeNotificationListener"))
            {
                InetAddress addr = (InetAddress)mi.getParameter("ip");
                Integer port = (Integer)mi.getParameter("port");
                conn=getOrMakeConnection(addr,port);
            }
            ObjectName obj=null;
            if (conn==null)
            {
                InetAddress addr = (InetAddress)mi.getParameter("ip");
                Integer port = (Integer)mi.getParameter("port");
                conn = getOrMakeConnection(addr,port);
            }

            if (sig[start]!=null && sig[start].equals(ObjectName.class.getName()))
            {
                byte buf[] = mi.getArg(start);
                Object arg=SerializationHelper.deserialize(buf);
                obj = (ObjectName)arg;
            }
            for (int c=start;c<sig.length;c++)
            {
                String cn=sig[c];
                //Class clz=conn.classloader.loadClass(cn);
                //Class clz = getServer().getClassLoaderRepository().loadClass(cn);
                //Class clz = .loadClass(cn);
                cl[c]=conn.classloader.loadClass(cn);
            }

            ClassLoader ocl=Thread.currentThread().getContextClassLoader();
            //Thread.currentThread().setContextClassLoader(conn.classloader);
            //FIXME: this is NYI - ClassLoaderRepository mbcl = getServer().getClassLoaderRepository();
            ClassLoaderRepository mbcl = null;

            try
            {
                for (int c=start;c<sig.length;c++)
                {
                    byte buf[]=mi.getArg(c);
                    Object arg = null;
                    try
                    {
                        if (obj!=null)
                        {
                            // use the objectname classloader first
                            arg=loadObjectUsingObjectName(obj,conn.classloader,buf, sig[c]);
                            if (arg==null)
                            {
                                arg = deserialize(mbcl,sig[c],buf);
                            }
                        }
                        else
                        {
                            // use the default classloader associated with connection
                            //arg=SerializationHelper.deserialize(buf);
                            arg = deserialize(mbcl,sig[c],buf);
                        }
                    }
                    catch (ClassNotFoundException e)
                    {
                        Thread.currentThread().setContextClassLoader(conn.classloader);
                        arg = deserialize(mbcl,sig[c],buf);
                    }
                    args[c]=arg;
                }
            }
            finally
            {
                if (ocl!=null)
                {
                    Thread.currentThread().setContextClassLoader(ocl);
                }
            }

        }

        mi.setSignature(cl);
        mi.setArguments(args);
    }

    private Connection getOrMakeConnection (InetAddress addr,Integer port)
        throws Exception
    {
       Connection conn = null;
        synchronized (lock)
        {
            String ipKey = addr.toString() + port.toString();

            conn = (Connection) connections.get(ipKey);
            if (conn == null)
            {
                conn = new Connection(ipKey, addr, port.intValue());
                connections.put(ipKey, conn);
            }
            // update timestamp each hit
            conn.hit();
        }
        return conn;
    }

    /**
     * remove a listener, connector
     *
     * @param key
     * @param objName
     * @throws Exception
     */
    protected void remove(Connection conn,String key, ObjectName objName, boolean destroy)
            throws Exception
    {
        synchronized (lock)
        {
            RemoteListener listener = (RemoteListener) conn.listeners.get(key);
            if (listener != null)
            {
                try
                {
                    getServer().removeNotificationListener(objName, listener);
                }
                finally
                {
                    if (logMethod && log.isDebugEnabled())
                    {
                        log.debug("removing remote notification listener: "+listener+" with key: "+key);
                    }
                    conn.listeners.remove(key);
                    if (destroy || conn.listeners.size() <= 0)
                    {
                        conn.destroy();
                    }
                }
            }
        }
    }

}
TOP

Related Classes of org.jboss.mx.remote.connector.socket.SocketConnector$RemoteListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.