26
Aug/09
1

Java Middle-Tier Using Ibatis

This is going to be my first entry in a series on how to quickly and efficiently develop the middle-tier portion of an enterprise level flex application that will allow you to communicate with your application via the most efficient and simple means available, that being Remote Procedure Calls. By the time I finish the series, my hope is that you will be able to knock out the middle-tier portion of your application in no time at all, giving you a maximum amount of time to develop your front end Flex app.

The focus of this particular entry is ibatis, which is a framework which allows you to configure the population of java objects from the database using xml files. Ibatis has a ton of features which you can use, and detailing the full litany of them is a more laborious task than I’m up for. For the purposes of this entry, I’m going to show you how to map database fields to java fields, select, update, and insert data.

For the sake of these examples, I will have an embarassingly simple object model, reflected by the following VO:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.softwarejesus.ibatis.domain;

public class FirstObject {
    private String fieldOne;
    private String fieldTwo;
    private int fieldThree;
    public String getFieldOne() {
        return fieldOne;
    }
    public void setFieldOne(String fieldOne) {
        this.fieldOne = fieldOne;
    }
    public String getFieldTwo() {
        return fieldTwo;
    }
    public void setFieldTwo(String fieldTwo) {
        this.fieldTwo = fieldTwo;
    }
    public int getFieldThree() {
        return fieldThree;
    }
    public void setFieldThree(int fieldThree) {
        this.fieldThree = fieldThree;
    }
}

The first step in using ibatis, is to add the necessary jar files to your application’s buildpath. The jar files are available at the ibatis site here
Step two, is to determine what type of interactions you’ll need with your database. Here’s my Data Access Object

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package com.softwarejesus.ibatis.dao;

import java.io.IOException;
import java.io.Reader;
import java.sql.SQLException;
import java.util.List;

import com.ibatis.common.resources.Resources;
import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.client.SqlMapClientBuilder;
import com.softwarejesus.ibatis.domain.FirstObject;

public class FirstDao {
    private static SqlMapClient client;
   
    public FirstDao() {
        try {
            Reader reader = Resources.getResourceAsReader("sql-map-config.xml");
            client = SqlMapClientBuilder.buildSqlMapClient(reader);
            reader.close();
          } catch (IOException e) {
            System.out.println("There are better ways to do this, and I'll show you in a later post");
          }
    }
   
    public List<FirstObject> getAllFirstObjects() {
        List<FirstObject> returnList = null;
        try {
            returnList = client.queryForList("getAllFirstObjects");
        } catch (SQLException sqlE) {
            sqlE.printStackTrace();
        }
        return returnList;
    }
   
    public List<FirstObject> getFirstObjects(String param) {
        List<FirstObject> returnList = null;
        try {
            returnList = client.queryForList("getFirstObjects");
        } catch (SQLException sqlE) {
            sqlE.printStackTrace();
        }
        return returnList;
    }
   
    public void updateFirstObject(FirstObject object) {
        try {
            client.update("updateFirstObject", object);
        } catch (SQLException sqlE) {
            sqlE.printStackTrace();
        }
    }
   
    public void insertFirstObject(FirstObject newObject) {
        try {
            client.insert("insertFirstObject", newObject);
        } catch (SQLException sqlE) {
            sqlE.printStackTrace();
        }
    }

}

As you can see, I have two ways I can retrieve data, one will return all the data, the other will select based on some input criteria. I also have an update and insert method. The constructor creates a SqlMapClient object, the SqlMapClient object is basically where all the magic happens with ibatis. I won’t focus on the constructor, because my next entry will show you how to use Spring to populate this, and many other necessary objects.

Now that we know what interactions we want to have with the database, we need to set up our SQL-Map XML. My SQL Map includes only 6 different node types: typeAlias, resultMap, result, select, update, and insert.
The typeAlias node creates a shorthand Alias for a specific java class.
The resultMap node create another Alias, indicating how a specific set of fields will populate a java object.
The result node matches a specific result set field to a java field.
The select, update, and insert nodes should all be relatively self-explanatory.
Here’s my SQL-Map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE sqlMap
       PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
       "http://ibatis.apache.org/dtd/sql-map-2.dtd">

<sqlMap namespace="Model">
    <typeAlias alias="myFirstObject" type="com.softwarejesus.ibatis.domain.FirstObject" />
   
    <resultMap id="firstObjectResult" class="myFirstObject">
        <result property="fieldOne" column="oneField"/>
        <result property="fieldTwo" column="twoField"/>
        <result property="fieldThree" column="threeField"/>
    <resultMap>
   
    <select id="getAllFirstObjects" resultMap="firstObjectResult">
        Select * from firstTable
    </select>
   
    <select id="getFirstObjects" resultMap="firstObjectResult" parameterClass="java.lang.String">
        Select * from firstTable where oneField = #value#
    </select>
   
    <update id="updateFirstObject" parameterClass="myFirstObject">
        UPDATE firstTable
        SET oneField = #fieldOne#
            ,twoField = #fieldTwo#
            ,threeField = #fieldThree#
        WHERE oneField = #fieldOne#
    </update>
   
    <insert id="insertFirstObject" parameterClass="myFirstObject">
        INSERT INTO firstTable
        VALUES (#fieldOne#, #fieldTwo#, #fieldThree#)
    </insert>
</sqlMap>

Obviously this was a very simple example of what can be done using ibatis. I like how flexible ibatis is though, notice how the database calls (select, update, and insert nodes) have an attribute called parameterClass. What’s nice is that you can set the parameter class to either a typeAlias or the fully qualified Java class name. I almost always use the typeAlias, for the sake of readability, but, it’s not necessary to do so. The same goes for the result type, you can either use a resultMap defined in your SQL Map or set the resultClass attribute to a fully qualified Java class.

You may have noticed that sometimes in the select/insert/update portions of my SQLMap I use #value# to inject values into the SQL and sometime I use $value$ to inject values. The difference between the two is that using the #value# formats the value to ensure that it is SQL, for example handling apostrophes in Strings. Whereas, $value$ indicates that the value is already SQL formatted.

In the DAO all I had to provide ibatis with was the ID of the SQL statement I wanted to run, and any parameters necessary for the statement, that’s it! I love using ibatis because it makes my java piece so tiny and essentially maintenance free.

One thing to be careful of when using ibatis, is that it struggles with null values returned from the database, usually you’ll have to indicate in the resultMap what the java type is if there’s a null value returned.

Ibatis has tons of really great features, and I’ve only scratched the surface on them here. But one of the things I like the most is if the result set fields match the java fields, you don’t have to define a resultMap, because ibatis will handle it for you.

Tagged as: ,
23
Aug/09
0

Quick-Hit: JOTM on Tomcat for LiveCycle

If you’ve tried to set up a Tomcat Server to run LiveCycle Data Services you may know that it can be quite a bit more difficult than you anticipated, especially if you’re familiar with BlazeDS.

I thought things were going well until I tried to update the data that I had received from LiveCycle. At that point I received the infamous “Unable to access UserTransaction in DataService” error.

Adobe has some great information on fixing it, available here.

I followed their advice, but it still wasn’t working. Finally, one of my friends suggested that instead of putting the jar files in the {CATALINA_HOME}/common/lib folder that we put them in our WEB-INF/lib folder. We restarted our Tomcat Server, and thankfully it worked like a charm.

19
Aug/09
3

Flex Configurations made easy

If you have worked on a project of any size using Flex and Cairngorm one of the issues that you might have encountered is how to allow the application to connect to the necessary services in different environments. For example with your local development you might connect to a Tomcat server running on your machine, but in the test environment it would connect to a different server. To solve this I used to have code that looked like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.softwarejesus.configuration.model
{
   import com.adobe.cairngorm.model.ModelLocator;

   public class OldModelLocator implements ModelLocator
   {
      private static var modelLocator : OldModelLocator;

      public var SERVICE_URL:String = "http://localhost:8080/sampleServices/messagebroker/amf";
//    public var SERVICE_URL:String = "http://devServer:8080/sampleServices/messagebroker/amf";

      public static function getInstance() : OldModelLocator
      {
         if ( modelLocator == null ) {
            modelLocator = new OldModelLocator;
         }

         return modelLocator;
      }
   
      public function OldModelLocator()
      {
         if ( modelLocator != null )
            throw new Error( "Only one ModelLocator instance should be instantiated" );
      }

   }
}

You’ll notice that I have different endpoints commented out in my ModelLocator. In order to create a Test build, I would un-comment the second line and rebuild the application, but I was never entirely happy with that solution.
What some coworkers and I came up with, was creating a simple configuration XML that would reside in the same directory as the generated SWF. At startup, the application would load any environment specific constants before any real work began.
Now, how did this impact my code? The only major deviation from the standard Cairngorm pattern is that we had to wait to add the Services to our application until after the application had received confirmation that the configuration has been loaded.
As you’ll see here my Services.mxml code looks exceptionally ordinary.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
<ServiceLocator xmlns="com.adobe.cairngorm.business.*" xmlns:mx="http://www.adobe.com/2006/mxml">
   <mx:Script>
      <![CDATA[
         import com.softwarejesus.configuration.model.ConfigModelLocator;

         [Bindable]
         private var serviceUrl : String = ConfigModelLocator.getInstance().SERVICE_URL;
      ]]>
   </mx:Script>
   <mx:RemoteObject
      id="sampleService"
      destination="sampleService"
      showBusyCursor="true"
      endpoint="{serviceUrl}"/>
   
</ServiceLocator>

Here is how I’ve written my ModelLocator, notice that when I instantiate the model, I retrieve the configuration:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.softwarejesus.configuration.model
{
   import com.adobe.cairngorm.control.CairngormEvent;
   import com.adobe.cairngorm.control.CairngormEventDispatcher;
   import com.adobe.cairngorm.model.ModelLocator;

   import flash.events.Event;
   import flash.events.IOErrorEvent;
   import flash.net.URLLoader;
   import flash.net.URLRequest;

   import mx.controls.Alert;

   public class ConfigModelLocator implements ModelLocator
   {
      private static var modelLocator : ConfigModelLocator;
      private static const CONFIG_FILE_NAME:String = "config.xml";

      public var SERVICE_URL:String;

      public static const CONFIG_LOAD_COMPLETE:String = "ConfigLoaded";

      public static function getInstance() : ConfigModelLocator
      {
         if ( modelLocator == null ) {
            modelLocator = new ConfigModelLocator;
            modelLocator.loadXML();
         }

         return modelLocator;
      }
   
      public function ConfigModelLocator()
      {
         if ( modelLocator != null )
            throw new Error( "Only one ModelLocator instance should be instantiated" );
      }

      public function loadXML():void {
         var configPath:String = CONFIG_FILE_NAME;        
         var loader:URLLoader = new URLLoader(new URLRequest(configPath));
         loader.addEventListener(IOErrorEvent.IO_ERROR, handleIOError);
         loader.addEventListener(Event.COMPLETE, handleXML);    
 
      }

      private function handleXML(event:Event):void
      {
         var xml:XML = new XML((event.currentTarget as URLLoader).data);
         SERVICE_URL  = xml.system.serviceUrl;

         CairngormEventDispatcher.getInstance().dispatchEvent(new CairngormEvent(CONFIG_LOAD_COMPLETE));
      }
       
      private function handleIOError(event:Event):void {
         Alert.show("Unable to open config.xml. Please contact the your IT representative");
      }        

   }
}

Tying it all together, in my Application file, we I initialize the app, I retrieve the model, and add a listener for when it has completed the configuration process. Once the configuration complete event has been dispatched, I know I can now add the services and get to work making Service Calls

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" initialize="doInit()">
   <mx:Script>
      <![CDATA[
         import com.adobe.cairngorm.control.CairngormEventDispatcher;
         import com.softwarejesus.configuration.services.Services;
         import com.adobe.cairngorm.control.CairngormEvent;
         import com.softwarejesus.configuration.model.ConfigModelLocator;

         private function doInit():void {
            ConfigModelLocator.getInstance();    
            CairngormEventDispatcher.getInstance().addEventListener(ConfigModelLocator.CONFIG_LOAD_COMPLETE, addServices);
         }

         private function addServices(event:CairngormEvent):void {
            var serv:Services = new Services();
            addChild(serv);
         }
      ]]>
   </mx:Script>
</mx:Application>

I found this solution to be perfectly suited to address an issue I ran into on another project. In that project, the client was a manufacturer of goods, and had distributors that sold his products. Initially it was set up so that when someone logged in they could browse through the entire catalog of products. A couple of months in, he told me that each of his distributors had a finite set of products they could sell, and that none of them could sell all of the products. As a result, the distributors could not show their customer’s the app, for fear that the customer would decide to purchase something they were not authorized to sell.

The solution I came up with was to host the web services, and all the assets (and believe me, the number of assets was disgusting) centrally, and develop a swf that could be given to the distributors to host on their respective websites. Along with the swf, the customer was also given a config.xml that would determine which products were available through the application. This solution allowed me to introduce brand new functionality and all I had to do was implement one new service call.

Source