asia.stampy.common.parsing.StompMessageParser.java Source code

Java tutorial

Introduction

Here is the source code for asia.stampy.common.parsing.StompMessageParser.java

Source

/*
 * Copyright (C) 2013 Burton Alexander
 * 
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 * 
 */
package asia.stampy.common.parsing;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

import org.apache.commons.lang.NotImplementedException;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import asia.stampy.client.message.abort.AbortMessage;
import asia.stampy.client.message.ack.AckMessage;
import asia.stampy.client.message.begin.BeginMessage;
import asia.stampy.client.message.commit.CommitMessage;
import asia.stampy.client.message.connect.ConnectMessage;
import asia.stampy.client.message.disconnect.DisconnectMessage;
import asia.stampy.client.message.nack.NackMessage;
import asia.stampy.client.message.send.SendMessage;
import asia.stampy.client.message.stomp.StompMessage;
import asia.stampy.client.message.subscribe.SubscribeMessage;
import asia.stampy.client.message.unsubscribe.UnsubscribeMessage;
import asia.stampy.common.StampyLibrary;
import asia.stampy.common.message.AbstractBodyMessage;
import asia.stampy.common.message.AbstractBodyMessageHeader;
import asia.stampy.common.message.StampyMessage;
import asia.stampy.common.message.StompMessageType;
import asia.stampy.common.serialization.SerializationUtils;
import asia.stampy.server.message.connected.ConnectedMessage;
import asia.stampy.server.message.error.ErrorMessage;
import asia.stampy.server.message.message.MessageMessage;
import asia.stampy.server.message.receipt.ReceiptMessage;

/**
 * This class parses STOMP messages into {@link StampyMessage}s.
 */
@StampyLibrary(libraryName = "stampy-core")
public class StompMessageParser {
    private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

    /** The Constant EOM. */
    public static final String EOM = "\000";

    /**
     * Parses the message.
     * 
     * @param <MSG>
     *          the generic type
     * @param stompMessage
     *          the stomp message
     * @return the msg
     * @throws UnparseableException
     *           the unparseable exception
     */
    public <MSG extends StampyMessage<?>> MSG parseMessage(String stompMessage) throws UnparseableException {
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new StringReader(stompMessage));

            String messageType;
            while ((messageType = reader.readLine()).length() < 2)
                ; //skip emtpy lines... //TODO: should check for key words like CONNECT, SEND,...

            StompMessageType type = StompMessageType.valueOf(messageType);

            List<String> headers = new ArrayList<String>();
            String hdr = reader.readLine();

            while (StringUtils.isNotEmpty(hdr)) {
                headers.add(hdr);
                hdr = reader.readLine();
            }

            String body = reader.readLine();
            body = body == null || body.equals(EOM) ? null : fillBody(body, reader);

            MSG msg = createStampyMessage(type, headers);

            if (!StringUtils.isEmpty(body) && msg instanceof AbstractBodyMessage<?>) {
                AbstractBodyMessage<?> abm = (AbstractBodyMessage<?>) msg;
                abm.setBody(isText(headers) ? body : convertToObject(body, abm.getHeader().getContentType()));
            }
            return msg;
        } catch (Exception e) {
            throw new UnparseableException("The message supplied cannot be parsed as a STOMP message", stompMessage,
                    e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    log.warn("Could not close reader", e);
                }
            }
        }
    }

    /**
     * Converts the specified string to an object based upon the specified content
     * type. Only base64 encoding is supported for Java objects.
     * 
     * @param body
     *          the body
     * @param contentType
     *          the content type
     * @return the object
     * @throws IllegalObjectException
     *           the illegal object exception
     * @throws ClassNotFoundException
     *           the class not found exception
     * @throws IOException
     *           Signals that an I/O exception has occurred.
     */
    protected Object convertToObject(String body, String contentType)
            throws IllegalObjectException, ClassNotFoundException, IOException {
        if (!AbstractBodyMessage.JAVA_BASE64_MIME_TYPE.equals(contentType)) {
            throw new NotImplementedException(
                    "Subclass this class and override convertToObject to enable conversion using mime type "
                            + contentType);
        }

        Object o = SerializationUtils.deserializeBase64(body);

        illegalObjectCheck(o);

        return o;
    }

    /**
     * Blank implementation; override to add any object checking logic.
     * 
     * @param o
     *          the o
     * @throws IllegalObjectException
     *           the illegal object exception
     */
    protected void illegalObjectCheck(Object o) throws IllegalObjectException {

    }

    /**
     * Checks if is text.
     * 
     * @param headers
     *          the headers
     * @return true, if is text
     */
    protected boolean isText(List<String> headers) {
        boolean text = false;
        boolean content = false;
        for (String hdr : headers) {
            if (hdr.contains(AbstractBodyMessageHeader.CONTENT_TYPE)) {
                content = true;
                text = hdr.contains("text/");
            }
        }

        return !content || (content && text);
    }

    /**
     * Creates the stampy message.
     * 
     * @param <MSG>
     *          the generic type
     * @param type
     *          the type
     * @param headers
     *          the headers
     * @return the msg
     * @throws UnparseableException
     *           the unparseable exception
     */
    @SuppressWarnings("unchecked")
    protected <MSG extends StampyMessage<?>> MSG createStampyMessage(StompMessageType type, List<String> headers)
            throws UnparseableException {

        MSG message = null;

        switch (type) {

        case ABORT:
            message = (MSG) new AbortMessage();
            break;
        case ACK:
            message = (MSG) new AckMessage();
            break;
        case BEGIN:
            message = (MSG) new BeginMessage();
            break;
        case COMMIT:
            message = (MSG) new CommitMessage();
            break;
        case CONNECT:
            message = (MSG) new ConnectMessage();
            break;
        case CONNECTED:
            message = (MSG) new ConnectedMessage();
            break;
        case DISCONNECT:
            message = (MSG) new DisconnectMessage();
            break;
        case ERROR:
            ErrorMessage error = new ErrorMessage();
            message = (MSG) error;
            break;
        case MESSAGE:
            MessageMessage mm = new MessageMessage();
            message = (MSG) mm;
            break;
        case NACK:
            message = (MSG) new NackMessage();
            break;
        case RECEIPT:
            message = (MSG) new ReceiptMessage();
            break;
        case SEND:
            SendMessage send = new SendMessage();
            message = (MSG) send;
            break;
        case STOMP:
            message = (MSG) new StompMessage();
            break;
        case SUBSCRIBE:
            message = (MSG) new SubscribeMessage();
            break;
        case UNSUBSCRIBE:
            message = (MSG) new UnsubscribeMessage();
            break;
        default:
            break;

        }

        message.getHeader();

        addHeaders(message, headers);

        return message;
    }

    private <MSG extends StampyMessage<?>> void addHeaders(MSG message, List<String> headers)
            throws UnparseableException {
        for (String header : headers) {
            StringTokenizer st = new StringTokenizer(header, ":");

            if (st.countTokens() < 1) {
                log.error("Cannot parse STOMP header {}", header);
                throw new UnparseableException("Cannot parse STOMP header " + header);
            }

            String value = "";
            String key = st.nextToken();
            if (st.hasMoreTokens()) {
                value = header.substring(key.length() + 1);
            }

            message.getHeader().addHeader(key, value);
        }
    }

    /**
     * Fills the body of the STOMP message.
     * 
     * @param body
     *          the body
     * @param reader
     *          the reader
     * @return the string
     * @throws IOException
     *           Signals that an I/O exception has occurred.
     */
    protected String fillBody(String body, BufferedReader reader) throws IOException {
        StringBuilder builder = new StringBuilder(trimEOM(body));

        String s = reader.readLine();

        while (s != null) {
            builder.append(trimEOM(s));
            s = reader.readLine();
        }

        return builder.toString();
    }

    /**
     * Trims the terminating byte.
     * 
     * @param s
     *          the s
     * @return the string
     */
    protected String trimEOM(String s) {
        String trimmed = s;
        if (s.contains(EOM)) {
            int idx = s.indexOf(EOM);
            trimmed = s.substring(0, idx);
        }

        return trimmed;
    }
}