Bug when setting transportConnector URI options in activemq.xml
---------------------------------------------------------------

                 Key: AMQ-1233
                 URL: https://issues.apache.org/activemq/browse/AMQ-1233
             Project: ActiveMQ
          Issue Type: Bug
          Components: Broker
    Affects Versions: 4.x
            Reporter: David Martín Clavo


As you may know, there are some problems when setting options in the URI for a 
TransportConnector in the broker configuration file activemq.xml (at least for 
me when launching the console with the Main class and the start command).
For example, the option 'socket.tcpNoDelay=true' works fine in a producer or 
consumer URI, but doesn't make the broker have TCP_NODELAY sockets when it is 
set in the activemq.xml file.

I think I have this more or less nailed down, I will try to explain.
During all my explanation, I'm using the last source from SVN at the time of 
writing.

Let's imagine you have the following piece of xml in your activemq.xml file:

    <transportConnectors>
      <transportConnector uri="tcp://localhost:61616?trace=true&foo=bar"/>
    </transportConnectors>

(side note: i have not found in the documentation the need to use ' & a m p ;' 
in this file, instead of just '&' like in Java code, would be a nice addition 
for clueless people like myself)
(side note 2: in the forum the & a m p ; appears as just &, but you have to 
write '& a m p ;' without the spaces of course).

How do these options get processed? Well at some point the method 
'org.apache.activemq.transport.tcp.TcpTransportFactory.doBind' gets executed.

    public TransportServer doBind(String brokerId, final URI location) throws 
IOException {
        try {
            Map options = new HashMap(URISupport.parseParamters(location));

We now have a nice HashMap with our options: {trace=true, foo=bar}

            ServerSocketFactory serverSocketFactory = 
createServerSocketFactory();
            TcpTransportServer server = createTcpTransportServer(location, 
serverSocketFactory);
            server.setWireFormatFactory(createWireFormatFactory(options));
            IntrospectionSupport.setProperties(server, options);

'IntrospectionSupport.setProperties' is a method who uses reflexion to find the 
setter methods of a class, and if there is a setter method with the same name 
as a key of our HashMap, sets the value.
The 'doBind' method finishes wish

            Map transportOptions = 
IntrospectionSupport.extractProperties(options, "transport.");
            server.setTransportOption(transportOptions);
            server.bind();
           
            return server;
        }
        catch (URISyntaxException e) {
            throw IOExceptionSupport.create(e);
        }
    }

This is what happens when you start the broker.
Now, what happens when a client connects and the broker has to create a 
Transport object for the connection?
The method org.apache.activemq.transport.tcp.TcpTransportServer.run() gets 
executed:

    [some stuff]
                        HashMap options = new HashMap();
                        options.put("maxInactivityDuration", new 
Long(maxInactivityDuration));
                        options.put("minmumWireFormatVersion", new 
Integer(minmumWireFormatVersion));
                        options.put("trace", new Boolean(trace));
                        options.putAll(transportOptions);
                        WireFormat format = 
wireFormatFactory.createWireFormat();
                        Transport transport = createTransport(socket, format);
                        Transport configuredTransport = 
transportFactory.serverConfigure(transport, format, options);
                        getAcceptListener().onAccept(configuredTransport);
    [more stuff]

As you see this method constructs a new HashMap options object, which is used 
to configure the Transport with the line:
                        Transport configuredTransport = 
transportFactory.serverConfigure(transport, format, options);
If you have a look at this method, you will see that after a call to another 
method, the properties of the Transport are set again with 
'IntrospectionSupport.setProperties'.

So what's the problem? The problem is that for the properties to be recognized, 
they have to be declared as setters in 2 classes. In this case, TcpTransport 
and TcpTransportServer.
When a property is set in the connection URI of a client, the ActiveMQ classes 
don't use TcpTransportServer. They directly set the options on the TcpTransport.
However in a broker, the execution path goes through the TcpTransportServer 
class. If the options don't have setters declared, and are not later put into 
the 'options' object in the The method 
'org.apache.activemq.transport.tcp.TcpTransportServer.run()' method, the 
Transport created by the broker does not have these options set.

In our example, trace=true will be recognized but not foo=bar.

If you have a look at the TcpTransportServer class, you will see that the only 
options accepted are:
-maxInactivityDuration
-minmumWireFormatVersion (small mispelling btw)
-trace
-transport.* options

Furthermore, the way things work, if you put another option on the URI, it will 
be silently ignored. You can put 'foo=bar' or 'socket.tcpNoDelay=true' and no 
error message appears. So people may have been using options and thinking they 
had no effect, where in fact they were ignored without warning, like in the 
TCP_NODELAY case.

I don't know how the ActiveMQ devs want to design this class, but I see 2 
solutions:
-manually add the missing options to TcpTransportServer, and update the docs so 
that they are synchronized with the actual options that have to be used (like 
socket.tcpNoDelay=true instead of wireFormat.tcpNoDelayEnabled=true).
-somehow eliminate the double IntrospectionSupport.setProperties usage, and 
pass the options directly to the TcpTransport class. This will save the need to 
remember to put options in 2 classes every time one is added. And of course, 
there is still the need to keep the docs synchronized... :)

If you want to reproduce my analysis, I used Eclipse, executing ActiveMQ source 
using the console Main class, in debug mode, and using breakpoints in the 
methods above mentioned.

-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.

Reply via email to