How To Save BlackBerry Settings in The Persistent Store

One of the options the BlackBerry API provides for persisting information across device resets is the Persistent Store.  As the Persistent Store supports saving to the smartphone’s flash memory, but not to the SD card, it is a good choice for storing data that does not require large amounts of space, such as application settings and user preferences.

Today we will create a generic Class that we can use in our BlackBerry Java applications to save and retrieve settings and user preferences from the Persistent Store.

We will also create a sample screen that uses this Class.  The screen will contain an EditField instance whose text will be retrieved and saved to the Persistent Store.  This is how the screen will look like:

Let’s get started.

The DataContext Class as a Persistent Store Wrapper

The DataContext Class will encapsulate our interactions with the Persistent Store, allowing us to save and retrieve application settings.

We can start with the private members:

class DataContext {

    private PersistentObject persistentObject;
    private Hashtable settingsTable;

    // Continue implementation...

}

The only private members of DataContext are settingsTable and persistentObject.  The settingsTable variable is a hashtable that will contain our application’s settings.  The persistentObject variable is an instance of the PersistentObjec Class that we will use to save the settings hashtable to the Persistent Store.

With this in mind, let’s continue and define DataContext’s constructor:

public DataContext() {    

    // Hash of examples.persistentstore.
    persistentObject = PersistentStore.getPersistentObject(0xc8027082ac5f496cL);

    synchronized(persistentObject) {

        settingsTable = (Hashtable)persistentObject.getContents();
        if (null == settingsTable) {
            settingsTable = new Hashtable();
            persistentObject.setContents(settingsTable);
            persistentObject.commit();
        }
    }

}

Persistent objects are instances of the PersistentObject Class – in the form of key-value pairs – that can be committed to the persistent store and later retrieved.  Applying this concept, in the constructor we create our persistentObject instance using the static method getPersistentObject(long key).  We provide a hash of “examples.persistentstore” as the unique key that identifies this object as belonging to our application.

As shown in the following screenshot, this hash is created using the “Convert to long” context menu while highlighting the project’s package name.

Once we obtain the PersistentObject reference, we proceed to retrieve our settings table.  If the settings table has never been saved, we put an empty table in the persistent object using persistentObject.setContents(object contents).  This change takes place when we call persistentObject.commit().

With the constructor in place, we can implement the methods that will allow us to save and retrieve specific settings.  These operations are so simple that they do not require an explanation:

public Object get(String key) {

    return settingsTable.get(key);

}

public void set(String key, Object value) {

    settingsTable.put(key, value);

}

public void commit() {

    persistentObject.commit();

}

Using the Persistent Store Wrapper in a BlackBerry Application

Let’s now create a sample screen that uses the DataContext Class.  As I already mentioned, this screen will contain an EditField instance whose text will be retrieved and saved to the Persistent Store using the DataContext Class.

class HomeScreen extends MainScreen {

    private EditField homepageEditField;

    private MenuItem saveMenu = new MenuItem("Save", 100, 100) {
        public void run() {

            Screen screen = UiApplication.getUiApplication().getActiveScreen();
            try {
                screen.save();
            } catch (java.io.IOException ex) {
                Dialog.inform("Could not save settings.");
            }
            screen.close(); 

        }
    };

    public HomeScreen() {

        super();

        this.setTitle("Persistent Store Example");

        DataContext dataContext = new DataContext();        

        homepageEditField = new EditField("Home page: ",(String)dataContext.get("HomePage"),256,EditField.FIELD_RIGHT);
        this.add(homepageEditField);

    }

    protected void makeMenu(Menu menu, int instance) {

        super.makeMenu(menu, instance);

        menu.add(saveMenu);
    }

    public void save() throws java.io.IOException {

        DataContext dataContext = new DataContext();

        dataContext.set("HomePage",homepageEditField.getText().trim());
        dataContext.commit();

    }
}

There are two places in the screen where the DataContext Class is used.  The first place is the constructor, where we call dataContext.get(key) to retrieve the text for the homepage EditField.  The second place is the save() method.  There we use dataContext.set(key, value) and dataContext.commit() to save the edited value back to the the Persistent Store.

Conclusion

I hope you find this approach useful when working with the Persistent Store in your BlackBerry applications.

Do you use a method like this to work with the Persistent Store?

Using Backgrounds and Borders in BlackBerry Super Apps

The use of field background colors and rounded borders is a great way to enhance the user experience in your BlackBerry applications.

Let’s examine how to create a screen with a nice linear background, and a rounded border for the region that contains the input fields, as shown in the following picture:

Setting a field’s background color in a BlackBerry application

We will use the setBackground family of functions of the Field Class to specify a field’s background for the different states of the field; and we will use the BackgroundFactory Class to create the type of background we need (BackgroundFactory provides solid, linear and bitmap backgrounds).

To set the background of our screen, we set the background of its main manager like so:

class MyScreen extends MainScreen {

    public MyScreen() {

        this.setTitle("My Screen");

        // Set the linear background.
        this.getMainManager().setBackground(
            BackgroundFactory.createLinearGradientBackground(0x0099CCFF,
            0x0099CCFF,0x00336699,0x00336699)
        );

    }
}

Now it’s time to work on the rounded border.

Creating fields with rounded borders

The BorderFactory Class has a variety of functions that create different types of borders.  We will use the createBitmapBorder function to specify the rounded border for the region that contains the input fields on the screen:

class MyScreen extends MainScreen {

    public MyScreen() {

        this.setTitle("My Screen");

        // Linear background goes here...        

        // Create a vertical field manager with a rounded border.
        Bitmap borderBitmap = Bitmap.getBitmapResource("rounded.png");
        VerticalFieldManager m = new VerticalFieldManager();
        m.setBorder(
            BorderFactory.createBitmapBorder(
                new XYEdges(12,12,12,12), borderBitmap
            )
        ); 

    }
}

Here’s the rounded.png image used for the border bitmap:

With the background and border ready, we can add fields to the vertical field manager:

class MyScreen extends MainScreen {

    public MyScreen() {

        this.setTitle("My Screen");

        // Set the linear background.
        this.getMainManager().setBackground(
            BackgroundFactory.createLinearGradientBackground(0x0099CCFF,
            0x0099CCFF,0x00336699,0x00336699)
        );        

        // Create a vertical field manager with a rounded border.
        Bitmap borderBitmap = Bitmap.getBitmapResource("rounded.png");
        VerticalFieldManager m = new VerticalFieldManager();
        m.setBorder(
            BorderFactory.createBitmapBorder(
                new XYEdges(12,12,12,12), borderBitmap
            )
        ); 

        java.util.Date today = new java.util.Date();
        DateField dateField = new DateField("Date:",
            today.getTime(),DateField.DATE | DrawStyle.LEFT);
        m.add(dateField);
        m.add(new SeparatorField());

        AutoTextEditField projectField = new AutoTextEditField("Project:",
            "",1024,EditField.FILTER_DEFAULT);
        m.add(projectField);
        projectField.setBorder(fieldBorder);
        m.add(new SeparatorField());

        EditField hoursField = new EditField("Hours:",
            "",4,EditField.FILTER_DEFAULT);
        m.add(hoursField);
        hoursField.setBorder(fieldBorder);
        m.add(new SeparatorField());

        AutoTextEditField notesField = new AutoTextEditField("Notes:",
            "",1024,EditField.FILTER_DEFAULT);
        notesField.setBorder(fieldBorder);
        m.add(notesField);

        add(m);

    }
}

Conclusion

This has been a simple example of how to use backgrounds and borders to enhance the user experience in your BlackBerry applications.  (You can find more information in the BlackBerry Support Forums.)

Is this something you do in your applications?  What approaches do you follow?

Debug Server Code of a BlackBerry Application with Visual Studio

This is how you debug the .NET server code that handles your BlackBerry application’s requests. You can set breakpoints in the server page or module and have them hit when the BlackBerry simulator makes a request to the server. The steps below use my end-to-end BlackBerry application as an example.

  1. Open the solution with Visual Studio and set the web application, KowledgeBase.Web.UI in this case, as the startup project.
  2. Open the web project’s properties, Web tab. Set the handler (BBHandler.ashx) as the start page.
  3. Set breakpoints in the handler’s code and start the debugger


  4. Browse to the handler page. See how the breakpoint is hit. Press F5 to go past the breakpoints and allow the ProcessRequest function to return. Now the debugger is attached and you can move on to the BlackBerry project.

  5. Start the BlackBerry MDS simulator.
  6. Start debugging your BlackBerry project in your favorite IDE. Make sure the BlackBerry application is pointing to your server project’s URL. You can grab this URL from your browser’s address bar, since VS pointed the browser to it.
  7. Now you can exercise the functionality that talks to the server. You should be able to step through the server code once you hit the first breakpoint in it.

How to Add a Simulator to the BlackBerry JDE

I am currently fixing a problem with one of our BlackBerry applications. The problem only shows on the 8330 devices and while trying to debug it, I realized that the 8330’s simulator was not showing in the JDE’s list of available simulators.

In this post I will explain what I did to add the 8330’s simulator to the list of available profiles.

After downloading the 8330 simulator pakage from the BlackBerry developers site and installing it, I opened the SimPackage-JDE.rc file located in the JDE installation folder (You can open it with a text editor). This file, which contains a couple of entries per simulator profile, is the settings file used by the JDE to run the different simulators. This is how the entries look:

## RIM Java Development Environment
# Settings file

SimulatorCommand7130-JDE=<install_dir>\fledge.exe /app=Jvm.dll /handheld=7130 /session=7130 /app-param=DisableRegistration /app-param=JvmAlxConfigFile:7130.xml /data-port=0x4d44 /data-port=0x4d4e /pin=0x2100000A

SimulatorDirectory7130-JDE=<install_dir>
SimulatorCommand7130e-JDE=<install_dir>\fledge.exe /app=Jvm.dll /handheld=7130e /session=7130e /app-param=DisableRegistration /app-param=JvmAlxConfigFile:7130e.xml /data-port=0x4d44 /data-port=0x4d4e /pin=0x2100000A
SimulatorDirectory7130e-JDE=<install_dir>
SimulatorCommand8100-JDE=<install_dir>\fledge.exe /app=Jvm.dll /handheld=8100 /session=8100 /app-param=DisableRegistration /app-param=JvmAlxConfigFile:8100.xml /data-port=0x4d44 /data-port=0x4d4e /pin=0x2100000A
SimulatorDirectory8100-JDE=<install_dir>

As you can see, each simulator’s settings consist of an entry that specifies the command used to run the simulator and an entry that specifies the folder where the simulator resides.

Knowing this, I created two more entries form my just-installed simulator. In this case, instead of using the <install_dir> placeholder, I used the actual path to the simulator. I also used the command line parameters for the 8330 simulator, which I grabbed from the 8330.bat file in C:\Program Files\Research In Motion\BlackBerry Smartphone Simulators 4.3.0\4.3.0.124 (8330):

SimulatorCommand8800-JDE=”C:\Program Files\Research In Motion\BlackBerry Smartphone Simulators 4.3.0\4.3.0.124 (8330)\fledge.exe /app=Jvm.dll” /handheld=8330 /session=8330 /app-param=DisableRegistration /app-param=JvmAlxConfigFile:8330.xml /data-port=0x4d44 /data-port=0x4d4e /pin=0x2100000A
SimulatorDirectory8800-JDE=”C:\Program Files\Research In Motion\BlackBerry Smartphone Simulators 4.3.0\4.3.0.124 (8330)”

After doing this, the 8330’s profile showed in the JDE’s simulators list:

How to Set a Rollover Icon for Your BlackBerry Application

In this post I will explain the different methods you can use to set the rollover icon that your BlackBerry application will display on the device’s Home Screen.

Accompanying sample code: BB_Rollover_Icon_Sample

First, get the right icon size and resolution for your device and theme

You have probably observed that the size of the application’s icon is not the same across device models and themes. This is a consequence of the different screen sizes and evolving graphic capabilities of the smartphones.

The following table lists the different device models and common icon sizes (An up-to-date version of this information can be obtained from the BlackBerry developers Developers Knowledge Base).

ModelIcon Dimensions
BlackBerry® 6200 Series and BlackBerry® 6500 Series28 x 28
BlackBerry® 7100 Series and BlackBerry® Pearl™ 8100 SeriesIcon theme: 60 x 55
Today and Zen theme: 48 x 36
BlackBerry® 7200 Series and BlackBerry® 7700 Series32 x 32
BlackBerry® 7290 smartphone36 x 36
BlackBerry® Curve™ 8300 Series, BlackBerry® 8700 Series, and BlackBerry® 8800 SeriesIcon theme: 53 x 48
Zen theme: 48 X 36
BlackBerry® Bold™ 9000 smartphonePrecision theme: 80 X 80

If you intend to use the same icon on multiple BlackBerry smartphone models, you need to understand how this icon will be displayed. These are the scenarios you will encounter.

  • On smartphones running BlackBerry Device Software prior to version 4.2, icon images that are larger than the maximum dimensions are cropped.
  • On smartphones running BlackBerry Device Software version 4.2 to 4.5, if you use an icon with dimensions larger than those displayed on the smartphone, the icon will be scaled down to the device’s maximum icon size. If scaling occurs on an image that is not square and the result is an image that is square (or vice versa), the image may appear distorted.
  • On smartphones running BlackBerry Device Software version 4.5 or earlier, if you use an icon of the smallest dimensions, it will not be scaled up on the smartphone with a higher resolution. The actual image size will be the same and the remainder of the icon area will be transparent, so the icon will appear smaller than the standard icons.
  • On devices with Device Software 4.6, icons are scaled in the application grid menu to match the icons of the current theme. If your icon is larger than the current theme icon, the icon is scaled down to match. If it is just marginally smaller, it will be left un-scaled. If it is 25% (or more) smaller, it will be scaled up to match the current theme icon.

Specifying rollover icons in versions of the BlackBerry JDE prior to 4.7

The ability to add a rollover icon to an application was introduced in the BlackBerry API 4.1 set through the function HomeScreen.setRolloverIcon(…). This function  sets the icon to use when the application icon is in focus.

Since you need the icon in place before the device user scrolls to your application, the function must be called at startup time. You can achieve this by defining an alternate entry point to the application. This entry point will run on startup and will produce the call to HomeScreen.setRolloverIcon(…).

In order to allow the application to identify when it was launched by the user from the ribbon and when it was started from the alternate entry point, you will configure the alternate entry to pass in an argument to the main() method. If main() encounters this argument, it will proceed to set the rollover icon and exit the application afterwards. In the absence of the argument, the assumption is that the user launched the application from the ribbon and the application should start normally.

The main() method would look like this

public static void main(String[] args)
{
   if ( args != null && args.length > 0 && args[0].equals("alternate") ){
      //  Alternate entry point 
      Bitmap icon = Bitmap.getBitmapResource("rollover_icon.png");
      HomeScreen.setRolloverIcon(icon);
   } else {
      //  Main entry point 
      Test theApp = new Test();
      theApp.enterEventDispatcher();
   }
}

Now, there’s an issue in BlackBerry Device Software 4.1  where the HomeScreen.setRolloverIcon(…) method can throw an IllegalArgumentException. This issue has been resolved in BlackBerry Device Software 4.2. You can work around this issue by allowing the application to finish its startup before setting the rollover icon. In code, it would look like this

public class RolloverIconApp extends UiApplication
{
    public RolloverIconApp(boolean autoStart)
    {
        if (autoStart)
        {
        // If the application started using the auto start 
        // entry point, we setup the our icons.
        final Bitmap regularIcon = Bitmap.getBitmapResource("unfocused_icon.png");
        final Bitmap rolloverIcon = Bitmap.getBitmapResource("rollover_icon.png");
        invokeLater(new Runnable()
        {
            public void run()
            {
                ApplicationManager myApp =
                  ApplicationManager.getApplicationManager();
                boolean inStartup = true;
                while (inStartup)
                {
                    if (myApp.inStartup())
                    {
                        // Wait for the device to finish the startup process.
                        try
                        {
                            Thread.sleep(1000);
                        }
                        catch (Exception ex)
                        {
                          // TODO: handle exception.
                        }
                    }
                    else
                    {
                        HomeScreen.updateIcon(regularIcon, 0);
                        HomeScreen.setRolloverIcon(rolloverIcon, 0);
                        inStartup = false;
                    }
                 }
                 // We're done setting up the icons. Exit the app.
                 System.exit(0);
            }
        });
    }
    else
    {
         // The application was started by the user, display the UI.
         MainScreen ms = new MainScreen();
         ms.setTitle("Did you see the rollover icon?");
         pushScreen(ms);
    }
  }
    public static void main(String[] args)
    {
        RolloverIconApp theApp;
        //Check for the argument defined in the project properties.
        if (args != null && args.length > 0 && args[0].equals("autostart"))
        {
            theApp = new RolloverIconApp(true);
        }
         else
        {
            theApp = new RolloverIconApp(false);
        }
        theApp.enterEventDispatcher();
    }
}

Specifying rollover icons in the BlackBerry JDE 4.7

As opposed to previous versions, the BlackBerry JDE 4.7 allows you to specify a rollover icon via the project’s properties. These are the steps you need to follow.

  1. In BlackBerry JDE, right-click the project.
  2. Select Properties.
  3. Select the Resources tab.
  4. Add the non rollover/unfocused application icon in the Icon Files field and add the rollover/focused icon in the Focus Icon Files field.

Setting rollover icons in the BlackBerry JDE Plug-in for Eclipse™

While this option is always present in the BlackBerry® JDE Plug-in for Eclipse™ it will only take effect when the application is built using the BlackBerry JDE Component Package 4.7.0 or later.

  1. In the Eclipse™ Package Explorer, right-click the project.
  2. Select Properties.
  3. Select BlackBerry Project Properties.
  4. Select the Resources tab.
  5. Add the non-rollover/unfocused application icon in the Icon Files field and add the rollover/focused icon in the Focus Icon Files field.

Conclusion

This pretty much covers the information available on the rollover icons topic as of the date of this post. Remember to check the sample project: BB_Rollover_Icon_Sample

Mobile Development Tips: Preventing Unbounded Result Sets

An issue that too many times goes overlooked is the possibility that database queries return more data (rows) than what your application can handle.

It is typical for applications to send a query to the database and process each row of the result set. Often times, this processing is accompanied by the creation of a data structure (list, collection, etc.) that holds a one or more business objects derived from the query results.

Unbounded result sets are a threat to your mobile application

Do you know what would happen if your mobile application sent a query to the database and received a million rows instead of a hundred? Or if it made a request to a web service and received a collection with ten thousand objects?  A cause of countless problems on application servers, unbounded result sets are also a threat to your mobile applications. Given the memory and processing constraints in mobile devices, paying attention to this issue is of particular importance.

Result sets without bounds are more likely to occur when your application makes a request without specifying what type of response it can accept and how much of it can process. You are also asking for trouble when your application blindly trusts the systems it calls, database server included.

An ounce of prevention…

So, what are some of the measures you can take to prevent the occurrence of unbounded result sets?

Limit the number of retrieved rows. If you have control over the data access code, limit the number of rows you retrieve in your queries. Remember Murphy’s_Law. Even when you know that a table will never have more than fifty rows, write your SQL code so it retrieves no more than fifty rows.

-- MySQL
select client_id, client_name from clients
limit 50

Limit the number of elements in retrieved collections. Design your web services routines so they accept a parameter indicating the maximum number of elements you are prepared to process.

int maxNumberOfClients = 50;
List<Client> clients = service.GetClients(maxNumberOfClients);

Break out of loops. When looping over elements of a result set or a collection, break out when you reach the maximum number of elements you can process. Although this measure does not avoid the retrieval of all the results, it stops the pernicious effects from propagating to other areas of your application.

int clientsCount = 0;
int maxClients = 50;
foreach (DataRow clientRow in clientsTable.Rows) {
    Client client = Client.FromDataRow(clientRow);
    clientsList.Add(client);
    clientsCount ++;
    if (maxClients == clientsCount ) break;
}

Conclusion

I hope you find this helpful. As usual, let me know your thoughts!

Optimized HTTP Handling for Your BlackBerry Java Application

In this post I’m going to explore some examples of the approaches to reading HTTP responses in your BlackBerry Java application and how they can impact the application’s performance.

I realize that the performance of your HTTP-handling routines might not impose critical limitations to your application. But if you are considering ways to optimize these routines, I encourage you not only to take a look at these approaches, but also measure their performance characteristics in the context of your application.

The first approach, which I have successfully used in a commercial application, consists of reading the contents of the HTTP response one character at a time. Although slow, this approach is attractive because it is simple and does not require any HTTP-specific behavior from your code:

StreamConnection c = null;
InputStream s = null;
try {
    c = (StreamConnection)Connector.open(url);
    s = c.openInputStream();
    int ch;
    StringBuffer response = new StringBuffer();
    while ((ch = s.read()) != -1) {
        response.append((char)i);
    }
} finally {
    if (s != null)
        s.close();
    if (c != null)
        c.close();
}

Reading the response data in bulk is a way to gain efficiency. However, this code relies on the underlying connection being able to provide the length of the response:

ContentConnection c = null;
DataInputStream dis = null;
try {
    c = (ContentConnection)Connector.open(url);
    int len = (int)c.getLength();
    dis = c.openDataInputStream();
    if (len > 0) {
        byte[] data = new byte[len];
        dis.readFully(data);
    } else {
        int ch;
        while ((ch = dis.read()) != -1) {
            ...
        }
    }
} finally {
    if (dis != null)
        dis.close();
    if (c != null)
        c.close();
}

A version of the previous approach that I use in my KnowledgeBase application is this:

HttpConnection c = null;
InputStream in = null;
String response = null;
try {
    c = (HttpConnection)Connector.open(url)
    in = c.openInputStream();
    int len = (int)c.getLength();
    if (len > 0) {
      int actual = 0;
      int bytesread = 0 ;
      byte[] data = new byte[len];
      while ((bytesread != len) && (actual != -1)) {
        actual = in.read(data, bytesread, len - bytesread);
        bytesread += actual;
      }
      response = new String(data);
    }
}

While the methods above produce a byte array and thus require a parsing or conversion step – omitted from the sample code – in order to deserialize the data, if your application has intimate knowledge of the response’s contents, you can use specialized methods of the DataInputStream class to read and deserialize the different values in the response in one step:

HttpConnection c = null;
DataInputStream is = null;
int rc;
try {
    c = (HttpConnection)Connector.open(url);
    rc = c.getResponseCode();
    if (rc != HttpConnection.HTTP_OK) {
        throw new IOException("HTTP response code: " + rc);
    }
    is = (DataInputStream)c.openInputStream();
    String lastName = i.readUTF();
    String firstName = i.readUTF();
    int numberOfOrders = i.readInt();
    boolean isActive= i.readBoolean();
} catch (ClassCastException e) {
    throw new IllegalArgumentException("Not an HTTP URL");
} finally {
    if (is != null)
        is.close();
    if (c != null)
        c.close();
}

As you can see, the performance gains have a cost in the amount of complexity of your code and the knowledge of the response contents that your application should have. You will find that this is true for the approaches above as well as other approaches that exist. When deciding on one, consider the tradeoffs and always measure your gains before making a decision.

End-To-End BlackBerry Application (Part 6)

In this sixth delivery of my end-to-end BlackBerry application walk-through, I will cover the following topics:

  • Creating a .NET http handler to send and receive information from the handheld application
  • Consuming the server-side business logic services from our http handler
  • Making requests from the handheld application and receiving data

While the previous articles of this series dealt with creating the Java application that will be installed on the handheld device, today I will be working on the server-side modules. Let’s take another look at our basic building blocks:

The server-side modules consist of a .NET HTTP handler, a set of class libraries that contain the business logic and data access code, and a SQL Server database that will serve as the articles repository of our Knowledge Base application.

Creating The HTTP Handler

The mission of  the HTTP Handler is to extract the information from the HTTP requests made from the handheld, determine what type of action is required and ask the business logic layer to execute such actions. It will also append the results produced by the business logic to HTTP responses and send them to the handheld application.

In order for the handheld application and the handler to be able to talk to each other, all the commands that the handheld application can possibly send need to be defined within the handler as they were defined in the handheld:

// The commands the handheld can send us.
private const int CMD_SEARCH_ARTICLES = 1;
private const int CMD_GET_ARTICLES_BY_TAG = 2;
private const int CMD_GET_TAG_COUNTS = 3;

The keys identifying  each of the request parameters need to be defined as well:

// The request keys.
private const string KEY_COMMAND = "cmd";
private const string KEY_SEARCH_PHRASE = "sp";
private const string KEY_TAG = "tg";

KEY_SEARCH_PHRASE identifies the request parameter carrying the search phrase when a request to search the existing articles is made. KEY_TAG identifies the parameter carrying the tag name when the request is to retrieve the articles for a given tag.

Determining what to do is a matter of inspecting the request parameter marked with the KEY_COMMAND key:

string commandString = request.Form[KEY_COMMAND];
if (!int.TryParse(commandString, out currentCommand))
{
    // Bail if we don't receive a numeric command.
    return;
}
articlesServer = Shared.GetArticlesServer(context);
switch (currentCommand)
{
    case CMD_SEARCH_ARTICLES:
        string searchPhrase = request.Params[KEY_SEARCH_PHRASE];
        responseString = SearchArticles(searchPhrase);
        break;
    case CMD_GET_ARTICLES_BY_TAG:
        string tag = request.Params[KEY_TAG];
        responseString = GetArticlesForTag(tag);
        break;
    case CMD_GET_TAG_COUNTS:
        responseString = GetTagCounts();
        break;
    default:
        return;
}

Consuming The Business Logic Services

I won’t go into the details of the construction of the business logic and data access layers today, since they were covered in my End-to-end ExtJS application series. Something noteworthy, though, is the fact that these layers support storage of the articles in a file or in a SQL Server database. The code download for today’s article defaults to file-based storage, but you can run the included SQL script to install the database and point the business layer to it by changing the web application’s configuration file.

In the HTTP handler, SearchArticles(), GetArticlesForTag() and GetTagCounts() simply forward the requests to the business logic layer:

private string SearchArticles(string searchPhrase)
{
    Debug.Assert(searchPhrase != null);
    if (null == searchPhrase)
    {
        ExceptionPolicy.HandleException(
                new ArgumentNullException("searchPhrase"),
                Shared.KB_EXCEPTION_PLCY);
    }
    string result = string.Empty;
    try
    {
        List<IArticle> articles =
            articlesServer.GetArticlesForSearchPhrase(searchPhrase);
        if (null == articles) return
                string.Empty;
        result = SerializeArticlesList(articles);
    }
    catch (Exception ex)
    {
        ExceptionPolicy.HandleException(ex, Shared.KB_EXCEPTION_PLCY);
    }
    return result;
}
private string GetArticlesForTag(string tag)
{
    Debug.Assert(tag != null);
    if (null == tag)
    {
        ExceptionPolicy.HandleException(
            new ArgumentNullException("tag"),
            Shared.KB_EXCEPTION_PLCY);
    }
    string result = string.Empty;
    try
    {
        List<IArticle> articles = articlesServer.GetArticlesForTag(tag);
        if (null == articles) return
                string.Empty;
        result = SerializeArticlesList(articles);
    }
    catch (Exception ex)
    {
        ExceptionPolicy.HandleException(ex, Shared.KB_EXCEPTION_PLCY);
    }
    return result;
}
private string GetTagCounts()
{
    StringBuilder sb = new StringBuilder("");
    try
    {
        Dictionary<string, int> tagCounts = articlesServer.GetTagCounts();
        if (null != tagCounts && tagCounts.Count > 0)
        {
            foreach (KeyValuePair<string, int> tagStat in tagCounts)
            {
                sb.Append(string.Format("{0}^~^{1}~^~",
                    tagStat.Key,
                    tagStat.Value));
            }
        }
    }
    catch (Exception ex)
    {
        ExceptionPolicy.HandleException(ex, Shared.KB_EXCEPTION_PLCY);
    }
    return sb.ToString();
}

After the business logic results are delivered to the handler I need to serialize the results according the formatting convention I had established when I wrote the handheld code. For example, this is the routine that converts a list of articles into a string that will be sent to the handheld as part of the HTTP response:

private string SerializeArticlesList(List<IArticle> articles)
{
    Debug.Assert(articles != null);
    if (null == articles)
    {
        ExceptionPolicy.HandleException(
            new ArgumentNullException("articles"),
                Shared.KB_EXCEPTION_PLCY);
    }
    StringBuilder sb = new StringBuilder("");
    string body;
    foreach (IArticle article in articles)
    {
        // Decode the html characters in the body of the article.
        body = HttpUtility.HtmlDecode(HttpUtility.UrlDecode(article.Body));
        // Strip the html characters (We're using a RichTextField on the handheld,
        // which isn't capable yet of displaying html.
        body = Regex.Replace(body, @"<(.|\n)*?>", string.Empty);
        sb.Append(string.Format("{0}^~^{1}^~^{2}^~^{3}^~^",
            article.Id.ToString(),
            article.Title,
            article.DateCreated.ToShortDateString(),
            article.Author));
        sb.Append(string.Format("{0}^~^{1}~^~",
            article.Tags, body));
    }
    return sb.ToString();
}

Did you notice that in the code above I’m also removing the Html characters from the body of the article? I have to do this because the field I’m using to display the body of the article on the handheld is not capable of rendering Html.

All that’s left now is to send the data to the handheld:

response.ContentType = "text/plain";
int contentLength = response.Output.Encoding.GetByteCount(responseString);
response.AppendHeader("content-length", contentLength.ToString());
response.Write(responseString);
response.End();

Well,  I know the walk through the server-side code went pretty fast. I encourage you to download the code for today’s post and check it out to see all the details. Also, reading the End-to-end ExtJS application post can give you more insight on the business logic and data access layers.

Making Requests From The Handheld Application And Receiving Data

Let’s return to the device-side code and ready it to connect to the HTTP handler. The first thing I will do is stop using the MockHTTPTransport instance and start using KbHTTPTransport. While MockHTTPTransport simply simulates an HTTP request to allow for testing of all the rendering logic on the device, KbHTTPTransport is the real deal. This is how I use it from the Articles Screen or the Tags Screen:

private void sendHttpRequest(String[] keys, String[] values) {
   articlesList.set(null);  // Show "No Articles" while we download.
   String url = DataStore.getAppServerUrl();
   // Make sure we can create an http request.
   // If the app. server URL has not been set, we cannot make any http requests.
    if (null == url || url.length() == 0) {
        Dialog.alert(resources.getString(
            KnowledgeBaseResource.ERR_INVALID_APP_SERVER_URL));
        return;
    }
    statusDlg.show();
    byte[] requestContents = TransportUtils.createPostData(keys, values);
    KbHTTPTransport transport = new KbHTTPTransport();
    transport.setHTTPTransportListener(this);
    transport.send(KbHTTPTransport.REQ_METHOD_POST, url, requestContents);
}

At this point all the pieces are in place and I’m going to use the BlackBerry simulator and MDS simulator to test the application with real data. After firing up both simulators, I will check the Options Screen and make sure that I’m pointing to the right URL for my HTTP handler:

Back to the Main Screen, I choose Browse Tags:

And here I am getting real data from the handler:

Selecting a tag should bring back some articles:

And selecting an article should display its contents:

Conclusion

I hope you’ve found this series of posts helpful. I think the application is at a reasonable quality level where you can use its blocks as a foundation for your own projects. Although I’ll move on into other topics, I’m willing to come back to work on or talk more about any areas of your interest. Please let me know your thoughts.

Downloads

Download the source code for this article: KnowledgeBaseBBAndWeb-7-15-08.zip