Showing posts with label java4. Show all posts
Showing posts with label java4. Show all posts

Thursday, June 23, 2011

Type safety in Java Set and Map in JDK 1.4

Probably many of you still remember the lack of type checking in Java 1.4 Collections and how much hassle it was to deal with casting the collection elements, not to mention how many errors this introduced to the code. Since introduction of generics in Java 1.5 this have really improved and one might think that nowadays the language itself protects the programmer from the silliest of typing mistakes. Generics themselves brought with them a set of new complications, but it seems reasonable to think that in the basic code situations when using Java’s Sets or Maps and when there is no kung-fu complications like wildcards or casting we are safe and secure. Are we really?
Recently I have encountered a bug in a production code when dealing with a simple (really simple) usage of a Map. The embarassing part of this issue was that it took lots of effort to debug it and the cause of it was… well… trivial. The code below shows a sketch of how the code looked like before the bug was introduced. In real life the class was a part of a really old and rarely edited legacy code, obviously it was much larger than the one below:

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

public class EmployeeDataLookup {
    // A map storing relation between employee ID and name.
    private Map<Integer, String> employeeIdToName;

    public EmployeeDataLookup() {
        // Create a new EmployeeDataLookup and initialize
        // it with employee names and IDs.
        employeeIdToName = new HashMap<Integer, String>();
        addEmployee(301, "John Doe");
        addEmployee(302, "Mary Poppins");
        addEmployee(303, "Andy Stevens");
    }

    public void addEmployee(int employeeId, String employeeName) {
        employeeIdToName.put(employeeId, employeeName);
    }

    // This class is very complicated and has many other methods...

    // Lookup method for finding employee name for given employee ID.
    public String findEmployeeName(int employeeId) {
        return employeeIdToName.get(employeeId);
    }

    public static void main(String[] args) {
        // Create a EmployeeDataLookup instance
        EmployeeDataLookup employeeLookup = new EmployeeDataLookup();
        // Find the name of an employee with ID = 301
        String employeeName = employeeLookup.findEmployeeName(301);
        System.out.print("Employee 301 : " + employeeName);
    }
}

So what went wrong? Well, at some point the project functionality requirements have changed (surprising, right?) and the key of the map storing data inside of the class had to be changed. In terms of the example above you might say that a company has merged with one of the vendors, so the system storing the employee data had to be made compatible with the ID system of the vendor. Because the vendor company used 13 digit IDs to identify its employees the change to the code in Java terms meant that instead of using Integers we had to change to Longs. Simple right? Its even easier if you use an IDE like Eclipse – just change/refactor the Map key type in line 7 from Integer to Long, let the editor show you all the errors, fix them and that is it! In five minutes all is done:

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

public class EmployeeDataLookup2 {
    // A map storing relation between employee ID and name.
    private Map<Long, String> employeeIdToName;

    public EmployeeDataLookup2() {
        // Create a new EmployeeDataLookup and initialize
        // it with employee names and IDs.
        employeeIdToName = new HashMap<Long, String>();
        addEmployee(301, "John Doe");
        addEmployee(302, "Mary Poppins");
        addEmployee(303, "Andy Stevens");
    }

    public void addEmployee(long employeeId, String employeeName) {
        employeeIdToName.put(employeeId, employeeName);
    }

    // This class is very complicated and has many other methods...

    // Lookup method for finding employee name for given employee ID.
    public String findEmployeeName(int employeeId) {
        return employeeIdToName.get(employeeId);
    }

    public static void main(String[] args) {
        // Create a EmployeeDataLookup instance
        EmployeeDataLookup employeeLookup = new EmployeeDataLookup();
        // Find the name of an employee with ID = 301
        String employeeName = employeeLookup.findEmployeeName(301);
        System.out.print("Employee 301 : " + employeeName);
    }
}

The change above introduced the bug. Can you see it? When you run the main method now you will find out that the name of the employee with ID=301 is ‘null’! Ha!

If you do not see the trouble (or are to lazy to try) I’ll give you a hint: check out the function findEmployeeName . If you have seen it, try to imagine that this class in real life had 1000+ lines of code unrelated to the changed map. Since there are no compile errors it was like looking for a needle in a haystack… so what’s the bug exactly?

The problem is that after introducing generics, probably due to backward compatibility, some of the methods in Collection and Map interface have not been changed and still do not perform type checking. One of them is Map.get(Object) which have caused the bug in the code above. After the ’simple change’ we have still been using Integer keys to access the map that contained Longs, so even though we had a record of employee 301 in our system the get method returned null. And because of the lack of type checking there were no warnings… Busted.

So what’s the lesson from this? Be cautious about type checking even in the most basic situations. Remember that access methods in Sets, Lists and Maps can behave and bite you in the behind. The biggest pain is that the bugs introduced this way are usually hard to spot by a human, so sometimes a dozen of engineers staring at the code can have trouble finding it. There is a way to deal with them though – most of them can be easily found if you are using a static code analysis tool (eg: FindBugs).

Note:
If you are using JDK 6 or higher, its no worries for you. Because in any case you would get the correct result.

Thursday, April 14, 2011

USING SPRING’S STOREDPROCEDURE

One very useful portion of the Spring Framework is the StoredProcedure wrapper’s and the RowMapper objects. Together these allow you to call a stored procedure and then parse the result set back into a collection of objects with very little pain. Below is an example of how to do just this for a simple query like a user query.

public class MyStoredProcedure extends StoredProcedure {

    public MyStoredProcedure (DataSource ds, String spname,
            Map map, String sqlOutKey, Integer returnType,
            RowMapper rowmapper) {
        super();
        setDataSource(ds);

        /* resultset has to be declared first over other declare parameters */
        if (rowmapper != null) {
            declareParameter(new SqlReturnResultSet(sqlOutKey, rowmapper));
        }

        if (map != null) {
            Iterator itr = map.keySet().iterator();
            while (itr.hasNext()) {
                String key = (String) itr.next();
                Integer value = (Integer) map.get(key);
                declareParameter(new SqlParameter(key, value.intValue()));
            }
        }

        /*
         * sql out paramter has to be declared based on the order in stored
         * procedures, In all our stored procedures we have it after input
         * parameters
         */
        if (returnType != null) {
            declareParameter(new SqlOutParameter(sqlOutKey, returnType
                    .intValue()));
        }

        setSql(spname);
        compile();
    }
}

Next, we have the Mapper class:

public class UserMapper implements RowMapper {
    public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
        User user = new User();
        user.setUserId(rs.getString(Constants.USER_ID));
        user.setFirstName(rs.getString(Constants.FIRST_NAME));
        user.setLastName(rs.getString(Constants.LAST_NAME));
        user.setOrganizationName(rs.getString(Constants.ORGANIZATION_NAME));
        return user;
    }
}

Next, we have to query the actual stored procedure from a DAO. Here’s a sample method that would do just such a thing:

public Collection searchUsers(User user) throws Exception {

        Map lhm = new LinkedHashMap(4);
        lhm.put(Constants.USER_ID, new Integer(Types.VARCHAR));
        lhm.put(Constants.FIRST_NAME,new Integer(Types.VARCHAR));
                lhm.put(Constants.LAST_NAME,new Integer(Types.VARCHAR));
                lhm.put(Constants.ORGANIZATION_NAME,new Integer(Types.VARCHAR));

        UserMapper mapper = new UserMapper();

        // Call Stored Procedure
        EntitlementsStoredProcedure proc = new EntitlementsStoredProcedure(
            ds, StoredProcedureConstants.USER_SEL, lhm,
            Constants.RESULTSET, null, mapper);

        // Collect the criteria for the search
        Map map = new LinkedHashMap(4);
        map.put(Constants.USER_ID, user.getUserId());
        map.put(Constants.FIRST_NAME, user.getFirstName());
        map.put(Constants.LAST_NAME, user.getLastName());
        map.put(Constants.ORGANIZATION_NAME, user.getOrganizationName());

        Map results =  proc.execute(map);
        List resultList = (LinkedList)results.get(Constants.RESULTSET);

        //iterate of results list and print
        for (Iterator it=resultList.iterator(); it.hasNext(); ) {
            User user1 = (User)it.next();
            System.out.println(user1);
        }

        return resultList;
    }

That’s all there is to it! This shows just how simple it is to do queries in an object oriented way, and have generic row mappers. There are full object relational mapping solutions, such as Hibernate, that do a great job of solving the working with relational data in an OO way paradigm, but they take a LOT of configuration and can be daunting if you’re not accustomed to working with them. This solution, however, I feel works very well in simpler scenarios. It also allows someone who is used to looking at code to quickly read through and get an idea of how to use this.

One point to note: this gets even simpler when using generics that are introduced in Java 1.5.

Chitika