Search code examples
hazelcastapache-tomeejca

TomEE, Hazelcast and jca-rar


I am attempting to add transactional support to my Hazelcast cache. To accomplish this I found out that Hazelcast has a jca-rar which gives me a connectionfactory I can use. This is what I have done so far:

  1. I have put this .rar file into my WEB-INF/lib folder.
  2. I set tomee.unpack.dir = work/ in the system.properties file.
  3. My pom.xml file:

    <project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
        http://maven.apache.org/xsd/maven-4.0.0.xsd">
    
        <modelVersion>4.0.0</modelVersion>
        <groupId>test</groupId>
        <artifactId>cachetest</artifactId>
        <version>0.0.1</version>
        <packaging>war</packaging>
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <hazelcast.version>3.6.2</hazelcast.version>
        </properties>
        <build>
            <finalName>${project.artifactId}</finalName>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-war-plugin</artifactId>
                    <version>2.5</version>
                    <configuration>
                        <failOnMissingWebXml>false</failOnMissingWebXml>
                        <archive>
                            <manifestEntries>
                                <Dependencies>com.hazelcast</Dependencies>
                            </manifestEntries>
                        </archive>
                    </configuration>
                </plugin>
            </plugins>
        </build>
        <dependencies> 
            <dependency>
                <groupId>com.fasterxml.jackson.jaxrs</groupId>
                <artifactId>jackson-jaxrs-json-provider</artifactId>
                <version>2.4.3</version>
            </dependency>
            <dependency>
                <groupId>com.hazelcast</groupId>
                <artifactId>hazelcast</artifactId>
                <version>${hazelcast.version}</version>
            </dependency>
            <dependency>
                <groupId>com.hazelcast</groupId>
                <artifactId>hazelcast-jca</artifactId>
                <version>${hazelcast.version}</version>
            </dependency>
            <dependency>
                <groupId>com.hazelcast</groupId>
                <artifactId>hazelcast-jca-rar</artifactId>
                <version>${hazelcast.version}</version>
                <type>rar</type>
            </dependency>
            <dependency>
                <groupId>javax</groupId>
                <artifactId>javaee-web-api</artifactId>
                <version>6.0</version>
                <scope>provided</scope>
            </dependency>
        </dependencies>
    </project>
    
  4. I add a rest-service for testing-purposes.

    @Path("resttest")
    @Stateless
    public class RestService {
    
        @Resource(mappedName = "hazelcast-jca-rar-3.6.2RA")
        private ConnectionFactory connectionFactory;
    
        @GET
        @Path("create")
        public void createGame() throws ResourceException {
            connectionFactory.getConnection();//ClassCastException
        }
    }
    

My web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">

    <display-name>Cachetest</display-name>

    <session-config>
        <session-timeout>90</session-timeout>
    </session-config>

</web-app>

My tomee.xml:

<?xml version="1.0" encoding="UTF-8"?>
<tomee>

    <Deployments dir="apps" />
</tomee>

This is really all the code that I have in my simple test, and it fails when attempting to get a connection from the connection factory with a ClassCassException:

INFO: HZ Connection Event <<FACTORY_INIT>> for hazelcast.ManagedConnectionFactoryImpl [1] in thread [http-bio-8080-exec-7]
java.lang.Exception: Hz Connection Event Call Stack
    at com.hazelcast.jca.ManagedConnectionFactoryImpl.logHzConnectionEvent(ManagedConnectionFactoryImpl.java:167)
    at com.hazelcast.jca.ManagedConnectionFactoryImpl.createConnectionFactory(ManagedConnectionFactoryImpl.java:91)
    at com.hazelcast.jca.ManagedConnectionFactoryImpl.createConnectionFactory(ManagedConnectionFactoryImpl.java:44)
    at org.apache.openejb.core.ConnectorReference.getObject(ConnectorReference.java:50)
    at org.apache.openejb.core.ivm.naming.IvmContext.lookup(IvmContext.java:175)
...
SEVERE: EjbTransactionUtil.handleSystemException: com.sun.proxy.$Proxy114 cannot be cast to com.hazelcast.jca.HazelcastConnectionImpl
java.lang.ClassCastException: com.sun.proxy.$Proxy114 cannot be cast to com.hazelcast.jca.HazelcastConnectionImpl
    at com.hazelcast.jca.ConnectionFactoryImpl.getConnection(ConnectionFactoryImpl.java:89)
    at com.hazelcast.jca.ConnectionFactoryImpl.getConnection(ConnectionFactoryImpl.java:79)
    at com.hazelcast.jca.ConnectionFactoryImpl.getConnection(ConnectionFactoryImpl.java:36)

Does anyone know why this happens? Do I miss some kind of configuration?

My TomEE versjon i 1.7.4 and hazelcast versjon 3.6.2 (as written in pom).


Solution

  • This is bad. In short: you're in trouble.

    The error description is pretty clear: JDK Dynamic Proxy cannot be cast to a class. This is an expected behaviour since JDK Proxies can only implement interfaces. This is a TomEE-specific issue as most major application server vendors use custom proxying technologies with classes support (JBoss Javassist, Spring CGLIB, etc.), while TomEE is stuck with JDK proxying. TomEE kinda admits the problem but they state it's 'unlikely to get resolved anytime soon'. See there for further information.

    The reason Hazelcast fails here is that they cast their connection object to it's implementation, not it's interface (see source). There's nothing criminal in such behaviour for internal code, but sometimes it doesn't come in handy.

    Now, to the practical part. I don't see any possibility to fix it 'by configuration'. If you're ready to patch Hazelcast RA, then you may either replace class casting with interface-based one or use DissociatableManagedConnection, there were mentions of it being handy in such situations.

    public class HazelcastManagedConnectionImpl extends JcaBase 
                implements ManagedConnection, LocalTransaction, 
                LocalTransaction, DissociatableManagedConnection {
    
          @Override
          public void dissociateConnections() throws ResourceException {
                conn = null;
          }
    ...
    }
    

    But, in fact, I would advise you to rethink your application server choice. TomEE's market share is small not without a reason: it users it's own containers for almost everything making it harder for its developers to maintain it in the up-to-date state. Wildfly might be a good option (at least, 9th version is quite stable and my resource adapters work well in it).