/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2000-2009 Sun Microsystems, Inc. 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.html
 * or mq/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 mq/legal/LICENSE.txt.  Sun designates
 * this particular file as subject to the "Classpath" exception as provided by
 * Sun in the GPL Version 2 section of the License file that accompanied this
 * code.  If applicable, add the following below the License Header, with the
 * fields enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyrighted [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. 
 */

/*
 * @(#)Debug.java	1.16 06/27/07
 */ 

package com.sun.messaging.jmq.jmsclient;

import java.io.*;
import java.util.StringTokenizer;
import java.util.Date;
import java.util.logging.Level;
import com.sun.messaging.jmq.io.*;

/**
 * This class is used to debug JMQ client API implementation.  To turn on
 * debugging mode, you can do one of the following options:
 *
 * 1. Define system property 'imq.debug'. For example, java -Dimq.debug TestClass ...
 * This will print out Exceptions that are 'absorbed' by the system (us).  These
 * are the exceptions that do not concern with the developer.  Such as when
 * Socket is closed, the ReadChannel will be interrupted.  We catch the
 * SocketException and exit.  Te exception will be printed out to the
 * PrintStream if 'imq.debug' property is set.
 *
 * 2. Define system property 'debug.verbose'.  For example,
 * java -Dimq.debug.verbose TestClass ...
 * This will printout #1 debugging messages plus some verbose messages in
 * the code.
 *
 * 3. Define system property 'ClassName'.  To debug a specific class, you
 * define 'imq.debug' system property and the 'className' you wish to debug.
 * For example, to debug TopicConnectionImpl,
 * java -Dimq.debug -DTopicConnectionImpl TestClass ...
 * This will dump the connection's set up information.
 * NOTE: To debug a super class, you have to define the class name of the
 * currentClass/subclass.
 *
 * 4. To debug packet, you have the following options:
 *    4.1 Define system property 'ReadWritePacket' or 'ReadOnlyPacket'
 *    or both.  If 'ReadWritePacket' is defined, the outgoing packet
 *    will be dumped to the print stream. If 'ReadOnlyPacket' is defined
 *    the incoming packet will be dumped to the print stream.
 *
 *    For example,
 *    java -Dimq.debug -DReadOnlyPacket -DReadWritePacket testProg
 *
 *    4.2 Define system property 'imq.packetType'.
 *    If a specific packet type is defined and 4.1 properties are not,
 *    the packet type defined will be dumpped to the print stream.
 *    For example,
 *    java -Dimq.debug -Dimq.packetType="28|29" TestProg
 *    will dump GOODBYE (28) and GOODBYE_REPLY (29) packets.
 *
 *
 * 4. Define 'imq.debug.all' system property.  This will dump debugging messages
 * include #1, #2, and #3.
 *
 * 5. You can also call Traceable.dump() anytime you wish.  Any class that
 * implements Traceable has a method dump() that prints information about
 * the class.
 *
 * 6. Define 'imq.debug.file=fileName' if the output is desired to go to a file.
 * For example,
 * java -Dimq.debug.file=debug.out -DReadWritePacket TestProg
 * This will dump all outgoing packets to debug.out file.
 * 
 * 7. Define 'imq.debug.transaction' syatem property will dump all pkts related to
 * transactions.
 */
public class Debug {

    public static boolean debug = false;
    private static boolean debugAll = false;
    private static boolean debugVerbose = false;

    private static boolean silentMode = false;

    //default print stream
    private static PrintStream ps = System.out;


    //although all packets read are also ReadWritePacket type, this is chosen
    //so that the usage pattern is the same as all other Traceable classes.
    private static final String READ_ONLY_PACKET =
                                   "ReadOnlyPacket";
    private static final String READ_WRITE_PACKET =
                                   "ReadWritePacket";

    private static final String PACKET_TYPE = "imq.packetType";
    
    public static final String WRITING_PACKET = " -------writing packet----->";
    public static final String READING_PACKET = " <------reading packet------"; 
    
    private static boolean debugTransaction = false;

    private static boolean useLogger = false;
    
    static {

        if ( System.getProperty("imq.debug") != null ) {
            debug = true;
        }

        //debug all classes - every Debug.printpl will be printed.
        if ( System.getProperty ("imq.debug.all") != null ) {
            debug = true;
            debugVerbose = true;
            debugAll = true;
        }

        if ( System.getProperty( "imq.debug.verbose") != null ) {
            debug = true;
            debugVerbose = true;
        }

        String debugFile = System.getProperty( "imq.debug.file" );
        if (debugFile != null) {
            debug = true;
            try {
                //set append mode to false
                FileOutputStream fos = new FileOutputStream ( debugFile, false );
                // set auto flush to true
                ps = new PrintStream ( fos, true );
            } catch ( Exception e ) {
                //System.out is used by default
                e.printStackTrace();
            }
        }

        if ( System.getProperty("imq.silent") != null ) {
            silentMode = true;
        }
        
        if ( System.getProperty("imq.debug.transaction") != null ) {
        	debug = true;
        	debugTransaction = true;
        }
        
    }

    public Debug() {
    }

    public static void println ( String msg ) {
        if ( debugVerbose ) {
            synchronized (ps) {
                ps.println(msg);
                ps.flush();
            }
        }
    }

    /**
     * print info message.
     * @param msg
     */
    public static void info (String msg ) {

        if ( silentMode == false ) {
            synchronized (ps) {
                ps.println(msg);
                ps.flush();
            }
        }
    }

    /**
     * print all packets read from protocol handler.
     */
    public static void printReadPacket ( ReadOnlyPacket pkt ) {
        String debugClass = System.getProperty(READ_ONLY_PACKET);

        boolean debugPacket = matchPacketType (pkt);

        if ( debugAll || (debugClass != null) || debugPacket ) {
            printPacket (pkt, READING_PACKET);
        }
    }

    /**
     * prints all packets written from protocol handler.
     */
    public static void printWritePacket ( ReadOnlyPacket pkt ) {
        String debugClass = System.getProperty(READ_WRITE_PACKET);

        boolean debugPacket = matchPacketType (pkt);

        if ( debugAll || (debugClass != null) || debugPacket ) {
            printPacket (pkt, WRITING_PACKET);
        }
    }

    /**
     * Match if packet type matches any defined properties.
     */
    public static boolean matchPacketType (ReadOnlyPacket pkt) {

        String prop = System.getProperty(PACKET_TYPE);

        boolean isMatched = matchPacketType (pkt, prop);
        
        if ( isMatched == false ) {
        	//if not defined individually, try if imq.debug.transaction is deifned
        	if ( debugTransaction ) {
        		isMatched = isTransactedPacket (pkt);
        	}
        }

        return isMatched;
    }
    
    /**
     * Match if packet type matches any defined properties.
     */
    public static boolean matchPacketType (ReadOnlyPacket pkt, String prop) {

        if ( prop != null ) {
            StringTokenizer tokenizer = new StringTokenizer ( prop, "|" );
            while ( tokenizer.hasMoreTokens() ) {
                String packetType = tokenizer.nextToken();
                if ( pkt.getPacketType() == Integer.parseInt(packetType) ) {
                    return true;
                }
            }
        } 

        return false;
    }
    
    /**
     * prints all packets.
     */
    private static void printPacket ( ReadOnlyPacket pkt, String msg ) {
    	
        synchronized (ps) {
			ps.println(new Date().toString() + msg);
			pkt.dump(ps);
			ps.flush();
		}
    }
    
    /**
     * print pkt if pkt type matches one of the types specified in prop.
     * @param pkt the pkt to be dumped.
     * @param prop a list of pkt types separated by '|'.  For example, "21|22".
     * @param msg the message to be printed before the pkt dump.
     */
    public static void 
    matchAndPrintPacket (ReadOnlyPacket pkt, String pktFilter, String msg) {
    	
    	boolean shouldPrint = true;
    	
    	if ( pktFilter != null ) {
    		shouldPrint = matchPacketType (pkt, pktFilter);
    	}
    	
    	if ( shouldPrint ) {
    		printPacket (pkt, msg);
    	}
    }

    /**
	 * Dump class info if Traceable object is defined in the system property.
	 * For example, java -DSessionReader=t ...
	 */
    public static void println ( Traceable traceable ) {

    	try {
			String debugClassFullName = traceable.getClass().getName();
			String debugClassName = debugClassFullName;
			String debugClass = null;

			// The following block is trying to obtain the class name (without
			// package name).
			int index = debugClassFullName.lastIndexOf('.');

			if (index >= 0) {
				debugClassName = debugClassFullName.substring(index + 1);
			}

			debugClass = System.getProperty(debugClassName);

			if (debugAll || (debugClass != null)) {
				synchronized (ps) {
					ps.println("-------- begin dump class: "
							+ traceable.getClass().getName());
					traceable.dump(ps);
					ps.println("^^^^^^^^ end dump class: "
							+ traceable.getClass().getName());
					ps.flush();
				}
			}
		} catch (Exception ex) {
			ex.printStackTrace();
		}
    }

    public static void setUseLogger(boolean b) {
        useLogger = b;
    }

    /**
	 * Print stack trace.
	 */
    public static synchronized void printStackTrace( Throwable e ) {

        if ( silentMode ) {
            return;
        }
        if (useLogger) {
        ExceptionHandler.rootLogger.log(Level.INFO, e.getMessage(), e);
        } else {

        e.printStackTrace(ps);
        }
    }

    /**
     * Get debug print stream.
     */
    public static PrintStream getPrintStream() {
        return ps;
    }
    
    public static boolean isTransactedPacket(ReadOnlyPacket pkt) {
    	
    	int type = pkt.getPacketType();
    	
    	switch (type ) {
    		case PacketType.ACKNOWLEDGE:
    		case PacketType.ACKNOWLEDGE_REPLY:
    		case PacketType.COMMIT_TRANSACTION:
    		case PacketType.COMMIT_TRANSACTION_REPLY:	
    		case PacketType.END_TRANSACTION:
    		case PacketType.END_TRANSACTION_REPLY:
    		case PacketType.PREPARE_TRANSACTION:
    		case PacketType.PREPARE_TRANSACTION_REPLY:
    		case PacketType.REDELIVER:
    		case PacketType.ROLLBACK_TRANSACTION:
    		case PacketType.ROLLBACK_TRANSACTION_REPLY:
    		case PacketType.RECOVER_TRANSACTION:
    		case PacketType.RECOVER_TRANSACTION_REPLY:
    		case PacketType.START_TRANSACTION:
    		case PacketType.START_TRANSACTION_REPLY:
    		case PacketType.START:
    		case PacketType.STOP:
    		case PacketType.STOP_REPLY:
    		case PacketType.VERIFY_TRANSACTION:
    		case PacketType.VERIFY_TRANSACTION_REPLY:
    			
    			return true;
    			
    		default:
    			
    			return false;
    	}
    }
    
}
    
/*
 * EOF
 */
