Package com.sun.messaging.jmq.jmsclient

Source Code of com.sun.messaging.jmq.jmsclient.ConnectionRecover

/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2000-2010 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, 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/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt.  See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license."  If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above.  However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/

/*
*  @(#)ConnectionRecover.java  1.43 04/03/08
*/

package com.sun.messaging.jmq.jmsclient;

import javax.jms.*;
//import com.sun.messaging.jmq.io.*;
import com.sun.messaging.*;
//import java.io.*;
import java.util.Enumeration;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
//import com.sun.messaging.jmq.jmsclient.notification.EventHandler;
//import com.sun.messaging.jms.notification.ConnectionReconnectedEvent;

import com.sun.messaging.jmq.jmsclient.resources.*;

/**
*
* This class provide a way for the client to recover itself if the
* connection goes away.
*
* <p>The API user is transparent to the recovering attempt.  All
* consumers are registered to the broker with their original IDs.
* All producers are added with the same parameters.
*
*/


public class ConnectionRecover implements Runnable {

    protected ConnectionImpl connection = null;
    //protected ProtocolHandler protocolHandler = null;

    protected final static String iMQConnectionRecover =
        "iMQConnectionRecover-";
    private boolean debug = Debug.debug;

    protected static final int RECOVER_INACTIVE = 0;
    protected static final int RECOVER_STOPPED = 1;
    protected static final int RECOVER_STARTED = 2;
    protected static final int RECOVER_IN_PROCESS = 3;
    protected static final int TRANSPORT_CONNECTED = 4;
    protected static final int RECOVER_SUCCEEDED = 5;
    protected static final int RECOVER_FAILED = 6;
    protected static final int RECOVER_ABORTED = 7;
   
    //string recover state -- used for logging
    protected static final String STATES[] = {
      "RECOVER_INACTIVE",
      "RECOVER_STOPPED",
      "RECOVER_STARTED",
      "RECOVER_IN_PROCESS",
      "RECOVER_TRANSPORT_CONNECTED",
      "RECOVER_SUCCEEDED",
      "RECOVER_FAILED",
      "RECOVER_ABORTED"
    };
   
    //this flag is set to true when reconnected.
    private int recoverState = RECOVER_INACTIVE;

    private int maxRetries = 100;

    private int failedCount = 0;

    protected Thread recoverThread = null;

    //wait 3 secs and check status.
    private static final int WAIT_TIME = 3000;

    //total wait time out is 600 secs.  This is the time for ReadChannel to
    //wait for this object to become inactive state.
    private static final int MAX_WAIT_COUNT = 200;

    //recover delay time.
    public int recoverDelay = 3000;
   
    private Logger connLogger = ConnectionImpl.connectionLogger;
   
    //enable connection consumer reconnect
    private boolean enableCCReconnect = true;
    public ConnectionRecover(ConnectionImpl connection) {
        this.connection = connection;

        String prop = connection.getTrimmedProperty("imq.recover.maxRetries");
        if ( prop != null ) {
            maxRetries = Integer.parseInt(prop);
        }

        prop = connection.getTrimmedProperty("imq.recover.delay");
        if ( prop != null ) {
            recoverDelay = Integer.parseInt(prop);

            if ( connection.isConnectedToHABroker ) {
                if (recoverDelay < ConnectionInitiator.HA_RECONNECT_DELAY) {
                   recoverDelay = ConnectionInitiator.HA_RECONNECT_DELAY;
                }
            }
        }
       
        //HACC
        //we can disable this just in case we breaks something unexpected.
        prop = connection.getTrimmedProperty("imq.recover.connectionConsumer");
        if ( prop != null ) {
           
            boolean flag = Boolean.getBoolean(prop);
            if (flag == false) {
                this.enableCCReconnect = false;
            }
           
        }
       
        logRecoverState(this.RECOVER_INACTIVE);

        //init();
    }

    protected void init() throws JMSException {
        //protocolHandler = connection.protocolHandler;

        Debug.println("*** in ConnectionRecover.init() ..." );

        if ( connection.isConnectedToHABroker ) {

            if (recoverDelay < ConnectionInitiator.HA_RECONNECT_DELAY) {
                recoverDelay = ConnectionInitiator.HA_RECONNECT_DELAY;
            }

            sleep (recoverDelay);
        }

        //if ( connection.isConnectedToHABroker ) {
        //    sleep (recoverDelay);
        //}

        closeProtocolHandler();

        connection.protocolHandler.init(true);
       
        logRecoverState(TRANSPORT_CONNECTED);
      
    }

    /**
     * Start recover in a different thread.
     */
    public void start() {

        //create a new thread and start recover interests, etc
        recoverThread = new Thread(this);
        if (connection.hasDaemonThreads()) {
            recoverThread.setDaemon(true);
        }
        //thread.setName(iMQConnectionRecover + connection.getConnectionID());
        recoverThread.setName(iMQConnectionRecover + "-" +
                              connection.getLocalID() +
                              "-" + connection.getConnectionID());
       
        /**
         * fix for bug 6520902 - client runtime gave up reconnecting.
         *
         * The state must be set before the thread is started.  This makes it impossible
         * to have the recover thread to finish before the state is set to STARTED again.
         */
        setRecoverState(RECOVER_STARTED);
       
        recoverThread.start();
        //recoverState = RECOVER_RUNNING;
        //logRecoverState(RECOVER_RUNNING);
    }

    /**
     * Connection recover implementation.
     *
     * The connection recover in HAWK has improved such that client runtime
     * will continue to retry when the cocovery failed.
     *
     *
     *
     */
    public void run() {

        //set the current thread reference.
        connection.protocolHandler.recoverThread = Thread.currentThread();

        try {

            //recoverState = RECOVER_IN_PROCESS;
          
          setRecoverState(RECOVER_IN_PROCESS);
         
            recover();

            //recoverState = RECOVER_SUCCEEDED;
            setRecoverState (RECOVER_SUCCEEDED);

            failedCount = 0;

        } catch (JMSException jmse) {
         
          setRecoverState(RECOVER_FAILED);
         
          //log exception
          connLogger.log(Level.WARNING, jmse.toString(), jmse);
         
            //XXX trigger connection recover failed event.
            connection.triggerConnectionReconnectFailedEvent(jmse);
            checkForMaxRetries();
            closeProtocolHandler();
        } finally {

            if (recoverState == RECOVER_SUCCEEDED ) {
              connection.triggerConnectionReconnectedEvent();
            }

            connection.protocolHandler.recoverThread = null;
           
            //reset state
          setRecoverState(RECOVER_INACTIVE);
        }
    }

    /**
     * Recover the connection when it's broken.
     * 1. Delete messages in the session queue and receive queue.
     * 2. Delete unacked messages.
     * 3. Reconnect.
     * 4. hello to broker.
     * 5. Register interests.
     *
     * NOTE: We do not recover if any of the following conditions exists:
     * 1. There are active temporary destinations associated with the
     * connection.
     * 2. There are transacted sessions.
     * 3. There are unacked messages for client acked session.
     */
    protected void recover() throws JMSException {

        try {
            if (debug) {
                Debug.println("BEGIN ConnectionRecover.recover()...");
            }

            //set the current thread reference.
            //protocolHandler.recoverThread = Thread.currentThread();

            /**
             * Synchronized on protocolHandler so that message
             * producers will be block if trying to produce
             * messages during connection recovery.
             *
             * Bug 6157462 -- no need to sync anymore.  all pkt out
             * is synced on the Connection.reconnectSyncObj obj.
             */
            //synchronized ( protocolHandler ) {

            if (connection.isCloseCalled) {
                connection.setReconnecting(false);
                return;
            }

            checkConnectionConsumers();
           
            //HACC -- clear readQs in each CC.
            resetConnectionConsumers();
           
            resetSessions();

            //hand shaking to the broker
            connection.hello(true);
            //this.hello();

            connection.protocolHandler.resetClientID();

            //XXX PROTOCOL3.5
            //Create sessions.
            addSessions();
           
            //register consumers
            addConsumers();

            releaseConnectionConsumers();

            //add producers
            addProducers();
           
            //HACC -- add connection consumer
            //addConnectionConsumers();

            //} //synchronized connection


            //if connection was started, restart the connection
            if ((connection.isStopped == false) && (connection.eventListener == null)) {
                connection.protocolHandler.start();
            }

            if (connection.getEventHandler() != null) {
                connection.getEventHandler().resendConsumerInfoRequests(
                                             connection.protocolHandler);
            }

            if (debug) {
                Debug.info("ConnectionRecover.recover() SUCCESS!!!");
            }

        } catch (JMSException e) {

            if (debug) {
                Debug.println("ConnectionRecover failed.");
                Debug.printStackTrace(e);
            }
           
            throw e;

            //If we catches any exception here, abort ...
            //connection.abort(e);
        } finally {
            releaseConnectionConsumers();

            if (debug) {
                Debug.println("END ConnectionRecover.recover()!!!");
            }

            //protocolHandler.recoverThread = null;

            //if (recoverState && connection.eventListener != null) {
            //    connection.triggerConnectionReconnectedEvent();
            //} else {
            //    connection.setReconnecting(false);
            //}
        }
    }

    /**
     * HACC -- clear cc read queue.
     */
    private void resetConnectionConsumers() {
     
      try {
        int size = this.connection.connectionConsumerTable.size();
       
        for (int i = 0; i<size; i++) {
          ConnectionConsumerImpl ccImpl =
          (ConnectionConsumerImpl) connection.connectionConsumerTable.get(i);

                ccImpl.setFailoverInprogress(true);
          ccImpl.getReadQueue().clear();
        }
       
      } catch (Exception e) {
        this.connLogger.log(Level.WARNING, e.getMessage(), e);
      }
    }

    private void releaseConnectionConsumers() {

        try {
            int size = this.connection.connectionConsumerTable.size();

            for (int i = 0; i<size; i++) {
                try {
                    ConnectionConsumerImpl ccImpl =
                    (ConnectionConsumerImpl) connection.connectionConsumerTable.get(i);
               
                    ccImpl.setFailoverInprogress(false);
                } catch (Exception e) {
                    this.connLogger.log(Level.WARNING, e.getMessage(), e);
                }
            }

        } catch (Exception e) {
            this.connLogger.log(Level.WARNING, e.getMessage(), e);
        }
    }

   
    protected void checkConnectionConsumers() throws JMSException {
       
        //from 4.2, by default we enable reconnect for CC.
        //so no more checking for the table size below.
        //HACC
        if (this.enableCCReconnect) {
            return;
        }

        if (connection.connectionConsumerTable.size() > 0) {
            String errorString = AdministeredObject.cr.getKString(
                AdministeredObject.cr.X_CONNECT_RECOVER);

            JMSException jmse =
            new com.sun.messaging.jms.IllegalStateException
               (errorString,AdministeredObject.cr.X_CONNECT_RECOVER);

            ExceptionHandler.throwJMSException(jmse);
        }

    }

    //XXX chiaming REVISIT: handle connectionConsumer?
    protected void resetSessions() throws JMSException {
        Enumeration enum2 = connection.sessionTable.elements();
        while (enum2.hasMoreElements()) {
            SessionImpl session = (SessionImpl) enum2.nextElement();

            //HACC -- sine 4.2, we allow reconnect for connection consumer
            if ((enableCCReconnect==false) &&  session.getMessageListener() != null) {
                String errorString = AdministeredObject.cr.getKString(
                    AdministeredObject.cr.X_CONNECT_RECOVER);


                JMSException jmse =
                new com.sun.messaging.jms.IllegalStateException
                   (errorString,AdministeredObject.cr.X_CONNECT_RECOVER);

                ExceptionHandler.throwJMSException(jmse);
            }

            session.reset();
        }
    }

    protected void addSessions() throws JMSException {
        Enumeration enum2 = connection.sessionTable.elements();
        while (enum2.hasMoreElements()) {
            SessionImpl session = (SessionImpl) enum2.nextElement();
            session.recreateSession();
        }
    }

    /**
     * Reregister all consumers.
     */
    protected void addConsumers() throws JMSException {
        Object tmp[] = connection.interestTable.toArray();
       
        Vector v = new Vector();
       
        for (int i = 0; i < tmp.length; i++) {
         
          if ( ((Consumer) tmp[i]).isClosed == false ) {
            connection.protocolHandler.addInterest((Consumer) tmp[i]);
          } else {
            v.add(tmp[i]);
          }
         
        }
       
        //clean up closed consumer
        while (v.isEmpty() == false) {
          Object o = v.firstElement();
          connection.interestTable.remove( (Consumer) o);
          v.remove(o);
        }
       
    }

    /**
     * Reregister all producers in a connection
     */
    protected void
        addProducers() throws JMSException {
        Enumeration enum2 = connection.sessionTable.elements();
        while (enum2.hasMoreElements()) {
            SessionImpl session = (SessionImpl) enum2.nextElement();
            addSessionProducers(session);
        }
    }

    /**
     * Add producers in a session. Called by addProducers().
     */
    protected void
        addSessionProducers(SessionImpl session) throws JMSException {
        Enumeration enum2 = session.producers.elements();
        while (enum2.hasMoreElements()) {

            MessageProducerImpl producer =
                (MessageProducerImpl) enum2.nextElement();

            producer.recreateProducer();
        }
    }

    protected synchronized void setRecoverState(int state) {

        if (recoverState != RECOVER_ABORTED) {
            this.recoverState = state;
            logRecoverState(state);
        } else {
          logRecoverState (RECOVER_ABORTED);
        }

        notifyAll();
    }

    protected synchronized int getRecoverState() {
        return this.recoverState;
    }

    private void closeProtocolHandler() {

        try {
            connection.protocolHandler.close();
        } catch (Exception e) {
            if (debug) {
                Debug.printStackTrace(e);
            }
        }
    }

    /**
     * Check if we should continue to retry.  JMS recover failure may consume
     * broker resources and instability.  We should exit when it is
     * not recoverable.
     */
    private void checkForMaxRetries() {

        failedCount++;

        if ( maxRetries == -1 ) {
            return;
        }

        if (failedCount > maxRetries) {

            setRecoverState(RECOVER_ABORTED);

            if (debug) {
                Debug.println("*** reached max internal retry count: " +
                              maxRetries);
            }
           
            String msg =AdministeredObject.cr.getKString(
          ClientResources.I_CONNECTION_RECOVER_ABORTED, this.connection.getBrokerAddressList(), maxRetries);
           
            this.connLogger.log(Level.SEVERE, msg);
        }
    }


    public synchronized void waitUntilInactive() throws JMSException {

        int wcounter = 0;
       
        int lctr = 0;

        if ( recoverState == RECOVER_ABORTED ) {
            JMSException jmse = new JMSException ("ConnectionRecover aborted!");
            ExceptionHandler.throwJMSException(jmse);
        }

        while (recoverState != RECOVER_INACTIVE  &&
               (recoverState != RECOVER_ABORTED)) {

            try {
             
                wait(WAIT_TIME); //wake up every 3 secs.
               
                lctr ++; //log counter
                if (lctr == 5) { //log every 15 secs.
          String msg = AdministeredObject.cr.getKString(
              ClientResources.I_CONNECTION_RECOVER_STATE,
              STATES[getRecoverState()], this.connection
                  .getLastContactedBrokerAddress());

          connLogger.log(Level.INFO, msg);
          lctr = 0; //reset.
        }

            } catch (Exception e) {
                connLogger.log(Level.WARNING, e.toString(), e);
            }

            //don't wait if closed.
            if ( connection.isCloseCalled ) {
                setRecoverState(RECOVER_ABORTED);
                return;
            }

            connection.readChannel.closeIOAndNotify();

            wcounter ++;

            //This should never happen.  But we want to exit should this happen.
            if ( wcounter > MAX_WAIT_COUNT ) { //max wait is 10 minutes.
             
              //if ( debug ) {
              //  Debug.getPrintStream().println("*** RUN AWAY THREAD: ConnectionRecover *** ");
              //  recoverThread.dumpStack();
              //}
             
                //abort
                setRecoverState(RECOVER_ABORTED);

                JMSException jmse =
                    new JMSException ("Timeout on ConnectionRecover object.  Broker: " + connection.getLastContactedBrokerAddress());

                //throw jmse;
                ExceptionHandler.throwJMSException(jmse);
            }
        }

    }

    private void sleep (int sleepTime) {

        try {
            int count = sleepTime/WAIT_TIME;

            for ( int i=0; i<count; i++) {

                if (debug) {
                    Debug.println("*** ConnectionRecover, sleeping " + WAIT_TIME * (i+1) + " milli secs");
                }

                Thread.sleep(WAIT_TIME);

                if ( connection.isCloseCalled ) {
                    return;
                }
            }

        } catch (InterruptedException e) {
            ;
        }
    }
   
    /**
     * log recover state.
     * @param state the current recover state.
     */
    private void logRecoverState (int state) {
     
      if (connLogger.isLoggable(Level.INFO)) {

      String addr = this.connection.getLastContactedBrokerAddress();

      String msg = AdministeredObject.cr.getKString(
          ClientResources.I_CONNECTION_RECOVER_STATE, STATES[state],
          addr);

      connLogger.log(Level.INFO, msg);
    }
    }

}
TOP

Related Classes of com.sun.messaging.jmq.jmsclient.ConnectionRecover

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.