<?xml version="1.0"?>
<rss version="2.0">
<channel>
  <title>The Kitchen in the Zoo - j2me tag</title>
  <link>http://blog.maxant.co.uk:80/pebble/tags/j2me/</link>
  <description>&lt;small&gt;A blog where Ant writes about anything he finds interesting! &lt;a href=&#039;http://www.linkedin.com/in/maxant&#039;&gt;&lt;font color=&#039;white&#039;&gt;Who is Ant?&lt;/font&gt;&lt;/a&gt;      &lt;a href=&#039;/pebble/pages/copyright.html&#039;&gt;&lt;font color=&#039;white&#039;&gt;Copyright 2005-2012 Ant Kutschera&lt;/font&gt;&lt;/a&gt;&lt;/small&gt;</description>
  <language>en</language>
  <copyright>Ant Kutschera</copyright>
  <lastBuildDate>Thu, 10 May 2012 20:07:00 GMT</lastBuildDate>
  <generator>Pebble (http://pebble.sourceforge.net)</generator>
  <docs>http://backend.userland.com/rss</docs>
  
  
  <item>
    <title>Auto-update of Midlets</title>
    <link>http://blog.maxant.co.uk:80/pebble/2010/03/18/1268939460000.html</link>
    
      
        <description>
          &lt;p&gt;Any professional application should be capable of updating itself over the internet. Even Midlets! The idea is fairly easy in that the Midlet needs to make a call to the server to check what version the latest software has, and compare that to the installed version. If there is a newer version available, then it needs to start the (mobile) device&#039;s browser and point it at the URL of the new JAD file. The device will take care of the download and installation after that. So, the following article shows you exactly how this can be implemented.&lt;br /&gt;
&lt;br /&gt;
First off, refer to the &lt;a href=&#034;/pebble/2010/02/21/1266791340000.html&#034;&gt;previous article&lt;/a&gt;, which described a framework for making server calls.&lt;br /&gt;
&lt;br /&gt;
The sequence diagram looks like this:&lt;br /&gt;
&lt;br /&gt;
&lt;img src=&#034;/pebble/images/autoUploadMidletsSequenceDiagram.jpg&#034; alt=&#034;&#034; /&gt;&lt;br /&gt;
&lt;br /&gt;
Based on the framework, the following JSP can be installed serverside, which reads the version number out of the JAD file:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr size=&#034;2&#034; width=&#034;100%&#034; /&gt;
&lt;div style=&#034;border: thin none; overflow: auto; width: 525px; height: 300px; background-color: rgb(255, 255, 255);&#034;&gt;
&lt;pre&gt;
&amp;lt;%@page import=&amp;quot;java.io.FileInputStream&amp;quot;%&amp;gt;
&amp;lt;%@page import=&amp;quot;java.io.RandomAccessFile&amp;quot;%&amp;gt;
&amp;lt;%@page import=&amp;quot;java.io.File&amp;quot;%&amp;gt;
&amp;lt;%@page import=&amp;quot;java.util.List&amp;quot;%&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;%
RandomAccessFile raf = null;
try{
	String version = null;
	String path = request.getSession().getServletContext().getRealPath(&amp;quot;index.jsp&amp;quot;);
	File f = new File(path);
	f = f.getParentFile();
	f = new File(f, &amp;quot;jads/nameOfJad.jad&amp;quot;);
	raf = new RandomAccessFile(f, &amp;quot;r&amp;quot;);
	String curr = null;
	while((curr = raf.readLine()) != null){
		if(curr.startsWith(&amp;quot;MIDlet-Version:&amp;quot;)){
			version = curr.substring(16);
			break;
		}
	}
	%&amp;gt;OK
	&amp;lt;%=version%&amp;gt;|

	add other master data like the users details, their roles, etc. here...

	&amp;lt;%
}catch(Exception e){
	log.warn(&amp;quot;failed to read master data&amp;quot;, e);
	%&amp;gt;ERROR
	&amp;lt;%=e.getMessage() %&amp;gt;
	&amp;lt;%
}finally{
	if(raf != null) raf.close();
}
%&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;hr size=&#034;2&#034; width=&#034;100%&#034; /&gt;
&lt;p&gt;This JSP is called when the Midlet starts, and effectively delivers &amp;quot;master data&amp;quot; to the device, such as the users roles, account details and importantly, the version number of the latest available JAD file on the server. Based on this version number, which is read as the MIDlet-Version property out of the JAD on the server, the client can decide if the currently installed version is up to date. The Midlet does this by calling the &lt;code&gt;getAppProperty(String)&lt;/code&gt; method, passing it the String &amp;quot;MIDlet-Version&amp;quot;, which reads the version of the local JAD, that was used to install the current version.&lt;br /&gt;
&lt;br /&gt;
Once the client has decided that there is a newer version available, it can ask the user if they wish to update by making an Alert the current screen. If the user chooses to install the update now, then all that is left is to open the devices browser to point at the new JAD which is online, and then close the application. Putting it together in a convenient method gives the following:&lt;/p&gt;
&lt;hr size=&#034;2&#034; width=&#034;100%&#034; /&gt;
&lt;div style=&#034;border: thin none; overflow: auto; width: 525px; height: 300px; background-color: rgb(255, 255, 255);&#034;&gt;
&lt;pre&gt;
private void checkForUpdate(){
	final String currentVersion = getAppProperty(&amp;quot;MIDlet-Version&amp;quot;); //eg 1.0.62
	new MasterDataGetter(currentVersion, model, this) {
		public void onSuccess(Object md) {
			MasterData masterData = (MasterData) md;
			model.setMasterData(masterData);

			if(currentVersion != null &amp;amp;&amp;amp; masterData.getLastestVersion() != null){
				if(isUpdateAvailable(masterData.getLastestVersion(), currentVersion)){
					//does the user want to?
					
					Alert a = new Alert(
								&amp;quot;An update is available (version &amp;quot; + masterData.getLastestVersion() + &amp;quot;.\r\n&amp;quot; +
								&amp;quot;Would you like to download and install it now?&amp;quot;);
					a.setType(AlertType.CONFIRMATION);
					a.removeCommand(Alert.DISMISS_COMMAND);
					final Command no = new Command(&amp;quot;No&amp;quot;, Command.ITEM, 1);
					final Command yes = new Command(&amp;quot;Yes&amp;quot;, Command.ITEM, 2);
					a.addCommand(yes);
					a.addCommand(no);
					a.setCommandListener(new CommandListener() {
						public void commandAction(Command c, Displayable d) {
							if(c == yes){
								try {
									platformRequest(Constants.getBaseUrl() + &amp;quot;jads/nameOfJad.jad&amp;quot;);
									destroyApp(true); //coz we are performing an update!
								} catch (ConnectionNotFoundException e) {
									//tough titties :-( ie platformRequest couldnt be called
									e.printStackTrace();
									handleException(&amp;quot;Failed to open browser to get installation files.&amp;quot;, e, AlertType.ERROR);
								} catch (MIDletStateChangeException e) {
									handleException(&amp;quot;Failed to destroy midlet&amp;quot;, e, AlertType.ERROR);
								} finally {
									notifyDestroyed();
								}
							}else if(c == no){
								showCurrentView();
							}
						}
					});
					Display.getDisplay(Main.this).setCurrent(a, currentView);

				} //endif is update available?
			} //endif neither version is null

			//now we have already called maxant, and the user has confirmed that they are
			//happy to go online (eg BlackBerry shows them the URL of the first connection
			//attempt), we can continue to GA. we are trying to hide this fact from the user
			//as it may put of some users.
			gaUploader.track(&amp;quot;start&amp;quot;);

		}//end #onSuccess

		public void onError(int code, String result) {
			if(model.isShowDebugInfo()) sendDebugLog.callServer(&amp;quot;error with update check &amp;quot; + code + &amp;quot;: &amp;quot; + result);
			//tough titties, dont offer to upgrade
		}

	}.callServer();
}
&lt;/pre&gt;
&lt;/div&gt;
&lt;hr size=&#034;2&#034; width=&#034;100%&#034; /&gt;
&lt;p&gt;&lt;br /&gt;
&lt;br /&gt;
Copyright (c) 2010 Dr Ant Kutschera &lt;/p&gt;&lt;div class=&#034;tags&#034;&gt;&lt;span&gt;Social Bookmarks : &lt;/span&gt;&amp;nbsp;&lt;a href=&#034;http://slashdot.org/bookmark.pl?url=http://blog.maxant.co.uk:80/pebble/2010/03/18/1268939460000.html&amp;amp;title=Auto-update+of+Midlets&#034; target=&#034;_blank&#034; title=&#034;Add this post to Slash Dot&#034;&gt;&lt;img src=&#034;common/images/slashdot.png&#034; alt=&#034;Add this post to Slashdot&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;http://digg.com/submit?url=http://blog.maxant.co.uk:80/pebble/2010/03/18/1268939460000.html&amp;amp;title=Auto-update+of+Midlets&#034; target=&#034;_blank&#034; title=&#034;Digg this post&#034;&gt;&lt;img src=&#034;common/images/digg.png&#034; alt=&#034;Add this post to Digg&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;http://reddit.com/submit?url=http://blog.maxant.co.uk:80/pebble/2010/03/18/1268939460000.html&amp;amp;title=Auto-update+of+Midlets&#034; target=&#034;_blank&#034; title=&#034;Add this post to Reddit&#034;&gt;&lt;img src=&#034;common/images/reddit.png&#034; alt=&#034;Add this post to Reddit&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;http://del.icio.us/post?url=http://blog.maxant.co.uk:80/pebble/2010/03/18/1268939460000.html&amp;amp;title=Auto-update+of+Midlets&#034; target=&#034;_blank&#034; title=&#034;Save this post to Del.icio.us&#034;&gt;&lt;img src=&#034;common/images/delicious.png&#034; alt=&#034;Add this post to Delicious&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;http://www.stumbleupon.com/submit?url=http://blog.maxant.co.uk:80/pebble/2010/03/18/1268939460000.html&amp;amp;title=Auto-update+of+Midlets&#034; target=&#034;_blank&#034; title=&#034;Stumble this post&#034;&gt;&lt;img src=&#034;common/images/stumbleupon.png&#034; alt=&#034;Add this post to Stumble it&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;http://www.google.com/bookmarks/mark?op=edit&amp;amp;bkmk=http://blog.maxant.co.uk:80/pebble/2010/03/18/1268939460000.html&amp;amp;title=Auto-update+of+Midlets&#034; target=&#034;_blank&#034; title=&#034;Add this post to Google&#034;&gt;&lt;img src=&#034;common/images/google.png&#034; alt=&#034;Add this post to Google&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;http://technorati.com/faves?add=http://blog.maxant.co.uk:80/pebble/2010/03/18/1268939460000.html&#034; target=&#034;_blank&#034; title=&#034;Add this post to Technorati&#034;&gt;&lt;img src=&#034;common/images/technorati.png&#034; alt=&#034;Add this post to Technorati&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;http://www.bloglines.com/sub/http://blog.maxant.co.uk:80/pebble/2010/03/18/1268939460000.html&#034; target=&#034;_blank&#034; title=&#034;Add this post to Bloglines&#034;&gt;&lt;img src=&#034;common/images/bloglines.png&#034; alt=&#034;Add this post to Bloglines&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;http://www.facebook.com/share.php?u=http://blog.maxant.co.uk:80/pebble/2010/03/18/1268939460000.html&#034; target=&#034;_blank&#034; title=&#034;Add this post to Facebook&#034;&gt;&lt;img src=&#034;common/images/facebook.png&#034; alt=&#034;Add this post to Facebook&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;http://www.furl.net/storeIt.jsp?u=http://blog.maxant.co.uk:80/pebble/2010/03/18/1268939460000.html&amp;amp;t=Auto-update+of+Midlets&#034; target=&#034;_blank&#034; title=&#034;Add this post to Furl&#034;&gt;&lt;img src=&#034;common/images/furl.png&#034; alt=&#034;Add this post to Furl&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;https://favorites.live.com/quickadd.aspx?mkt=en-us&amp;amp;url=http://blog.maxant.co.uk:80/pebble/2010/03/18/1268939460000.html&amp;amp;title=Auto-update+of+Midlets&#034; target=&#034;_blank&#034; title=&#034;Add this post to Windows Live&#034;&gt;&lt;img src=&#034;common/images/windowslive.png&#034; alt=&#034;Add this post to Windows Live&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;http://bookmarks.yahoo.com/toolbar/savebm?opener=tb&amp;amp;u=http://blog.maxant.co.uk:80/pebble/2010/03/18/1268939460000.html&amp;amp;t=Auto-update+of+Midlets&#034; target=&#034;_blank&#034; title=&#034;Add this post to Yahoo!&#034;&gt;&lt;img src=&#034;common/images/yahoo.png&#034; alt=&#034;Add this post to Yahoo!&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&lt;/div&gt;
        </description>
      
      
    
    
    
    <comments>http://blog.maxant.co.uk:80/pebble/2010/03/18/1268939460000.html#comments</comments>
    <guid isPermaLink="true">http://blog.maxant.co.uk:80/pebble/2010/03/18/1268939460000.html</guid>
    <pubDate>Thu, 18 Mar 2010 19:11:00 GMT</pubDate>
  </item>
  
  <item>
    <title>Transfer-Encoding: chunked</title>
    <link>http://blog.maxant.co.uk:80/pebble/2010/02/24/1266992820000.html</link>
    
      
        <description>
          &lt;p&gt;The J2ME HTTPConnection which comes with MIDP lets you make HTTP requests to your server. It doesn&#039;t do much at a high level, for example the API doesn&#039;t have methods like addCookie() - you need to manually add them with a request header. But the implementation is clever enough to turn any request body which is greater than around 2Kb into a chunked request.&lt;br /&gt;
&lt;br /&gt;
With HTTP 1.0, the request had to contain a header called Content-Length which told the server how many bytes to read off the input stream when reading the body. HTTP 1.1 introduced the Transfer-Encoding header, which lets the client omit the Content-Length header, and instead create chunks of request body, which optimises the upload meaning that a) the server can start processing before it has everything, and b) more importantly for J2ME where memory might be a valuable resource, it lets the client send a bit of the request, free up that allocated memory and then send some more of the request.&lt;br /&gt;
&lt;br /&gt;
For a POST request, with no chunking, the headers and body might look like this:&lt;/p&gt;
&lt;pre&gt;
	POST /log.jsp HTTP/1.1
	User-Agent: Mozilla/4.0 (maxant J2ME Client) 
	Accept: text/html,application/xhtml+xml,application/xml
	Content-Type: application/x-www-form-urlencoded
	&lt;b&gt;Content-Length: 51&lt;/b&gt;
	Host: wwwchaseamatecom:8089

	problem=Failed%20to%20get%20installation%20response
&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;
Chunked, that becomes:&lt;/p&gt;
&lt;pre&gt;
	POST /ota/addInstallation.jsp HTTP/1.1
	User-Agent: Mozilla/4.0 (maxant J2ME Client) 
	Accept: text/html,application/xhtml+xml,application/xml
	Content-Type: application/x-www-form-urlencoded
	Host: wwwchaseamatecom:8089
	&lt;b&gt;Transfer-Encoding: chunked&lt;/b&gt;

	problem=Failed%20to%20get%20installation%20response
&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;
You&#039;ll notice that the body of the second example, &amp;quot;problem=...&amp;quot; doesn&#039;t contain chunk headers (search Wikipedia for chunking to see an example). The reason is that I copied that text out of the TCP/IP Monitor in Eclipse 3.5 and it seems kind enough to hide that information and simply show you the unchunked body. Not great if you aren&#039;t expecting that, somewhat useful none the less.&lt;br /&gt;
&lt;br /&gt;
Anyway, about time I got to the point of this blog article. Any version of Tomcat before 5.5.28 or 6.0.21 (ie. the latest versions at the time of writing!) had a nasty bug in them whereby POST requests did NOT get the body parsed and put into request.getParameter(String) or request.getQueryString(). Very nasty indeed for J2ME developers. I trawled the internet and found people were having problems from around 2002 when HTTP 1.1 became available in J2ME. Only with the following bug fix has this finally been corrected on Tomcat, some 8 years later. &lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;a href=&#034;https://issues.apache.org/bugzilla/show_bug.cgi?id=37794&#034;&gt;https://issues.apache.org/bugzilla/show_bug.cgi?id=37794&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Work arounds included using Apache to put together the chunks before sending the request on to Tomcat, or manually splitting the request on the device, into small chunks and upload each one and then joining them together at the end - what a load of work to have to do! &lt;br /&gt;
&lt;br /&gt;
I&#039;m working with an old 5.5.9 Tomcat installation and have just upgraded it to 5.5.28 to fix this problem. &lt;br /&gt;
&lt;br /&gt;
However before I found this bug report I did consider creating a filter and configuring it in my web.xml. The idea was that the filter would read the request body, URL decode it, unlock the requests parameter map (Catalina has a lockable map which is locked after parsing the request), and put the request bodies request parameters into the map. Something like this:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr size=&#034;2&#034; width=&#034;100%&#034; /&gt;
&lt;div style=&#034;border: thin none ; overflow: auto; width: 525px; height: 300px; background-color: rgb(255, 255, 255);&#034;&gt;
&lt;pre&gt;
/*  
 * Copyright (c) 2010 Ant Kutschera, maxant
 * 
 * This file is part of Ant Kutschera&#039;s blog, http://blog.maxant.co.uk
 * 
 * This is free software: you can redistribute it and/or modify
 * it under the terms of the Lesser GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * It is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * Lesser GNU General Public License for more details.
 * You should have received a copy of the Lesser GNU General Public License
 * along with Foobar.  If not, see .
 */
import java.io.IOException;
import java.io.InputStream;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Map.Entry;

import javax.servlet.http.HttpServletRequest;

//nasty, but we are afterall fixing a bug in catalina!
import org.apache.catalina.util.ParameterMap;

public final class RequestHandler implements Filter {

	private static final String TRANSFER_ENCODING = &amp;quot;Transfer-Encoding&amp;quot;;
	private static final String CHUNKED = &amp;quot;chunked&amp;quot;;

	public void destroy() {
	}

	public void init(FilterConfig filterConfig) throws ServletException {
	}
	
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {

		String header = request.getHeader(TRANSFER_ENCODING);
		
		InputStream is = null;
		try{
			if(header != null &amp;amp;&amp;amp; header.equals(CHUNKED)){
				//time to work our magic

				((ParameterMap)request.getParameterMap()).setLocked(false);
				is = request.getInputStream();

				//read the body as a map
				Properties props = new Properties();
				props.load(is);
				
				//get the encoding to use for url-decoding
				String enc = request.getCharacterEncoding();
				if(enc == null){
					enc = &amp;quot;UTF-8&amp;quot;;
				}

				for(Entry e : props.entrySet()){
					
					String key = (String) e.getKey();

					//need to decode it!
					key = URLDecoder.decode(key, enc);

					if(!request.getParameterMap().containsKey(key)){
						String val = (String) e.getValue();

						//need to decode it!
						val = URLDecoder.decode(val, enc);

						request.getParameterMap().put(key, val);
					}
				}
			}				
				
			((ParameterMap)request.getParameterMap()).setLocked(true);
				
		}finally{
			if(is != null) is.close();
		}
	}
}
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;
I wasted hours because of this rubbish bug. The problem is that its full of red herrings, making you think for example that because the monitor shows the request body unchunked, that the device isn&#039;t chunking the body properly. The web is also full of very old posts related to servers not being HTTP 1.1 compatible. And there are lots of work arounds which people went to the effort of because they had no other choice until most recently. So hopefully if you have had chunking problems with J2ME, you found this article without too much hassle!&lt;br /&gt;
&lt;br /&gt;
Copyright (c) 2010 Ant Kutschera&lt;br /&gt;
&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div class=&#034;tags&#034;&gt;&lt;span&gt;Social Bookmarks : &lt;/span&gt;&amp;nbsp;&lt;a href=&#034;http://slashdot.org/bookmark.pl?url=http://blog.maxant.co.uk:80/pebble/2010/02/24/1266992820000.html&amp;amp;title=Transfer-Encoding%3A+chunked&#034; target=&#034;_blank&#034; title=&#034;Add this post to Slash Dot&#034;&gt;&lt;img src=&#034;common/images/slashdot.png&#034; alt=&#034;Add this post to Slashdot&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;http://digg.com/submit?url=http://blog.maxant.co.uk:80/pebble/2010/02/24/1266992820000.html&amp;amp;title=Transfer-Encoding%3A+chunked&#034; target=&#034;_blank&#034; title=&#034;Digg this post&#034;&gt;&lt;img src=&#034;common/images/digg.png&#034; alt=&#034;Add this post to Digg&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;http://reddit.com/submit?url=http://blog.maxant.co.uk:80/pebble/2010/02/24/1266992820000.html&amp;amp;title=Transfer-Encoding%3A+chunked&#034; target=&#034;_blank&#034; title=&#034;Add this post to Reddit&#034;&gt;&lt;img src=&#034;common/images/reddit.png&#034; alt=&#034;Add this post to Reddit&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;http://del.icio.us/post?url=http://blog.maxant.co.uk:80/pebble/2010/02/24/1266992820000.html&amp;amp;title=Transfer-Encoding%3A+chunked&#034; target=&#034;_blank&#034; title=&#034;Save this post to Del.icio.us&#034;&gt;&lt;img src=&#034;common/images/delicious.png&#034; alt=&#034;Add this post to Delicious&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;http://www.stumbleupon.com/submit?url=http://blog.maxant.co.uk:80/pebble/2010/02/24/1266992820000.html&amp;amp;title=Transfer-Encoding%3A+chunked&#034; target=&#034;_blank&#034; title=&#034;Stumble this post&#034;&gt;&lt;img src=&#034;common/images/stumbleupon.png&#034; alt=&#034;Add this post to Stumble it&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;http://www.google.com/bookmarks/mark?op=edit&amp;amp;bkmk=http://blog.maxant.co.uk:80/pebble/2010/02/24/1266992820000.html&amp;amp;title=Transfer-Encoding%3A+chunked&#034; target=&#034;_blank&#034; title=&#034;Add this post to Google&#034;&gt;&lt;img src=&#034;common/images/google.png&#034; alt=&#034;Add this post to Google&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;http://technorati.com/faves?add=http://blog.maxant.co.uk:80/pebble/2010/02/24/1266992820000.html&#034; target=&#034;_blank&#034; title=&#034;Add this post to Technorati&#034;&gt;&lt;img src=&#034;common/images/technorati.png&#034; alt=&#034;Add this post to Technorati&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;http://www.bloglines.com/sub/http://blog.maxant.co.uk:80/pebble/2010/02/24/1266992820000.html&#034; target=&#034;_blank&#034; title=&#034;Add this post to Bloglines&#034;&gt;&lt;img src=&#034;common/images/bloglines.png&#034; alt=&#034;Add this post to Bloglines&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;http://www.facebook.com/share.php?u=http://blog.maxant.co.uk:80/pebble/2010/02/24/1266992820000.html&#034; target=&#034;_blank&#034; title=&#034;Add this post to Facebook&#034;&gt;&lt;img src=&#034;common/images/facebook.png&#034; alt=&#034;Add this post to Facebook&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;http://www.furl.net/storeIt.jsp?u=http://blog.maxant.co.uk:80/pebble/2010/02/24/1266992820000.html&amp;amp;t=Transfer-Encoding%3A+chunked&#034; target=&#034;_blank&#034; title=&#034;Add this post to Furl&#034;&gt;&lt;img src=&#034;common/images/furl.png&#034; alt=&#034;Add this post to Furl&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;https://favorites.live.com/quickadd.aspx?mkt=en-us&amp;amp;url=http://blog.maxant.co.uk:80/pebble/2010/02/24/1266992820000.html&amp;amp;title=Transfer-Encoding%3A+chunked&#034; target=&#034;_blank&#034; title=&#034;Add this post to Windows Live&#034;&gt;&lt;img src=&#034;common/images/windowslive.png&#034; alt=&#034;Add this post to Windows Live&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;http://bookmarks.yahoo.com/toolbar/savebm?opener=tb&amp;amp;u=http://blog.maxant.co.uk:80/pebble/2010/02/24/1266992820000.html&amp;amp;t=Transfer-Encoding%3A+chunked&#034; target=&#034;_blank&#034; title=&#034;Add this post to Yahoo!&#034;&gt;&lt;img src=&#034;common/images/yahoo.png&#034; alt=&#034;Add this post to Yahoo!&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&lt;/div&gt;
        </description>
      
      
    
    
    
    <comments>http://blog.maxant.co.uk:80/pebble/2010/02/24/1266992820000.html#comments</comments>
    <guid isPermaLink="true">http://blog.maxant.co.uk:80/pebble/2010/02/24/1266992820000.html</guid>
    <pubDate>Wed, 24 Feb 2010 06:27:00 GMT</pubDate>
  </item>
  
  <item>
    <title>A J2ME Library and a simple HTTP Service Framework</title>
    <link>http://blog.maxant.co.uk:80/pebble/2010/02/21/1266791340000.html</link>
    
      
        <description>
          J2ME&#039;s support for calling a server is rather simple and low level. Not only do you have to deal with the HTTP Connection at a low level, there is no high level support for cookies, authentication or remote procedure calling. So if you want to pass an object to the server and get a response, you need to figure out how to do that. XML over HTTP is one solution, but presents its own problems like the serialisation and deserialisation of objects, not to mention higher network traffic because of the meta-data held within the XML. JAX Binding is certainly not supported in J2ME-land which results in you having to use a SAX parser.
&lt;br&gt;&lt;br&gt;
In previous projects I have toyed with a simple way of providing services over JSPs, which take and receive delimited text. The idea is to implement your own simple serialization and deserialisation of simple objects allowing you to make simple calls to the server and receive simple responses. I purposefully used the word &#034;simple&#034; four times in that last sentence to impress upon you the idea that server calls should be kept simple.
&lt;br&gt;&lt;br&gt;
Take for example a J2ME application which tracks a GPS location. To send the location of the user it can simply send a line of text like this:
&lt;br&gt;&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;006.574438|045.453345|11022344843373
&lt;br&gt;&lt;br&gt;
What&#039;s it mean?
&lt;br&gt;&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;longitude | latitude | timestamp
&lt;br&gt;&lt;br&gt;
The serialising and deserialising of the data is VERY simple using a StringTokenizer (erm, which doesn&#039;t exist in J2ME, so see later!). And the server could respond with a simply OK or it might want to take the opportunity to update the client with some traffic information:
&lt;br&gt;&lt;br&gt;
&amp;nbsp;M4|4|6|20|Accident
&lt;br&gt;&lt;br&gt;
which means:
&lt;br&gt;&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;road | from junction | to junction | how many minutes delay | reason
&lt;br&gt;&lt;br&gt;
Most server calls really can be that simple, especially when being involved in J2ME applications which tend to be relatively simple themselves.
&lt;br&gt;&lt;br&gt;
So the above presents a few questions... How secure is the data and how do you know who the update is coming from? Well the data should be sent over SSL to ensure that its secure and if the data is sent over an authenticated session, then the server knows who the logged in user is. But to get a log in and session to work, you need two things, namely cookies (for the session ID to be passed between client and server) and some form of authentication. Cookies in J2ME aren&#039;t too easy to handle since there is no built in API for handling them at a high level. You can set a cookie in the request header, but storing cookies from responses is the hard part. I implemented rudimentary cookie handling by sending the response to a method which checks for any set-cookie headers and adds them to the cache as well as cleaning out expired entries. When a request is sent, I call a method which adds all relevant cookies to the request header. I have not implemented the &lt;a target=&#039;_blank&#039; href=&#039;http://www.faqs.org/rfcs/rfc2965.html&#039;&gt;RfC for cookies&lt;/a&gt; and don&#039;t handle the differences between Cookie and Cookie2. In fact I didn&#039;t even go as far as checking the path of the cookie before sending it to the server, because in my environment, it&#039;s not even relevant. A proper cookie implementation would need to do those things and more, and perhaps one day, such a library will exist.. I did manage to find &lt;a target=&#039;_blank&#039; href=&#039;http://www.javaworld.com/javaworld/jw-04-2002/jw-0426-cookie.html?page=6&#039;&gt;this&lt;/a&gt; which refers to the JCookie sourceforge project and J2ME, but checking out its &lt;a target=&#039;_blank&#039; href=&#039;http://jcookie.sourceforge.net/&#039;&gt;site&lt;/a&gt; I couldn&#039;t find anything that would work with J2ME.
&lt;br&gt;&lt;br&gt;
HTTP authentication I originally handled by adding the &#034;authorization&#034; request header and applying a Base64 encoded basic authentication string to it. This caused its own problems, because J2ME doesn&#039;t have a Base64 encoder out of the box. Luckily the org.apache.common.codecs.binary.Base64 class works (which I took from the Apache Codecs 1.3 library. It depends upon the following classes which are in the distributable JAR and also J2ME compatible: BinaryDecoder, BinaryEncoder, Decoder, DecoderException, Encoder, EncoderException, all found in the org.apache.commons.codec package.
&lt;br&gt;&lt;br&gt;
I ran into a different problem when I wanted my web application for the web site to be the same as the web application for my services. Namely, for services I wanted to use basic authentication and for the web site I wanted to use form login. The servlet spec doesn&#039;t let you define more than one login-config per web-app! So the J2ME class I had written for accessing services (ServerCaller) had to be extended, but that wasn&#039;t hard. When a web application using Form Login needs you to authenticate, it simply returns the login form instead of the expected response. If you parse the response and check to see if it is your login form, and then simply submit that form with your username and password, the server then logs you in and returns a 302 code telling you to call the original page again. Assuming you provided the correct login credentials, it all works. So my class recursed into itself if it spotted the login form and that was enough to make it work transparently.
&lt;br&gt;&lt;br&gt;
The next problem was the parsing of the responses in order to deserialise them. For this I created a very simple StringTokenizer, since neither CLDC nor MIDP gives you one :-( The implementation is below.
&lt;br&gt;&lt;br&gt;
URL encoding and decoding is also important because request parameters (for a GET its the request string, for a POST the request body). Luckily there are some options out there. Catalina has one in its util package, which almost works for J2ME. So I did a quick fix to it to remove the dependency on java.util.BitSet which also doesn&#039;t exist in J2ME.
&lt;br&gt;&lt;br&gt;
The last problem I had related to chunking of the request by the HTTPConnection, which you can read more about &lt;a href=&#039;http://blog.maxant.co.uk/pebble/2010/02/24/1266992820000.html&#039;&gt;here&lt;/a&gt;.
&lt;br&gt;&lt;br&gt;
So, finally to some code! The magic class is the ServerCaller which you can download below. It is abstract and to use it you simply need to extend it, for example:
&lt;br&gt;&lt;br&gt;
&lt;hr size=&#034;2&#034; width=&#034;100%&#034; /&gt;
&lt;div style=&#034;border: thin none ; overflow: auto; width: 525px; height: 300px; background-color: rgb(255, 255, 255);&#034;&gt;
&lt;pre&gt;
package uk.co.maxant.cam.c;

import java.util.Vector;

import uk.co.maxant.cam.m.MasterData;
import uk.co.maxant.j2me.core.CredentialsProvider;
import uk.co.maxant.j2me.core.ServerCaller;
import uk.co.maxant.j2me.core.StringTokenizer;

/**
 * calls the server to get master data like roles and the version of the latest available download.
 */
public abstract class MasterDataGetter extends ServerCaller implements ServerCaller.Listener {

	private ExceptionHandler eh;
	
	public MasterDataGetter(CredentialsProvider credentialsProvider, ExceptionHandler eh) {
		super(credentialsProvider, Constants.getBaseUrl(), &#034;ota/getMasterData.jsp&#034;);
		this.eh = eh;
		setListener(this);
	}
	
	public void callServer(){
		properties.clear();
		properties.put(&#034;installUid&#034;, String.valueOf(InstallHelper.getInstallationUid(eh)));

		doCallAsync();
	}
	
	protected void handleSuccess(String str) {
		StringTokenizer lines = new StringTokenizer(str, &#034;|&#034;);
		String version = lines.nextToken().trim();
		String sessionId = lines.nextToken().trim();
		String screenName = lines.nextToken().trim();
		String roles = lines.nextToken().trim();
		StringTokenizer rs = new StringTokenizer(roles, &#034;,&#034;);
		Vector rolesv = new Vector();
		while(rs.hasMoreTokens()){
			rolesv.addElement(rs.nextToken());
		}
		
		String s = lines.nextToken().trim();
		boolean isSendDebugLog = s.equalsIgnoreCase(&#034;true&#034;);
		
		MasterData masterData = new MasterData(version, rolesv, sessionId, screenName, isSendDebugLog);
		onSuccess(masterData );
	}
	
}
&lt;/pre&gt;
&lt;/div&gt;
&lt;br&gt;&lt;br&gt;
An example of a JSP for the server is:
&lt;br&gt;&lt;br&gt;
&lt;hr size=&#034;2&#034; width=&#034;100%&#034; /&gt;
&lt;div style=&#034;border: thin none ; overflow: auto; width: 525px; height: 300px; background-color: rgb(255, 255, 255);&#034;&gt;
&lt;pre&gt;
&lt;%@page import=&#034;uk.co.maxant.cam.web.VersionHelper&#034;%&gt;
&lt;%@page import=&#034;org.apache.log4j.Logger&#034;%&gt;
&lt;%@page import=&#034;java.io.FileInputStream&#034;%&gt;
&lt;%@page import=&#034;java.util.Properties&#034;%&gt;
&lt;%@page import=&#034;java.io.RandomAccessFile&#034;%&gt;
&lt;%@page import=&#034;java.io.File&#034;%&gt;
&lt;%@page import=&#034;uk.co.maxant.util.ServiceLocator&#034;%&gt;
&lt;%@page import=&#034;uk.co.maxant.cam.services.OrchestrationService&#034;%&gt;
&lt;%@page import=&#034;uk.co.maxant.cam.data.Party&#034;%&gt;
&lt;%@page import=&#034;java.util.List&#034;%&gt;
&lt;%@page import=&#034;uk.co.maxant.cam.services.OrchestrationService.MasterData&#034;%&gt;
&lt;html&gt;
&lt;body&gt;
&lt;%
Logger log = Logger.getLogger(this.getClass().getCanonicalName());
String user = &#034;&#034;;
String roles = &#034;&#034;;

//either they are logged in, or we can check their login against the authentication header, since its ALWAYS sent
try{
	//we know who they are. if the installUid is known, lets add it to their profile!
	String installUid = request.getParameter(&#034;installUid&#034;);
	if(installUid != null &amp;&amp; installUid.equals(&#034;null&#034;)) installUid = null;

	String auth = request.getHeader(&#034;authorization&#034;);

	OrchestrationService os = ServiceLocator.getInstance().getOrchestrationService();
	MasterData md = os.getMasterData(auth, installUid);
	if(md.party != null) user = md.party.getScreenname();
	for(String role : md.roles){
		roles += role + &#034;,&#034;;
	}
	String version = VersionHelper.getVersionFromJad(request);
	
	boolean sendDebugLog = true; //change to false if we get swamped. but really, we shouldnt ever get this stuff.
	
	%&gt;OK
	&lt;%=version%&gt;|
	&lt;%=session.getId()%&gt;|
	&lt;%=user%&gt;|
	&lt;%=roles%&gt;|
	&lt;%=sendDebugLog%&gt;|
	&lt;%
}catch(Exception e){
	log.warn(&#034;failed to read master data&#034;, e);
	%&gt;ERROR
	&lt;%=e.getMessage() %&gt;
	&lt;%
}
%&gt;
&lt;/body&gt;
&lt;/html&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;br&gt;&lt;br&gt;
The result, is the maxant J2ME library, which runs on CLDC 1.1 and MIDP 2.0, and can be downloaded &lt;a target=&#039;_blank&#039; href=&#039;http://www.maxant.co.uk/tools.jsp&#039;&gt;here&lt;/a&gt;. It includes classes for all of the following, and more, and is released under the LGPL Licence:
&lt;br&gt;&lt;br&gt;
&lt;ul&gt;
&lt;li&gt;URLEncoder - from Apache Catalina&lt;/li&gt;
&lt;li&gt;Base64 - from Apache Commons Codec&lt;/li&gt;
&lt;li&gt;CredentialsProvider - an interface which the ServerCaller uses to get server credentials for logging in&lt;/li&gt;
&lt;li&gt;Formatter - formats for example decimals nicely&lt;/li&gt;
&lt;li&gt;GAUploader - loads an event to Google Analytics&lt;/li&gt;
&lt;li&gt;Math - some math stuff thats not in CLDC&lt;/li&gt;
&lt;li&gt;Point - a 2 dimensional point in a plane&lt;/li&gt;
&lt;li&gt;ServerCaller - a high level API for calling a server, as described above&lt;/li&gt;
&lt;li&gt;StringTokenizer - simple, and a lot like that from J2SE&lt;/li&gt;
&lt;li&gt;URL - an encapsulation of a protocol, domain, port and page&lt;/li&gt;
&lt;li&gt;ImageHelper - code taken from the web for scaling images&lt;/li&gt;
&lt;li&gt;DirectoryChooser - a UI for choosing a local directory on the device&lt;/li&gt;
&lt;li&gt;FileChooser - a UI for choosing a local file on the device&lt;/li&gt;
&lt;/ul&gt;
&lt;br&gt;&lt;br&gt;
To use the library, add it to the &#034;lib&#034; folder of your J2ME project, then ensure its on the build path, and in Eclipse Pulsar, you need to also check the box in the build path on the last tab, to ensure the classes in the JAR are exported to your JADs JAR.
&lt;br&gt;&lt;br&gt;
Copyright (c) 2010 Ant Kutschera
&lt;br&gt;&lt;br&gt;&lt;div class=&#034;tags&#034;&gt;&lt;span&gt;Social Bookmarks : &lt;/span&gt;&amp;nbsp;&lt;a href=&#034;http://slashdot.org/bookmark.pl?url=http://blog.maxant.co.uk:80/pebble/2010/02/21/1266791340000.html&amp;amp;title=A+J2ME+Library+and+a+simple+HTTP+Service+Framework&#034; target=&#034;_blank&#034; title=&#034;Add this post to Slash Dot&#034;&gt;&lt;img src=&#034;common/images/slashdot.png&#034; alt=&#034;Add this post to Slashdot&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;http://digg.com/submit?url=http://blog.maxant.co.uk:80/pebble/2010/02/21/1266791340000.html&amp;amp;title=A+J2ME+Library+and+a+simple+HTTP+Service+Framework&#034; target=&#034;_blank&#034; title=&#034;Digg this post&#034;&gt;&lt;img src=&#034;common/images/digg.png&#034; alt=&#034;Add this post to Digg&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;http://reddit.com/submit?url=http://blog.maxant.co.uk:80/pebble/2010/02/21/1266791340000.html&amp;amp;title=A+J2ME+Library+and+a+simple+HTTP+Service+Framework&#034; target=&#034;_blank&#034; title=&#034;Add this post to Reddit&#034;&gt;&lt;img src=&#034;common/images/reddit.png&#034; alt=&#034;Add this post to Reddit&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;http://del.icio.us/post?url=http://blog.maxant.co.uk:80/pebble/2010/02/21/1266791340000.html&amp;amp;title=A+J2ME+Library+and+a+simple+HTTP+Service+Framework&#034; target=&#034;_blank&#034; title=&#034;Save this post to Del.icio.us&#034;&gt;&lt;img src=&#034;common/images/delicious.png&#034; alt=&#034;Add this post to Delicious&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;http://www.stumbleupon.com/submit?url=http://blog.maxant.co.uk:80/pebble/2010/02/21/1266791340000.html&amp;amp;title=A+J2ME+Library+and+a+simple+HTTP+Service+Framework&#034; target=&#034;_blank&#034; title=&#034;Stumble this post&#034;&gt;&lt;img src=&#034;common/images/stumbleupon.png&#034; alt=&#034;Add this post to Stumble it&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;http://www.google.com/bookmarks/mark?op=edit&amp;amp;bkmk=http://blog.maxant.co.uk:80/pebble/2010/02/21/1266791340000.html&amp;amp;title=A+J2ME+Library+and+a+simple+HTTP+Service+Framework&#034; target=&#034;_blank&#034; title=&#034;Add this post to Google&#034;&gt;&lt;img src=&#034;common/images/google.png&#034; alt=&#034;Add this post to Google&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;http://technorati.com/faves?add=http://blog.maxant.co.uk:80/pebble/2010/02/21/1266791340000.html&#034; target=&#034;_blank&#034; title=&#034;Add this post to Technorati&#034;&gt;&lt;img src=&#034;common/images/technorati.png&#034; alt=&#034;Add this post to Technorati&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;http://www.bloglines.com/sub/http://blog.maxant.co.uk:80/pebble/2010/02/21/1266791340000.html&#034; target=&#034;_blank&#034; title=&#034;Add this post to Bloglines&#034;&gt;&lt;img src=&#034;common/images/bloglines.png&#034; alt=&#034;Add this post to Bloglines&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;http://www.facebook.com/share.php?u=http://blog.maxant.co.uk:80/pebble/2010/02/21/1266791340000.html&#034; target=&#034;_blank&#034; title=&#034;Add this post to Facebook&#034;&gt;&lt;img src=&#034;common/images/facebook.png&#034; alt=&#034;Add this post to Facebook&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;http://www.furl.net/storeIt.jsp?u=http://blog.maxant.co.uk:80/pebble/2010/02/21/1266791340000.html&amp;amp;t=A+J2ME+Library+and+a+simple+HTTP+Service+Framework&#034; target=&#034;_blank&#034; title=&#034;Add this post to Furl&#034;&gt;&lt;img src=&#034;common/images/furl.png&#034; alt=&#034;Add this post to Furl&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;https://favorites.live.com/quickadd.aspx?mkt=en-us&amp;amp;url=http://blog.maxant.co.uk:80/pebble/2010/02/21/1266791340000.html&amp;amp;title=A+J2ME+Library+and+a+simple+HTTP+Service+Framework&#034; target=&#034;_blank&#034; title=&#034;Add this post to Windows Live&#034;&gt;&lt;img src=&#034;common/images/windowslive.png&#034; alt=&#034;Add this post to Windows Live&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&#034;http://bookmarks.yahoo.com/toolbar/savebm?opener=tb&amp;amp;u=http://blog.maxant.co.uk:80/pebble/2010/02/21/1266791340000.html&amp;amp;t=A+J2ME+Library+and+a+simple+HTTP+Service+Framework&#034; target=&#034;_blank&#034; title=&#034;Add this post to Yahoo!&#034;&gt;&lt;img src=&#034;common/images/yahoo.png&#034; alt=&#034;Add this post to Yahoo!&#034; border=&#034;0&#034; /&gt;&lt;/a&gt;&lt;/div&gt;
        </description>
      
      
    
    
    
    <comments>http://blog.maxant.co.uk:80/pebble/2010/02/21/1266791340000.html#comments</comments>
    <guid isPermaLink="true">http://blog.maxant.co.uk:80/pebble/2010/02/21/1266791340000.html</guid>
    <pubDate>Sun, 21 Feb 2010 22:29:00 GMT</pubDate>
  </item>
  
  </channel>
</rss>

