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.