One of the things I've been looking into recently is the number of
connections that TomEE will make to an ActiveMQ broker. If you consider an
application that has 10 Message Driven Beans, and another bean that sends
messages using a connection factory, that application when started will
make 20 connections to ActiveMQ - 10 for the connection pool, and an
additional connection for each message driven bean.

Conversely, I could create (and it occurs to me that I should) a Spring
application that listens on 10 destinations, sends messages from another
bean, and uses just 1 connection: a JMS connection is capable of managing
several sessions at the same time. At a small scale, the number of
connections isn't an issue, but if you have hundreds of applications
connecting to ActiveMQ, each making dozens of connections, this can become
a bit of a challenge.

There are some options around this:

* Have the MDBs use connections from the connection pool - this is already
possible using an activation property "ConnectionFactoryLookup", for
example:

    <Resource id="MyJmsResourceAdapter" type="ActiveMQResourceAdapter"

class-name="org.apache.openejb.resource.activemq.ActiveMQResourceAdapter">
        # Do not start the embedded ActiveMQ broker
        BrokerXmlConfig  =
        ServerUrl = tcp://localhost:61616
        UserName system
        Password manager
    </Resource>

    <Resource id="MyJmsConnectionFactory"
type="javax.jms.ConnectionFactory">
        ResourceAdapter = MyJmsResourceAdapter
        PoolMaxSize 10
        PoolMinSize 0
    </Resource>

    <Container id="MyJmsMdbContainer" ctype="MESSAGE">
        ResourceAdapter = MyJmsResourceAdapter
        activation.ConnectionFactoryLookup=MyJmsConnectionFactory
    </Container>

This means that the connections for the message driven beans will come from
the same pool as connections used to send messages, so you can at least
manage the full set. You'll still need at least <number of mdbs> + 1
connections in that pool, however.

* Override the resource adapter behaviour where connections are created.
Connections are made here:
https://github.com/apache/tomee/blob/main/container/openejb-core/src/main/java/org/apache/openejb/resource/activemq/jms2/TomEEManagedConnectionFactory.java#L67.
I hacked up some code to override makeConnection():

private final Map<ActiveMQConnectionRequestInfo, ActiveMQConnection>
physicalConnections = new HashMap<>();

    @Override
    public ActiveMQConnection makeConnection(ActiveMQConnectionRequestInfo
connectionRequestInfo, ActiveMQConnectionFactory connectionFactory) throws
JMSException {
        ActiveMQConnection activeMQConnection = null;

        if (singleton) {
            synchronized (this) {
                activeMQConnection =
physicalConnections.get(connectionRequestInfo);
                if (activeMQConnection == null) {
                    activeMQConnection =
super.makeConnection(connectionRequestInfo, connectionFactory);
                    physicalConnections.put(connectionRequestInfo,
activeMQConnection);
                }
            }
        } else {
            activeMQConnection =
super.makeConnection(connectionRequestInfo, connectionFactory);
        }

        return activeMQConnection;
    }

The idea here is that only one physical connection per
username/password/client ID combination would be created, and can be shared
by different sessions. I added a parameter to the connection factory called
"singleton" (perhaps needs a better name) to turn this behaviour on.

The good news is that broadly speaking, it does work - I'm working on some
itests, but unit tests and actually running TomEE and ActiveMQ look good.

Does anyone have any thoughts or reservations on this (or any specific
cases that ought to be tested)?

Jon

Reply via email to