Wednesday, December 1, 2010

Plugin Based Architecture With Spring Integration

Introduction :

                                The purpose of this article is to demonstrate that it is possible to achieve a pluggable architecture using Spring Integration and the patterns it supports. If i were to give an introduction to Spring Integration it is a fairly new addition to the spring's solutions suite. It implements most of the Enterprise Integration Patterns currently known which makes it easier for developers as they do not need to re-invent the wheel. Some of the solutions provided by Spring Integration are as follows;

  1. Router
  2. Transformer
  3. Gateway
  4. Splitter
There are many more. As i am just getting my feet wet with Spring integration this is all i have covered up to now.

Pre-requisites :
 In order to run this example you need the following jar files;
  1. com.springsource.org.aopalliance-1.0.0.jar
  2. commons-logging-1.1.1.jar
  3. spring-aop-3.0.3.RELEASE.jar
  4. spring-asm-3.0.3.RELEASE.jar
  5. spring-beans-3.0.3.RELEASE.jar
  6. spring-context-3.0.3.RELEASE.jar
  7. spring-context-3.0.5.RELEASE.jar
  8. spring-context-support-3.0.3.RELEASE.jar
  9. spring-core-3.0.3.RELEASE.jar
  10. spring-expression-3.0.3.RELEASE.jar
  11. spring-tx-3.0.3.RELEASE.jar
  12. spring-integration-core-2.0.0.RC2.jar 

Note that i have used Spring 3.0.3 for this project. If you are using Spring 2.0 the required jars will be less. But as i used Spring Integration 2.0 i wanted to go with Spring 3.0.

Proposed Solution :
I have used the banking domain to demonstrate my example. The solution is to develop a system which will allows you to make payments to any banking system. The architecture is such that all code interfacing to any banking system is developed independently which can later be integrated to the main application as an when required. And you just have to inject the respective spring-integration config xml along with the plugin developed which can be injected to the project. 

In this solution i have done it in the same code base but in real life the plugin development should be in a different module.

Implmentation :
First let me give you an overview diagram of the proposed solution;

   

As you can see this is a typical architecture for a j2EE project. The controller i have specified here can be anything from struts to JSF to Spring MVC. The Service layer is basically the Spring layer which the controller will mainly be in contact with.

The main point to note is the spring integration layer. This is what injects all the plugins in the plugin repository. Next i will explain in detail the patterns of spring integration used in the solution. The following digram clearly explains this;


I will not go into detail on this digram as the diagram it self is self explanatory. So now lets get our hands dirty with some code;

First off i will start with the service layer;

package com.paymentgateway.services;

import com.paymentgateway.dto.PaymentRequestDTO;
import com.paymentgateway.dto.PaymentResponseDTO;

/**
 * The service interface is what the client from our application interacts with
 * the client is not aware of spring integration being used.
 * 
 * @author dinuka
 */
public interface PaymentService {

    public PaymentResponseDTO makePayment(PaymentRequestDTO paymentRequestDTO);

}


package com.paymentgateway.services;

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.message.GenericMessage;
import org.springframework.stereotype.Component;

import com.paymentgateway.dto.PaymentRequestDTO;
import com.paymentgateway.dto.PaymentResponseDTO;
import com.paymentgateway.dto.PaymentStatusCode;
import com.paymentgateway.dto.SystemActions;
import com.paymentgateway.gateway.CentralPaymentGateway;

@Component("paymentService")
public class PaymentServiceImpl implements PaymentService {

    @Autowired
    private CentralPaymentGateway gateway;

    @Override
    public PaymentResponseDTO makePayment(PaymentRequestDTO paymentRequestDTO) {
        /**
         * Here you can do any validation checks for null values if you need
         * and throw any relevant exception as needed. For simplicity purposes
         * i have not done so here.
         */

        /**
         * In the header we specify the banking system this message needs to be routed to<br>
         * Then in the
         */
        Map headerMap = new HashMap();
        headerMap.put("BANKING_SYSTEM", paymentRequestDTO.getBankingSystem());
        headerMap.put("ACTION", SystemActions.PAYMENT.toString());
        GenericMessage<PaymentRequestDTO> paymentRequestMsg = new GenericMessage<PaymentRequestDTO>(paymentRequestDTO,
                headerMap);
        PaymentResponseDTO paymentResponseDTO = gateway.makePayment(paymentRequestMsg);

        if (paymentResponseDTO.getStatusCode() == PaymentStatusCode.FAILURE) {
            /**
             * Throw relevant exception
             */
        }
        return paymentResponseDTO;

    }

}


And the DTOs used are as follows;



package com.paymentgateway.dto;

import java.io.Serializable;

/**
 * This DTO holds the data that needs to be passed to the
 * relevant plugin in order to make a payment
 * 
 * @author dinuka
 */
public class PaymentRequestDTO implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 582470760696219645L;

    /**
     * The account number of the customer
     */
    private String accountNumber;

    /**
     * The amount needed to be reduced
     */
    private Double deductAmount;

    /**
     * The First Name of the customer
     */
    private String firstName;

    /**
     * The Last Name of the customer
     */
    private String lastName;

    /**
     * This should ideally be moved to a CommonDTO as this will be reused by all
     * subsequent DTOs. Default banking system is "abc". The client needs to set
     * which banking system is needed to connect to.
     */
    private String bankingSystem = "abc";

    public String getAccountNumber() {
        return accountNumber;
    }

    public void setAccountNumber(String accountNumber) {
        this.accountNumber = accountNumber;
    }

    public Double getDeductAmount() {
        return deductAmount;
    }

    public void setDeductAmount(Double deductAmount) {
        this.deductAmount = deductAmount;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getBankingSystem() {
        return bankingSystem;
    }

    public void setBankingSystem(String bankingSystem) {
        this.bankingSystem = bankingSystem;
    }

    @Override
    public String toString() {
        return "PaymentRequestDTO [accountNumber=" + accountNumber + ", deductAmount=" + deductAmount + ", firstName="
                + firstName + ", lastName=" + lastName + "]";
    }

}


package com.paymentgateway.dto;

import java.io.Serializable;

/**
 * This is the default payment response DTO that every plugin
 * must return back to the system
 * 
 * @author dinuka
 */
public class PaymentResponseDTO implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 2773607380706313950L;

    /**
     * The account number of the customer
     */
    private String accountNumber;

    /**
     * The first name of the customer
     */
    private String firstName;

    /**
     * The last name of the customer
     */
    private String lastName;

    /**
     * The remaining balance in the account of the customer
     */
    private Double availableBalance;

    /**
     * The balance reduced from the customer account
     */
    private Double reducedBalance;

    /**
     * The status code indicating whether the transaction was a success or not
     */
    private PaymentStatusCode statusCode = PaymentStatusCode.SUCCESS;

    /**
     * The transaction id assigned to the relevant transaction
     */
    private Long transationId;

    public String getAccountNumber() {
        return accountNumber;
    }

    public void setAccountNumber(String accountNumber) {
        this.accountNumber = accountNumber;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public Double getAvailableBalance() {
        return availableBalance;
    }

    public void setAvailableBalance(Double availableBalance) {
        this.availableBalance = availableBalance;
    }

    public Double getReducedBalance() {
        return reducedBalance;
    }

    public void setReducedBalance(Double reducedBalance) {
        this.reducedBalance = reducedBalance;
    }

    public PaymentStatusCode getStatusCode() {
        return statusCode;
    }

    public void setStatusCode(PaymentStatusCode statusCode) {
        this.statusCode = statusCode;
    }

    public Long getTransationId() {
        return transationId;
    }

    public void setTransationId(Long transationId) {
        this.transationId = transationId;
    }

    @Override
    public String toString() {
        return "PaymentResponseDTO [accountNumber=" + accountNumber + ", firstName=" + firstName + ", lastName="
                + lastName + ", availableBalance=" + availableBalance + ", reducedBalance=" + reducedBalance
                + ", statusCode=" + statusCode + ", transationId=" + transationId + "]";
    }

}



package com.paymentgateway.dto;

/**
 * The status codes returned from each plugin indicating
 * if the transaction was a success or not
 * 
 * @author dinuka
 */
public enum PaymentStatusCode {

    SUCCESS, FAILURE
}



package com.paymentgateway.dto;

import com.paymentgateway.util.PaymentRouter;

/**
 * This enum defines the system wide actions
 * We use this name in our {@link PaymentRouter}
 * to decide which channel to route the message
 * 
 * @author dinuka
 */
public enum SystemActions {

    PAYMENT {
        @Override
        public String toString() {

            return "Payment";
        }
    }
}



Those are the DTOs i have used. Moving on, as the second diagram above specified we have defined a Central Gateway & A Router. So lets see how we have implemented those using spring integration;

package com.paymentgateway.gateway;

import org.springframework.integration.message.GenericMessage;

import com.paymentgateway.dto.PaymentRequestDTO;
import com.paymentgateway.dto.PaymentResponseDTO;

/**
 * This interface represents the common gateway which
 * will be used by Spring Integration to wire up the plugins
 * and also will be the central and first point of contact
 * by any client calling our business layer
 * 
 * @author dinuka
 */
public interface CentralPaymentGateway {

    /**
     * This method takes a parameter type of {@link GenericMessage} which wraps&lt;br&gt;
     * an instance of {@link PaymentRequestDTO}. Usage of sending an instance of&lt;br&gt;
     * Generic Message is so that we can add header values which can indicate&lt;br&gt;
     * which banking system to call to
     * 
     * @param paymentRequestDTO
     * @return
     */
    public PaymentResponseDTO makePayment(GenericMessage&lt;PaymentRequestDTO&gt; paymentRequestDTO);
}

Note that the gateway is just an interface defining our input parameters. We have used the GenericMessage defined by Spring integration. If you go back to the service layer implementation you can see that we have populated an instance of GenericMessage with the relevant DTO which is passed onto the gateway. The gateway here acts as a mediation layer.

Moving on with the Router implementation;

package com.paymentgateway.util;

import org.springframework.integration.Message;

/**
 * This is the base Router for All payment related functions
 * We route the message based on the banking system and the action
 * which comes in the header of the message. Ofcourse we can enhance this
 * to put the message on an error queue if the {@link Message} does not have the
 * relevant header values.
 * 
 * @author dinuka
 */
public class PaymentRouter {

    public String resolveBankChannel(Message message) {
        return (String) message.getHeaders().get("BANKING_SYSTEM") + (String) message.getHeaders().get("ACTION")
                + "Channel";
    }
}


Again if you go back to the PaymentServiceImpl class you can see we set the two headers BANKING_SYSTEM and ACTION. The router decides which channel this message should go on. You can see this in the next section when we wire up all this together.

The Spring configurations are as follows;

First off i present to you the main config file named context-config.xml. This mainly injects the service layer beans.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

 <context:component-scan base-package="com.paymentgateway.services" />
 <context:annotation-config />
</beans>


Next we look at the core configuration where we wire up the Spring Integration related components;

spring-integration-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:int="http://www.springframework.org/schema/integration"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.0.xsd">

 <!-- The generic input channel which would be used to pass through all messages 
  coming into the Central Gateway -->
 <int:channel id="inputChannel"></int:channel>

 <!-- Here we wire up the Central Gateway which is the central point of access 
  from our service layer -->
 <int:gateway id="gateway" default-request-channel="inputChannel"
  service-interface="com.paymentgateway.gateway.CentralPaymentGateway"
  default-reply-channel="outputChannel"></int:gateway>



 <!-- This is the generic Output channel which will be used by to send the 
  output from any plugin. -->
 <int:channel id="outputChannel"></int:channel>

 <!-- The router is the one who decides which channel to send the message 
  passed in from input channel into. The client should send the name of the 
  Banking system where by 'SearchChannel' keyword is appended by the defaultRouter 
  bean. -->
 <int:router id="centralRouter" ref="defaultRouter" method="resolveBankChannel"
  input-channel="inputChannel"></int:router>

 <bean id="defaultRouter" name="defaultRouter"
  class="com.paymentgateway.util.PaymentRouter" />

 
 
</beans>


That is the core configuration which wires up the Gateway, Router and defines the Channels required by the application. Next off lets go into our plugin( of many plugins to come) the ABC Bank Plugin.



First we define the Base plugin interface which all plugin developers should adhere to;


package com.paymentgateway.plugins;

import com.paymentgateway.dto.PaymentRequestDTO;
import com.paymentgateway.dto.PaymentResponseDTO;

/**
 * This is the base plugin interface. All plugin developers should adhere to<br>
 * this interface when they write new plugins connecting to different banking<br>
 * systems.
 * 
 * @author dinuka
 */
public interface BasePlugin {

    public PaymentResponseDTO makePayment(PaymentRequestDTO paymentRequestDTO);

}


And the implementation of this interface is as follows;

package com.paymentgateway.plugins;

import com.paymentgateway.dto.PaymentRequestDTO;
import com.paymentgateway.dto.PaymentResponseDTO;
import com.paymentgateway.dto.PaymentStatusCode;

/**
 * This is the plugin used to connect to the ABC banking system
 * in order to do the payment transaction.
 * 
 * @author dinuka
 */
public class ABCBankPlugin implements BasePlugin {

    @Override
    /**
     * Right now we just return a mock value. But when the true implementation
     * comes you will deal with any connection rellated information
     * at this point.
     */
    public PaymentResponseDTO makePayment(PaymentRequestDTO paymentRequestDTO) {
        PaymentResponseDTO paymentResponseDTO = new PaymentResponseDTO();
        paymentResponseDTO.setAccountNumber("abc123");
        paymentResponseDTO.setAvailableBalance(10000d);
        paymentResponseDTO.setFirstName("Dinuka");
        paymentResponseDTO.setLastName("Arseculeratne");
        paymentResponseDTO.setReducedBalance(500d);
        paymentResponseDTO.setStatusCode(PaymentStatusCode.SUCCESS);
        paymentResponseDTO.setTransationId(1233424234l);
        return paymentResponseDTO;
    }

}


As this is just a mock implementation i have just returned the response DTO with values filled. Now that we have developed our plugin lets wire it up;



abc_bank_plugin-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:int="http://www.springframework.org/schema/integration"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.0.xsd">

 <!-- Start of ABC Banking System Plugin Injection -->

 <!-- This is the payment channel used for the ABC banking system -->
 <int:channel id="abcPaymentChannel"></int:channel>

 <!-- Wire up the ABC Banking plugin -->
 <bean id="abcBakingSysPlugin" name="abcBakingSysPlugin"
  class="com.paymentgateway.plugins.ABCBankPlugin" />

 <!-- This service activator is used to handle the payment response from 
  ABC banking system -->
 <int:service-activator input-channel="abcPaymentChannel"
  ref="abcBakingSysPlugin" method="makePayment" output-channel="outputChannel"></int:service-activator>

 <!-- End of ABC Banking System Plugin Injection -->
</beans>

And lastly i present a test class just so that you can run the solution given above;

package com.paymentgateway.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.paymentgateway.dto.PaymentRequestDTO;
import com.paymentgateway.dto.PaymentResponseDTO;
import com.paymentgateway.services.PaymentService;

/**
 * This is a test class showing how it all comes together
 * 
 * @author dinuka
 */
public class TestBankingApp {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("context-config.xml",
                "spring-integration-config.xml","abc_bank_plugin-config.xml");

        PaymentService paymentService = (PaymentService) context.getBean("paymentService");

        PaymentRequestDTO paymentRequestDTO = new PaymentRequestDTO();
        PaymentResponseDTO paymentResponseDTO = paymentService.makePayment(paymentRequestDTO);

        /**
         * We just print out the resulting DTO returned from the plugin<br>
         * as this is just a tutorial
         */
        System.out.println(paymentResponseDTO);
    }
}


Thats it. Your done with your plugin architecture. If you ever do develop another plugin all you have to do is implement the BasePlugin Interface and as well as give the wiring up spring file. The following diagram explains the flow in which the message travels which will give you an even clearer picture of what we have accomplished;


Future Enhancements:
  1. Implement a transformer pattern which will do the conversion of DTOs to application specific DTOs.
  2. Introduce an error channel where any error populated will be put in to. 

Thats it guys. Your comments and suggestions are most welcome.

References:

[1] http://static.springsource.org/spring-integration/docs/2.0.0.RELEASE/reference/htmlsingle/

Friday, November 26, 2010

SQL with Hibernate Criteria

Hibernate's Criteria is a very comprehensive API which provides the user alot of flexibility to write dynamic queries. But of course nothing is perfect. I came across a situation where i had to truncate a date field in order to get the correct result set without considering the time portion. While going through the Criteria API I did not find anything which allowed me to do this. And hence in the path for a solution i found out that Criteria allows for plain SQL syntax to be included which i thought was a big plus point because it gives the developer the flexibility without restricting him/her just to the API.

The following code depicts the way you can incorporate plain SQL to your criteria API.

DetachedCriteria testCirteria = DetachedCriteria.forClass(Employee.class);
 SimpleDateFormat dateFormatForSearch = new SimpleDateFormat("dd/MM/yyyy");
 Calendar joinDate = empSearchDTO.getJoinDate();

  if (joinDate != null) {
      /**
 The following uses DateUtils of apache commons to truncate the<br>
 date object.
      **/
      joinDate = DateUtils.truncate(joinDate, Calendar.DATE);
      String dateAsStr = dateFormatForSearch.format(joinDate.getTime());
      testCirteria.add(Restrictions.sqlRestriction("trunc(emp_join_date)=to_date('" + dateAsStr + "','dd/mm/yyyy')"));

   }

As you can see the Criteria API allows to put any valid SQL within the Restrics.sqlRestriction() method. Here i have used the trunc function.

Thats it. If you have any queries or suggestions pls do leave a comment.

Cheers!!!!







Sunday, November 21, 2010

Why the saying "If all you have is a hammer then everything looks like a nail" is so true in the IT industry

"If all you have is a hammer then everything looks like a nail" - Now do not ask me who quoted this or from where i found it.. I vaguely remember seeing it on one of my buddies gtalk status some time back. But the reality of this statement struck me a week back.

We had got this new project in our company. Not a major high scale project. But involved some calculations. Was very interesting. Our company has a few frameworks developed around the years which is used as best practice for many projects. When our architect got this project he without even considering the architectural perspective of the project put in an already existing framework to start development.

Note that this was a project that involved just two(three including the login page) pages. But the technologies included in the framework were as follows;
  1. EJB 3.0
  2. JMS
  3. Spring
  4. Hibernate
  5. Freemarker
  6. Jasper
  7. Struts 2.0
and a few others which i cannot recall at this moment. And i was thinking to my self from all these what we actually needed to do this project. And this was all bundled up into an EAR which came to a size of a thumping 30MB :S... I was amazed at why such a person with so many years of experience would go for such an inappropriate solutions. Of course looking at it from his perspective im sure the higher management would have given him ridiculously tight dead lines to finish the project which lead him into jumping to an existing framework. And also this project was under a maven build. I mean come on, to build a freaking 3 page module it takes around 2mins because of all the dependent modules existing in the framework which is almost always never ever going to be used within this project. Talk about time waste.

In my opinion my technology stack for this project would have been using a simple ANT build using Spring to wire up and handle transactions with Hibernate and struts to handle the mediation layer and bundle it up as a WAR. Ofcourse some time back i did a solution where all the commonly used JARs were bundled up in a single jar which was deployed only once(Note that we use JBoss A/S as our application servers). This is because if you really look at it, the project it self is only a few MBs of size. But what takes most of the space is the third party libraries we use. This solution lead to lesser deployment time because else it takes alot of time to do a remote deployment to the servers if the file size is large.

The fact lies with why we cant think beyond what already exists. To think the same framework works in all situations is like thinking one antibiotic will cure all viruses. Of course time lines are an essential component. But if you really look at it, using the right tool for the right job will help you finish up the job with time to spare rather than using an existing framework and removing all parts which you do not need which is both time consuming and error prone. 

So my theory is dont have just a hammer, but have a tool kit with various options under your belt which will serve you and your team mates better where by will be better for your company as well.

Friday, October 29, 2010

XML Parsing with AXIOM

Recently a friend of mine inquired about what the best way is to parse XML. I have to say im not an expert in this subject but from what i have read i instantly remembered that AXIOM is a pull parser which means that when you request for a particular element within your XML document push parsers will give you that exact element where as other pull parsers will build the whole XML document before handing over the relevant document element to you. Hence AXIOM will leave the least memory foot print.

The problem was i could not find a clear and concise tutorial explaining how to deal with XML using AXIOM. After playing around with it i figured out how to manipulate XML with AXIOM which i should say is so much better that the cumbersome code you have to deal with when manipulating with DOM or JDOM.

So following i show a simple example of how to manipulate XML with AXIOM;

First off i present the XML i will be parsing

<?xml version="1.0" encoding="utf-8" ?>
<my_servers>
 <server>
  <server-name>PROD</server-name>
  <server-ip>xx.xx.xx.xx</server-ip>
  <server-port>80</server-port>
  <server-desc>Running A/S</server-desc>
 </server>

 <server>
  <server-name>PROD2</server-name>
  <server-ip>xx1.xx1.xx1.xx1</server-ip>
  <server-port>80</server-port>
  <server-desc>Running A/S</server-desc>
 </server>
</my_servers>

Next i wrote a factory method to handout StaxBuilder instances depending on the XML file you pass. I have done as such so as to minimize the task of creating new StaxBuilder instances everytime.


import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.xml.stream.XMLStreamException;

import org.apache.axiom.om.impl.builder.StAXOMBuilder;

public class AxiomStaxBuilderFactory {

    private static Map<String, StAXOMBuilder> staxBuilderMap = new ConcurrentHashMap<String, StAXOMBuilder>();

    /**
     * The factory method stores the {@link StAXOMBuilder} instance created for each XML file<br>
     * passed in so that we do not need to create unnecessary objects every time.<br>
     * An instance of {@linkplain ConcurrentHashMap} is used so as to make the<br>
     * instances thread safe.
     * 
     * @param xmlFilePath the path of the XML file
     * @return an instance of the {@link StAXOMBuilder} from the cache or newly created
     */
    public static StAXOMBuilder getAxiomBuilderForFile(String xmlFilePath) {
        StAXOMBuilder staxBuilder = null;
        if (staxBuilderMap.containsKey(xmlFilePath)) {
            staxBuilder = staxBuilderMap.get(xmlFilePath);
        } else {
            try {
                staxBuilder = new StAXOMBuilder(new FileInputStream(xmlFilePath));
                staxBuilderMap.put(xmlFilePath, staxBuilder);
            } catch (FileNotFoundException e) {
                throw new AxiomBuilderException(e);
            } catch (XMLStreamException e) {
                throw new AxiomBuilderException(e);
            }
        }

        return staxBuilder;

    }
}



I have used a Concurrent Hash map so that this wil work well in a multi threaded application. If your not bothered with that you might as well use a normal HashMap for better performance which in this case would be negligible. I have also used a custom exception as i did not want the user to have to handle exceptions so i wrapped the exceptions thrown to my custom run time exception. Following is that code. Nothing major just a normal extension of the RuntimeException class;

/**
 * This exception class wraps all exceptions thrown from the Axiom API
 * as the user does not need to be bound by such checked exceptions.
 * @author dinuka
 *
 */
public class AxiomBuilderException extends RuntimeException {

    /**
     * 
     */
    private static final long serialVersionUID = -7853903625725204661L;

    public AxiomBuilderException(Throwable ex) {
        super(ex);
    }

    public AxiomBuilderException(String msg) {
        super(msg);
    }
}



Next off i have written a utility class to deal with the XML parsig. Ofcourse this is not needed but i just had it so that client calls will be much cleaner without having to deal with XML releated coding which would be abstracted by the utility class. Note -The current method only reads the root level elements passed in.



import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.xml.namespace.QName;

import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.impl.builder.StAXOMBuilder;

/**
 * The utility class provides abstraction to users so that <br>
 * the user can just pass in the xml file name and the node he/she<br>
 * wants to access and get the values without having to bother with<br>
 * boilerplate xml handling info.
 * 
 * @author dinuka
 */
public class AxiomUtil {

    /**
     * This method is used if you have for example a node with multiple children<br>
     * Note that this method assumes the node in query is within the root element
     * 
     * @param xmlFilePath the path of the xml file
     * @param nodeName the node name from which you want to retrieve values
     * @return the list containing key value pairs containing the values of the sub elements within<br>
     *         the nodeName passed in.
     */
    public static List<Map<String, String>> getNodeWithChildrenValues(String xmlFilePath, String nodeName) {
        List<Map<String, String>> valueList = new ArrayList<Map<String, String>>();

        StAXOMBuilder staxBuilder = AxiomStaxBuilderFactory.getAxiomBuilderForFile(xmlFilePath);

        OMElement documentElement = staxBuilder.getDocumentElement();
        Iterator nodeElement = documentElement.getChildrenWithName(new QName(nodeName));

        while (nodeElement.hasNext()) {
            OMElement om = (OMElement) nodeElement.next();

            Iterator it = om.getChildElements();
            Map<String, String> valueMap = new HashMap<String, String>();
            while (it.hasNext()) {
                OMElement el = (OMElement) it.next();

                valueMap.put(el.getLocalName(), el.getText());

            }

            valueList.add(valueMap);
        }
        return valueList;
    }

}



And finally i give to you a sample class to test out the XML parsing example i have presented to you here.



import java.util.List;
import java.util.Map;

/**
 * Test class depicting the use of Axiom parsing XML
 * 
 * @author dinuka
 */
public class testServerConfigXML {

    public static void main(String argv[]) {

        List<Map<String, String>> values = AxiomUtil.getNodeWithChildrenValues("/home/dinuka/serverInfo.xml", "server");

        for (Map<String, String> mapVals : values) {
            for (String keys : mapVals.keySet()) {
                System.out.println(keys + "=" + mapVals.get(keys));
            }
        }

    }

}


Thats it. If you have any queries or any improvement points you see pls do leave a comment which would be highly appreciated. Hope this helps anyone out there looking for a similar basic tutorial on AXIOM XML parsing.


Cheers

Thursday, October 14, 2010

My First Experience with MongoDB

I know im pretty much slow to this concept of NoSQL. But nevertheless better late than never right ? :) ... So this is my first post on getting my feet wet in the world NoSQL. I have to say coming from a RDMS background it was not much hard to get my self familiar with MongoDB. Ofcourse there are a number of NoSQL implementations out there. But going through each of them MongoDB was the one i felt inclined to go with as the learning curve for me using it was pretty low.

So i was reading through multiple posts, articles to get a feel of what MongoDB can really do. After going through that i wanted to try out an example to get a feel of it. So i first downloded Mongo DB from here.  Im on Ubuntu 8.04 so i downloaded the Linux 32-bit one. They do say about a limitation of using the 32-bit version. But i was not much concerned as this is my stage of getting used to it.

So coming from a J2EE development background mostly using annotations i was inclined to search for a solution using annotations to deal with MongoDB so that the transition from usual JPA/Hibernate mappings to MongoDB will be minimal. I know that the way of thinking in terms of MongoDB is different to the way we map things in a traditional RDMS as there is no concept as foreign keys etc. But i wanted the transition to be minimal. So going on those steps i found this project done by google supporting annotation based mapping facility for the MongoDB called Morphia. This was exactly what i needed.

So i did a quich write up to test it. You need to download Morphia  and also get the Java driver for MongoDB. Next start your mongodb. Go to your MongoDB installation path's bin directory and do as follows;




So i specified the parameters as 


./mongod --dbpath<path_to_your_data_store>;
This starts Mongo DB in its default port. Afterwards i present to you a simple program using the above mentioned libraries.


I have a simple class called MyInfo which has an embedded object called Address. Code is as follows;

import org.bson.types.ObjectId;

import com.google.code.morphia.annotations.Entity;
import com.google.code.morphia.annotations.Id;



@Entity("myInfo")
public class MyInfo {

    @Id ObjectId id;
    private String name;
    private int age;
    
    private Address address;
    
    public ObjectId getId() {
        return id;
    }
    public void setId(ObjectId id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Address getAddress() {
        return address;
    }
    public void setAddress(Address address) {
        this.address = address;
    }
    
}


import com.google.code.morphia.annotations.Embedded;

@Embedded
public class Address {
    
    
    private String adderss1;
    
    private String address2;

    public String getAdderss1() {
        return adderss1;
    }

    public void setAdderss1(String adderss1) {
        this.adderss1 = adderss1;
    }

    public String getAddress2() {
        return address2;
    }

    public void setAddress2(String address2) {
        this.address2 = address2;
    }
    
    
}


Those were my domain classes. Next i present the main class which does the storing of my objects within the MongoDB.

import java.net.UnknownHostException;

import com.google.code.morphia.Datastore;
import com.google.code.morphia.Morphia;
import com.mongodb.Mongo;
import com.mongodb.MongoException;

/**
 * This class Creates a test Monog Connection and persists an instance of {@linkplain MyInfo}
 * @author dinuka
 *
 */
public class MainMongoPersist {

    public static void main(String[] args) throws UnknownHostException, MongoException {
        
        /**
         * Creates a connection on the local Mongo DB server
         */
        Mongo mon = new Mongo("localhost",27017);
        
        /**
         * Add the classes annotated with @Entity.
         * Note that you do not need to add the Address as we Embedded it within 
         * the MyInfo instance. 
         */
        Morphia morphia = new Morphia();
        morphia.map(MyInfo.class);
        
        /**
         * Create a data source by giving the DB you want to connect to.
         */
        Datastore ds = morphia.createDatastore(mon, "mydb");
        
        MyInfo inf = new MyInfo();
        inf.setName("Roshan123");
        inf.setAge(1);
        Address ad = new Address();
        ad.setAdderss1("No 42");
        ad.setAddress2("3424234234");
        inf.setAddress(ad);
        
        /**
         * Persist the object
         */
        ds.save(inf);
    }
}


That is it. Now i start my mongo process which allows me to query the DB to check whether the data really did get stored within the DB. So i go to my mongo installation path's bin directory and start a mongo client. Following snippet shows how to do that;


dinuka@dinuka:~/software/mongodb-linux-i686-1.6.3/bin$ ./mongo 
MongoDB shell version: 1.6.3
connecting to: test
> use mydb
switched to db mydb
> db.myInfo.find();
{ "_id" : ObjectId("4cb6d81d166cacce8ab4e4e8"), "className" : "MyInfo", "name" : "Dinuka", "stars" : 1 }
{ "_id" : ObjectId("4cb6d8f71757accef6a9784d"), "className" : "MyInfo", "name" : "Roshan", "stars" : 1 }
{ "_id" : ObjectId("4cb6d910f9d0accee936df12"), "className" : "MyInfo", "name" : "Roshan123", "stars" : 1, "address" : { "adderss1" : "fsdfsdfsd", "address2" : "3424234234" } }
> 



I switched the database as i used the database called mydb which is the one i specified when i created the data source. This is just a basic write up on Mongo DB. There is so much more in it which i am on the verge of learning. As it is a very interesting topic to explore on for me. Up to now the potential i see of using Mongo DB is;

  • It allows you to have a dynamic schema so that adding a column will be no hassle. 
  • You can scale the database with ease using the auto sharding facility provided by MongoDB.
  • I love the fact that you can think in terms of how you map your json data in the front end to the database as it is.
Many more is there which im not familiar with so i rather not comment on anything i do not know :) .. If anyone out there can mention any experience using this in a production system with regards to performance and reliability that would be a great help.

Would love to hear your thoughts/comments on this regards.


And the journey of learning Mongo continues............

Thursday, September 23, 2010

APIs you can use in your web site

This article lists down some useful web APIs you can use in your day to day web site development. I found the Google chart API to be quite impressive.

Wednesday, September 22, 2010

Caching with Hazlecast using Spring

Found an article explaining how to wire up Hazlecast caching with Spring. You can check out the article here.