javawildfly-10jcaironjacamar

Dynamically creatable Resource Adapter


Our currently implemented JCA TCP/IP adapter needs to be re-evaluated and a new but important requirement got passed to me by project leads.

It is required to add new TCP connections and start them without restarting the server. ( Additionally modifying existing ones ) Currently it is only possible with adding a new property to the standalone.xml and this requires a restart/reload of the server.

I'm looking at a lot of blog posts and "tutorials" of implementing JCA with MDB's and I think I get the hang of it but I don't see a way to dynamically, and preferrably by code, create new connections.

I thought for a second I can use the ActivationSpec in combination with the ResourceAdapter class but I can't find out how that would be possible.

EDIT: Our Resource Adapter is by all means implemented very incorrectly but I would still declare it as bidirectional. It can either listen for connections or open a connection and also receive and send messages.

The resource adapter is added to the wildfly modules system as an unzipped .rar with an module.xml inside of

C:\wildfly-10.0.0.Final\modules\com\company\server\TcpConnectorServerModule\5.1.0.0

Content of module.xml:

<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="com.company.server.TcpConnectorServerModule"
  slot="5.1.0.0">
  <resources>
    <resource-root path="." />
  </resources>
  <dependencies>
    <module name="javax.api"/>
    <module name="javax.resource.api"/>
  </dependencies>
</module>

Content in standalone.xml:

<subsystem xmlns="urn:jboss:domain:ee:4.0">
            <global-modules>
                <module name="org.infinispan.cdi.embedded" slot="ispn-8.2"/>
                <module name="org.jgroups" slot="ispn-8.2"/>
                <module name="com.company.server.TcpConnectorServerModule" slot="5.1.0.0" meta-inf="true"/>
            </global-modules>

TL;DR:

Is it possible to design an JCA Resource adapter in a way to dynamically (and hopefully by code) create and modify connections without restarting the server?

If it is not possible without restarting in JCA, I would appreciate alternatives to enable TCP/IP connections in Wildfly to external devices ( printers, scanners, scales and much more ). We need to be able to interact with these devices in our business logic.


Solution

  • I found out that while it isn't possible with CLI to add a new connection, since I could only restart the whole server and not subsystems, it is possible with JMX.

    I can add new connections with JMX and "restart" the subsystem via the operation "activate". The operation acts like a restart, if the subsystem is already active.

    Afterwards I can get the new connection just like before with the InitialConext.listBindings() function in my application.

    Here is an example JMX script with Groovy:

    import java.lang.management.ManagementFactory;
    import javax.management.ObjectName;
    import java.lang.String;
    import java.lang.Integer;
    import java.lang.Boolean;
    import java.lang.Long;
    import javax.management.openmbean.TabularData;
    import javax.management.MBeanInfo;
    
    def mBeanServer = ManagementFactory.getPlatformMBeanServer()
    
    def tcpConnectorMBean = new ObjectName("jboss.as:subsystem=resource-adapters,resource-adapter=TCPConnector")
    def definitionMBean = new ObjectName( "jboss.as:subsystem=resource-adapters,resource-adapter=TCPConnector,connection-definitions=*" )
    
    
    println 'before test creation:'
    printAvailableBeans(mBeanServer,definitionMBean, 'jndiName')
    
    //createTcpConnection(mBeanServer,tcpConnectorMBean)
    
    //println 'After test creation:'
    //printAvailableBeans(mBeanServer,definitionMBean, 'jndiName')
    
    /* 
    * After adding a new Connection and configuring the needed properties, the Resource Adapter needs to be (re)activated in order to register
    * the newly added connection and make it available with the JNDI
    */
    //activateBean(mBeanServer,tcpConnectorMBean)
    
    /**
    * This function retrieves the Object for the given name and tries to retrieve all MBeans for it and extracts some information of it.
    */
    void printAvailableBeans(def mBeanServer, ObjectName objectName, String attribute){
    
        mBeanServer.queryMBeans( objectName, null ).each {
            print 'Class name: ' + it.className
            print 'Object name : ' + it.name
            print ' -> '
            def handler = mBeanServer.getAttribute( it.name,attribute )
            println handler
        }
    
        println ''
    
    }
    
    void createTcpConnection(def mBeanServer,ObjectName objectName){
    
        final String name = 'TestJMX'
        final Integer allocationRetry = null
        final Long allocationRertyWaitMills = null
        final Boolean backgroundValidation = null
        final Long backgroundValidationMills = null
        final Long blockingTimeoutWaitMills = null
        final String capacityDecrementerClass = null
        final TabularData capacityDecrementerProperties = null
        final String capacityIncrementerClass = null
        final TabularData capacityIncrementerProperties = null
        final String className = 'TCPManagedConnectionFactory'
        final Boolean connectable = false
        final Boolean enabled = true
        final Boolean enlistment = true
        final Boolean enlistmentTrace = null
        final String flushStrategy = 'FailingConnectionOnly'
        final Long idleTimeoutMinutes = null
        final Integer initialPoolSize = null
        final Boolean interleaving = false
        final String jndiName = 'java:/tcp/TestJMX'
        final Integer maxPoolSize = 20
        final String mcp = null
        final Integer minPoolSize = 0
        final Boolean noRecovery = false
        final Boolean noTxSeparatePool = false
        final Boolean padXid = false
        final Boolean poolFair = true
        final Boolean poolPrefill = false
        final Boolean poolUseStrictMin = false
        final String recoveryPassword = null
        final String recoveryPluginClassName = null
        final TabularData recoveryPluginProperties = null
      final String recoverySecurityDomain = null
        final String recoveryUsername = null
        final Boolean sameRmOverride = null
        final Boolean securityApplication = false
        final String securityDomain = null
        final String securityDomainAndApplication = null
        final Boolean sharable = true
        final Boolean tracking = null
        final Boolean useCcm = true
        final Boolean useFastFail = false
        final Boolean useJavaContext = true
        final Boolean validateOnMatch = null
        final Boolean wrapXaResource = true
        final Integer xaResourceTimeout = null
    
    
        Object[] opParam =[name,allocationRetry,allocationRertyWaitMills,backgroundValidation,backgroundValidationMills,blockingTimeoutWaitMills,capacityDecrementerClass,capacityDecrementerProperties,capacityIncrementerClass,capacityIncrementerProperties,className,connectable,enabled,enlistment,enlistmentTrace,flushStrategy,idleTimeoutMinutes,initialPoolSize,interleaving,jndiName,maxPoolSize,mcp,minPoolSize,noRecovery,noTxSeparatePool,padXid,poolFair,poolPrefill,poolUseStrictMin,recoveryPassword,recoveryPluginClassName,recoveryPluginProperties,recoverySecurityDomain,recoveryUsername,sameRmOverride,securityApplication,securityDomain,securityDomainAndApplication,sharable,tracking,useCcm,useFastFail,useJavaContext,validateOnMatch,wrapXaResource,xaResourceTimeout]
    
        String[] opSig = [name.getClass().getName(),allocationRetry.getClass().getName(),allocationRertyWaitMills.getClass().getName(),backgroundValidation.getClass().getName(),backgroundValidationMills.getClass().getName(),blockingTimeoutWaitMills.getClass().getName(),capacityDecrementerClass.getClass().getName(),capacityDecrementerProperties.getClass().getName(),capacityIncrementerClass.getClass().getName(),capacityIncrementerProperties.getClass().getName(),className.getClass().getName(),connectable.getClass().getName(),enabled.getClass().getName(),enlistment.getClass().getName(),enlistmentTrace.getClass().getName(),flushStrategy.getClass().getName(),idleTimeoutMinutes.getClass().getName(),initialPoolSize.getClass().getName(),interleaving.getClass().getName(),jndiName.getClass().getName(),maxPoolSize.getClass().getName(),mcp.getClass().getName(),minPoolSize.getClass().getName(),noRecovery.getClass().getName(),noTxSeparatePool.getClass().getName(),padXid.getClass().getName(),poolFair.getClass().getName(),poolPrefill.getClass().getName(),poolUseStrictMin.getClass().getName(),recoveryPassword.getClass().getName(),recoveryPluginClassName.getClass().getName(),recoveryPluginProperties.getClass().getName(),recoverySecurityDomain.getClass().getName(),recoveryUsername.getClass().getName(),sameRmOverride.getClass().getName(),securityApplication.getClass().getName(),securityDomain.getClass().getName(),securityDomainAndApplication.getClass().getName(),sharable.getClass().getName(),tracking.getClass().getName(),useCcm.getClass().getName(),useFastFail.getClass().getName(),useJavaContext.getClass().getName(),validateOnMatch.getClass().getName(),wrapXaResource.getClass().getName(),xaResourceTimeout.getClass().getName()]
    
        mBeanServer.invoke(objectName,'addConnectionDefinitions',opParam,opSig)
    }
    
    void activateBean(def mBeanServer, ObjectName mBean){
        Object[] opParam = []
        String[] opSig = []
    
        mBeanServer.invoke(mBean, 'activate', opParam, opSig)
    }
    
    return