recordmanagment2
DESCRIPTION
TRANSCRIPT
Record Management Store
By using the RMS…let’s see how.
Persistent Storage Basics
• Simple record-oriented database (RMS) stored in Flash mem
• Device-independent API• Records are arrays of bytes that live in record
stores• Record stores are shared within MIDlet suite– MIDP 2.0 allows for optional sharing of record
stores between MIDlet suites • Support for enumeration, sorting, and filtering• Atomic update for single records
RMS Classes and Interfaces
• Defined in javax.microedition.rms package• RecordStore– Collection of records
• RecordEnumerator– RecordStore enumerator
• Interfaces– RecordComparator– RecordFilter– RecordListener
Using Records
• Nothing fancy here– Array of bytes
• Integer values used a unique ID• Records can be written using java.io API’s in
CLDC support:– DataInputStream DataOutputStream– ByteArrayInputStream ByteArrayOutputStream
int id byte[] data
int id byte[] data
int id byte[] data
J2MERecordStore
OldRecordStore
RecordStore Operations
• Standard operations you would expect:– openRecordStore(name,create)– removeRecordStore(name)
• Get list of all known record stores in a MIDlet suite– listRecordStores()
• Get the size of the RS– int getSize() - # of bytes used by the RS– Int getSizeAvailable() – get amount of space
available for both record data and overhead
Creating a RecordStore
• Centered around record stores– Small database that contains data called records
• javax.microedition.rms.RecordStore• Record stores are identified by namepublic static final String RS_NAME = "Tasks";try { m_RS = RecordStore.openRecordStore(RS_NAME,
bCreateIfNecessary); } catch (RecordStoreException ex) { this.m_bRecordStoreError = true; this.m_sRecordStoreError = ex.toString(); }
Closing/Deleting RecordStore
• Need to make sure that you release the resource
protected void destroyApp(boolean parm1) throws javax.microedition.midlet.
MIDletStateChangeException {try { m_RS.closeRecordStore(); RecordStore.deleteRecordStore(RS_NAME); } catch (RecordStoreException ex) { ex.printStackTrace(); System.out.println(ex.getMessage()); } }
RMSMidlet• Sample MIDlet so that you can become familiar with RMS
and take a little tour.– com.sb.samples.midlet.rms package
• Our objective is to introduce RMS basics including:
1. RecordStore creation/closing2. Record adding3. Record lookup filtering via RecordFilter4. RecordStore deletion (no deletions take place
though very easy to add)5. Encapsulating RecordStore I/O operations in
static Utility Class methods for consistency and program simplification
RMSMidlet Description
• Very simple• Creates 5 TaskRow objects.• Only object 3 has been designated as 'already
uploaded to server'.• Shows a single form with only those TaskRows
that have not been uploaded to the server (there are 4 of them in this case).
• On the form is the Record ID of the Task• User can press 'View' button to see Task
information
TaskRow Class
• com.sb.samples.midlet.rms.TaskRow class that acts as:– Container object for Task values– Decouples Task data from RecordStore
representation– Static utility tool for consistent, maintainable
TaskRow-to-RecordStore I/O
Adding Records
• TaskRow offers a number of ways to add, read, and update a record
public static int addRecord(RecordStore rs, TaskRow taskRow) throws Exception {
byte[] bData = TaskRow.toByteArray(taskRow); int iRecordID = rs.addRecord(bData, 0,
bData.length); return iRecordID; } public static void updateRecord(RecordStore rs,
TaskRow taskRow) throws Exception { byte[] bData = TaskRow.toByteArray(taskRow); rs.setRecord(taskRow.iID, bData, 0,
bData.length); }
Read Record public static TaskRow readRecord(RecordStore rs, int iRecordID) throws Exception {
byte[] bData = rs.getRecord(iRecordID); TaskRow tr = readRecord(bData); tr.iID = iRecordID; return tr; } // Maintain a single, consistent InputStream-based read mechanism // for use by readRecord and any other usage public static TaskRow readRecord(DataInputStream dis) throws Exception {
TaskRow tr = new TaskRow();
// the order of these reads must match // the reads in TaskRow.toByteArray() with respect to: // 1) the ordering of the values // 2) the data type of each value tr.iRadioID = dis.readInt(); tr.bUploaded = dis.readBoolean(); tr.sTask = dis.readUTF(); tr.sLongitude = dis.readUTF(); tr.sLatitude = dis.readUTF(); tr.sStatus = dis.readUTF(); return tr; }
Read Record – Byte Array // Offer a Byte Array-based reader for parts of the program that // do not have a reference to RecordStore // (e.g. by RecordFilter or RecordComparator that receive only
byte arrays)
public static TaskRow readRecord(byte[] bData) throws Exception { ByteArrayInputStream bais = new ByteArrayInputStream(bData); DataInputStream dis = new DataInputStream(bais); try { return readRecord(dis); } finally { if (null != dis){ try { dis.close(); } catch (Exception e){
} } } }
Lookup Filtering
• The RMSMidlet keeps track of records that have not been uploaded to a server.
• And then we use a Filter to determine which records need to be uploaded.
Filter for Records protected TaskRow[] getUnuploadedTaskRows() throws Exception { // Filter for tasks not yet uploaded to server RecordFilter rf = new RecordFilterTaskNotUploaded(); // If we cared about ordering (e.g. Data created), we'd // create a RecordComparator for that purpose. For now, don't sort. RecordComparator nullRecordComparator = null; boolean bKeepUpdated = false; RecordEnumeration re = m_RS.enumerateRecords(rf,nullRecordComparator,
bKeepUpdated); TaskRow[] aTaskRows = new TaskRow[re.numRecords()]; int i = 0; while (re.hasNextElement()) { int iNextRecord = 0; try { iNextRecord = re.nextRecordId(); TaskRow tr = TaskRow.readRecord(m_RS, iNextRecord); aTaskRows[i] = tr; i++; } catch (Exception ex) { throw ex; } } return aTaskRows; }
RecordFilterTaskNotUploaded Class
public class RecordFilterTaskNotUploaded implements RecordFilter { // only allow TaskRows that have not been uploaded to // be accepted public boolean matches(byte[] bCandidate) { try { TaskRow tr = TaskRow.readRecord(bCandidate); if (tr.bUploaded == false) { return true; } else { return false; } } catch (Exception e) { return false; }}
RMS Tour
• That concludes our RMS tour.• Unlike most tours, our RMS tour doesn’t end
at a gift shop…
Enterprise J2ME(The whirlwind tour…)
J2ME and J2EE
• MIDP App’s become a client with Middle-tier access
• XML data provided by a Servlet at well-known URL or through Web Services
• Received as streaming data using networking API’s
• Converted to String for parsing (i.e. kXML)• Display using MIDP UI elements
Wireless Enterprise
HTTP
Tomcat BackendSystems
Business TierWeb Tier
XMLRPCServlet
Mobile Services
kXML-RPC Usage
MIDP & XML
• MIDP networking allows access to data formats like WML, XML– We’re just interested in XML
• Upside– Easy to work with– Most are familiar with XML
• Downside– Expensive– Heavy String manipulation– Footprint for the parser
• For some devices, not as big a deal because available resources are greater
MIDP XML Parsers
• A few parsers are available for MIDP• kXML– http://www.kxml.org– Pull parser– SAX and DOM support– Written for J2ME/CLDC/MIDP
• NanoXml– http://nanoxml.sourceforge.net/kvm.html– DOM– Ported
• Both support XSLT
kXML-RPC
• kXML-RPC is a J2ME implementation of the XML-RPC protocol built on top of the kXML parser.
• Extremely lightweight mechanism for exchanging data and invoking web services in a neutral, standardized XML format.
• Hides the implementation of kXML
Dealing with Payloads
• Not really all that much to do here when using kXML-RPC, the payload is handled for you on the client, and the server uses a TaskHandler to get the parameters
• Let’s look at making a call, and then a sample of what a payload might look like if you wanted to sniff the wire…
Making an XML-RPC Call
• Using XML-RPC is as simple as doing the following:
String connect = “http://www.switchbacksoftware.com/service.do”;
String ticker = "Submitting to Server"; updateUI(); XmlRpcClient xmlrpc = new XmlRpcClient(connect); try { String result = (String)
xmlrpc.execute(SB.submitTaskInfo, paramsArray);
Payload Example<methodCall> <methodName>SB.submitTaskInfo</methodName> <params> <param> <value> <string>Task1</string> </value> </param> <param> <value> <string>W 12.32560</string> </value> </param> <param> <value> <string>N 72.09090</string> </value> </param>
</params></methodCall>
J2ME Web Services API (WSA) JSR-172 (CLDC 1.0 & 1.1) - API's standardize remote service invocation and XML parsing - subsets of based on JAX-RPC 1.1 and SAX2
WTK 2.1, includes the libraries you need to develop MIDlets that take advantage of J2ME WS and also includes a JAX-RPC stub generator
Good article http://developers.sun.com/techtopics/mobility/apis/articles/wsa/
Couple more advanced topics…
Timers & System Properties
Timer and TimerTask
• Introduced in J2SE 1.3 to schedule tasks for execution by a background thread
• MIDP includes the Timer and TimerTask classes
• Only J2SE classes that are not included in the CLDC but are included in MIDP
• Timer API identical to J2SE version except (there’s always an exception…) the constructor that specifies whether the thread is a daemon is missing.– Remember…daemon threads aren’t supported in
MIDP• TimerTask exactly the same in J2SE & MIDP
Timer MIDlet
• Found in com.sb.samples.midlet.timer package
• Objectives:1. Introduce Timer and TimerTask2. Extend RMS basics to include an ‘Updating
Task Records’
TimerMIDLet Description
• Creates a TimerTask that does the following each time it is invoked:– Scans RecordStore for TaskRows that have 'not yet
been uploaded‘– Picks the first Task in the list and uploads it via
submitTask()– Updates it's status value and flags it as 'uploaded‘– Updates the respective record in the RecordStore– Makes a sound upon upload– Rebuilds the UI to reflect a dwindling list of 'not
uploaded' Tasks
Developer Note
• We’re not going to walk through the building of the UI, since we’ve already seen how Commands are added and handled in other examples
TimerMidletpublic class TimerMidlet extends RMSMidlet { Command m_CommandRefresh = null; Timer m_TimerUpload = null; TimerTask m_TimerTaskUploadATaskRow = null;
public TimerMidlet() { super();
// need to create a TimerTask that gets the list of not uploaded TaskRows and
// uploads them (or just the first) and // updates the record to bUploade = true; // m_TimerTaskUploadATaskRow = new TimerTask(){
TimerMidletpublic void run() { try { // Get the list of un-uploaded Tasks TaskRow[] aTaskRows = getUnuploadedTaskRows();
if (aTaskRows.length > 0){ TaskRow tr = m_aTaskRows[0]; // Submit them to server submitTask(tr); try { // and update the RMS record TaskRow.updateRecord(m_RS, tr); }
TimerMidletcatch (Exception ex) { Alert a = new Alert("Task", "Error updating task "
+ tr.sTask + ":" + ex.getMessage(), null, AlertType.ERROR); a.setTimeout(Alert.FOREVER); AlertType.ERROR.playSound(m_Display); m_Display.setCurrent(a, m_Display.getCurrent()); } try { buildUI(); } catch (Exception e) { } m_Display.setCurrent(m_FormTasks); } } catch (Exception e) { } } };
Start the Timer
m_TimerUpload = new Timer(); m_TimerUpload.schedule(this.m_TimerTaskUploadATaskRow, 5000, 10000);
}
Submitting The Task protected void submitTask(TaskRow tr) {
Vector vParams = new Vector(); vParams.addElement(tr.sTask); vParams.addElement(tr.sLongitude); vParams.addElement(tr.sLatitude); vParams.addElement(new Integer(tr.iRadioID)); // radio Id String sResult = null;
// normally would use a constants file or properties singleton // or MIDlet attributes String sConnect = HomeBase1.HOME_BASE_SERVER + HomeBase1.HOME_BASE_URL_FILE; XmlRpcClient xmlrpc = new XmlRpcClient(sConnect); try { sResult = (String) xmlrpc.execute(HomeBase1.HOME_BASE_SUBMIT_RPC, vParams); // When not using real phone, sleep so tos // simulate delay for testing destroyApp(false) // Thread.sleep(10000); AlertType.INFO.playSound(m_Display); tr.bUploaded = true; } catch (Exception e) { sResult = "Error Submitting:" + e.getMessage(); AlertType.ERROR.playSound(m_Display); } tr.sStatus = sResult; }
System Properties
Use of System Properties
• CLDC does not include the java.util.Properties class
• Limited set of properties available using System.getProperty( String key)
• microedition.platform - Name of the host platform or device (implementation-dependent)
• microedition.encoding - Default character encoding Default value:“ISO-8859-1”
• microedition.configuration - Name and version of the supported configuration “CLDC-1.1”
• microedition.profiles - Names of the supported profiles (implementation-dependent)
Bootcamp Wrap-up• J2ME is an exciting development opportunity• Fairly easy to transition to if you are at Java-
eese.• Need to make some adjustments in your
programming habits (Come to the J2ME Best Practices session to find out more!)
• J2ME/J2EE integration will be powerful for a new breed of applicationsIf your company is interested in the full 2-day intensive J2ME developer bootcamp or requires contracting services for mobile projects, contact
me directly at [email protected]
Thanks!