A Simple Transactional File JCA 1.5 Connector (4 Classes / 2 Reusable)

There is a common prejudice that JCA connectors have to be too complicated for day to day use. "Custom & lightweight" solutions are built instead, which are usually orders of magnitudes more complex, than a pragmatic JCA implementation. So, how to built one:

  • JCA connectors are deployed as .rar archives with internal structure similar to ejb-jar archives. You can reuse your existing packaging and just change the ending from .jar to .rar
  • Start with the ra.xml deployment descriptor. You "only" have to implement the elements listed in this deployment descriptor:
<connector xmlns="http://java.sun.com/xml/ns/j2ee"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
                   http://java.sun.com/xml/ns/j2ee/connector_1_5.xsd"
                     version="1.5">
                <display-name>Generic JCA</display-name>
                  <vendor-name>adam-bien.com</vendor-name>
                    <eis-type>Generic JCA</eis-type>
                      <resourceadapter-version>1.0</resourceadapter-version>
                        <resourceadapter>
                              <outbound-resourceadapter>
                                    <connection-definition>
                                          <managedconnectionfactory-class>...genericjca.GenericManagedConnectionFactory</managedconnectionfactory-class>
                                            <connectionfactory-interface>...genericjca.DataSource</connectionfactory-interface>
                                              <connectionfactory-impl-class>...genericjca.FileDataSource</connectionfactory-impl-class>
                                                <connection-interface>...genericjca.Connection</connection-interface>
                                                  <connection-impl-class>...genericjca.FileConnection</connection-impl-class>
                                                </connection-definition>
                                                  <transaction-support>LocalTransaction</transaction-support>
                                                    <authentication-mechanism>
                                                          <authentication-mechanism-type>BasicPassword</authentication-mechanism-type>
                                                            <credential-interface>javax.resource.spi.security.PasswordCredential</credential-interface>
                                                          </authentication-mechanism>
                                                            <reauthentication-support>false</reauthentication-support>
                                                          </outbound-resourceadapter>
                                                        </resourceadapter>

                                                      </connector>

                                                       

                                                      •  GenericManagedConnectionFactory and GenericManagedConnection are mostly reusable. These classes care about connection management. You will be able to manage a connector through e.g. the Glassfish admin console. The code is surprisingly simple - its mainly book keeping and logging. See  http://kenai.com/projects/javaee-patterns/, project GenericJCA.
                                                      • The "core" business logic resides in the FileConnection:
                                                      public class FileConnection implements Connection, LocalTransaction{

                                                              private String buffer;
                                                                private FileOutputStream fileOutputStream;
                                                                  private ConnectionRequestInfo connectionRequestInfo;
                                                                    public final static String FILE_NAME = "/temp/jcafile.txt";
                                                                      private GenericManagedConnection genericManagedConnection;
                                                                        private PrintWriter out;

                                                                            public FileConnection(PrintWriter out,GenericManagedConnection genericManagedConnection,ConnectionRequestInfo connectionRequestInfo) {
                                                                                  this.out = out;
                                                                                    this.genericManagedConnection = genericManagedConnection;
                                                                                      this.connectionRequestInfo = connectionRequestInfo;
                                                                                        this.initialize();
                                                                                      }

                                                                                          private void initialize(){
                                                                                                try {
                                                                                                      this.buffer = null;
                                                                                                        this.fileOutputStream = new FileOutputStream(FILE_NAME,true);
                                                                                                      } catch (FileNotFoundException ex) {
                                                                                                            Logger.getLogger(FileConnection.class.getName()).log(Level.SEVERE, null, ex);
                                                                                                              throw new IllegalStateException("Cannot initialize OutputStream: " + FILE_NAME);
                                                                                                            }

                                                                                                            }

                                                                                                                public void write(String content) {
                                                                                                                      this.buffer = content;
                                                                                                                    }

                                                                                                                        public void close() {
                                                                                                                                  this.genericManagedConnection.close();
                                                                                                                            }

                                                                                                                                public void destroy(){
                                                                                                                                      try {
                                                                                                                                            if(this.fileOutputStream != null)
                                                                                                                                                  this.fileOutputStream.close();
                                                                                                                                              this.fileOutputStream = null;
                                                                                                                                                this.buffer = null;
                                                                                                                                                 } catch (IOException ex) {
                                                                                                                                                      Logger.getLogger(FileConnection.class.getName()).log(Level.SEVERE, null, ex);
                                                                                                                                                        throw new IllegalStateException("Cannot close stream: " +ex,ex);
                                                                                                                                                      }
                                                                                                                                                    }

                                                                                                                                                        public void begin() throws ResourceException {
                                                                                                                                                              this.initialize();
                                                                                                                                                            }

                                                                                                                                                                public void commit() throws ResourceException {
                                                                                                                                                                      out.println("#FileConnection.commit "  +toString());
                                                                                                                                                                        try {
                                                                                                                                                                           this.fileOutputStream.write(this.buffer.getBytes());
                                                                                                                                                                             this.fileOutputStream.flush();
                                                                                                                                                                               this.fileOutputStream.close();
                                                                                                                                                                                } catch (IOException ex) {
                                                                                                                                                                                      Logger.getLogger(FileConnection.class.getName()).log(Level.SEVERE, null, ex);
                                                                                                                                                                                        throw new ResourceException(ex);
                                                                                                                                                                                      }
                                                                                                                                                                                    }

                                                                                                                                                                                        public void rollback() throws ResourceException {
                                                                                                                                                                                              out.println("#FileConnection.rollback  " +toString());
                                                                                                                                                                                                this.buffer = null;
                                                                                                                                                                                                  try {
                                                                                                                                                                                                        this.fileOutputStream.close();
                                                                                                                                                                                                      } catch (IOException ex) {
                                                                                                                                                                                                            Logger.getLogger(FileConnection.class.getName()).log(Level.SEVERE, null, ex);
                                                                                                                                                                                                              throw new ResourceException(ex);
                                                                                                                                                                                                            }


                                                                                                                                                                                                      The nice thing are transaction callbacks. You will be notified about the transaction progress by the container. At commit time, you just write the buffer to the file - in case of a rollback you have to clear the buffer. This sample is not fully transactional - because in general you will have to deal with corrupted files etc. - but it should be clear how it works.

                                                                                                                                                                                                      After installation, you will be able to inject the interface (and so the FileConnection) to your business logic.

                                                                                                                                                                                                      @Stateless

                                                                                                                                                                                                      public class JCAClientBean implements JCAClientRemote {

                                                                                                                                                                                                          @Resource(mappedName="jca/FileFactory")

                                                                                                                                                                                                          private DataSource dataSource;

                                                                                                                                                                                                       ...and the transactions will be propagated transparently for you.

                                                                                                                                                                                                      JCA connectors are not as lean as simple as EJB 3.1 or CDI,  but orders of magnitude more robust and more maintainable, than solutions and workarounds which are usually built instead.

                                                                                                                                                                                                      You will find a working example (tested with Glassfish v2) in http://kenai.com/projects/javaee-patterns/. See GenericJCA, GenericJCAAPI and JCAClient projects. I described this solution in dedicated chapter "Generic JCA", page 181 in Real World Java EE Patterns - Rethinking Best Practices".

                                                                                                                                                                                                      The example presented here is based on the ancient Java EE 5 technology. JCA 1.6 connectors are lot simpler and more elegant - stay tuned. 

                                                                                                                                                                                                      Comments:

                                                                                                                                                                                                      Hi Adam,

                                                                                                                                                                                                      Thanks a lot for the JCA Adpater!

                                                                                                                                                                                                      Kind Regards,
                                                                                                                                                                                                      Simon

                                                                                                                                                                                                      Posted by Simon Martinelli on September 09, 2010 at 03:53 PM CEST #

                                                                                                                                                                                                      @Simon,

                                                                                                                                                                                                      you are welcome! This implementation was tested, but not stress tested - so don't use it in production.

                                                                                                                                                                                                      Contributions are welcome!,

                                                                                                                                                                                                      thanks!,

                                                                                                                                                                                                      adam

                                                                                                                                                                                                      Posted by adam-bien.com on September 09, 2010 at 04:13 PM CEST #

                                                                                                                                                                                                      Hi Adam,

                                                                                                                                                                                                      Thanks for sharing this! I always feel that those of us who write JCA connectors are in a small club :).

                                                                                                                                                                                                      We're currently exploring writing fully 2-phase transactional FTP/SFTP connectors for Ikasan (open source middleware), it's an interesting problem to crack!

                                                                                                                                                                                                      If anyone out there has given this a go we're happy to share ideas!

                                                                                                                                                                                                      We're definitely looking forward to JCA 1.6, anything that simplifies this space is welcome.

                                                                                                                                                                                                      Here's hoping that JCA 1.7 will utilise DI, it'll make testing for us much simpler :)

                                                                                                                                                                                                      Cheers,
                                                                                                                                                                                                      Martijn

                                                                                                                                                                                                      Posted by Martijn Verburg on September 09, 2010 at 04:19 PM CEST #

                                                                                                                                                                                                      Great sample! But it's quite funny to hear about JEE 5 being ancient ;-) There're plenty of companies that haven't switched from 1.4 yet.

                                                                                                                                                                                                      Posted by Jakub Grabowski on September 09, 2010 at 10:32 PM CEST #

                                                                                                                                                                                                      Adam, thank you for bringing back JCA to the public interest. I hardly thought that I am the only one writing adapters. ;-)

                                                                                                                                                                                                      Reading the title I was curious to see how you solved transactions with files, as that is what the GPL'ed FileRA (available at http://sourceforge.net/projects/filera/ and used in production since 2005) is missing. Unfortunately I can't apply your solution to FileRA, as FileRA would need to transact much more complex scenarios like rolling back deletes and moves, isolate transaction on structure operations, etc., which can only be handled by some kind of rollback logs IMHO. If anybody is interested in contribution this, please write to markus@headcrashing.eu. :-)

                                                                                                                                                                                                      A little more promotion: See also the GPL'ed SMTP-Inbound adapter available since 2008 at http://sourceforge.net/projects/mailra/. It can trigger MDBs by processing incoming STMP, so it preserves processing time by not polling using POP3 and such provides results in real time due to the missing delay. :-)

                                                                                                                                                                                                      Posted by Markus KARG on September 11, 2010 at 01:45 PM CEST #

                                                                                                                                                                                                      JCA, the hidden gem in the Java EE API ;)

                                                                                                                                                                                                      Anyway, *REALLY* looking forward to a JCA 1.6 example.

                                                                                                                                                                                                      Posted by Robert Tuinman on September 11, 2010 at 06:49 PM CEST #

                                                                                                                                                                                                      Hi Adam,
                                                                                                                                                                                                      tried Your example from Kenai:
                                                                                                                                                                                                      packagenames in ra.xml are partially wrong (you missed some "spi"s...).
                                                                                                                                                                                                      Works for me on GF2.
                                                                                                                                                                                                      On GF3 one has to package Connector and Client into one EAR in order to get it running out of the box.
                                                                                                                                                                                                      Deploying Connector and Client separately ends up in
                                                                                                                                                                                                      Caused by: javax.naming.NamingException: Lookup failed for '__SYSTEM/pools/abien' in SerialContext [Root exception is javax.naming.NameNotFoundException: abien not found]
                                                                                                                                                                                                      ("abien" was the name of the pool :)

                                                                                                                                                                                                      I was not able to figure out what's wrong, yet...

                                                                                                                                                                                                      I also replaced the ra-descriptor using annotations, is it JCA 1.6 now? ;-)

                                                                                                                                                                                                      Thanks for the example!
                                                                                                                                                                                                      Björn

                                                                                                                                                                                                      Posted by Björn Behrens on September 14, 2010 at 04:23 PM CEST #

                                                                                                                                                                                                      Hi Adam
                                                                                                                                                                                                      I have been trying to write a connector in JCA for my EIS by providing user name, password and TCP/IP under JBoss server. Can you please suggest a solution for that.

                                                                                                                                                                                                      Posted by jashmeet on October 07, 2010 at 09:59 AM CEST #

                                                                                                                                                                                                      Hello,

                                                                                                                                                                                                      first of all thanks for this article.

                                                                                                                                                                                                      My question is the following: I want to parse XML-Files in my EJB.
                                                                                                                                                                                                      If I open a connection to the file with your JCA adapter and make the internal FileInputStream available to my EJB for read-only access(parsing), does this meet the EJB restrictions concerning file access?
                                                                                                                                                                                                      If this breaks the restrictions, what would be a correct way to do this?

                                                                                                                                                                                                      Thanks and greetings,

                                                                                                                                                                                                      Johann

                                                                                                                                                                                                      Posted by Johann Mayer on October 27, 2010 at 03:12 PM CEST #

                                                                                                                                                                                                      Hi Adam,

                                                                                                                                                                                                      is there a working example of an JCA 1.6 implementation (with the omit of the ra.xml) planned for your patterns-project@kenai?

                                                                                                                                                                                                      Would be nice, due to SecurityContext, WorkManager and these interesting new features with JavaEE6...

                                                                                                                                                                                                      Regards,
                                                                                                                                                                                                      Robert

                                                                                                                                                                                                      Posted by Robert on November 12, 2010 at 11:21 PM CET #

                                                                                                                                                                                                      Hi,
                                                                                                                                                                                                      ...just FYI

                                                                                                                                                                                                      A JCA 1.5 connector to do similar stuff (transactional behavior over file-systems, with XA) is available as XADisk (https://xadisk.dev.java.net/); an open-source project I have been involved in. I thought, it may be interesting to people coming across this relevant article.

                                                                                                                                                                                                      Thanks,
                                                                                                                                                                                                      Nitin

                                                                                                                                                                                                      Posted by Nitin on January 07, 2011 at 09:57 PM CET #

                                                                                                                                                                                                      Really interesting! Thanks for sharing it.

                                                                                                                                                                                                      But how can it be modified to access a file in a given directory, passing the file name at run-time? Like a simple file uploader? Or better as a document manager: the system builds a document for the user, and the document must be archived in a fixed directory with a name composed from various parameters (as request type, user-id, etc.).

                                                                                                                                                                                                      Posted by Daniele on January 20, 2011 at 02:02 PM CET #

                                                                                                                                                                                                      Hello adam ,

                                                                                                                                                                                                      Thanks for your useful articles.
                                                                                                                                                                                                      I have a misunderstanding regarding the connection handle sharing in JCA.

                                                                                                                                                                                                      I want to know if the connection sharing only happens during a phase of transaction ( multiple components call ) or the connection sharing is possible amongst multiple clients accessing a component that has the same EIS destination.

                                                                                                                                                                                                      I created my own resource adapter on Glassfish application server 3.1. The problem is the resource adapter doesn't perform any connection sharing (no associateConnection method call).

                                                                                                                                                                                                      Thanks,

                                                                                                                                                                                                      Posted by Sam on September 24, 2012 at 12:15 PM CEST #

                                                                                                                                                                                                      Hi Sam,

                                                                                                                                                                                                      please also checkout: http://connectorz.adam-bien.com (see also http://realworldpatterns.com). It is a newer JCA 1.6 implementation.

                                                                                                                                                                                                      Whether a connection is shared, or not, depends on the ManagedConnectionFactory#matchManagedConnections method implementation.

                                                                                                                                                                                                      thanks!,

                                                                                                                                                                                                      adam

                                                                                                                                                                                                      Posted by Adam Bien on September 24, 2012 at 09:32 PM CEST #

                                                                                                                                                                                                      Post a Comment:
                                                                                                                                                                                                      • HTML Syntax: NOT allowed
                                                                                                                                                                                                      ...the last 150 posts
                                                                                                                                                                                                      ...the last 10 comments
                                                                                                                                                                                                      License