Over a million developers have joined DZone.

A Simple Transactional File JCA 1.5 Connector

· Java Zone

Discover how AppDynamics steps in to upgrade your performance game and prevent your enterprise from these top 10 Java performance problems, brought to you in partnership with AppDynamics.

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, here's how to build 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 is the 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.

From http://www.adam-bien.com/roller/abien/entry/a_simple_transactional_file_jca

The Java Zone is brought to you in partnership with AppDynamics. AppDynamics helps you gain the fundamentals behind application performance, and implement best practices so you can proactively analyze and act on performance problems as they arise, and more specifically with your Java applications. Start a Free Trial.

Topics:

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}