Monday, February 18, 2013

Custom XML Generator

This is a simple XML Generator that can be used to send in any object (tested with List and custom objects) and get the XML out of it. This could be useful while building java to xml transformations for any objects.

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class XMLGeneratorImpl {

    public static final String GET_METHOD = "get";
    public static final String IS_METHOD = "is";
    public static final String EMPTY = "";
    public static final String START_TAG = "<";
    public static final String END_TAG = ">";
    public static final String SLASH = "/";

    private boolean trimPackageNames;

    private static final Set<Class> WRAPPER_TYPES = new HashSet(Arrays.asList(
            Boolean.class, Character.class, Byte.class, Short.class,
            Integer.class, Long.class, Float.class, Double.class, Void.class,
            String.class));

    private enum ObjectFormat {
        PRIMITIVE, OBJECT, LIST, MAP, SET, ARRAY;
    }

    public String toXML(Object input) {
        ObjectFormat format = getFormat(input);
        return convertToXml(input, format);
    }

    private String convertToXml(Object input, ObjectFormat format) {
        StringBuilder xml = new StringBuilder();
        String objectName = getObjectName(input);
        switch (format) {
        case OBJECT:
            xml.append(START_TAG).append(objectName).append(END_TAG);
            xml.append(getXMLFromObject(input));
            xml.append(START_TAG).append(SLASH).append(objectName)
                    .append(END_TAG);
            break;
        case ARRAY:
            xml.append(getXMLFromArray(input));
            break;
        case LIST:
            xml.append(getXMLFromList(input));
            break;
        case PRIMITIVE:
            xml.append(getXMLFromPrimitive(null, input));
            break;
        default:
            throw new IllegalArgumentException("Unsupported request object");
        }

        return xml.toString();
    }

    private String getXMLFromPrimitive(String key, Object input) {
        StringBuilder xml = new StringBuilder();
        String xmlTag = key == null ? getObjectName(input) : key;
        xml.append(START_TAG).append(xmlTag).append(END_TAG)
                .append(input.toString()).append(START_TAG).append(SLASH)
                .append(xmlTag).append(END_TAG);
        return xml.toString();
    }

    private String getXMLFromObject(Object input) {
        StringBuilder xml = new StringBuilder();
        List<Method> methods = getAllowedMethods(input);
        for (Method method : methods) {
            xml.append(getXMLValue(input, method));
        }
        return xml.toString();
    }

    private String getXMLFromArray(Object input) {
        StringBuilder xml = new StringBuilder();
        Object[] objects = (Object[]) input;
        for (Object object : objects) {
            ObjectFormat of = getFormat(object);
            xml.append(convertToXml(object, of));
        }
        return xml.toString();
    }

    private String getXMLFromList(Object input) {
        StringBuilder xml = new StringBuilder();
        List objects = (List) input;
        for (Object object : objects) {
            ObjectFormat of = getFormat(object);
            xml.append(convertToXml(object, of));
        }
        return xml.toString();
    }

    private List<Method> getAllowedMethods(Object input) {
        List<Method> getters = new ArrayList<Method>();
        Method[] methods = input.getClass().getDeclaredMethods();
        for (Method method : methods) {
            if (isGetterMethod(method)) {
                getters.add(method);
            }
        }
        return getters;
    }

    private boolean isGetterMethod(Method method) {
        boolean getter = false;
        if (method.getName().startsWith(GET_METHOD)
                || method.getName().startsWith(IS_METHOD)) {
            getter = true;
        }
        return getter;
    }

    private String getXMLValue(Object input, Method method) {
        StringBuilder xml = new StringBuilder();
        try {
            Object value = method.invoke(input, null);
            String xmlTagName = getObjectName(null, method);
            if (value != null) {
                ObjectFormat of = getFormat(value);
                switch (of) {
                case PRIMITIVE:
                    xml.append(START_TAG).append(xmlTagName).append(END_TAG)
                            .append(value).append(START_TAG).append(SLASH)
                            .append(xmlTagName).append(END_TAG);
                    break;
                case OBJECT:
                    xml.append(convertToXml(value, of));
                    break;
                case LIST:
                case MAP:
                case SET:
                case ARRAY:
                    xml.append(START_TAG).append(xmlTagName).append(END_TAG)
                            .append(convertToXml(value, of)).append(START_TAG)
                            .append(SLASH).append(xmlTagName).append(END_TAG);
                    break;
                }
            }
        } catch (IllegalArgumentException e) {
            // TODO: handle exception
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return xml.toString();
    }

    private ObjectFormat getFormat(Object input) {
        ObjectFormat format = ObjectFormat.OBJECT;
        Class clazz = input.getClass();
        if (isWrapperType(clazz)) {
            format = ObjectFormat.PRIMITIVE;
        } else if (clazz.getName().contains("List")) {
            format = ObjectFormat.LIST;
        }
        return format;
    }

    private boolean isWrapperType(Class clazz) {
        return WRAPPER_TYPES.contains(clazz);
    }

    private String getXMLTagName(Method method) {
        String xmlTagName = null;

        if (method.getName().startsWith(GET_METHOD)) {
            xmlTagName = method.getName().substring(3);
        } else {
            xmlTagName = method.getName().substring(2);
        }

        return xmlTagName;
    }

    public boolean isTrimPackageNames() {
        return trimPackageNames;
    }

    public void setTrimPackageNames(boolean trimPackageNames) {
        this.trimPackageNames = trimPackageNames;
    }

    private String getObjectName(Object input) {
        return getObjectName(input, null);
    }

    private String getObjectName(Object input, Method method) {
        String xmlTagName = EMPTY;
        if (input != null) {
            xmlTagName = input.getClass().getName();
            if (trimPackageNames && xmlTagName.indexOf(".") != -1) {
                xmlTagName = xmlTagName
                        .substring(xmlTagName.lastIndexOf(".") + 1);
            }
        } else if (method != null) {
            xmlTagName = getXMLTagName(method);
            if (trimPackageNames && xmlTagName.indexOf(".") != -1) {
                xmlTagName = xmlTagName
                        .substring(xmlTagName.lastIndexOf(".") + 1);
            }
        }
        return xmlTagName;
    }
} 

Saturday, February 9, 2013

@Controller and @RequestMapping

Since Spring 2.5 onwards, we can configure controllers through Spring Annotations which helps us in many ways.

  1. Spring bean definition files to be of minimal size.
  2. No need to extend any spring controllers and easy to create form and multi-action controller
@Controller:
Tells the spring framework that this class is used as a controller and should be considered by Dispatcher Servlet for delegating the requests to this controller.

@RequestMapping:
Tells the spring framework what are all the URLs that would be handled by this controller.

com.controller.test;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.servlet.ModelAndView;

@Controller("HelloWordController")

@RequestMapping("/hello")

public class HelloWorldController {

    @RequestMapping(value = "/world", method = RequestMethod.GET)

    public ModelAndView helloWord() {

        return new ModelAndView("HelloWorld");

    }

}

Now tell spring framework to scan your package for annotations. and define the HelloWorld webpage as per your requirement.

<context:component-scan base-package="com.controller.test" />

Thats it. Now deploy & start your application. Hit the below URL and see the actions.

http://localhost:8080/yourproject/hello/world

Sunday, February 3, 2013

Generating POJOs from Database Scema (Used HSQL)

Ok, Now in a project we have the tables created in a database server. How can i leverage those tables to generate my hibernate pojos or domain objects easily?

    
          a. Install Hibernate Tools.
          b. Go to Hibernate Perspective (Window -> Open Perspective -> Other -> Hibernate)
          c. In the Hibernate Configurations View, Right click -> Add Configuration
          d. Under Main Tab,
                * Select the project where the POJOS need to be generated.
                * Select the database connection as Hibernate Configured Connection.
                * Create the hibernate Configuration file (Properties file is not mandatory) with the following values.
                    <?xml version="1.0" encoding="UTF-8"?>
                    <!DOCTYPE hibernate-configuration PUBLIC
                            "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
                            "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
                    <hibernate-configuration>
                        <session-factory name="TechFes">
                            <property name="hibernate.connection.driver_class">org.hsqldb.jdbcDriver</property>
                            <property name="hibernate.connection.url">jdbc:hsqldb:hsql://localhost:9001/TechFes</property>
                            <property name="hibernate.connection.username">SA</property>
                            <property name="hibernate.dialect">org.hibernate.dialect.HSQLDialect</property>
                        </session-factory>
                    </hibernate-configuration>
          e. Now go to Run icon (the run icon in Hibernate perspective) and click on Hibernate Code Generation Configurations.
          f. Under Main Tab,
                * Select the output directory where the files has to get generated.
                * Select the "Reverse Engineer from JDBC Connection" checkbox.
                            * Provide the package name under which the domain java objects need to be created.
                            * Uncheck "Detect many-to-many" associations. This will help in generating the pojo objects for the tables that are maintained only for many-many association.
          g. Under Exporters Tab,
                * Check the Domain Code, hibernate cfg file and if required annotations, etc.
               
          h. Click on Run and your domain code should have got generated.

Standalone HSQL DB Setup

I have a project and have the schemas available. But I don't have an actual database setup yet. Now run HSQLDB in standalone mode from command line and load the tables and get going :-) What are the steps to do that?

          a. Download latest version of HSQLDB from http://hsqldb.org.

          b. Goto hsql db folder and create a file named server.properties with the following entries.
                  server.database.0=file:hsqldb/TechFes
                server.dbname.0=TechFes
          c. Once the JAVA_HOME and PATH variables are updated with the correct java location,
               execute this command from hsqldb folder where server.properties file is created
                 
                  >java -classpath lib/hsqldb.jar org.hsqldb.server.Server
                 
               NOTE: This command would create a folder hsqldb and create the files for TechFes database under that folder.
             
          d. Now we need to open the database with additional params to the above command.
         
                >java -classpath lib/hsqldb.jar org.hsqldb.server.Server --database.0 file:hsqldb/TechFes --dbname.0 TechFes
               
             NOTE: This would open the database so clients can connect to it.
            
          e. Now we can use the below command to launch the UI for this database and make changes to the schema.
         
                >java -cp lib/hsqldb.jar org.hsqldb.util.DatabaseManagerSwing