ConnectionManager.java :  » JMS » Open-Message-Queue » com » sun » messaging » jmq » jmsserver » service » Java Open Source

Java Open Source » JMS » Open Message Queue 
Open Message Queue » com » sun » messaging » jmq » jmsserver » service » ConnectionManager.java
/*
 * The contents of this file are subject to the terms
 * of the Common Development and Distribution License
 * (the "License").  You may not use this file except
 * in compliance with the License.
 *
 * You can obtain a copy of the license at
 * https://glassfish.dev.java.net/public/CDDLv1.0.html.
 * See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL
 * HEADER in each file and include the License file at
 * https://glassfish.dev.java.net/public/CDDLv1.0.html.  
 *
 * If applicable add the following below this CDDL HEADER,
 * with the fields enclosed by brackets "[]" replaced with 
 * your own identifying information: Portions Copyright 
 * [year] [name of copyright owner]
 */

/*
 * @(#)ConnectionManager.java  1.57 05/02/06
 *
 * Copyright 2006 Sun Microsystems, Inc.  All Rights Reserved.
 */ 

package com.sun.messaging.jmq.jmsserver.service;

import java.util.*;
import java.io.*;
import com.sun.messaging.jmq.io.*;
import com.sun.messaging.jmq.util.lists.WeakValueHashMap;
import com.sun.messaging.jmq.util.GoodbyeReason;
import com.sun.messaging.jmq.jmsserver.util.*;
import com.sun.messaging.jmq.jmsserver.Globals;
import com.sun.messaging.jmq.jmsserver.management.agent.Agent;
import com.sun.messaging.jmq.jmsserver.resources.BrokerResources;
import com.sun.messaging.jmq.util.log.Logger;
import com.sun.messaging.jmq.io.Status;

import com.sun.messaging.jmq.util.timer.JMQTimer;
import com.sun.messaging.jmq.util.timer.JMQTimerTask;

/**
 * This class maintains the list of all connections
 * currently active in the system.
 * It allows ConnectionUID's to be mapped to Connections
 * and may be used to check connection licensing.
 */

public class ConnectionManager extends WeakValueHashMap
{
    public static boolean DEBUG = false;
    private Logger logger = Globals.getLogger();

    long lastConCheck = 0;


    private int limit = 0;

    public static int pingTimeout = Globals.getConfig().getIntProperty(
                           Globals.IMQ + ".ping.interval", 120) * 1000;

    public boolean PING_ENABLED = Globals.getConfig().getBooleanProperty(
                           Globals.IMQ + ".ping.enabled", true);


    private ConnectionWatcher connectionWatcher = null;

    // lock and counters to allow:
    //     routing to access connection manager at same time
    //     prevent adding/removing on connections @ same time
    //     (although multiple add or destroy operations can happen at once)
    // The adding/removing of multiple connections should not happen
    // because there is "state" information on one connection (e.g. the
    //     acked messages) which should be handled before the messages
    //     are forwarded to a new connection

    private int destroyCount =0; 
    private int addCount =0; 
    public Object addLock = new Object();

    public ConnectionManager(int limit) {
        super("ConnectionManager");
        this.limit = limit;

    }

    public Connection matchProperty(String name, Object value)
    {
        List al = getConnectionList(null);
        Iterator itr = al.iterator();
        while (itr.hasNext()) {
            Connection c = (Connection)itr.next();
            Object v = c.getClientData(name);
            if (value == v || 
                (value != null && v != null &&  value.equals(v)))
            {
                return c;
            }
        }
        return null;
    }


    private void startTimer() {
        if (PING_ENABLED && connectionWatcher == null) {
            lastConCheck = System.currentTimeMillis();
            JMQTimer timer = Globals.getTimer();
            connectionWatcher = new ConnectionWatcher();
            try {
                timer.schedule(connectionWatcher, pingTimeout, pingTimeout);
            } catch (IllegalStateException ex) {
                logger.log(Logger.DEBUG,"Timer shutting down", ex);
            }
        }
    }

    private void stopTimer() {
        if (connectionWatcher != null) {
            connectionWatcher.cancel();
            connectionWatcher = null;
        }

    }  


    public void updateConnectionUID(ConnectionUID currentID,
               ConnectionUID oldid)
    {
  synchronized(addLock) {
            if (destroyCount > 0) {
                try {
                    addLock.wait();
                } catch (InterruptedException ex){
                }
             }
             addCount ++;
        }
        try {
            synchronized(this) {
                Object o = this.remove(oldid);
                if (o != null)
                this.put(currentID, o);
            }
        } finally {
             synchronized(addLock) {
                 addCount --;
                 addLock.notifyAll();
             }
        }
    }

    /**
     * add a new connection 
     *
     * @param con connection to add to the list
     * @throws BrokerException if the connection can not
     *       be added (e.g. licensing has expired)
     */
    public  void addConnection(Connection con)
        throws BrokerException
    {
  synchronized(addLock) {
            if (destroyCount > 0) {
                try {
                    addLock.wait();
                } catch (InterruptedException ex){
                }
             }
             addCount ++;
        }
        try {
            if (this.size() > limit) { // throw exception
                  throw new BrokerException(
                       Globals.getBrokerResources().getKString(
                          BrokerResources.X_CONNECTION_LIMIT_EXCEEDED, 
                          con.toString(),
                          String.valueOf(limit)), 
                       BrokerResources.X_CONNECTION_LIMIT_EXCEEDED,
                       (Throwable)null, Status.ERROR);
            }
            synchronized(this) {
                this.put(con.getConnectionUID(), con);
                if (size() == 1 && PING_ENABLED) { // first  connection
                    startTimer();
                }
            }

        } finally {
             synchronized(addLock) {
                 addCount --;
                 addLock.notifyAll();
             }
        }

        if (DEBUG) {
            logger.log(Logger.DEBUG, BrokerResources.I_NEW_CONNECTION,
            con.toString(), String.valueOf(size()));
            logCM(Logger.DEBUGHIGH);
        }
    }


    /**
     * retrive a connection object from a connection ID
     * 
     * @param con connection to remove
     */
    public synchronized Connection getConnection(ConnectionUID id) {
        return (Connection)this.get(id);
    }

    /**
     * code to handle removing ALL reverences to a connection
     * when it goes away or is closed
     */
    public void removeConnection(ConnectionUID id, boolean say_goodbye,  
            int reason, String reasonStr)
    {
  /*
   * Unregister/Destroy connection MBeans in the JMX agent in the broker.
   */
  Agent agent = Globals.getAgent();
  if (agent != null)  {
      agent.notifyConnectionClose(id.longValue());
      agent.unregisterConnection(id.longValue());
  }

  synchronized(addLock) {
            if (addCount > 0) {
                try {
                    addLock.wait();
                } catch (InterruptedException ex){
                }
             }
             destroyCount ++;
        }
        try  {
            // first destroy the connection
            Connection con = null;
            synchronized(this) {
                con= (Connection)this.remove(id);
                if (size() == 0 && PING_ENABLED) { // first  connection
                    stopTimer();
                }
            }
            if (con != null) {
                destroyConnectionData(con, say_goodbye, reason, reasonStr);
            }
        } finally {
             synchronized(addLock) {
                 destroyCount --;
                 addLock.notifyAll();
             }
        }
    }

    /**
     * method to clean up all stuff associated with a
     * connection
     */

     private void destroyConnectionData(Connection con, boolean say_goodbye,
                 int reason, String reasonStr)
     {
        if (con == null) return;

        
        if (DEBUG) {
            logger.log(Logger.DEBUG, BrokerResources.I_REMOVE_CONNECTION,
            con.toString(),String.valueOf(size()));
        }


        if (reason == GoodbyeReason.SHUTDOWN_BKR ) {
            con.shutdownConnection(reasonStr);
        } else {
            con.destroyConnection(say_goodbye, reason, reasonStr);
        }


        if (DEBUG)
            logCM(Logger.DEBUGHIGH);
     }


    /**
     * list out all current connections
     */
    private void logCM(int loglevel)
    {
        logger.log(loglevel, "ConnectionManager: " + size());
        Set keys = entrySet();
        Iterator itr = keys.iterator();
        int i = 0;
        while (itr.hasNext()) {
            logger.log(loglevel, "{0}:{1}", String.valueOf(i), itr.next().toString());
            i ++;
        }
    }


    /**
     * remove all connections associated with a Service
     *
     * should only be called when the svc stoped to accept new connections  
     */
    /* FOO
    public  void shutdownAllConnections(Service svc, String reason)
    {
        if (DEBUG)
            logger.log(Logger.DEBUGHIGH, "Removing all connections for  {0} ", svc.toString());

        List cons = getConnectionList(svc);
        Connection con;
        for (int i = cons.size()-1; i >= 0; i--) {
             con = (Connection)cons.get(i);
             destroyConnection(con.getConnectionUID(), true, GoodbyeReason.SHUTDOWN_BKR, reason);
        }

    } */

    public Vector getDebugState(Service svc) {
        List cons = getConnectionList(svc);
        Vector v = new Vector();
        Connection con;
        for (int i = cons.size()-1; i >= 0; i--) {
             con = (Connection)cons.get(i);
             v.add(String.valueOf(con.getConnectionUID().longValue()));
        }
        return v;
    }

    /**
     * Return number of connections for a particular service.
     *
     * @param svc   Service to count connections for. Null to return
     *              total number of connections.
     *
     * @return      Number of open connections for the Service (or
     *              total number of open connections if null svc).
     */
    public synchronized int getNumConnections(Service svc)
    {
        int total = 0;

        Iterator itr = values().iterator();
        while (itr.hasNext()) {
            Connection con = (Connection)itr.next();
            if (svc == null || con.getService() == svc) {
                total++;
            }
        }

        return total;
    }

    /**
     * Returns the list of connections for a particular service.
     *
     * @param svc   Service to count connections for. Null to return
     *              total number of connections.
     *
     * @return      List of open connections for the Service (or
     *              total List of open connections if null svc).
     */
    public synchronized List getConnectionList(Service svc)
    {
        List list = new ArrayList();

        Iterator itr = values().iterator();
        while (itr.hasNext()) {
            Connection con = (Connection)itr.next();
            if (svc == null || con.getService() == svc) {
                list.add(con);
            }
        }

        return list;
    }

    public void debug() {
        List l = getConnectionList(null);
        logger.log(Logger.INFO,"Connection count " + l.size());
        Iterator itr = l.iterator();
        while (itr.hasNext()) {
            Connection c = (Connection)itr.next();
            logger.log(Logger.INFO,"Connection " + c);
            c.debug("\t");
        }
    }

    /**
     * broadcast a message to all connections with flush no-wait
     *
     * should only be called when all services stoped accept new connections
     * @param reason a reason from GoodbyeReason
     * @param msg why the goodbye is being broadcast (debugging)
     */
    public void broadcastGoodbye(int reason, String msg) {
        List cons = getConnectionList(null);
        Connection con;
        for (int i = cons.size()-1; i >= 0; i--) {
           con = (Connection)cons.get(i);
           con.sayGoodbye(reason, msg);
        }
    }

    /**
     * flush all control messages
     *
     * should only be called when all services stoped accept new connections
     */
    public void flushControlMessages(long time) {
        List cons = getConnectionList(null);
        Connection con;
        for (int i = cons.size()-1; i >= 0; i--) {
           con = (Connection)cons.get(i);
           con.flushConnection(time);
        }
    }


    /**
     * flush all control messages
     *
     * should only be called when all services stoped accept new connections
     */

    public void checkAllConnections() {
        List cons = getConnectionList(null);
        Connection con;
        for (int i = cons.size()-1; i >= 0; i--) {
            con = (Connection)cons.get(i);
            long access = con.getAccessTime();
            if (lastConCheck != 0 && access != 0 &&
                access < lastConCheck) {
                con.checkConnection();
            }
        }
        lastConCheck = System.currentTimeMillis();
    }


    class ConnectionWatcher extends JMQTimerTask {

        public void run() {
            checkAllConnections(); 
        }
    }    


    public void cleanupMemory(boolean persistent) {
        // make sure we quit adding connections unti the
        // connections have been cleaned up
        if (persistent)
            logger.log(Logger.DEBUG,"Swapping all unacknowldged messages from memory (messages will remain persisted )");
        else 
            logger.log(Logger.DEBUG,"Swapping all unacknowldged messages from memory (messages will be swapped to disk)");

    }

}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.