All Downloads are FREE. Search and download functionalities are using the official Maven repository.

mq5.0-source.main.comm-io.src.main.java.com.sun.messaging.jmq.io.Packet Maven / Gradle / Ivy

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2000-2013 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.
 */

/*
 * @(#)Packet.java	1.57 11/26/07
 */ 

package com.sun.messaging.jmq.io;

import java.util.Hashtable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import com.sun.messaging.jmq.io.JMSPacket;
import com.sun.messaging.jmq.util.JMQXid;

/**
 *
 * Encapsulation of an iMQ packet. A packet consists of three parts:
 *
 *  1) A fixed sized header (size HEADER_SIZE bytes)
 *  2) A variable sized header (for holding strings, and optional packet fields)
 *  3) A serialized java.util.Hashtable (for holding message properties)
 *  4) the packet body (a bag of bytes)
 *
 * This class directly manages #1, uses PacketVariableHeader to manage #2,
 * and uses PacketPayload to manage #3 and #4.
 *
 * This class also employs the use of a buffer pool to manager reuse of
 * direct ByteBuffers. Direct ByteBuffers are an order of magnitude more
 * expensive to allocate than normal ByteBuffers, so keeping a pool of them
 * around is a big win (which is not typically the case for memory
 * allocated off of the Java heap).
 *
 */
public class Packet implements JMSPacket {

    private static boolean DEBUG = false;

    protected boolean destroyed = false;

    // Packet magic number. NEVER change this
    public static final int   MAGIC = 469754818;
    // Packet version numbers
    public static final short VERSION1 = 103;
    public static final short VERSION2 = 200;
    public static final short VERSION3 = 301;
    public static final short CURRENT_VERSION = VERSION3;

    // Number of bytes in fixed header portion of packet
    public static final int  HEADER_SIZE = 72;

    public static final int  DEFAULT_POOL_SIZE = 1024 * 1024;
    public static final int  DEFAULT_POOL_BLOCKSIZE = 128;

    // Global sequence number (optionally) tagged onto packets
    protected static int sequenceNumber = 0;

    // A global pool of ByteBuffers. This is used to reuse direct
    // ByteBuffers which are expensive to allocate and deallocate
    // Note: the pool is only used if the packet is using direct ByteBuffers.
    protected static final ByteBufferPool bbPool;

    // Maximum size a packet can be, and the lower and upper bounds
    // this value can be. We put a relatively high lower bound
    // so that control packets are not impacted. 
    protected static long     SIZE_LOWER_BOUND = 512  * 1024;
    protected static final long     SIZE_UPPER_BOUND = Integer.MAX_VALUE;
    protected static long     maxPacketSize = SIZE_UPPER_BOUND;

    // true to automatically generate timestamps
    protected   boolean genTimestamp      = true;

    // true to automatically generate sequence numbers
    protected   boolean genSequenceNumber = true;

    // Buffer to hold fixed portion of header
    protected ByteBuffer fixedBuf = null;

    // Dirty flag. This is true when the fixedBuf is out-of-date
    // with respect to the state of this object.
    protected boolean bufferDirty = false;

    // True to use direct ByteBuffers, else false.
    protected boolean useDirect = false;

    // Buffers used for reading packet off of wire. We keep these around just
    // in case we want to reuse them for subsequent reads if the packet
    // is reused
    protected ByteBuffer varBuf = null;
    protected ByteBuffer propBuf = null;
    protected ByteBuffer bodyBuf = null;

    // Array used to hold buffers during reads and writes. 
    protected ByteBuffer[] writeBufs = new ByteBuffer[4];
    protected ByteBuffer[] readBufs  = new ByteBuffer[4];

    // XXX 1/24/2002 dipol: Needed to work around nio bug (imq 4627557)
    protected int[] readBufsLimits  = new int[4];
    protected int nBufs = 0;

    // A list of all buffers allocated by this packet. We track this
    // so we can easily return them to the pool when the packet is destroyed
    protected ArrayList allocatedBuffers = new ArrayList(8);

    // Values in fixed header
    protected short	version        = VERSION3;
    protected int	magic          = MAGIC;
    protected short	packetType     = 0;
    protected int	packetSize     = 0;
    protected long	expiration     = 0;
    protected int	propertyOffset = 0;
    protected int	propertySize   = 0;
    protected byte	encryption     = 0;
    protected long	transactionID  = 0;
    protected byte	priority       = 5;
    protected int	bitFlags       = 0;
    protected long	consumerID     = 0;

    // Holds sequence, IPaddr, port and timestamp
    protected SysMessageID sysMessageID = new SysMessageID();

    // Classes that handle the variable portion of the packet and the payload
    protected PacketVariableHeader packetVariableHeader = null;
    protected PacketPayload packetPayload = null;

    // Used to track state of in-progress non-blocking I/O
    protected boolean readInProgress = false;
    protected boolean writeInProgress = false;
    protected boolean versionMismatch = false;
    protected int     headerBytesRead = 0;
    protected int     ropBytesRead = 0;
    protected int     bytesWritten = 0;

    static {
        // XXX - the buffer pool should be tunable through properties
        bbPool = new ByteBufferPool(DEFAULT_POOL_SIZE, true);
        bbPool.setBlockSize(HEADER_SIZE);
    }

    // Maximum size packet we should read. This let's the broker
    // protect against denial of service attacks if an overly
    // large packet is sent.

    /**
     * Set the max packet size that should be read. If a packet
     * is larger than this the packet read methods will skip the 
     * bytes and throw an exception.
     *
     * There is a lower bound on the smallest value you can set
     * the max packet size to. So the actual value set may be
     * different than the value passed. This method returns the
     * actual value set.
     */
    public synchronized static long setMaxPacketSize(long n) {
        if (n < SIZE_LOWER_BOUND) {
            maxPacketSize = SIZE_LOWER_BOUND;
	} else {
            maxPacketSize = n;
        }
        return maxPacketSize;
    }

    public synchronized static long getMaxPacketSize() {
        return maxPacketSize;
    }

    public static void setSizeLowerBound(long n) {
        SIZE_LOWER_BOUND = n;
    } 

    public static long getSizeLowerBound() {
        return SIZE_LOWER_BOUND;
    } 

    public synchronized static ByteBufferPool getBufferPool() {
        return bbPool;
    }

    /**
     * Constructs an empty packet that will use direct buffers
     */
    public Packet() {
        this(false);
    }

    /**
     * Construct a packet indicating whether or not to use direct
     * ByteBuffers. 
     *
     * If you plan on reading and writing packets using the methods that
     * take a channel as a parameter then you should specify useDirect=true.
     *
     * If you plan on reading and writing packets using the methods that
     * take an IO Stream as a parameter then you should specify 
     * useDirect = false.
     *
     * Default is to use direct buffers.
     *
     */
    public Packet(boolean useDirect) {
        this.useDirect = useDirect;
	this.reset();
    }

    /**
     * Fill this packet with the contents of sourcePacket. The fixed and
     * variable headers are copied. The payload (properties and body) are
     * shared. This method turns off timestamp and sequence number
     * generation since we assume you want the copied header contents to
     * remain unchanged.
     */
    public synchronized void fill(Packet sourcePacket) throws IOException {
        fill(sourcePacket, false);
    }
    public synchronized void fill(Packet sourcePacket, boolean deep) throws IOException {
	this.reset();

        synchronized (sourcePacket) {

        sourcePacket.updateBuffers();

        ByteBuffer buf = null;

        // Copy fixed header from source packet
        buf = sourcePacket.getHeaderBytes();
        buf.rewind();
        fixedBuf.rewind();
        fixedBuf.put(buf);
        buf.rewind();
        fixedBuf.rewind();

        // Copy variable header from source packet. Note this IS a copy.
        try {
            buf = sourcePacket.getPacketVariableHeader().getBytes();
        } catch (IOException e) {
            // Should never happen
            System.out.println("Could not get variable header" + e);
            return;
        }

        if (buf != null) {
            ByteBuffer newBuf = packetVariableHeader.getBytes();
            if (newBuf == null || newBuf.capacity() < buf.limit()) {
                // Need a new buffer
                newBuf = allocateBuffer(buf.limit());
            } else {
                // Can reuse existing buffer
                newBuf.limit(buf.limit());
                newBuf.rewind();
            }
            newBuf.put(buf);
            newBuf.rewind();
            buf.rewind();
            packetVariableHeader.setBytes(newBuf);
        }

        // Get payload from source packet. Note this is NOT a copy 
	PacketPayload sourcePayload = sourcePacket.getPacketPayload();
	if (sourcePayload != null) {
            // If not deep: set our payload buffers to be those of the source packet.
            // The packets will share payload content, but each must have
            // their own position, mark and limit.
            ByteBuffer b = sourcePayload.getPropertiesBytes(version);
            if (b != null) {
                if (deep) { // copy everything
                    ByteBuffer newb = allocateBuffer(b.limit());
                    newb.put(b);
                    newb.rewind();
                    packetPayload.setPropertiesBytes(
                        newb, version);

                } else { // shallow - use the same buffer
                    packetPayload.setPropertiesBytes(
                        (ByteBuffer)b.duplicate().rewind(), version);
                }
            }
            b = sourcePayload.getBodyBytes();
            if (b != null) {
                if (deep) { // copy everything
                    ByteBuffer newb = allocateBuffer(b.limit());
                    newb.put(b);
                    newb.rewind();
                    packetPayload.setBody(newb);
                } else {
                    packetPayload.setBody((ByteBuffer)b.duplicate().rewind());
                }
                b.rewind();
            }
	}

        } // end synchronized

        // Populate packet with the new info
        parseFixedBuffer(fixedBuf);
        // Forces transaction ID to be populated if VERSION2
        getTransactionID();

        // Since a filled packet clones the header of another packet we
        // turn off sequence and timestamp generation.
        generateSequenceNumber(false);
        generateTimestamp(false);

        return;
    }

    /**
     * Parse the fixed packet header into instance variables.
     */
    protected void parseFixedBuffer(ByteBuffer buf)
	throws StreamCorruptedException, IOException {

        buf.rewind();
	magic   = buf.getInt();

	if (magic != MAGIC) {
	    throw new StreamCorruptedException("Bad packet magic number: " +
			magic + ". Expecting: " + MAGIC);
	}
	   version = buf.getShort();
	packetType = buf.getShort();
	packetSize = buf.getInt();

        // Up to this point the packet header stays the same between 
        // versions. After this point it may be different depending
        // on the version. If the version is something we don't understand
        // then the following will generate garbage values, but we will
        // catch the version error later.


        // In VERSION1 the transactionID is a 32 bit value in the header.
        // In VERSION2 It became a 64 bit value in the variable portion
        // of the packet.
	if (version == VERSION1) {
	    transactionID  = buf.getInt();
        }

	expiration = buf.getLong();

	// Reads timestamp, source IP address, source port and sequence number
        // Hack to get a data input stream for SysMessageID.
        InputStream is = new JMQByteBufferInputStream(buf);
        DataInputStream dis = new DataInputStream(is);
	sysMessageID.readID(dis);
        dis.close();
        is.close();

	propertyOffset = buf.getInt();
	propertySize   = buf.getInt();

	priority   = buf.get();
	encryption = buf.get();

	bitFlags   = (int)(buf.getShort());

        // In VERSION2 the consumerID is a long
        if (version == VERSION1) {
	    consumerID = buf.getInt();
            // There was a bug in the 2.0 packet code that left cruft in the
            // uppper 8 bits of the bitFlags. The upper 8 bits was never 
            // used in 2.0 so this problem was not noticed until later.
            // See bug 4948563 for more info.
            // Zero out the upper 8 bits to clear the garbage.
            bitFlags = bitFlags & 0x00FF;
	} else {
	    consumerID = buf.getLong();
        }

        buf.rewind();
	return;
    }

    public boolean getFlag(int flag) {
	return((bitFlags & flag) == flag);
    }

    public int getVersion() {
	return version;
    }

    public int getMagic() {
	return magic;
    }

    public int getPacketType() {
	return packetType;
    }
    
    /**
     * Return whether this is a reply packet
     * 
     * By convention, a reply packet has a packetType 
     * which is odd and is >=9
     * 
     * In addition, a AUTHENTICATE_REQUEST is considered a reply
     * despite having an odd packetType  
     * 
     * @return whether this is a reply packet
     */
    public boolean isReply(){
    	return (((packetType>=9) && (packetType % 2 == 1)) || (packetType==PacketType.AUTHENTICATE_REQUEST));
    }

    public synchronized int getPacketSize() {
        // Make sure value is correct. If packet is not dirty the update is
        // a no-op
        try {
            updateBuffers();
        } catch (IOException e) {};
	return packetSize;
    }

    public synchronized long getTransactionID() {
        // In VERSION1 the transactionID was in the fixed header. It moved
        // to the variable header in VERSION2.
        if (version >= VERSION2) {
            transactionID =
                packetVariableHeader.getLongField(PacketString.TRANSACTIONID);
        }

        return transactionID;
    }

    public synchronized long getProducerID() {
        return packetVariableHeader.getLongField(PacketString.PRODUCERID);
    }

    public synchronized long getDeliveryTime() {
        return packetVariableHeader.getLongField(PacketString.DELIVERY_TIME);
    }

    public synchronized int getDeliveryCount() {
        return packetVariableHeader.getIntField(PacketString.DELIVERY_COUNT);
    }

    public synchronized long getTimestamp() {
	return sysMessageID.timestamp;
    }

    public synchronized long getExpiration() {
	return expiration;
    }

    public int getPort() {
	return sysMessageID.port;
    }

    public synchronized String getIPString() {
	return sysMessageID.toString();
    }

    public synchronized byte[] getIP() {
	return sysMessageID.getIPAddress();
    }

    public int getSequence() {
	return sysMessageID.sequence;
    }

    public synchronized int getPropertyOffset() {
        try {
            updateBuffers();
        } catch (IOException e) {};
	return propertyOffset;
    }

    public synchronized int getPropertySize() {
        try {
            updateBuffers();
        } catch (IOException e) {};
        return propertySize;
    }

    public int getEncryption() {
	return encryption;
    }

    public int getPriority() {
	return priority;
    }

    public synchronized long getConsumerID() {
	return consumerID;
    }

    public boolean getPersistent() {
	return getFlag(PacketFlag.P_FLAG);
    }

    public boolean getRedelivered() {
	return getFlag(PacketFlag.R_FLAG);
    }

    public boolean getIsQueue() {
	return getFlag(PacketFlag.Q_FLAG);
    }

    public boolean getSelectorsProcessed() {
	return getFlag(PacketFlag.S_FLAG);
    }

    public boolean getSendAcknowledge() {
	return getFlag(PacketFlag.A_FLAG);
    }

    public boolean getIsLast() {
	return getFlag(PacketFlag.L_FLAG);
    }

    public boolean getFlowPaused() {
	return getFlag(PacketFlag.F_FLAG);
    }

    public boolean getIsTransacted() {
	return getFlag(PacketFlag.T_FLAG);
    }

    public boolean getConsumerFlow() {
	return getFlag(PacketFlag.C_FLAG);
    }

    public boolean getIndempotent() {
	return getFlag(PacketFlag.I_FLAG);
    }

    public boolean getWildcard() {
	return getFlag(PacketFlag.W_FLAG);
    }

    /**
     * Get the MessageID for the packet. If the client has set a MessageID
     * then that is what is returned. Otherwise the system message ID is 
     * returned (see getSysMessageID())
     *
     * @return     The packet's MessageID
     */
    public synchronized String getMessageID() {
	String messageID =
            packetVariableHeader.getStringField(PacketString.MESSAGEID);

	if (messageID == null) {
	    return sysMessageID.toString();
        } else {
	    return messageID;
	}
    }

    public synchronized void setMessageID(String messageID) {
         // sets the MESSAGEID string
         packetVariableHeader.setStringField(PacketString.MESSAGEID, messageID);
    }


    /**
     * Get the system message ID. Note that this is not the JMS MessageID
     * set by the client. Rather this is a system-wide unique message ID
     * generated from the timestamp, sequence number, port number and
     * IP address of the packet.
     * 

* WARNING! This returns a references to the Packet's SysMessageID * not a copy. * * @return The packet's system MessageID * */ public synchronized SysMessageID getSysMessageID() { return sysMessageID; } public synchronized String getDestination() { return packetVariableHeader.getStringField(PacketString.DESTINATION); } public synchronized String getDestinationClass() { return packetVariableHeader.getStringField(PacketString.DESTINATION_CLASS); } public synchronized String getCorrelationID() { return packetVariableHeader.getStringField(PacketString.CORRELATIONID); } public synchronized String getReplyTo() { return packetVariableHeader.getStringField(PacketString.REPLYTO); } public synchronized String getReplyToClass() { return packetVariableHeader.getStringField(PacketString.REPLYTO_CLASS); } public synchronized String getMessageType() { return packetVariableHeader.getStringField(PacketString.TYPE); } /** * Return the property hashtable for this packet. * * WARNING! This method emphasizes performance over safety. The * HashTable object returned is a reference to the HashTable object * in the object -- it is NOT a copy. Modifying the contents of * the HashTable will have non-deterministic results so don't do it! */ public synchronized Hashtable getProperties() throws IOException, ClassNotFoundException { return packetPayload.getProperties(); } /** * Return the size of the message body in bytes * * @return Size of message body in bytes */ public synchronized int getMessageBodySize() { return packetPayload.getBodySize(); } /** * Return the message body as a ByteBuffer. * */ public synchronized ByteBuffer getMessageBodyByteBuffer() { return packetPayload.getBodyBytes(); } /** * Return the message body as a byte array */ public synchronized byte[] getMessageBodyByteArray() { ByteBuffer bb = packetPayload.getBodyBytes(); if (bb != null && bb.hasArray()) { return bb.array(); } return null; } /** * Clear the message body of this packate */ public synchronized void clearMessageBody() { if (packetPayload != null) { packetPayload.setBody(null); } } /** * Return an InputStream that contains the contents of the * message body. * * @return An InputStream from which the message body can * be read from. Or null if no message body. */ public synchronized InputStream getMessageBodyStream() { return packetPayload.getBodyStream(); } /** * Reset packet to initial values. This does not free buffers just * in case we can reused them. */ public synchronized void reset() { version = VERSION3; magic = MAGIC; packetType = 0; packetSize = 0; expiration = 0; propertyOffset = 0; propertySize = 0; encryption = 0; priority = 5; bitFlags = 0; consumerID = 0; transactionID = 0; readInProgress = false; headerBytesRead = 0; ropBytesRead = 0; bufferDirty = false; sysMessageID.clear(); if (fixedBuf != null) { fixedBuf.clear(); } else { fixedBuf = allocateBuffer(HEADER_SIZE); } if (varBuf != null) { varBuf.clear(); } if (propBuf != null) { propBuf.clear(); } if (bodyBuf != null) { bodyBuf.clear(); } if (packetVariableHeader != null) { packetVariableHeader.reset(); } else { packetVariableHeader = new PacketVariableHeader(); } if (packetPayload != null) { packetPayload.reset(); } else { packetPayload = new PacketPayload(); } } /** * Update all buffers to reflect the current state of the object. */ public void updateBuffers() throws IOException { if (!bufferDirty) { return; } // Forces variable header and payload to update their buffers. // Then we can compute sizes correctly ByteBuffer varBytes; if (version == VERSION1) { // The 2.0 packet code has a bug where it requires something // in the variable portion of the header, even if it is just // the Null list terminator. getBytes2() addresses this. varBytes = packetVariableHeader.getBytes2(); } else { varBytes = packetVariableHeader.getBytes(); } ByteBuffer propBytes = packetPayload.getPropertiesBytes(version); ByteBuffer bodyBytes = packetPayload.getBodyBytes(); propertySize = ((propBytes == null) ? 0 : propBytes.limit()); int varSize = ((varBytes == null) ? 0 : varBytes.limit()); int bodySize = ((bodyBytes == null) ? 0 : bodyBytes.limit()); propertyOffset = HEADER_SIZE + varSize; packetSize = propertyOffset + propertySize + bodySize; fixedBuf.rewind(); // Write data into fixed header fixedBuf.putInt(MAGIC); fixedBuf.putShort(version); fixedBuf.putShort(packetType); fixedBuf.putInt(packetSize); // In VERSION1 transactionID was a 32bit value in the header if (version == VERSION1) { fixedBuf.putInt((int)transactionID); } fixedBuf.putLong(expiration); // Writes timestamp, source IP addr, source port, and sequence number // Hack to get a data output stream for SysMessageID. OutputStream os = new JMQByteBufferOutputStream(fixedBuf); DataOutputStream dos = new DataOutputStream(os); sysMessageID.writeID(dos); try { dos.close(); os.close(); } catch (IOException e) { ; } fixedBuf.putInt(propertyOffset); fixedBuf.putInt(propertySize); fixedBuf.put(priority); fixedBuf.put(encryption); fixedBuf.putShort((short)bitFlags); // In VERSION1 consumerID is 32bits. In VERSION2 it is 64bits if (version == VERSION1) { fixedBuf.putInt((int)consumerID); } else { fixedBuf.putLong(consumerID); } bufferDirty = false; fixedBuf.rewind(); } /** * Set the packet type. * * @param pType The type of packet */ public synchronized void setPacketType(int pType) { packetType = (short)pType; bufferDirty = true; } public synchronized void setTimestamp(long t) { sysMessageID.setTimestamp(t); bufferDirty = true; } public synchronized void setExpiration(long e) { expiration = e; bufferDirty = true; } public synchronized void setPort(int p) { sysMessageID.port = p; bufferDirty = true; } public synchronized void setIP(byte[] ip) { sysMessageID.setIPAddress(ip); bufferDirty = true; } public synchronized void setIP(byte[] ip, byte[] mac) { sysMessageID.setIPAddress(ip, mac); bufferDirty = true; } public synchronized void setSequence(int n) { sysMessageID.setSequence(n); bufferDirty = true; } // Version should be VERSION1, VERSION2 or VERSION3. Default is VERSION3 public synchronized void setVersion(int n) { if (version != (short)n) { version = (short)n; // We are going from VERSION1 to VERSION2 packet. We explicitly // set the transactionID to make sure it gets set in the // variable portion of the packet. if (version >= VERSION2) { setTransactionID(transactionID); } bufferDirty = true; } } public synchronized void setTransactionID(long n) { transactionID = n; bufferDirty = true; // For version2 packets value is in the varible header. We save // it in the fixed header as well -- just in case somebody changes // the version later. if (version >= VERSION2) { packetVariableHeader.setLongField(PacketString.TRANSACTIONID, n); } } public synchronized void setProducerID(long n) { packetVariableHeader.setLongField(PacketString.PRODUCERID, n); bufferDirty = true; } public synchronized void setDeliveryCount(int n) { packetVariableHeader.setIntField(PacketString.DELIVERY_COUNT, n); bufferDirty = true; } public synchronized void setDeliveryTime(long n) { packetVariableHeader.setLongField(PacketString.DELIVERY_TIME, n); bufferDirty = true; } public synchronized void setEncryption(int e) { encryption = (byte)e; bufferDirty = true; } public synchronized void setPriority(int p) { priority = (byte)p; bufferDirty = true; } public synchronized void setConsumerID(long n) { consumerID = n; bufferDirty = true; } public void setPersistent(boolean b) { setFlag(PacketFlag.P_FLAG, b); } public void setRedelivered(boolean b) { setFlag(PacketFlag.R_FLAG, b); } public void setIsQueue(boolean b) { setFlag(PacketFlag.Q_FLAG, b); } public void setSelectorsProcessed(boolean b) { setFlag(PacketFlag.S_FLAG, b); } public void setSendAcknowledge(boolean b) { setFlag(PacketFlag.A_FLAG, b); } public void setIsLast(boolean b) { setFlag(PacketFlag.L_FLAG, b); } public void setFlowPaused(boolean b) { setFlag(PacketFlag.F_FLAG, b); } public void setIsTransacted(boolean b) { setFlag(PacketFlag.T_FLAG, b); } public void setConsumerFlow(boolean b) { setFlag(PacketFlag.C_FLAG, b); } public void setIndempotent(boolean b) { setFlag(PacketFlag.I_FLAG, b); } public void setWildcard(boolean b) { setFlag(PacketFlag.W_FLAG, b); } public synchronized void setFlag(int flag, boolean on) { if (on) { bitFlags = bitFlags | flag; } else { bitFlags = bitFlags & ~flag; } bufferDirty = true; } public synchronized void setDestination(String s) { packetVariableHeader.setStringField(PacketString.DESTINATION, s); bufferDirty = true; } public synchronized void setDestinationClass(String s) { packetVariableHeader.setStringField(PacketString.DESTINATION_CLASS, s); bufferDirty = true; } public synchronized void setCorrelationID(String s) { packetVariableHeader.setStringField(PacketString.CORRELATIONID, s); bufferDirty = true; } public synchronized void setReplyTo(String s) { packetVariableHeader.setStringField(PacketString.REPLYTO, s); bufferDirty = true; } public synchronized void setReplyToClass(String s) { packetVariableHeader.setStringField(PacketString.REPLYTO_CLASS, s); bufferDirty = true; } public synchronized void setMessageType(String s) { packetVariableHeader.setStringField(PacketString.TYPE, s); bufferDirty = true; } /** * Set the message properties. * WARNING! The Hashtable is NOT copied. * * @param props The message properties. */ public synchronized void setProperties(Hashtable props) { packetPayload.setProperties(props); bufferDirty = true; } /** * Set the message body. * 'body' is sliced to derive the message body so be careful * what the buffers position is! * Note: If you allocate a direct ByteBuffer and pass it here you * will get better performance than passing a byte[] * * @param body The message body. */ public synchronized void setMessageBody(ByteBuffer body) { packetPayload.setBody(body.slice()); bufferDirty = true; } /** * Set the message body. * WARNING! The byte array is NOT copied. * * @param body The message body. */ public synchronized void setMessageBody(byte[] body) { //ByteBuffer buf = ByteBuffer.wrap(body); ByteBuffer buf = null; //check if body is null if (body != null) { buf = ByteBuffer.wrap(body); } packetPayload.setBody(buf); bufferDirty = true; } /** * Set the message body. Specify offset and length of where to take * data from buffer. * WARNING! The byte array is NOT copied. * * @param body The message body. * @param off Offset into body that data starts * @param len Size of message body */ public synchronized void setMessageBody(byte[] body, int off, int len) { ByteBuffer buf = ByteBuffer.wrap(body, off, len); packetPayload.setBody(buf); bufferDirty = true; } protected ByteBuffer getHeaderBytes() { return fixedBuf; } protected PacketPayload getPacketPayload() { return packetPayload; } protected PacketVariableHeader getPacketVariableHeader() { return packetVariableHeader; } /** * Disable (and enable) sequence number generation. The JMQ packet * specification defines a "sequence number" field that is defined * to be an increasing sequence number. By default Packet * will automatically increment the sequence number and set it * on the packet every time writePacket() is called. * The sequence number is a class variable so all packets in a VM * share the same sequence. * * @param generate true to have the packet automatically generate * sequence numbers for you, false to not. Default * is "true". */ public void generateSequenceNumber(boolean generate) { genSequenceNumber = generate; } /** * Disable (and enable) timestamp generation. The JMQ packet specification * specifies a "timestamp" field that is defined to be the time the * packet was sent. By default ReadWritePacket will automatically * generate a timestamp and set it on the packet every time * writePacket() is called. * * @param generate true to have the packet automatically generate * a timestamp on each packet sent, false to not. * Default is "true". */ public void generateTimestamp(boolean generate) { genTimestamp = generate; } /** * Update the timestamp on the packet. If you do this * you should call generateTimestamp(false) before writing the * packet, otherwise the timestamp will be overwritten when * writePacket() is called. */ public synchronized void updateTimestamp() { setTimestamp(System.currentTimeMillis()); } /** * Update the sequence number on the packet. If you do this * you should call generateSequenceNumber(false) before writing the * packet, otherwise the sequence number will be overwritten when * writePacket() is called. */ public synchronized void updateSequenceNumber() { synchronized(Packet.class) { sequenceNumber++; setSequence(sequenceNumber); } } /** * Read a packet from a ReadableByteChannel. This method may be * used for blocking and non-blocking I/O. If you are using blocking * I/O then you should specify "true" for 'block'. If you are using * non-blocking then specify "false". * * Returns "true" if a complete packet has been read, else "false". * If this method returns false then you may call it again (as many * times as necessary) to try and complete the read. If 'block' * is true, then the method will block until a complete packet is read. * * If the packet being read is larger than the limit set by * setMaxPacketSize() then the packet bytes are skipped over * and this method will throw a BigPacketException. At that * point the packet is partially constructed, and only the header * fields in the fixed portion of the packet are valid. */ public synchronized boolean readPacket(ScatteringByteChannel channel, boolean block) throws IOException { if (writeInProgress) { // Should never happen throw new IOException("Can't read packet. Write in progress."); } if (destroyed) { throw new IOException("Packet has been destroyed"); } if (!readInProgress) { // No read in progress. Initialize. reset(); headerBytesRead = 0; ropBytesRead = 0; readInProgress = true; versionMismatch = false; } // If we haven't completed reading the fixed header, try to do so if (headerBytesRead < HEADER_SIZE) { // readFixedHeader returns false if it hasn't completed the read if (!readFixedHeader(channel, block)) { return false; } } // At this point we know the full header has been read. Parse it. fixedBuf.rewind(); parseFixedBuffer(fixedBuf); if (packetSize > maxPacketSize) { // This packet is too large. Skip it. skipBytes(channel, packetSize - HEADER_SIZE); throw new BigPacketException("Packet size (" + packetSize + ") is greater than the maximum allowed packet size (" + maxPacketSize + "). Disgarding packet." ); } if (ropBytesRead == 0) { // Initialize read buffers initializeReadBufs(); } // Try read of rest-of-packet. Will return FALSE if entire packet // was not read into read buffers. if (!readRestOfPacket(channel, block)) { return false; } // Yipeee! The entire packet has been read. Initialize packetVariableHeader.setBytes(varBuf); packetPayload.setPropertiesBytes(propBuf, version); packetPayload.setBody(bodyBuf); readInProgress = false; if (versionMismatch) { throw new IllegalArgumentException("Bad packet version number: " + version + ". Expecting: " + VERSION1 + " or " + VERSION2 + " or " + VERSION3); } return true; } private long skipBytes(InputStream is, long n) throws IOException { long total = 0; int zeroReads = 0; long r = 0; do { if (zeroReads > 1000) { // A kludge. If we are on a socket it might be // non-blocking. We don't want to spin tightly so // every 1000 zero byte reads we sleep for a bit. try { Thread.sleep(1); } catch (InterruptedException e) {} zeroReads = 0; } r = is.skip(n - total); if (r < 0) { throw new EOFException(); } else if (r == 0) { // No bytes skipped. Try again. Keep track of how often this // happens zeroReads++; } total += r; } while (total < n); return total; } /** * Skip over and discard n bytes of data from this channel. * The skip method may, for a variety of reasons, end up skipping over * some smaller number of bytes, possibly 0. This may result from any * of a number of conditions; reaching end of file before n bytes have * been skipped is only one possibility. The actual number of bytes * skipped is returned. If n is negative, no bytes are skipped. * * This simulates a blocking read. */ private long skipBytes(ReadableByteChannel ch, long n) throws IOException { int CHUNK_SIZE = 1024; ByteBuffer buf = ByteBuffer.allocate(CHUNK_SIZE); SelectableChannel sch = null; Selector selector = null; if (ch instanceof SelectableChannel) { sch = (SelectableChannel)ch; } if (sch != null && !sch.isBlocking()) { // If this is a non-blocking channel then register a selector // so we can block until data is ready. selector = Selector.open(); sch.register(selector, SelectionKey.OP_READ); } long total = 0; int zeroReads = 0; long r = 0; do { if (zeroReads > 1000) { // A kludge. Since we are on a channel it is likely // non-blocking. We don't want to spin tightly so // every 1000 zero byte reads we sleep for a bit. // The select should make this unecessary. try { Thread.sleep(1); } catch (InterruptedException e) {} zeroReads = 0; } buf.rewind(); r = ch.read(buf); if (r < 0) { throw new EOFException(); } else if (r == 0) { zeroReads++; // No data ready to be read, block until there is some if (selector != null) { selector.select(1000); } } else { total += r; } } while (total < n); return total; } /** * Read the fixed header into fixedBuf. Returns true if the entire * fixed header has been read. Returns false if it is a partial read. */ private boolean readFixedHeader(ScatteringByteChannel channel, boolean block) throws IOException { // Keep reading while there is data to be read, or // we are supposed to block and the full header hasn't been // read yet. int n = 0; do { n = channel.read(fixedBuf); if (n < 0) { throw new EOFException(); } headerBytesRead += n; } while ((n > 0 || block) && headerBytesRead < HEADER_SIZE); if (headerBytesRead < HEADER_SIZE) { return false; } return true; } /** * Initialize the readBufs to be the proper size. This must be * called after the fixed header has been read and parsed. * Returns the number of buffers allocated */ protected void initializeReadBufs() { if (version != VERSION1 && version != VERSION2 && version != VERSION3) { // This is a packet version we don't understand. Set values // so we swallow rest of packet as the body propertyOffset = HEADER_SIZE; propertySize = 0; versionMismatch = true; } // Now that we know the sizes we can allocate buffers to read // the rest of the packet. int size = 0; nBufs = 0; // Variable header buffer size = propertyOffset - HEADER_SIZE; if (size != 0) { if (varBuf == null || varBuf.capacity() < size) { varBuf = allocateBuffer(size); } else { varBuf.clear(); varBuf.limit(size); } readBufs[nBufs++] = varBuf; } // Properties buffer size = propertySize; if (size > 0) { if (propBuf == null || propBuf.capacity() < size) { propBuf = allocateBuffer(size); } else { propBuf.clear(); propBuf.limit(size); } readBufs[nBufs++] = propBuf; } // Body Buffer size = packetSize - propertyOffset - propertySize; if (size > 0) { if (bodyBuf == null || bodyBuf.capacity() < size) { bodyBuf = allocateBuffer(size); } else { bodyBuf.clear(); bodyBuf.limit(size); } readBufs[nBufs++] = bodyBuf; } // XXX 1/24/2002 dipol: Needed to work around nio bug (imq 4627557) for (int i = 0; i < readBufs.length; i++) { if (readBufs[i] != null) { readBufsLimits[i] = readBufs[i].limit(); } } } /** * Read the the rest of the packet into the read buffers. * Returns true if the rest of the packet has been read. * Returns false if it was only partially read. */ private boolean readRestOfPacket(ScatteringByteChannel channel, boolean block) throws IOException { // Use scattering read to read data into the three buffers // Keep reading while there is data to be read, or // we are supposed to block and the full packet hasn't been // read yet. long n = 0; do { // XXX 1/24/2002 dipol: Needed to work around nio memory leak bug //n = channel.read(readBufs, 0, nBufs); n = myChannelRead(channel, readBufs, 0, nBufs); if (n < 0) { throw new EOFException(); } ropBytesRead += n; // XXX 1/24/2002 dipol: Needed to work around nio bug (imq 4627557) for (int i = 0; i < readBufs.length; i++) { if (readBufs[i] != null && readBufs[i].limit() != readBufsLimits[i]) { readBufs[i].limit(readBufsLimits[i]); } } // XXX End work-around } while ((n > 0 || block) && ((ropBytesRead + headerBytesRead) < packetSize)); if (headerBytesRead + ropBytesRead != packetSize) { return false; } else { return true; } } /** * Initialize the buffers used for writing (writeBufs[]). Returns the * number of valid buffers in writeBufs */ private int initializeWriteBufs() throws IOException { nBufs = 0; // We may have written fixedBuf before. fixedBuf.rewind(); writeBufs[nBufs] = fixedBuf; nBufs++; if (version == VERSION1) { // The 2.0 packet code has a bug where it requires something // in the variable portion of the header, even if it is just // the Null list terminator. getBytes2() addresses this. writeBufs[nBufs] = packetVariableHeader.getBytes2(); } else { writeBufs[nBufs] = packetVariableHeader.getBytes(); } if (writeBufs[nBufs] != null) { nBufs++; } writeBufs[nBufs] = packetPayload.getPropertiesBytes(version); if (writeBufs[nBufs] != null) { nBufs++; } writeBufs[nBufs] = packetPayload.getBodyBytes(); if (writeBufs[nBufs] != null) { nBufs++; } return nBufs; } /** * Write a packet to a WritableByteChannel. This method may be * used for blocking and non-blocking I/O. If you are using blocking * I/O then you should specify "true" for 'block'. If you are using * non-blocking then specify "false". * * Returns "true" if a complete packet has been written, else "false". * If this method returns false then you may call it again (as many * times as necessary) to try and complete the write. If 'block' * is true, then the method will block until a complete packet is written. */ public synchronized boolean writePacket(GatheringByteChannel channel, boolean block) throws IOException { if (readInProgress) { // Should never happen throw new IOException("Can't write packet. Read in progress."); } if (!writeInProgress) { if (genSequenceNumber) { updateSequenceNumber(); } if (genTimestamp) { updateTimestamp(); } updateBuffers(); // Prepare write buffers for writing. initializeWriteBufs(); writeInProgress = true; bytesWritten = 0; } long n = 0; do { // XXX 1/24/2002 dipol: Needed to work around nio memory leak bug //n = channel.write(writeBufs, 0, nBufs); n = myChannelWrite(channel, writeBufs, 0, nBufs); bytesWritten += n; } while ((n > 0 || block) && (bytesWritten < packetSize)); if (bytesWritten != packetSize) { return false; } writeInProgress = false; return true; } /** * Return a unique string that identifies the packet */ public synchronized String toString() { return PacketType.getString(packetType) + ":" + sysMessageID.toString(); } /** * Return a string containing the contents of the packet in a human * readable form. * * @param prefix String to prefix string with. By default "********" */ public synchronized String dumpPacketString(String prefix) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); this.dump(new PrintStream(bos), prefix); return bos.toString(); } public String dumpPacketString() { return dumpPacketString(null); } public void dump(PrintStream os) { this.dump(os, null); } /** * Return a string representation of the data in the fixed header * portion of the packet. */ public synchronized String headerToString() { return ( " Magic/Version: " + magic + "/" + version + "\tSize: " + packetSize + "\t Type: " + PacketType.getString(packetType) + "\n" + " Expiration: " + expiration + ((version == VERSION1) ? (" TransactionID: " + transactionID) : "\t\t") + " Timestamp: " + sysMessageID.timestamp + "\n" + " Source IP: " + sysMessageID.ip.toString() + " Port: " + sysMessageID.port + "\tSequence: " + sysMessageID.sequence + "\n" + "Property Offset: " + propertyOffset + "\t\t\tProperty Size: " + propertySize + "\n" + " Encryption: " + encryption + "\tPriority: " + priority + "\n" + " Flags: " + PacketFlag.getString(bitFlags) + "\t\t\t consumerID: " + consumerID); } /** * Dump the contents of the packet in human readable form to * the specified OutputStream. * * @param os OutputStream to write packet contents to * @param prefix String prefix to print before the packet. * If null "********" is used. */ public synchronized void dump(PrintStream os, String prefix) { if (prefix == null) prefix = "********"; try { updateBuffers(); } catch (Exception e) { os.println("Warning!: Could not update buffers: " + e); } os.println(prefix + " Packet: " + this.toString() + "\n" + headerToString()); if (version >= VERSION2) { os.println(" TransactionID: " + getTransactionID()); } if (getProducerID() != 0L) { os.println(" ProducerID: " + getProducerID()); } if (getDeliveryTime() != 0L) { os.println(" DeliveryTime: " + getDeliveryTime()); } if (getDeliveryCount() != 0) { os.println(" DeliveryCount: " + getDeliveryCount()); } if (getDestination() != null) os.println(" Destination: " + getDestination()); if (getDestinationClass() != null) os.println("DestinationClass: " + getDestinationClass()); if (getMessageID() != null) os.println(" MessageID: " + getMessageID()); if (getCorrelationID() != null) os.println(" CorrelationID: " + getCorrelationID()); if (getReplyTo() != null) os.println(" ReplyTo: " + getReplyTo()); if (getReplyToClass() != null) os.println(" ReplyToClass: " + getReplyToClass()); if (getMessageType() != null) os.println(" MessageType: " + getMessageType()); os.flush(); Hashtable props = null; try { props = getProperties(); } catch (Exception e) { os.println("Exception getting properties: " + e.getMessage()); } if (props == null) { os.println(" Properties: null"); } else { os.println(" Properties: " + props); } PacketUtil.dumpBody(os, packetType, getMessageBodyStream(), getMessageBodySize(), props); os.println("Internal Buffers (useDirect=" + useDirect + "):"); os.println("Fixed Header Buffer:" + fixedBuf.toString()); /* os.print(" Variable Header Buffer:" ); String s = null; try { s = packetVariableHeader.getBytes().toString(); } catch (Exception e) { s = "Could not get buffer: " + e; } os.println(s); os.println(" Properties Buffer: " + packetPayload.getPropertiesBytes().toString()); os.println(" Body Buffer: " + packetPayload.getBodyBytes().toString()); */ } /** * Read packet from an InputStream. This method reads one packet * from the InputStream and sets the state of this object to * reflect the packet read. This method is not synchronized * and should only be called by non-concurrent code. * * If we read a packet with a bad magic number (ie it looks like * bogus data), we give up and throw a StreamCorruptedException. * * If we read a packet that does not match our packet version * we attempt to swallow the packet and then throw an * IllegalArgumentException. * * If this method throws an OutOfMemoryError, the caller can try * to free memory and call retryReadPacket(). * * If the packet being read is larger than the limit set by * setMaxPacketSize() then the packet bytes are skipped over * and this method will throw a BigPacketException. At that * point the packet is partially constructed, and only the header * fields in the fixed portion of the packet are valid. * * @param is the InputStream to read the packet from */ public synchronized void readPacket(InputStream is) throws IOException, EOFException, StreamCorruptedException, IllegalArgumentException { if (writeInProgress) { // Should never happen throw new IOException("Can't read packet. Write in progress."); } reset(); // ReadFixed buffer readFully(is, fixedBuf, true); // At this point we know the full header has been read. Parse it. fixedBuf.rewind(); parseFixedBuffer(fixedBuf); if (packetSize > maxPacketSize) { // This packet is too large. Skip it. skipBytes(is, packetSize - HEADER_SIZE); throw new BigPacketException("Packet size (" + packetSize + ") is greater than the maximum allowed packet size (" + maxPacketSize + "). Disgarding packet." ); } // Initialize read buffers initializeReadBufs(); // Read rest of packet for (int i = 0; i < nBufs; i++) { readFully(is, readBufs[i], true); } // Yipeee! The entire packet has been read. Initialize packetVariableHeader.setBytes(varBuf); packetPayload.setPropertiesBytes(propBuf, version); packetPayload.setBody(bodyBuf); if (versionMismatch) { throw new IllegalArgumentException("Bad packet version number: " + version + ". Expecting: " + VERSION1 + " or " + VERSION2 + " or " + VERSION3); } return; } /** * A version of readFully that reads from an InputStream and puts * the data into a ByteBuffer. */ private void readFully(InputStream in, ByteBuffer b, boolean retry) throws IOException, EOFException, InterruptedIOException { byte[] abuf = null; int offset = 0; int length = 0; boolean allocate = false; // If it is a direct buffer we won't be able to get backing array. // It is a bit faster to explicitly check this case rather than // rely on catching the UnsupportedOperationException. if (!b.hasArray()) { allocate = true; } if (!allocate) { // Get ByteBuffers backing byte array try { abuf = b.array(); offset = b.arrayOffset() + b.position(); length = b.remaining(); } catch (UnsupportedOperationException e) { allocate = true; } } if (allocate) { // Bummer. Allocate a byte[] abuf = new byte[b.remaining()]; allocate = true; offset = 0; length = abuf.length; } readFully(in, abuf, offset, length, true); b.rewind(); // If we had to allocate the byte[] then copy data into ByteBuffer. // We don't wrap because we don't want to change they type of backing // buffer used by ByteBuffer. if (allocate) { b.put(abuf); } } /** * Our own version of readFully(). This is identical to DataInputStream's * except that we handle InterruptedIOException by doing a yield, and * continuing trying to read. This is to handle the case where the * socket has an SO_TIMEOUT set. * * If retry is false we abandon the read if it times out and we haven't * read anything. If it is true we continue to retry the read. */ private void readFully(InputStream in, byte b[], int off, int len, boolean retry) throws IOException, EOFException, InterruptedIOException { if (len < 0) throw new IndexOutOfBoundsException(); int n = 0; int count; while (n < len) { count = 0; try { count = in.read(b, off + n, len - n); } catch (InterruptedIOException e) { // if we really have read nothing .. throw an ex if (!retry && n == 0 && count == 0 && e.bytesTransferred == 0) { throw new InterruptedIOException("no data available"); } count = e.bytesTransferred; Thread.yield(); } if (count < 0) { throw new EOFException("Trying to read " + (len - n) + " bytes. Already read " + n + " bytes."); } n += count; } } /** * Write the packet to an OutputStream. Blocking. * * @param os The OutputStream to write the packet to * */ public synchronized void writePacket(OutputStream os) throws IOException { if (genSequenceNumber) { updateSequenceNumber(); } if (genTimestamp) { updateTimestamp(); } // Update buffers and write them updateBuffers(); initializeWriteBufs(); byte[] b = null; int offset = 0; int length = 0; boolean allocate = false; for (int i = 0; i < nBufs; i++) { // Faster than relying on catching UnsupportedException if (writeBufs[i].isDirect()) { allocate = true; } if (!allocate) { try { b = writeBufs[i].array(); offset = writeBufs[i].arrayOffset(); length = writeBufs[i].remaining(); } catch (UnsupportedOperationException e) { // Bummer. Allocate a byte[] and copy data to it allocate = true; } } if (allocate) { b = new byte[writeBufs[i].remaining()]; writeBufs[i].get(b); offset = 0; length = b.length; } os.write(b, offset, length); } os.flush(); } /** * Retrieves the packet as an array of bytes. */ public synchronized byte[] getBytes() throws IOException { if (genSequenceNumber) { updateSequenceNumber(); } if (genTimestamp) { updateTimestamp(); } // Update buffers and write them updateBuffers(); initializeWriteBufs(); byte[] b = new byte[packetSize]; int offset = 0; int length = 0; for (int i = 0; i < nBufs; i++) { length = writeBufs[i].remaining(); writeBufs[i].get(b, offset, length); offset += length; } return b; } /** * Prepare this Packet for the sendMessage method of * the jmq.jmsservice.JMSService interface. */ public synchronized void prepareToSend() { //Update the global sequence number and //generate the timestamp on this packet. updateSequenceNumber(); updateTimestamp(); //Ensure MESSAGEID in StringField is cleared if (this.packetVariableHeader.getStringField(PacketString.MESSAGEID) !=null){ this.packetVariableHeader.setStringField(PacketString.MESSAGEID, null); } } /** * Allocates a ByteBuffer of the specified capacity. */ protected ByteBuffer allocateBuffer(int capacity) { ByteBuffer b = null; if (useDirect) { // The pool is only used for direct ByteBuffers if (bbPool == null) { b = ByteBuffer.allocateDirect(capacity); } else { b = bbPool.get(capacity); } // Track allocated direct ByteBuffers allocatedBuffers.add(b); } else { b = ByteBuffer.allocate(capacity); } return b; } /** * Destroy a packet and put all direct buffers back into the buffer pool. * * WARNING! * Once destroy is called the packet may not be used again. * Once destroy is called all buffers ever returned by the packet * are invalid! */ public synchronized void destroy() { destroyed = true; ByteBuffer b = null; this.reset(); Iterator iterator = allocatedBuffers.iterator(); while (iterator.hasNext()) { b = (ByteBuffer)iterator.next(); if (b.isDirect() && bbPool != null) { // put() ignores null and knows to disgard non-direct buffers bbPool.put(b); } iterator.remove(); } // Null all buffers to ensure they are not accessed again for (int i = 0; i < writeBufs.length; i++) { writeBufs[i] = null; } for (int i = 0; i < readBufs.length; i++) { readBufs[i] = null; } varBuf = null; propBuf = null; bodyBuf = null; fixedBuf = null; packetVariableHeader = null; packetPayload = null; } public static void dumpBufs(ByteBuffer bufs[]) { for (int i = 0; i < bufs.length; i++) { System.out.println("bufs[" + i + "]=" + bufs[i]); } } /** * myChannelRead and myChannelWrite are used to work-around an * nio bug. Nio scattering/gathering IO methods leak a ton of * memory, so we kludge our own versions. * * See iMQ bug 4629634 for more info */ public static long myChannelWrite( GatheringByteChannel channel, ByteBuffer[] bufs, int offset, int length) throws IOException { int n = 0; int r = 0; int i = offset; while (i < length) { int remains = bufs[i].remaining(); if (remains != 0) { r = channel.write(bufs[i]); if (r < 0) { return r; } if (r == 0) { return n; } n += r; if (r == remains) { // We've written all there is to write. Go to next buffer i++; } else { // More left in the buffer. Try same bufer again ; } } else { // Buffer has nothing left. Go to next buffer i++; } } return n; } public static long myChannelRead( ScatteringByteChannel channel, ByteBuffer[] bufs, int offset, int length) throws IOException { int n = 0; int r = 0; int i = offset; while (i < length) { int remains = bufs[i].remaining(); if (remains != 0) { r = channel.read(bufs[i]); if (r < 0) { return r; } if (r == 0) { return n; } n += r; if (r == remains) { // We've read all there is to read. Go to next buffer i++; } else { // More space in the buffer. Try same buffer again ; } } else { // Buffer has no more space. Go to next buffer i++; } } return n; } public javax.jms.Message getMessage() { return null; } public com.sun.messaging.jmq.io.Packet getPacket() { return this; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy