Package org.activemq.transport.stomp

Source Code of org.activemq.transport.stomp.StompWireFormat

/*
* Copyright (c) 2005 Your Corporation. All Rights Reserved.
*/
package org.activemq.transport.stomp;

import EDU.oswego.cs.dl.util.concurrent.Channel;
import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
import EDU.oswego.cs.dl.util.concurrent.CopyOnWriteArrayList;
import EDU.oswego.cs.dl.util.concurrent.LinkedQueue;
import org.activemq.io.WireFormat;
import org.activemq.message.ActiveMQDestination;
import org.activemq.message.ActiveMQTextMessage;
import org.activemq.message.ConnectionInfo;
import org.activemq.message.ConsumerInfo;
import org.activemq.message.Packet;
import org.activemq.message.Receipt;
import org.activemq.message.SessionInfo;
import org.activemq.util.IdGenerator;

import javax.jms.JMSException;
import javax.jms.Session;
import java.io.BufferedReader;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.ProtocolException;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
* Implements the TTMP protocol.
*/
public class StompWireFormat implements WireFormat
{

    static final IdGenerator PACKET_IDS = new IdGenerator();
    static final IdGenerator clientIds = new IdGenerator();

    private CommandParser commandParser = new CommandParser(this);
    private HeaderParser headerParser = new HeaderParser();

    private DataInputStream in;

    private String clientId;

    private Channel pendingReadPackets = new LinkedQueue();
    private Channel pendingWriteFrames = new LinkedQueue();
    private List receiptListeners = new CopyOnWriteArrayList();
    private String transactionId;
    private short sessionId;
    private Map subscriptions = new ConcurrentHashMap();


    void addReceiptListener(ReceiptListener listener)
    {
        receiptListeners.add(listener);
    }


    public Packet readPacket(DataInput in) throws IOException
    {
        Packet pending = (Packet) AsyncHelper.tryUntilNotInterrupted(new AsyncHelper.HelperWithReturn()
        {
            public Object cycle() throws InterruptedException
            {
                return pendingReadPackets.poll(0);
            }
        });
        if (pending != null)
        {
            return pending;
        }

        try
        {
            return commandParser.parse(in);
        }
        catch (ProtocolException e)
        {
            sendError(e.getMessage());
            return null;
        }
    }

    public Packet writePacket(final Packet packet, final DataOutput out) throws IOException, JMSException
    {
        flushPendingFrames(out);

        if (packet.getPacketType() == Packet.RECEIPT_INFO)
        {
            assert(packet instanceof Receipt);
            Receipt receipt = (Receipt) packet;
            for (int i = 0; i < receiptListeners.size(); i++)
            {
                ReceiptListener listener = (ReceiptListener) receiptListeners.get(i);
                if (listener.onReceipt(receipt, out))
                {
                    receiptListeners.remove(listener);
                    return null;
                }
            }
        }

        if (packet.getPacketType() == Packet.ACTIVEMQ_TEXT_MESSAGE)
        {
            assert(packet instanceof ActiveMQTextMessage);
            ActiveMQTextMessage msg = (ActiveMQTextMessage) packet;
            Subscription sub = (Subscription) subscriptions.get(msg.getJMSDestination());
            sub.receive(msg, out);
        }
        return null;
    }

    private void flushPendingFrames(final DataOutput out) throws IOException
    {
        boolean interrupted = false;
        do
        {
            try
            {
                String frame = (String) pendingWriteFrames.poll(0);
                if (frame == null) return;
                out.writeBytes(frame);
            }
            catch (InterruptedException e)
            {
                interrupted = true;
            }
        }
        while (interrupted);
    }

    private void sendError(final String message)
    {
        AsyncHelper.tryUntilNotInterrupted(new AsyncHelper.Helper()
        {
            public void cycle() throws InterruptedException
            {
                pendingWriteFrames.put(new FrameBuilder(Stomp.Responses.ERROR)
                        .addHeader(Stomp.Headers.Error.MESSAGE, message)
                        .toFrame());
            }
        });
    }

    /**
     * some transports may register their streams (e.g. Tcp)
     *
     * @param dataOut
     * @param dataIn
     */
    public void registerTransportStreams(DataOutputStream dataOut, DataInputStream dataIn)
    {
        this.in = dataIn;
    }

    /**
     * Some wire formats require a handshake at start-up
     *
     * @throws java.io.IOException
     */
    public void initiateServerSideProtocol() throws IOException
    {
        BufferedReader in = new BufferedReader(new InputStreamReader(this.in));
        String first_line = in.readLine();
        if (!first_line.startsWith(Stomp.Commands.CONNECT))
        {
            throw new IOException("First line does not begin with with " + Stomp.Commands.CONNECT);
        }

        Properties headers = headerParser.parse(in);
        //if (!headers.containsKey(TTMP.Headers.Connect.LOGIN))
        //    System.err.println("Required header [" + TTMP.Headers.Connect.LOGIN + "] missing");
        //if (!headers.containsKey(TTMP.Headers.Connect.PASSCODE))
        //    System.err.println("Required header [" + TTMP.Headers.Connect.PASSCODE + "] missing");

        // allow anyone to login for now

        String login = headers.getProperty(Stomp.Headers.Connect.LOGIN);
        String passcode = headers.getProperty(Stomp.Headers.Connect.PASSCODE);

        // skip to end of the packet
        while (in.read() != 0) {}
        final ConnectionInfo info = new ConnectionInfo();
        final Short packet_id = new Short(PACKET_IDS.getNextShortSequence());
        clientId = clientIds.generateId();
        commandParser.setClientId(clientId);

        info.setClientId(clientId);
        info.setReceiptRequired(true);
        info.setClientVersion(Integer.toString(getCurrentWireFormatVersion()));
        info.setClosed(false);
        info.setHostName("ttmp.fake.host.name");
        info.setId(packet_id.shortValue());
        info.setUserName(login);
        info.setPassword(passcode);
        info.setStartTime(System.currentTimeMillis());
        info.setStarted(true);

        AsyncHelper.tryUntilNotInterrupted(new AsyncHelper.Helper()
        {
            public void cycle() throws InterruptedException
            {
                pendingReadPackets.put(info);
            }
        });

        addReceiptListener(new ReceiptListener()
        {
            public boolean onReceipt(Receipt receipt, DataOutput out)
            {
                if (receipt.getCorrelationId() != packet_id.shortValue()) return false;
                final Short session_packet_id = new Short(PACKET_IDS.getNextShortSequence());
                sessionId = clientIds.getNextShortSequence();
                commandParser.setSessionId(sessionId);

                final SessionInfo info = new SessionInfo();
                info.setStartTime(System.currentTimeMillis());
                info.setId(session_packet_id.shortValue());
                info.setClientId(clientId);
                info.setSessionId(sessionId);
                info.setStarted(true);
                info.setSessionMode(Session.AUTO_ACKNOWLEDGE);
                info.setReceiptRequired(true);

                AsyncHelper.tryUntilNotInterrupted(new AsyncHelper.Helper()
                {
                    public void cycle() throws InterruptedException
                    {
                        pendingReadPackets.put(info);
                    }
                });

                addReceiptListener(new ReceiptListener()
                {
                    public boolean onReceipt(Receipt receipt, DataOutput out) throws IOException
                    {
                        if (receipt.getCorrelationId() != session_packet_id.shortValue()) return false;
                        StringBuffer buffer = new StringBuffer();
                        buffer.append(Stomp.Responses.CONNECTED).append(Stomp.NEWLINE);
                        buffer.append(Stomp.Headers.Connected.SESSION)
                                .append(Stomp.Headers.SEPERATOR)
                                .append(clientId)
                                .append(Stomp.NEWLINE)
                                .append(Stomp.NEWLINE);
                        buffer.append(Stomp.NULL);
                        out.writeBytes(buffer.toString());
                        return true;
                    }
                });

                return true;
            }
        });
    }

    /**
     * Creates a new copy of this wire format so it can be used in another thread/context
     */
    public WireFormat copy()
    {
        return new StompWireFormat();
    }

    /* Stuff below here is leaky stuff we don't actually need */

    /**
     * Some wire formats require a handshake at start-up
     *
     * @throws java.io.IOException
     */
    public void initiateClientSideProtocol() throws IOException
    {
        throw new UnsupportedOperationException("Not yet implemented!");
    }

    /**
     * Can this wireformat process packets of this version
     *
     * @param version the version number to test
     * @return true if can accept the version
     */
    public boolean canProcessWireFormatVersion(int version)
    {
        return version == getCurrentWireFormatVersion();
    }

    /**
     * @return the current version of this wire format
     */
    public int getCurrentWireFormatVersion()
    {
        return 1;
    }

    /**
     * @return Returns the enableCaching.
     */
    public boolean isCachingEnabled()
    {
        return false;
    }

    /**
     * @param enableCaching The enableCaching to set.
     */
    public void setCachingEnabled(boolean enableCaching)
    {
        // never
    }

    /**
     * some wire formats will implement their own fragementation
     *
     * @return true unless a wire format supports it's own fragmentation
     */
    public boolean doesSupportMessageFragmentation()
    {
        return false;
    }

    /**
     * Some wire formats will not be able to understand compressed messages
     *
     * @return true unless a wire format cannot understand compression
     */
    public boolean doesSupportMessageCompression()
    {
        return false;
    }

    /**
     * Writes the given package to a new datagram
     *
     * @param channelID is the unique channel ID
     * @param packet    is the packet to write
     * @return
     * @throws java.io.IOException
     * @throws javax.jms.JMSException
     */
    public DatagramPacket writePacket(String channelID, Packet packet) throws IOException, JMSException
    {
        throw new UnsupportedOperationException("Will not be implemented");
    }

    /**
     * Reads the packet from the given byte[]
     *
     * @param bytes
     * @param offset
     * @param length
     * @return
     * @throws java.io.IOException
     */
    public Packet fromBytes(byte[] bytes, int offset, int length) throws IOException
    {
        throw new UnsupportedOperationException("Will not be implemented");
    }

    /**
     * Reads the packet from the given byte[]
     *
     * @param bytes
     * @return
     * @throws java.io.IOException
     */
    public Packet fromBytes(byte[] bytes) throws IOException
    {
        throw new UnsupportedOperationException("Will not be implemented");
    }

    /**
     * A helper method which converts a packet into a byte array
     *
     * @param packet
     * @return a byte array representing the packet using some wire protocol
     * @throws java.io.IOException
     * @throws javax.jms.JMSException
     */
    public byte[] toBytes(Packet packet) throws IOException, JMSException
    {
        throw new UnsupportedOperationException("Will not be implemented");
    }

    /**
     * A helper method for working with sockets where the first byte is read
     * first, then the rest of the message is read.
     * <p/>
     * Its common when dealing with sockets to have different timeout semantics
     * until the first non-zero byte is read of a message, after which
     * time a zero timeout is used.
     *
     * @param firstByte the first byte of the packet
     * @param in        the rest of the packet
     * @return
     * @throws java.io.IOException
     */
    public Packet readPacket(int firstByte, DataInput in) throws IOException
    {
        throw new UnsupportedOperationException("Will not be implemented");
    }

    /**
     * Read a packet from a Datagram packet from the given channelID. If the
     * packet is from the same channel ID as it was sent then we have a
     * loop-back so discard the packet
     *
     * @param channelID is the unique channel ID
     * @param dpacket
     * @return the packet read from the datagram or null if it should be
     *         discarded
     * @throws java.io.IOException
     */
    public Packet readPacket(String channelID, DatagramPacket dpacket) throws IOException
    {
        throw new UnsupportedOperationException("Will not be implemented");
    }

    boolean isInTransaction()
    {
        return transactionId != null;
    }

    void setTransactionId(String transactionId)
    {
        this.transactionId = transactionId;
    }

    String getTransactionId()
    {
        return transactionId;
    }

    void clearTransactionId()
    {
        this.transactionId = null;
    }

    String getClientId()
    {
        return this.clientId;
    }

    public short getSessionId()
    {
        return sessionId;
    }

    public void addSubscription(Subscription s)
    {
        if (subscriptions.containsKey(s.getDestination()))
        {
            Subscription old = (Subscription) subscriptions.get(s.getDestination());
            ConsumerInfo p = old.close();
            enqueuePacket(p);
            subscriptions.put(s.getDestination(), s);
        }
        else
        {
            subscriptions.put(s.getDestination(), s);
        }
    }

    public void enqueuePacket(final Packet ack)
    {
        AsyncHelper.tryUntilNotInterrupted(new AsyncHelper.Helper()
        {
            public void cycle() throws InterruptedException
            {
                pendingReadPackets.put(ack);
            }
        });
    }

    public Subscription getSubscriptionFor(ActiveMQDestination destination)
    {
        return (Subscription) subscriptions.get(destination);
    }
}
TOP

Related Classes of org.activemq.transport.stomp.StompWireFormat

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.