http://www.developer.com/

Back to article

Building a J2ME Application in NetBeans 4.1


August 11, 2005

This article will show you how to use NetBeans 4.1 and the Mobility Pack to write a Java Mobile Edition application quickly and easily. It will demonstrate the simple GUI creation and storage facilities available to a mobile application developer, but will not delve into the online connection capabilities at this point (I will instead save that for the future).

The application I will demonstrate will be a simple vehicle mileage calculator and tracker, allowing you to use a mobile phone or other Java-enabled device to track your average fuel consumption and cost per mile. It is deliberately kept simple and I will note any simplifying assumptions made as the application is presented. It is, of course, entirely up to you if you want to extend this code into something more detailed and useful.

The toolset used for this demonstration is NetBeans 4.1 with the Mobility Pack, both of which are freely available for download from http://www.netbeans.org.

The full project source for this article is available for download from this link.

Getting Started

Before you can start building the application, you will need to get NetBeans 4.1 and the Mobility Pack if you don't already have them.

Both the NetBeans IDE and the Mobility Pack can be downloaded from http://www.netbeans.info/downloads/download.php?type=4.1.

Optionally, enter your e-mail address or uncheck the checkboxes, and click Next to get to the download area.

Be sure to read the Installation Instructions and Mobility Pack release notes using the links on the download page.

Create a New Project

  1. New Project->Mobile->Mobile Application
  2. Project Name: MileageCalculator
  3. Set the project location to where you want it on the disk.
  4. Uncheck the "Create Hello MIDlet" option.
  5. Click Next.
  6. For this application, choose Default Color Phone, CLDC-1.0, and MIDP-1.0 options.
  7. Click Finish.

Create the Visual MIDlet

The core of our application will be a visual MIDlet, which can be thought of as a visual JavaBean for a phone. In fact, this is selling it short—the MIDlet encompasses multiple forms and other GUI views, navigation information between these GUI parts, lifecycle methods for the application, as well as the usual event handlers, methods, and properties.

To create your MIDlet, do the following:

  1. Expand the MileageCalculator node in the Projects pane (if it is not already expanded).
  2. Right-click on the <default package> node and select New->Visual Midlet.
  3. Set the MIDlet name to MileageMidlet, and the MIDP Class Name to the same.
  4. Ignore the MIDlet Icon unless you have a really nice icon to use already (creating icons is beyond the scope of this article).
  5. For the package, type in something that makes sense. For example, I will use com.crispybits.j2me.demo.mileage.
  6. Choose MIDP Version to be MIDP-1.0.
  7. Click Finish.

You should now find yourself looking at the flow designer. There should be an icon that looks like a phone, with a Start Point and an Exit Point. This is one view of the MIDlet; there are two more selectable by buttons at the top of the main pane: Screen Design and Source. Click the Source button to see what is already in the MIDlet at present.

You should see a default constructor (empty) and three lifecycle methods: startApp(), pauseApp(), and DestroyApp(). I will come back to these later.

Creating the GUI

The first part of your application building will be the GUI. There will be three screens in your application. The "main" screen will be the summary page—where you can see the current mileage, MPG, and cost information summarized. There will also be menu options from this screen to the two others you need—an initial setup screen (to enter the vehicle's mileage when you first start using the calculator) and a screen to enter new details when you fill up.

The Summary Page

The summary page will simply give you a run down of the current details for your vehicle, including the current recorded mileage, the current MPG, and the cost per mile since you started keeping records.

Note: the "cost per mile" is simply what the gas is costing.If you want to track maintenance and other items, this application could easily be augmented to do so.

To create the summary page:

  1. Select the MileageMidlet and switch to the Flow Design view in the main pane.
  2. In the inspector pane (top right), right-click on the Screens node and select Add Form.
  3. A new form will appear in the Design view; drag it to the right of the phone icon.
  4. Right-click on the new form, and select Rename, call it SummaryForm.
  5. From the red dot next to Start Point on the phone, drag a line out to the red dot on the SummaryForm. This will make the SummaryForm be displayed as the starting form when you run the application.
  6. Double-click on the SummaryForm to go to the Screen Design for that form.

So far, so good (and nice and easy). You should now see an empty screen ready to take new widgets from the pallette (to the left of the empty form). Before you start adding components, I will take the chance to talk about GUI design in Java ME, and how it compares with desktop applications.

If you have designed a full swing GUI, you will notice some differences when using Java ME to create screens. The most obvious is that there are fewer components. Not so obvious, however, is that (at least for what we are doing) you cannot control the layout of the components as much as you might be used to in swing. The concept of different layout managers does not exist in this GUI builder, nor does it make sense if you stop and think about it.

When designing for a mobile device, you are likely to be dealing with any number of different screen sizes and configurations. Generally speaking, a Java ME application will use all of the screen, not a window within that screen, so that means that the application could be displayed at any resolution from fairly small (say 100x100 pixels for the sake of argument) up to much higher resolutions.

When you think about this, it makes sense that trying to control layout would be very hard. In fact, the device will largely control the layout based on what makes sense to it. I will put down the components you want in the order you want (using a simple flow layout for you Swing folks out there), and leave it to the device to figure out the display details.

Although it may feel a bit strange at first, this model of development is actually quite liberating after you get used to it, and you can certainly slap some GUIs together pretty fast. They might not win any beauty prizes, but they will be functional and they will work on any device (although if you pack a lot onto one screen, you might find yourself scrolling around a lot when running on lower-resolution devices).

The "device handles it" philosophy is carried over even further for the action items (like the menu). You will define the menu actions you want, and the device will figure out how to present them to the user. The likelihood is that the options will be attached as a menu on one of the device's "soft buttons," but it really is down to the device on which the application is running.

Note: If you have a particular device in mind (let's say your phone) and know the resolution, you can right-click on the screen in the Screen Designer, select "Set Screen Size," and then set it to the desired resolution. Although this won't guarantee what it will look like on your device, it will give you a better idea of how much information you can fit on the screen.
Adding the Components

To add the components to your summary screen, follow these steps:

  1. On the screen designer view, find the StringItem component in the palette and drag it over to the top of the empty screen.
  2. stringItem1 should be created. This is going to be your title, so click on the stringItem1 label and an edit box should appear. Type in Mileage Calculator to replace stringItem1.
  3. For the <Enter Text> part, select that so that you get an edit box, and then delete it.
  4. Now, add another StringItem by dragging it onto the screen. Note that when you drag it over the screen you will see a red dashed line appear to show where that component is going to go; you can use this to insert it between existing components if you like. For now, just add all components underneath the last one.
  5. The fields you are adding now will be used programmatically, so it is a good idea to rename the instance as well as changing the text. Find the Instance Name in the properties and change it to totalMileage.
  6. Following the same steps as for the first component you added, change the stringItem2 label to Total Mileage, and the <Enter Text> field to 0.
  7. Repeat adding components for:
    1. Average MPG (instance name avgMpg, label Average MPG, value 0)
    2. Average Cost/Mile (instance name avgCostPerMile, label Avg. Cost/Mile, value 0)
  8. When finished, your screen should look something like this (I have resized mine down to 128x128 as that is the size of my phone display):


  9. Click here for a larger image.

  10. Save everything.

Once you have completed these steps, you can run the application to see what it looks like. It won't do anything except show the screen you have created, but you will get to see your application run for the first time, and get an idea of how the emulator works.

Click the debug button in the toolbar; after a few seconds, a window containing a picture of a phone should appear. You should see MileageMidlet highlighted in the menu, and a label saying "Launch" over the right softkey of the phone. Click the softkey and you should see your new screen appear.

When finished, close the window (or choose exit from the menu). You may need to do this twice because the first time will quit your app, and the second will quit the emulator.

The Initialize Page

Although it will probably only be used once for your application, you need a page to set up the initial mileage on the vehicle when you start using it. This screen should show a reminder to only initialize the mileage after the vehicle has been filled with Gas (otherwise, the initial calculations will be thrown off). It should also allow the user to enter the current mileage, and reset all of the other totals to 0.

  1. On the Flow Design view, right-click on Screens in the Inspector and add a new Form.
  2. Drag it to where you want it on the Flow Design, and rename it to InitializeForm.
  3. Double-click to go to the screen designer.
  4. Add a StringItem, and set the label to "Enter Starting Mileage."
  5. For the value of the string item, type in "Reminder - you should do this only once, and after filling the gas tank!"
  6. Drag a TextField in under this notice.
  7. Change the Label to "Starting Mileage" and the Instance Name to startingMileage.
  8. Click on the ... button next to Constraints in the properties pane.
  9. Select Numeric from the popup list; this tells the component to accept only numeric input (and on a phone, makes the keys only enter numerics, saving time over having to cycle through T9 letter entry). Click OK.
  10. Save all.

Don't worry about the action code behind the scenes. You will write all that in a moment.

The Add Details Page

Finally, we need a new page to add details when you add fuel to the vehicle. This page should take the current mileage of the vehicle, along with the number of gallons and the cost of the gas:

  1. Again, from the Flow Design view, add a new Form.
  2. Rename it to AddDetailsForm.
  3. Double-click to edit the new screen
  4. Add a StringItem, change the label to "Add Details," and clear the value field.
  5. Drag a TextField in, change the instance name to currentMileage, and the Label to "Current Mileage."
  6. Change the constraints to numeric (see above).
  7. Add another Text Field, instance name gallons and label "Gallons."
  8. Add another Text Field, instance name cost and label "Cost."
  9. Remember to set the constraints for all three fields to be numeric.
  10. Save all.

Okay, you now have your three screens in the application, but no way to get between them, not to mention that there is no code at all to do anything useful.

First, hook up the screens:

Setting up Screen Navigation

Setup for navigating between screens is accomplished from the Flow Design view, so switch back to that.

Actions and navigation in a Java ME app are accomplished by creating commands, and then hooking those commands up to code and navigation events. This will make more sense after you have added a command.

The first command you will add to the summary page will be to add new details. This will be the default action, because it is the most likely thing you will want to do with your application. There are hints you can give to the device about this being a default, even though you can't directly control how the device will make the option available. In your case, you will add this command as an "OK" command in the hope that it will get a soft button of its own (again, this will be up to the device, but hopefully this hint will help).

To create your Add Details command:

  1. In the Flow Design, find the Ok Command in the palette and drag it over the top of the SummaryForm.
  2. When you drop it, you should see a new command on the right side (with a red dot next to it) called OK.
  3. You want to change the name to Add Detail, so look in the Inspector and expand the SummaryForm -> Assigned Commands node (if it is not already visible). You should see an okCommand1 under the assigned commands. Select it.
  4. In the properties, change the label to "Add Details."
  5. Now, you want the Add Details command to take you to the AddDetailsForm, so drag a line from the red dot next to the Add Details command, to the red dot to the left of the AddDetailsForm. This establishes the navigation to that form when the Add Details command is used.

You also want to get back to the main summary form from your details form, and there are two ways you might want to do this. One is to enter details and click OK, whereupon the totals and averages of the summary are updated; the other will be to cancel out of the screen (if you hit it by accident), in which case you should return without making any changes.

To add these two cases:

  1. Drag an Ok Command and then a Cancel Command from the palette onto the AddDetailsForm.
  2. Both of these commands can be left with the default labels because they are descriptive enough.
  3. Now, from both of the red navigation dots next to these commands, drag lines to the red dot to the left of the SummaryForm (indicating that either one goes back to the summary form).
  4. You will notice that you have not added any code to update the totals for the OK command. Don't worry; you will do that below.

Finally, you still need to hook up the Initialize screen:

  1. Drag an Ok Command from the palette over the Summary Form and drop it.
  2. Change the label to "Initialize" in the command properties.
  3. Also, as a precaution, set the Priority field to 2. This will ensure that if the device puts the Add Detail and Initialize options in the same menu, the Add Detail will be first in the list and selected, rather than Initialize.
  4. Drag a line between the Initialize navigation dot and the InitializeForm navigation dot.
  5. As for the AddDetailsForm, add OK and Cancel operations to come back to the Summary Form.

Okay, the navigation part of your application is set up. Start up the debugger again, and try navigating between the various screens. Even though the app doesn't do anything yet, you can see it taking shape.

Here is what my Flow Design looks like after adding the above navigation:



Click here for a larger image.

Adding the Rest

You are getting close now, but you have two major items left to add. The first is the functionality—updating the totals, doing the calculations, displaying the results, and so forth.

The second equally important one is to store and load the data. When you shut down your mileage calculator, you don't want to lose the data, so it has to be stored in the device memory somewhere.

Add the functionality code first, and then worry about persisting the data afterwards.

Adding the Functionality

The first thing you need in your application is some fields to store the data, both the totals and the averages:

  1. In the Projects pane (top left), expand out the tree until you can see your MileageMidlet (if it is not expanded already), and then expand that. You should see four nodes: Fields, Constructors, Methods, and Bean Patterns.
  2. You are going to add properties through the Bean Patterns node (the fastest way to add properties).
  3. First, right-click on Bean Patterns and select Add Property.
  4. For the name, type startingMileageVal, type is long, and mode is Read/Write. Leave all the other settings as default and click OK.
  5. Repeat for long properties currentMileageVal, totalMileageVal, totalFuelVal, totalCostVal, avgMpgVal, and avgCostVal.

Note that all the fields are added as long, not float. This is because in CLDC 1.0, floating point is not a part of the spec. You will be using fixed-point arithmetic to 2DP of accuracy for this example. You can use CLDC 1.1 in your project spec, which includes floating point support, but make sure your device supports it (my series 40 Nokia does not).

The solution using fixed-point arithmetic is to make some simplifying assumptions. For example, you will deal only with whole miles for the mileage (this will even out over time because you enter the vehicle's current mileage, not the difference since the last re-fuelling. Secondly, you will assume that the gallons and cost are entered using hundredths of gallons (with no decimal point) and in cents. In other words, instead of entering 12.43 gallons, you will enter 1243; and instead of $20.98, you will enter 2098. This makes it easier to type in using the phone keypad, and also keeps things nice and easy to manipulate in integers. When you calculate the summaries for the summary screen, you will use integer division and modulo to show the formatted gallons with two decimal places of accuracy.

Still with me? Yes, this is a compromise, but the art of writing Java ME applications involves a fair amount of compromise. Alternatives would be to allow a string to be entered into the numeric fields, then parse out the integer information from that, or use CLDC 1.1 and go for full floating point (just watch that you have support from your target devices). Either way, these are left as an exercise for the reader for the sake of brevity.

Okay, so you now have your fields. You can add some functionality to use them:

  1. From the Flow Design screen, double-click on the Add Details form to bring up the device screen.
  2. You will see the screen, but you should also see two assigned commands to the right of it. This is where you can attach the functionality.
  3. On the OK Command (probably named okCommand2), click the edit link.
  4. There are two editable areas: pre-action user code and post-action user code. Not surprisingly, these can contain code that happens either prior to the action of switching to another form, or after it. You are going to put some functionality in the Pre-Action user code section.
  5. There are really two ways to do this: You can put all the code you want in the editable section, but if you do so you will not get Intellisense help, so I prefer the other option. You will create a new method called this.updateDetails() and simply call it from this pre-action. So, in the text area, simply type this.updateDetails(); and click OK.
  6. Next, you have to supply the code for the updateDetails() method.
  7. In the Projects pane, right-click on the Methods node for the MileageMidlet and select Add Method.
  8. Name the method (of course) updateDetails. Leave the return type void and the parameters empty.
  9. This will create the method; double-click on the new method in the Projects pane to edit the body.
  10. Note: Alternatively to the above, if you prefer you could simply jump straight to the source and create the new method by typing it in there. It might be quicker.
  11. Add the following code to the body of the updateDetails method:.
  12. 
    this.currentMileageVal =
       Long.parseLong(this.currentMileage.getString());
    int currentGallons =
       Integer.parseInt(this.gallons.getString());
    int currentCost = Integer.parseInt(this.cost.getString());
    // figure out new totals
    this.totalMileageVal = this.currentMileageVal
                         - this.startingMileageVal;
    this.totalCostVal += currentCost;
    this.totalFuelVal += currentGallons;
    this.recalcAveragesAndUpdateSummary();
    
  13. YOu also need to define the recalcAveragesAndUpdateSummary() method. Either repeat the method definition procedure above from the Projects pane, or just add the method in the editor window. The method should be:
  14. 
    public void recalcAveragesAndUpdateSummary() {
       // fixed point arithmetic - keep everything multiplied
       // by a factor of 100
       // Need to protect against division by 0
       if(this.totalMileageVal == 0) {
          this.avgCostVal = 0;
       } else {
          this.avgCostVal = this.totalCostVal
                          / this.totalMileageVal;
       }
       if(this.totalFuelVal == 0) {
          this.avgMpgVal = 0;
       }
       else {
          // Note: 10000 multiplier is to take into account Fuel
          // 100 multiplier and another 100 to add 2 DP to average
          this.avgMpgVal = (this.totalMileageVal * 10000)
                         / this.totalFuelVal;
       }
       // Temporary String to hold formatted values
       String tempFormat = null;
       // set the current mileage just by converting to String
       this.totalMileage.setText(Long.toString(
          this.currentMileageVal));
       // For avg cost - format as 23 cents
       tempFormat = Long.toString(this.avgCostVal) + " cents";
       this.avgCostPerMile.setText(tempFormat);
       // and fixed point also for MPG
       tempFormat = Long.toString(this.avgMpgVal / 100) + "." +
          Long.toString(this.avgMpgVal % 100);
       this.avgMpg.setText(tempFormat);
    }
    

Next, you need to fill in the Initialization code. This is quite simple now that you have done the above work:

  1. From the Flow Design, double-click on the InitializeForm and then Edit the OK command.
  2. In the Pre-Action user code, enter this.initializeTotalsAndAverages();.
  3. Click OK.
  4. Create a new method:
  5. 
    public void initializeTotalsAndAverages() {
       this.startingMileageVal =
          Long.parseLong(this.startingMileage.getString());
       this.currentMileageVal  = this.startingMileageVal;
       this.totalMileageVal    = 0;
       this.totalCostVal       = 0;
       this.totalFuelVal       = 0;
       this.recalcAveragesAndUpdateSummary();
    }
    
  6. Save all and rebuild.

Okay, that's the functionality in place; as simple as it is. You can now debug the app again and try it out. Initialize it with a starting mileage and then add some details. Pretend you have filled up after 300 miles or something. You should see the summary details update when you OK the new details. Don't forget to enter gallons multiplied by a factor of 100 (1243 for 12.43 gallons) and cost in cents (2065 for $20.65).

There is, however, one last missing piece. When you quit the app right now, all data is lost. You need a way to persist it. Fortunately, the persistence mechanism provided in Java ME is dead simple.

Adding Persistence

The persistence mechanism you will be using is based around a RecordStore. The record store has a name assigned to it—something you choose for your application that hopefully will not clash with another one. You will call yours MileageCalcStore. Records within the record store have a numeric index, and are simply an array of bytes.

You will add two new methods to your class: saveCurrentState() and loadCurrentState(). For brevity, I will simply include the code here and discuss it below:


public void saveCurrentState() {
   RecordStore myStore = null;
   try {
      myStore =
         RecordStore.openRecordStore("MileageCalcStore", true);
      int n = myStore.getNumRecords();
      // create the byte array representations for storing
      byte[] startingMileageBytes =
         Long.toString(this.startingMileageVal).getBytes();
      byte[] currentMileageBytes =
         Long.toString(this.currentMileageVal).getBytes();
      byte[] totalFuelBytes =
         Long.toString(this.totalFuelVal).getBytes();
      byte[] totalCostBytes =
         Long.toString(this.totalCostVal).getBytes();
      // do we already have a record set to use?
      if(n == 0) {
         // no - create new records
         myStore.addRecord(startingMileageBytes, 0,
                           startingMileageBytes.length);
         myStore.addRecord(currentMileageBytes, 0,
                           currentMileageBytes.length);
         myStore.addRecord(totalFuelBytes, 0, totalFuelBytes.length);
         myStore.addRecord(totalCostBytes, 0, totalCostBytes.length);
      }
      else {
         // use existing records
         myStore.setRecord(1, startingMileageBytes,
                           0, startingMileageBytes.length);
         myStore.setRecord(2, currentMileageBytes,
                           0, currentMileageBytes.length);
         myStore.setRecord(3, totalFuelBytes,
                           0, totalFuelBytes.length);
         myStore.setRecord(4, totalCostBytes,
                           0, totalCostBytes.length);
      }
   }
   catch(Exception ex) {
      // omitted for brevity - but you should do something
      // with this
   }
   finally {
      if(myStore != null) {
         try {
            myStore.closeRecordStore();
         }
         catch(Exception ex) {
            // ignore
         }
      }
   }
}

Some notes on the above:

  • The "true" when opening the record store indicates that a store should be created by that name if it doesn't already exist.
  • No doubt, there are better ways to get a byte array from the values. This is a quick example and converting to strings and then bytes keeps things nice and clear and makes things easier to debug on loading too, but feel free to implement storage and retrieval in your own style.
  • You are only storing the totals and the starting mileage; everything else can be calculated on load.
  • When creating new records, use addRecord (and the index number will increment automatically); when re-using existing ones, the index number is the first argument.
  • Remember to add any includes needed to get this to compile (like RecordStore. Right-click and say "Fix Imports."
  • The finally block is important to close the record set should anything go wrong. You should always include the finally block to clean up any dangling record sets.

Now, you also need the code to load the values back in:


public void loadCurrentState() {
   RecordStore myStore = null;
   try {
      myStore = RecordStore.openRecordStore("MileageCalcStore", true);
      int n = myStore.getNumRecords();
      if(n == 0) {
         // if no record set, use safe values
         this.startingMileageVal = 0;
         this.currentMileageVal  = 0;
         this.totalFuelVal       = 0;
         this.totalCostVal       = 0;
      }
      else {
         String startMile, currentMile, totCost, totFuel;
         startMile   = new String(myStore.getRecord(1));
         currentMile = new String(myStore.getRecord(2));
         totFuel     = new String(myStore.getRecord(3));
         totCost     = new String(myStore.getRecord(4));
         // now parse out the values
         this.startingMileageVal = Long.parseLong(startMile);
         this.currentMileageVal  = Long.parseLong(currentMile);
         this.totalMileageVal    = currentMileageVal
                                 - startingMileageVal;
         this.totalFuelVal       = Long.parseLong(totFuel);
         this.totalCostVal       = Long.parseLong(totCost);
      }
   }
   catch(Exception ex) {
      // TODO: do something with exception (ommitted for brevity)
   }
   finally {
      if(myStore != null) {
         try {
            myStore.closeRecordStore();
         }
         catch(Exception ex) {
            // ignore
         }
      }
   }
   // now update the averages and summary
   this.recalcAveragesAndUpdateSummary();
}

This code should make sense if you understood the store code; it is basically a reversal of the store functionality. Strings are re-constructed from the byte arrays, and then parsed into the longs. Finally, the averages and summary update method are invoked, to get everything in sync.

So, now you have methods to save and load the data, but you are not calling them anywhere. You could choose to call the save method whenever the initialize or add details methods are used, and load on startup. I usually choose to put the save methods into the pauseApp and destroyApp lifecycle methods instead:

  1. Find the startApp() method, and after the initialize(); call, add
  2. this.loadCurrentState();
  3. Find the pauseApp() and destroyApp(boolean unconditional) methods and in both add the line:
  4. this.saveCurrentState();

Save and debug the app again. This time when you run it, you can exit the application (in the phone emulator) and then restart it again, and your current totals and averages should still be there. Note that if you actually close the emulator itself and restart it, your numbers will be lost; but the main thing is that it will store permanently on your device.

Getting the App onto a Device

Once you are happy with your application, it's time to get it onto a device. This requires the construction of a jar and jad file for the application. The jar file is the packaged executable, and the jad describes it to the device, so these two normally go in combination.

Fortunately, the NetBeans mobility pack makes it really easy to package up the application into these files. Simply right-click on the MileageCalculator project and select Deploy Project. This seems to give an error about a missing ant property, and I need to look into why this is (update in a later article). However, even in spite of this, the jad and jar files are created in the dist directory of the project home anyway and are ready to deploy to your device, so just pick them up from that directory.

As to actually getting them onto your device, there are numerous ways to do this. One is to put the jad and jar files out on a Web server and then browse to the jad file using WAP on your phone (that is probably the most universal way to do it, assuming you have WAP access on your phone). Alternatively, you may find that your phone or other device came with software, or has software for download, that will let you install the application. For example, the Nokia PC suite, a free download from nokia.com, lets me load applications directly onto my series 40 device.

Conclusion

In this article, you have put together a simple and not particularly pretty mileage calculator that should be able to run on any Java-enabled phone or other device. The application has illustrated the main points of Java ME development apart from online communication (which I hope to cover in a future article). The mileage app might be useful in its current form, but probably would be better if extended and improved.

I hope this demonstrates that Java ME application development is easy, particularly with NetBeans and the Mobility Pack. I also hope this spurs more people to create applications that I can use on my phone. :)

About the Author

Dick Wall is a Lead Systems Engineer for NewEnergy Associates, A Siemens Company based in Atlanta, GA that provides energy IT and consulting solutions for decision support and energy operations. He can be reached for comment on this and other matters at dick.wall@newenergyassoc.com. He also co-hosts the JavaCast, a podcast devoted to Java news and the Java community, which can be found at http://javacast.thepostmodern.net.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date