Tuesday, November 1, 2011

Creating mock OFX server

Nowadays more and more financial establishments use modern Web technologies to provide user with better experience. One of such technologies is OFX.

OFX stands for Open Financial Exchange, it is the format, that is based on XML, for data exchange between financial institutions. It was released in 1997 and evolved from Microsoft's Open Financial Connectivity (OFC) and Intuit's Open Exchange file formats.

In Java there are not many OFX libraries, the best one is OFX4J. Using this library you can develop both client and server. It has classes for every possible OFX (XML) element. On my opinion, the biggest flaw of this library is the bad documentation. Unfortunately, there are also not many examples online on usage of OFX4J. So here is my example that may help someone.

Let's write the simple JAX-WS OFX server. As always at first we need to download the library or specify Maven dependency:
<dependency>
 <groupId>net.sf.ofx4j</groupId>
 <artifactId>ofx4j</artifactId>
 <version>1.5</version>
</dependency>

Now we need the web-service interface:
package com.test.ofx.server.service;

import javax.activation.DataHandler;
import javax.jws.WebMethod;
import javax.jws.WebService;

@WebService
public interface OFXServerService {    
    @WebMethod
    DataHandler getResponse(DataHandler request);    
}

As you can see there was nothing difficult. Now it's time for the implementation:

package com.test.ofx.server.service.impl;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.TreeSet;
import java.util.UUID;

import javax.activation.DataHandler;
import javax.jws.WebService;

import net.sf.ofx4j.domain.data.ApplicationSecurity;
import net.sf.ofx4j.domain.data.RequestEnvelope;
import net.sf.ofx4j.domain.data.RequestMessage;
import net.sf.ofx4j.domain.data.RequestMessageSet;
import net.sf.ofx4j.domain.data.ResponseEnvelope;
import net.sf.ofx4j.domain.data.ResponseMessageSet;
import net.sf.ofx4j.domain.data.banking.AccountType;
import net.sf.ofx4j.domain.data.banking.BankAccountDetails;
import net.sf.ofx4j.domain.data.banking.BankStatementRequestTransaction;
import net.sf.ofx4j.domain.data.banking.BankStatementResponse;
import net.sf.ofx4j.domain.data.banking.BankStatementResponseTransaction;
import net.sf.ofx4j.domain.data.banking.BankingResponseMessageSet;
import net.sf.ofx4j.domain.data.common.Status;
import net.sf.ofx4j.domain.data.common.Status.Severity;
import net.sf.ofx4j.io.AggregateMarshaller;
import net.sf.ofx4j.io.AggregateUnmarshaller;
import net.sf.ofx4j.io.OFXParseException;
import net.sf.ofx4j.io.OFXWriter;
import net.sf.ofx4j.io.v2.OFXV2Writer;

import com.sun.xml.ws.util.ByteArrayDataSource;
import com.test.ofx.server.service.OFXServerService;

@WebService(endpointInterface = "com.test.ofx.server.service.OFXServerService", name = "ofxWebService")
public class OFXServerServiceImpl implements OFXServerService {
    
    @Override
    public DataHandler getResponse(DataHandler request) {
        try {
            //parse request            
            AggregateUnmarshaller<requestenvelope> unmarshaller = new AggregateUnmarshaller<requestenvelope>(
                    RequestEnvelope.class);
            RequestEnvelope requestEnvelope = unmarshaller.unmarshal(request.getInputStream());

            ResponseEnvelope responseEnvelope = getResponse(requestEnvelope);

            //prepare response to send
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            OFXWriter writer = new OFXV2Writer(baos);
            AggregateMarshaller marshaller = new AggregateMarshaller();
            
            marshaller.marshal(responseEnvelope, writer);
            writer.close();
            DataHandler dataHandler = new DataHandler(new ByteArrayDataSource(baos.toByteArray(),
                    "application/octet-stream"));
            return dataHandler;
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (OFXParseException e) {
            throw new RuntimeException(e);
        }
    }

    private ResponseEnvelope getResponse(RequestEnvelope requestEnvelope) {
        //declare variables to generate response
        ResponseEnvelope responseEnvelope = new ResponseEnvelope();            
        BankingResponseMessageSet responseMessageSet = new BankingResponseMessageSet();
        List<bankstatementresponsetransaction> transactions = new ArrayList<bankstatementresponsetransaction>();
        BankStatementResponse bankStatementResponse = new BankStatementResponse();
        BankAccountDetails bankAccountDetails;
        Random rand = new Random();

        //generate response transactions
        for (RequestMessageSet requestMessageSet : requestEnvelope.getMessageSets()) {
            for (RequestMessage requestMessage : requestMessageSet.getRequestMessages()) {
                //specify bank account details
                bankAccountDetails = new BankAccountDetails();
                bankAccountDetails
                        .setAccountNumber(((BankStatementRequestTransaction) requestMessage)
                                .getMessage().getAccount().getAccountNumber()
                                + "-" + rand.nextInt());
                bankAccountDetails.setBankId("Bank ID");
                bankAccountDetails.setAccountType(AccountType.CHECKING);
                
                //specify and add bank statment response transaction
                bankStatementResponse.setAccount(bankAccountDetails);
                BankStatementResponseTransaction bankStatementResponseTransaction = new BankStatementResponseTransaction();
                bankStatementResponseTransaction.setMessage(bankStatementResponse);
                Status status = new Status();
                status.setSeverity(Severity.INFO);        
                bankStatementResponseTransaction.setStatus(status);
                bankStatementResponseTransaction.setUID(UUID.randomUUID().toString());
                transactions.add(bankStatementResponseTransaction);
            }
        }

        //create response envelope
        responseMessageSet.setStatementResponses(transactions);
        responseEnvelope.setMessageSets(new TreeSet<responsemessageset>());
        responseEnvelope.getMessageSets().add(responseMessageSet);
        responseEnvelope.setSecurity(ApplicationSecurity.TYPE1);
        responseEnvelope.setUID(UUID.randomUUID().toString());
        return responseEnvelope;
    }
}
There are many classes in the library and at first the code may seem pretty incomprehensible. But you have to remember that almost all this variety of classes do nothing more than just wrap XML element. Every time you are not sure about meaning of the current operation feel free to look the library code. It is not very complicated.

By the way at first I've tried to get RequestEnvelope and send ResponseEnvelope in the web service, but I've stumbled upon implementation of ResponseEnvelope. It is dependable on some class that do not have public default constructor(UnknownStatusCode). And due to limitations of JAX-WS it was impossible to create stubs or use the common interface with the client.

That's all about the server part. In the next article we'll create the OFX client.

Sunday, October 30, 2011

Full working runtime annotation example

Annotations are metadata(data about data) that allows you to keep an additional information right in your code. Annotations have been introduced into Java in 5th edition.

Before annotations almost all configuration data had to be kept in XML format. And as the application grew, the number and complexity of its XML-files had been increasing exponentially. They called it XML hell.

And now when we have annotations we can keep the configuration of the code right with it. Besides annotations are tested and verified by the compiler.

But the use of annotations is not limited to configuration, annotations allow you to get rid of the boilerplate code (e.g. cross-cutting concerns such as transactions and security).

The boon of annotations can be very easily noted using Hibernate Annotation package, Spring 2.5 and newer, Seam. But how do they write the annotations of their own?

So here is the definition of our annotation @Testing:
package com.test.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Testing { }
As you can see this definition is not much unlike of the definition of an interface. And really it compiles to the class file as any other Java interface. As you can see the annotation definition requires @Target and @Retention annotations:
  • @Target defines where you can use your annotation. In this example it can be used only with methods. Here are all possible values and places where you can use annotations:
    • TYPE - class, interface (including annotation type), or enum declaration 
    • FIELD - field declaration (includes enum constants)
    • METHOD - method declaration
    • PARAMETER - parameter declaration
    • CONSTRUCTOR - constructor declaration
    • LOCAL_VARIABLE - local variable declaration
    • ANNOTATION_TYPE - annotation type declaration 
    • PACKAGE - package declaration
  • @Retention defines when the annotation can be available:
    • SOURCE - in the source code (discarded by the compiler)
    • CLASS - in the class files (not retained at run time). This is the default behavior.
    • RUNTIME - at run time (can be read reflectively)
Often annotations contain some values (as @Target and @Retention above). These values are represented as interface methods in the definition. If an annotation does not have any element than it is called marker annotation.

We will use proxy pattern to allow our processor intercept the calls to the methods annotated with @Testing. Java provides implementation of the proxy with the java.lang.reflect.Proxy. It is very powerful tool but unfortunately it does not provide possibility to create proxies just for the class, you have to provide the list of interfaces to create the proxy (if you need to proxy classes and not just interfaces then you should use CGLib).

So let's create the annotation processor:
package com.test.annotation.processor;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

import com.test.annotation.Testing;

public class TestingInvocationHandler implements InvocationHandler {
 private Object proxied;
 
 public TestingInvocationHandler(Object proxied) {
  this.proxied = proxied; } 

 @Override
 public Object invoke(Object proxy, Method method, Object[] args)
   throws Throwable {
  Method m = proxied.getClass().getMethod(method.getName(), method.getParameterTypes());
  if (m.isAnnotationPresent(Testing.class)) {
   System.out.println("\tIn the annotation processor");   
  } 
  return method.invoke(proxied, args);
 }
}
The annotation processor implements InvocationHandler interface. This interface is responsible for handling the proxy object invocation.
As you can see we only print additional line before invoking the original method.

Here is the helper class with the static method to get the proxy object:
package com.test.annotation.processor;
import java.lang.reflect.Proxy;

public class TestingProxy {
 
 public static Object getNewProxy(Object proxied, Class<?> interfaze) {
  Object proxy = Proxy.newProxyInstance(
      TestingInvocationHandler.class.getClassLoader(),
      new Class[] {interfaze}, 
      new TestingInvocationHandler(proxied));
  return proxy;
 }

}
Let's define the interface
package com.test.service;

public interface Observable {
 void print1();
 
 void print2(); 
}
and the class of the proxied objects
package com.test.service;

import com.test.annotation.Testing;
import com.test.annotation.processor.TestingProxy;

public class PrintService implements Observable {
 private static Observable instance;
 private PrintService(){
  
 }
 public static Observable getInstance() {
  if (instance == null) {   
   instance = (Observable) TestingProxy.getNewProxy(new PrintService(),
     Observable.class);   
  }
  return instance;
 }
 
 @Testing
 public void print1() {
  System.out.println("1 - Text in annotated method");
 }
 
 public void print2() {
  System.out.println("2 - Just ususal text");
 }
}
This class is implemented as a simple singleton and uses the proxy object in getInstance() method. Also this class implements both methods of the interface. One of this methods is annotated and the other is not.

Finally let's create the class with the main method:
package com.test;

import com.test.service.Observable;
import com.test.service.PrintService;

public class Application {
 public static void main(String[] args) {
  Observable printService = PrintService.getInstance();  
  printService.print1();  
  printService.print2();
 }
}
That's all.

Thursday, October 27, 2011

Java FTP client

If you are looking for Java FTP client I suggest you to look at Apache Commons Net library. There is a class FTPClient. It is very easy to use and has all the functionality you may want to have.
To use this library we have to download it from the official site or add maven dependency:
<dependency>
    <groupId>commons-net</groupId>
    <artifactId>commons-net</artifactId>
    <version>3.0.1</version>
</dependency>
After this if we want to upload the file to the server we should do something like this:
public static boolean uploadFile(File file, String workingDirectory, String fileName) {
  FTPClient client = new FTPClient();
  try {
    client.connect(FTP_HOST, FTP_PORT);
    // After connection attempt, we have to check the reply code to
    // verify success.
    int reply = client.getReplyCode();
    if (!FTPReply.isPositiveCompletion(reply)) {
      System.err.println("FTP server refused connection.");
      return false;
    }
  
    client.login(FTP_USERNAME, FTP_PASSWORD);
    client.changeWorkingDirectory(workingDirectory);
    client.storeFile(fileName, new FileInputStream(file));
    client.logout();
    return true;
  } catch (SocketException e) {
    throw new RuntimeException(e);
  } catch (IOException e) {
    throw new RuntimeException(e);
  } finally {
    if (client.isConnected()) {
      try {
        client.disconnect();
      } catch (IOException ioe) {
        // swallow exception
        System.err.println("Cannot disconnect from the host");
      }
    }
  } 
}
If you need to download, delete or append file it would be pretty similar.
To get listing of directories you may need to enter local passive mode. Here is an example:
public static void printFiles(String workingDirectory) {
  FTPClient client = new FTPClient();
  try {
    client.connect(FTP_HOST, FTP_PORT);   
    // After connection attempt, we have to check the reply code to
    // verify success.
    int reply = client.getReplyCode();
    if (!FTPReply.isPositiveCompletion(reply)) {
      System.err.println("FTP server refused connection.");
    }
  
    client.enterLocalPassiveMode();
    client.login(FTP_USERNAME, FTP_PASSWORD);
    FTPFile[] files = client.listFiles(workingDirectory);
     
    for (FTPFile file : files) {
      System.out.println(file.getName());
    }
    client.logout();
  } catch (SocketException e) {
    throw new RuntimeException(e);
  } catch (IOException e) {
    throw new RuntimeException(e);
  } finally {
    if (client.isConnected()) {
      try {
        client.disconnect();
      } catch (IOException ioe) {
        // swallow exception
        System.err.println("Cannot disconnect from the host");
      }
    }
  }
}
It was easy wasn't it? To learn more see the documentation.

Tuesday, October 25, 2011

Recursively unzip archive in memory

In addition to the previous post about recursive unzipping you may need to unzip archive in memory. In this situation the following code will help you:

package com.test;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

public class Unzipper {
 private final static int BUFFER_SIZE = 2048;
 private final static String ZIP_EXTENSION = ".zip";
 
 public static void main(String[] args) throws IOException {
  File f = new File("/home/anton/test/test.zip");
  FileInputStream fis = new FileInputStream(f);
  BufferedInputStream bis = new BufferedInputStream(fis);
  ByteArrayOutputStream baos = new ByteArrayOutputStream();
  BufferedOutputStream bos = new BufferedOutputStream(baos);
  byte[] buffer = new byte[BUFFER_SIZE];
  while (bis.read(buffer, 0, BUFFER_SIZE) != -1) {
   bos.write(buffer);
  }
  bos.flush();
  bos.close();
  bis.close();
  List<ByteArrayOutputStream> listFiles =  unzip(baos);
 }

 public static List<ByteArrayOutputStream> unzip(
   ByteArrayOutputStream zippedFileOS) {
  try {
   ZipInputStream inputStream = new ZipInputStream(
     new BufferedInputStream(new ByteArrayInputStream(
       zippedFileOS.toByteArray())));
   ZipEntry entry;

   List<ByteArrayOutputStream> result = new ArrayList<ByteArrayOutputStream>();
   while ((entry = inputStream.getNextEntry()) != null) {
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    System.out.println("\tExtracting entry: " + entry);
    int count;
    byte data[] = new byte[BUFFER_SIZE];

    if (!entry.isDirectory()) {
     BufferedOutputStream out = new BufferedOutputStream(
       outputStream, BUFFER_SIZE);
     while ((count = inputStream.read(data, 0, BUFFER_SIZE)) != -1) {
      out.write(data, 0, count);
     }
     out.flush();
     out.close();
     // recursively unzip files
     if (entry.getName().toLowerCase().endsWith(ZIP_EXTENSION)) {
      result.addAll(unzip(outputStream));
     } else {
      result.add(outputStream);
     }
    }
   }
   inputStream.close();
   return result;
  } catch (Exception e) {
   throw new RuntimeException(e);
  }
 }
}

You may also want to check how to recursively unzip archive to file

Recursively unzip archive

Once I had to unzip some difficult zip archive that consisted of inner zip archives, directories and files. I have googled for examples, though there were some solutions I didn't find the perfect one for me. So I decided to develop my own unzipper. Here is it, feel free to use it:

package com.test;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

public class Unzipper {  
    private final static int BUFFER_SIZE = 2048;
    private final static String ZIP_FILE = "/home/anton/test/test.zip";
    private final static String DESTINATION_DIRECTORY = "/home/anton/test/";
    private final static String ZIP_EXTENSION = ".zip";
 
    public static void main(String[] args) {
     System.out.println("Trying to unzip file " + ZIP_FILE); 
        Unzipper unzip = new Unzipper();  
        if (unzip.unzipToFile(ZIP_FILE, DESTINATION_DIRECTORY)) {
         System.out.println("Succefully unzipped to the directory " 
             + DESTINATION_DIRECTORY);
        } else {
         System.out.println("There was some error during extracting archive to the directory " 
             + DESTINATION_DIRECTORY);
        }
    } 

 public boolean unzipToFile(String srcZipFileName,
   String destDirectoryName) {
  try {
   BufferedInputStream bufIS = null;
   // create the destination directory structure (if needed)
   File destDirectory = new File(destDirectoryName);
   destDirectory.mkdirs();

   // open archive for reading
   File file = new File(srcZipFileName);
   ZipFile zipFile = new ZipFile(file, ZipFile.OPEN_READ);

   //for every zip archive entry do
   Enumeration<? extends ZipEntry> zipFileEntries = zipFile.entries();
   while (zipFileEntries.hasMoreElements()) {
    ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();
    System.out.println("\tExtracting entry: " + entry);

    //create destination file
    File destFile = new File(destDirectory, entry.getName());

    //create parent directories if needed
    File parentDestFile = destFile.getParentFile();    
    parentDestFile.mkdirs();    
    
    if (!entry.isDirectory()) {
     bufIS = new BufferedInputStream(
       zipFile.getInputStream(entry));
     int currentByte;

     // buffer for writing file
     byte data[] = new byte[BUFFER_SIZE];

     // write the current file to disk
     FileOutputStream fOS = new FileOutputStream(destFile);
     BufferedOutputStream bufOS = new BufferedOutputStream(fOS, BUFFER_SIZE);

     while ((currentByte = bufIS.read(data, 0, BUFFER_SIZE)) != -1) {
      bufOS.write(data, 0, currentByte);
     }

     // close BufferedOutputStream
     bufOS.flush();
     bufOS.close();

     // recursively unzip files
     if (entry.getName().toLowerCase().endsWith(ZIP_EXTENSION)) {
      String zipFilePath = destDirectory.getPath() + File.separatorChar + entry.getName();

      unzipToFile(zipFilePath, zipFilePath.substring(0, 
              zipFilePath.length() - ZIP_EXTENSION.length()));
     }
    }
   }
   bufIS.close();
   return true;
  } catch (Exception e) {
   e.printStackTrace();
   return false;
  }
 } 
}  
I have placed some comments inline so now it seems pretty straightforward, but if you have some questions feel free to ask them.
You may also want to check how to recursively unzip archive in memory