ruby client stompconnect weblogic 9.2 issue

View: New views
5 Messages — Rating Filter:   Alert me  

ruby client stompconnect weblogic 9.2 issue

by Charlie Sadler :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Greetings,

First, I'd like to say thank-you very much for StompConnect. However,
I've run into a small problem with the Ruby client and WebLogic 9.2. The
Ruby client has a content-type header in the SEND frame. I think
StompConnect attempts to forward this header to JMS. The error message
I'm seeing is:

ERROR
message:[JMSClientExceptions:055121]Invalid property name,
"content-type"
weblogic.jms.common.MessageFormatException:
[JMSClientExceptions:055121]Invalid property name, "content-type"
at
weblogic.jms.common.MessageImpl.setObjectProperty(MessageImpl.java:1353)
at
org.codehaus.stomp.jms.StompSession.copyStandardHeadersFromFrameToMessag
e(StompSession.java:234)

Please advise.
 
Thanks,
Charlie.Sadler<AT />mathworks.com

---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email


Re: ruby client stompconnect weblogic 9.2 issue

by andrewk :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hey Charlie,

Yeah, that is going to be a problem.  The only header explictly removed is content-length, not content-type, the copyStandardHeadersFromFrameToMessage method translates a few stomp headers to JMS equivalents, then copies the rest into the JMS headers - including content-type if it is there.

If weblogic is validating via a whitelist for acceptable headers (as it seems to be), some/any other stomp header that gets copied into the message will be a problem - i.e.  content-length gets removed, but other headers won't be, and they could cause a problem as well - perhaps there needs to be a cofigurable list of acceptable headers to filter against? (I smell a new feature...)

Anyway, below is what I am 90% sure is the code of interest, if you can't fix it/figure it out, and James doesn't get back to you, I'll give it a whirl (I am so booked right now) - I used to do lots of weblogic work before I caught the rails bug :)

Below is the code of interest, I am 90% sure this is all that needs to be altered...
https://svn.codehaus.org/stomp/trunk/stompconnect/src/main/java/org/codehaus/stomp/jms/StompSession.java

<pre>
    protected void copyStandardHeadersFromFrameToMessage(StompFrame command, Message msg) throws JMSException, ProtocolException {
        final Map headers = new HashMap(command.getHeaders());

        // the standard JMS headers
        msg.setJMSCorrelationID((String) headers.remove(Stomp.Headers.Send.CORRELATION_ID));

        Object o = headers.remove(Stomp.Headers.Send.TYPE);
        if (o != null) {
            msg.setJMSType((String) o);
        }

        o = headers.remove(Stomp.Headers.Send.REPLY_TO);
        if (o != null) {
            msg.setJMSReplyTo(convertDestination((String) o));
        }

        // now the general headers
        for (Iterator iter = headers.entrySet().iterator(); iter.hasNext();) {
            Map.Entry entry = (Map.Entry) iter.next();
            String name = (String) entry.getKey();
            Object value = entry.getValue();
            msg.setObjectProperty(name, value);
        }
    }

    protected Message convertFrame(StompFrame command) throws JMSException, ProtocolException {
        final Map headers = command.getHeaders();
        final Message msg;
        if (headers.containsKey(Stomp.Headers.CONTENT_LENGTH)) {
            headers.remove(Stomp.Headers.CONTENT_LENGTH);
            BytesMessage bm = session.createBytesMessage();
            bm.writeBytes(command.getContent());
            msg = bm;
        }
        else {
            String text;
            try {
                text = new String(command.getContent(), "UTF-8");
            }
            catch (Throwable e) {
                throw new ProtocolException("Text could not bet set: " + e, false, e);
            }
            msg = session.createTextMessage(text);
        }
        copyStandardHeadersFromFrameToMessage(command, msg);
        return msg;
    }

</pre>

Charlie Sadler wrote:
Greetings,

First, I'd like to say thank-you very much for StompConnect. However,
I've run into a small problem with the Ruby client and WebLogic 9.2. The
Ruby client has a content-type header in the SEND frame. I think
StompConnect attempts to forward this header to JMS. The error message
I'm seeing is:

ERROR
message:[JMSClientExceptions:055121]Invalid property name,
"content-type"
weblogic.jms.common.MessageFormatException:
[JMSClientExceptions:055121]Invalid property name, "content-type"
at
weblogic.jms.common.MessageImpl.setObjectProperty(MessageImpl.java:1353)
at
org.codehaus.stomp.jms.StompSession.copyStandardHeadersFromFrameToMessag
e(StompSession.java:234)

Please advise.
 
Thanks,
Charlie.Sadler<AT />mathworks.com

---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email

RE: ruby client stompconnect weblogic 9.2 issue

by Charlie Sadler :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi Andrew,

Thanks for the response. It seems like content-type is the only
auto-generated header from the Ruby client I'm having an issue with. All
the other auto-headers which make it to
copyStandardHeadersFromFrameToMessage for the SEND command seem to be ok
by WebLogic. I'll make a local code change to keep my project rolling.

On a related note, I'm wondering if I might leverage content-type within
org.codehaus.stomp.jms.StompSession.convertFrame to determine whether or
not to createBytesMessage or createTextMessage? It seems like the Ruby
client always sends the content-length header.

Thanks,
Charlie

-----Original Message-----
From: andrewk [mailto:andrew@...]
Sent: Wednesday, April 04, 2007 10:08 AM
To: dev@...
Subject: Re: [stomp-dev] ruby client stompconnect weblogic 9.2 issue


Hey Charlie,

Yeah, that is going to be a problem.  The only header explictly removed
is
content-length, not content-type, the
copyStandardHeadersFromFrameToMessage
method translates a few stomp headers to JMS equivalents, then copies
the
rest into the JMS headers - including content-type if it is there.

If weblogic is validating via a whitelist for acceptable headers (as it
seems to be), some/any other stomp header that gets copied into the
message
will be a problem - i.e.  content-length gets removed, but other headers
won't be, and they could cause a problem as well - perhaps there needs
to be
a cofigurable list of acceptable headers to filter against? (I smell a
new
feature...)

Anyway, below is what I am 90% sure is the code of interest, if you
can't
fix it/figure it out, and James doesn't get back to you, I'll give it a
whirl (I am so booked right now) - I used to do lots of weblogic work
before
I caught the rails bug :)

Below is the code of interest, I am 90% sure this is all that needs to
be
altered...
https://svn.codehaus.org/stomp/trunk/stompconnect/src/main/java/org/code
haus/stomp/jms/StompSession.java

<pre>
    protected void copyStandardHeadersFromFrameToMessage(StompFrame
command,
Message msg) throws JMSException, ProtocolException {
        final Map headers = new HashMap(command.getHeaders());

        // the standard JMS headers
        msg.setJMSCorrelationID((String)
headers.remove(Stomp.Headers.Send.CORRELATION_ID));

        Object o = headers.remove(Stomp.Headers.Send.TYPE);
        if (o != null) {
            msg.setJMSType((String) o);
        }

        o = headers.remove(Stomp.Headers.Send.REPLY_TO);
        if (o != null) {
            msg.setJMSReplyTo(convertDestination((String) o));
        }

        // now the general headers
        for (Iterator iter = headers.entrySet().iterator();
iter.hasNext();)
{
            Map.Entry entry = (Map.Entry) iter.next();
            String name = (String) entry.getKey();
            Object value = entry.getValue();
            msg.setObjectProperty(name, value);
        }
    }

    protected Message convertFrame(StompFrame command) throws
JMSException,
ProtocolException {
        final Map headers = command.getHeaders();
        final Message msg;
        if (headers.containsKey(Stomp.Headers.CONTENT_LENGTH)) {
            headers.remove(Stomp.Headers.CONTENT_LENGTH);
            BytesMessage bm = session.createBytesMessage();
            bm.writeBytes(command.getContent());
            msg = bm;
        }
        else {
            String text;
            try {
                text = new String(command.getContent(), "UTF-8");
            }
            catch (Throwable e) {
                throw new ProtocolException("Text could not bet set: " +
e,
false, e);
            }
            msg = session.createTextMessage(text);
        }
        copyStandardHeadersFromFrameToMessage(command, msg);
        return msg;
    }

</pre>


Charlie Sadler wrote:
>
> Greetings,
>
> First, I'd like to say thank-you very much for StompConnect. However,
> I've run into a small problem with the Ruby client and WebLogic 9.2.
The

> Ruby client has a content-type header in the SEND frame. I think
> StompConnect attempts to forward this header to JMS. The error message
> I'm seeing is:
>
> ERROR
> message:[JMSClientExceptions:055121]Invalid property name,
> "content-type"
> weblogic.jms.common.MessageFormatException:
> [JMSClientExceptions:055121]Invalid property name, "content-type"
> at
>
weblogic.jms.common.MessageImpl.setObjectProperty(MessageImpl.java:1353)
> at
>
org.codehaus.stomp.jms.StompSession.copyStandardHeadersFromFrameToMessag

> e(StompSession.java:234)
>
> Please advise.
>  
> Thanks,
> Charlie.Sadler<AT />mathworks.com
>
> ---------------------------------------------------------------------
> To unsubscribe from this list please visit:
>
>     http://xircles.codehaus.org/manage_email
>
>
>

--
View this message in context:
http://www.nabble.com/ruby-client-stompconnect-weblogic-9.2-issue-tf3522
646.html#a9837648
Sent from the stomp - dev mailing list archive at Nabble.com.


---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email


---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email


RE: ruby client stompconnect weblogic 9.2 issue

by andrewk :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hey Charlie, glad you got past that -

There has been discussion about this exact before - and I know someone offered up a version of the stomp ruby gem that does not set the content-length (or doesn't all the time anyway). Personally, I just get back the jms bytes message and have a simple method to get the text from it, so I guess I just worked around this instead of trying to change it - but that's me.

As for using content-type - this is not a standard header in the STOMP protocol, but content-length is.  To be honest there are very few official stomp headers, but content-length is one of them, which may be why it was used/overloaded by the stomp -> JMS translation code as the indicator of JMS message type to create.  As I said, this has been discussed before, so if you look back you can read about some of the thinking for yourself, I don't remember the details.

For you it may work very well between the ruby gem and StompConnect to use content-type, but unless the stomp standard is changed to include this as a standard but optional header, I wouldn't expect it to be added to StompConnect...just my opinion, I don't make no rules around here :)

It seems like this is a problem that should maybe be considered again though.
Would it be a bad thing to add content-type to the stomp standard, accepting the usual suspects (i.e. mime-type values)?
Or, as sTomp is about sending Text, wouldn't creating a jms TextMessage be a more obvious 'default'?
I don't know, just thinking about it.

So use content-type in your code if you want to, my suggestion in the short term is to suck it up and read the bytes messages, or tweak the ruby code via a monkeypatch to not send the content length.  I like working code better than pithy suggestions, so here's how I would do it.

Java message reader (I stole this from someone, I don't remember who, then tweaked it a bit):

    private String getMessageText(Message m) throws JMSException {
    String result = "";
    if (m instanceof BytesMessage) {
    BytesMessage bytMsg = (BytesMessage) m;
    StringBuffer msg = new StringBuffer();
    int c;
    try {
    while ((c=bytMsg.readByte()) != -1) {
    msg.append((char) c);
    }
    } catch (javax.jms.MessageEOFException e) {
                log.debug("end of message exception.");
            }
    log.debug("Received BytesMessage: " + msg);
    result = msg.toString();
    } else if (m instanceof TextMessage){
    TextMessage txtMsg = (TextMessage) m;
    log.debug("Received: TextMessage" + txtMsg.getText());
    result = txtMsg.getText();
    } else {
    log.error("Unexpected Message Type: " + m);
    }
    return result;
    }

Ruby monkey-patch option - really not tested, but something obvious like this would work:

Stomp::Connection.class_eval do

    def _transmit(s, command, headers={}, body='')
      @transmit_semaphore.synchronize do
        s.puts command
        headers.each {|k,v| s.puts "#{k}:#{v}" }
        s.puts "content-length: #{body.length}"
#        s.puts "content-type: text/plain; charset=UTF-8"
        s.puts
        s.write body
        s.write "\0"
      end
    end

end

-Andrew Kuklewicz

Charlie Sadler wrote:
Hi Andrew,

Thanks for the response. It seems like content-type is the only
auto-generated header from the Ruby client I'm having an issue with. All
the other auto-headers which make it to
copyStandardHeadersFromFrameToMessage for the SEND command seem to be ok
by WebLogic. I'll make a local code change to keep my project rolling.

On a related note, I'm wondering if I might leverage content-type within
org.codehaus.stomp.jms.StompSession.convertFrame to determine whether or
not to createBytesMessage or createTextMessage? It seems like the Ruby
client always sends the content-length header.

Thanks,
Charlie

RE: ruby client stompconnect weblogic 9.2 issue

by Charlie Sadler :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi Andrew,

I thought the issue with setting the content-type property on the JMS
message might be a protocol conflict. Not so, simply, a dash is an
illegal character for a property name. From:

Java Message Service, Version 1.1, April 12, 2002

3.5.1 Property Names
Property names must obey the rules for a message selector identifier.
See Section 3.8.1.1, "Message Selector Syntax," for more information.

3.8.1.1 Message Selector Syntax
* Identifiers:
- An identifier is an unlimited-length character sequence that must
begin
with a Java identifier start character; all following characters must be
Java identifier part characters. An identifier start character is any
character for which the method Character.isJavaIdentifierStart returns
true. This includes '_' and '$'. An identifier part character is any
character for which the method Character.isJavaIdentifierPart returns
true.

My fix was to modify
org.codehaus.stomp.jms.StompSession.copyStandardHeadersFromFrameToMessag
e to translate illegal characters to underlines for all
setObjectProperty name arguments. I don't know if this is an issue for
other JMS implementations and StompConnect. My only experience is with
WebLogic 9.2 and StompConnect. The one line change and surrounding lines
are:

// now the general headers
for (Iterator iter = headers.entrySet().iterator(); iter.hasNext(); ) {
    Map.Entry entry = (Map.Entry) iter.next();
    String name = (String) entry.getKey();
    name = convertPropertyName(name); // transform illegal characters
    Object value = entry.getValue();
    msg.setObjectProperty(name, value);
}

The new convertPropertyName method is:
protected String convertPropertyName(String propertyName) {
    if (null == propertyName) {
        return null;
    }
    final char goodChar = '_';
    final StringBuffer buf = new StringBuffer(propertyName);
    for (int i = 0; i < buf.length(); i++) {
        if ((0 == i) &&
(!Character.isJavaIdentifierStart(buf.charAt(i)))) {
            buf.setCharAt(i, goodChar);
        }
        else if (!Character.isJavaIdentifierPart(buf.charAt(i))) {
            buf.setCharAt(i, goodChar);
        }
    }
    return buf.toString();
}

FWIW, my thought on using the content-type to determine bytes or text
message is: if it's there, might as well use it. Maybe the Ruby STOMP
client people put the content-type header in for a good reason. Maybe
content-type is useful to ActiveMQ. I'm guessing the Ruby STOMP client
people aren't going to remove the header anytime soon. Anyway, the code
modifications below follow the current behavior when the content-type
header is missing - when the content-length header is present then, send
a bytes message else, send a text message.

Add Stomp.Headers.CONTENT_TYPE...
    public interface Headers {
        String SEPERATOR = ":";
        String RECEIPT_REQUESTED = "receipt";
        String TRANSACTION = "transaction";
        String CONTENT_LENGTH = "content-length";
        String CONTENT_TYPE = "content-type";

Modify org.codehaus.stomp.jms.StompSession...

Change...
import java.io.IOException;
To
import java.io.*;

Add...
import javax.mail.internet.*;
import java.nio.charset.*;

Change method convertFrame to...
protected Message convertFrame(StompFrame command)
    throws JMSException, ProtocolException {
    final Map headers = command.getHeaders();
    final Message msg;

    Charset charset = getCharset(headers);

    if (null == charset) {
        msg = session.createBytesMessage();
        ( (BytesMessage) msg).writeBytes(command.getContent());
    }
    else {
        try {
            String text = new String(command.getContent(),
charset.name());
            msg = session.createTextMessage(text);
        }
        catch (UnsupportedEncodingException ex) {
            throw new ProtocolException("Text could not be set: " + ex,
            false, ex);
        }
    }
    copyStandardHeadersFromFrameToMessage(command, msg);
    return msg;
}

Add method getCharset...
protected Charset getCharset(Map headers) throws ProtocolException {
    // determine text character set or null for byte message
    final Charset charset;
    final String defaultCharsetName = "UTF-8";
    if (headers.containsKey(Stomp.Headers.CONTENT_TYPE)) {
        final ContentType contentType;
        try {
            contentType =
                new ContentType(
                    (String) headers.get(Stomp.Headers.CONTENT_TYPE));
        }
        catch (ParseException ex) {
            throw new ProtocolException(
                "Cannot parse " + Stomp.Headers.CONTENT_TYPE +
                " header: " + ex, false, ex);
        }
        if (contentType.match("text/*")) {
            String csname = contentType.getParameter("charset");
            if (null == csname) {
                csname = defaultCharsetName;
            }
            charset = Charset.forName(csname);
        }
        else {
            charset = null;
        }
    }
    else if (headers.containsKey(Stomp.Headers.CONTENT_LENGTH)) {
        charset = null;
    }
    else {
        charset = Charset.forName(defaultCharsetName);
    }
    headers.remove(Stomp.Headers.CONTENT_LENGTH);
    return charset;
}

Thanks, Charlie


---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email