You Are Here:

Community: Developer Discussion Boards

#1 Old RMS Compound date Update - 2008-01-27, 01:47

Join Date: Jan 2008
Posts: 57
thiagobrunoms
Offline
Regular Contributor
Hi Folks,
i need to update data through RMS (Record Management System) system. I could use

recordStoreObj.setRecord(int recordId, byte[] newData, int offset, int numBytes).

However, this is a compound data, which has three primitive ''ints'', one String object, and one byte array (byte[]). To store such a thing, i have used ByteArrayOutputStream and DataOutputStream. To retrieve them, ByteArrayInputStream and DataInputStream are used.

How can i update the data!?!?!!?
Thanks!
Reply With Quote

#2 Old Re: RMS Compound date Update - 2008-01-27, 22:51

Join Date: Jan 2008
Posts: 57
thiagobrunoms
Offline
Regular Contributor
Hi Folks,
i'm trying to update a compound date through RMS. Below follow my code. However, after executing it, and when i try to read (again) the data, the following exception is thrown:

Code:
java.lang.ArrayIndexOutOfBoundsException
	at com.sun.midp.io.j2me.storage.RandomAccessStream.readBytes(+13)
	at com.sun.midp.rms.RecordStoreFile.read(+10)
	at javax.microedition.rms.RecordStore$RecordHeader.read(+36)
	at javax.microedition.rms.RecordStore.getRecord(+27)
	at br.cg.pactus.PersistenceManager.readStream(+75)
	at br.cg.pactus.Pactus.showReports(+88)
	at br.cg.pactus.Pactus.itemStateChanged(+192)
	at javax.microedition.lcdui.Form.callItemStateChanged(+27)
	at javax.microedition.lcdui.Display.callItemStateChanged(+14)
	at javax.microedition.lcdui.Display.access$900(+5)
	at javax.microedition.lcdui.Display$DisplayAccessor.callItemStateChanged(+8)
	at javax.microedition.lcdui.Display$DisplayManagerImpl.callItemStateChanged(+10)
	at com.sun.midp.lcdui.DefaultEventHandler.itemStateChangedEvent(+17)
	at com.sun.midp.lcdui.DefaultEventHandler$QueuedEventHandler.run(+835)
Code:
try {
   newsId               = 10;
   SearchFielter search = new SearchFielter(newsId);
   RecordEnumeration e  = this.record.enumerateRecords(s, null, false);

   if (e.numRecords() > 0) {
	int recordId = e.nextRecordId();

	ByteArrayOutputStream arrayOut  = new ByteArrayOutputStream();

	DataOutputStream streamDataType = new DataOutputStream(arrayOut);
	
        streamDataType.writeInt(newsId);				        streamDataType.writeInt(newsStatus);				        streamDataType.writeInt(newsContentType);
        streamDataType.writeUTF(newsDescription);
	streamDataType.write((byte[]) newsBytes);
				
	byte[] newsUpdated = arrayOut.toByteArray();
	this.record.setRecord(recordId, newsUpdated, 0, newsUpdated.length);

	arrayOut.close();
	streamDataType.close();
   
   } else {
	System.out.println("nenhum encontrado!");
   }
   
   search.closeSearchFilter(); 
   e.destroy();

Thanks for helping me!!!!!
Reply With Quote

#3 Old Re: RMS Compound date Update - 2008-01-28, 10:05

Join Date: Aug 2006
Posts: 65
Location: Spain
ncerezo2
Offline
Regular Contributor
Hi,
The exception does not match the code. You create a search filter, that you don't seem to use afterwards, so I understand you modified the code previously to post it here and removed the conflictive lines, as you are using variables that look to be the ones you read but are delcared or used anywhere.
The error raises in getRecord, and you are not calling getRecord here, but you might be doing in your filter ir order to filter out rows or in that piece of missing code.
Despite that, it is advisable to declare the streams outside a try-catch-finally bloc, making sure to close them in the finally block. And also advisable is to reuse objects as much as possible. In this case, you can use the output stream once and again just by calling arrayOut.reset() before each record.
I would create methods for reading and writting independently, so it's easier to deal with possible read and/or write errors.
You seem to be using a byte array to get the record data with getRecord (in the missing code), and I presume that the issue is that the array size is not the right one, you might be using an array smaller than the recordsize.
If you post the full code we might be able to help you further.
I've already posted how to retrieve/iterate the records of a record store, and there're many other examples out there.


-----------------------
Narciso Cerezo
CTO Elondra

OpenBaseMovil
http://www.openbasemovil.org
The Framework for enterprise class J2ME applications with a pure J2ME relational database engine and much more.
Reply With Quote

#4 Old Unhappy Re: RMS Compound date Update - 2008-01-28, 12:12

Join Date: Jan 2008
Posts: 57
thiagobrunoms
Offline
Regular Contributor
hI ncerezo2, thanks for helping. There r some codes bellow. The first method is responsible for writing streams:

Code:
ByteArrayOutputStream output    = new ByteArrayOutputStream();
DataOutputStream streamDataType = new DataOutputStream(output);
byte[] record                   = null;

streamDataType.writeInt(newsId);
streamDataType.writeInt(newsStatus);
streamDataType.writeInt(newsContentType);
streamDataType.writeUTF(newsDescription);

//newsStreams[] is an Object array.
streamDataType.write((byte[]) newsStreams[0]); 
streamDataType.flush();

record           = output.toByteArray();
this.recordIndex = this.record.addRecord(record, 0, record.length);

output.reset();
output.close();
streamDataType.close();
First of all, i can write some data through the above method. After do the storing, i would like to find a especific data. So, i created a filter:

Code:
public void matches(byte[] candidate) {
  this.inputByte = new ByteArrayInputStream(candidate);
  this.dataInput = new DataInputStream(this.inputByte);
  int idP = -1;
  
  try {
     idP = this.dataInput.readInt();
  } catch (IOException e) {
     e.printStackTrace(); 
  }
		
  this.closeByteArrayInputAndDataInputStream();
	
  if(this.id == idP) {
     return true;
  } else {
     return false;	
  }		
}
}
The filter approach is used with RecordEnumaration. The method bellow is responsible for finding the especific record:

Code:
SearchFielter s = new SearchFielter(newsId);
RecordEnumeration e = this.record.enumerateRecords(s, null, false);

if (e.numRecords() > 0) {

  int recordId = e.nextRecordId(); 
  e.destroy();

  ByteArrayOutputStream arrayOut  = new ByteArrayOutputStream();
  DataOutputStream streamDataType = new DataOutputStream(arrayOut);

  streamDataType.writeInt(newsId);
  streamDataType.writeInt(newsStatus);
  streamDataType.writeInt(newsContentType);
  streamDataType.writeUTF(newsDescription);
  streamDataType.write((byte[]) newsStreams[0]);
  arrayOut.close();
  streamDataType.close();
  byte[] newsUpdated = arrayOut.toByteArray();
  this.record.setRecord(recordId, newsUpdated, 0, newsUpdated.length);
}
So, just to recapitulate. The first method is used to store record. After do that, i would like to find a especific record. I created the filter and a RecordEnumation and i call this last method (the above one). If the record is found, i get new data to update the record. Finally, i read the all stored data with the method bellow:

Code:
byte[] recData = new byte[100000];

ByteArrayInputStream streamBytes = new ByteArrayInputStream(recData);
DataInputStream streamDataType = new DataInputStream(streamBytes);

Hashtable hash = new Hashtable();
Object[] news = null;
byte[] media = null;

RecordEnumeration enumeration = this.record.enumerateRecords(null,null,false);

while (enumeration.hasNextElement()) {
 this.record.getRecord(enumeration.nextRecordId(), recData, 0);
 media = new byte[streamBytes.available()];
 int newsId = streamDataType.readInt();
 int newsStatus = streamDataType.readInt();
 int newsContentType = streamDataType.readInt();
 String description = streamDataType.readUTF();
 streamDataType.read(media);
 streamBytes.reset();
}

enumeration.destroy();
streamBytes.close();
streamDataType.close();
This is all i do. If i just write and read data, everything runs Ok. Otherwise, if i try to update, sometimes the execution halts, or the execution go ahead and an exception is thrown when i read all data after updating.

Thanks one more time ncerezo2.
Last edited by thiagobrunoms : 2008-01-28 at 12:18.
Reply With Quote

#5 Old Re: RMS Compound date Update - 2008-01-28, 14:40

Join Date: Aug 2006
Posts: 65
Location: Spain
ncerezo2
Offline
Regular Contributor
There're a couple of wrong things here that might be raising the error:

1. First piece of code, it has just a small non-harmful issue:
a. You're calling output.reset(); before closing. That won't hurt, but is unnecessary. The purpose of reset is to reuse the byte buffer that the ByteArrayOutputStream has created for new output, it is just as if you were opening a new stream but with much less overhead and object churning. Since you declare the streams locally (apparently) it is no sense. It might be advisable to declare the streams as class members to reuse them, or if you are in a loop to declare them outside the loop, but it depends on how you use them.

2. Second one, some non-harmful issues:
a. You're declaring the streams as class members, and this is no sense here since you're assigning new values each time (no other way to do it as you receive the byte array).
b. The use of the exceptions is a bit misleading, the method could be easily rewritten as:
Code:
public void matches(byte[] candidate) 
{
    ByteArrayInputStream bais = null;
    DataInputStream in = null;
    try
    {
        bais = new ByteArrayInputStream( candidate );
        in = new DataInputStream( bais );
        int idP = in.readInt();
        return idP == id;
    }
    catch( IOException e )
    {
        e.printStackTrace(); 
        return false;
    }
    finally
    {
        if( in != null ) try{ in.close() }catch( Exception e ){}
        if( bais != null ) try{ bais.close() }catch( Exception e ){}
    }
}
This way you're sure to close the streams and that exceptions are treated always right.

3. Third one: one potentially harming issue, some minor.
a. You're closing the ByteArrayOutputStream first, then the DataOutputStream which depends on it. The close method for DataInputStream calls flush and then close on the underlying stream, so you could simply call close on the DataOutputStream. The way you do it, you could end up with some data in a buffer of the DataOutputStream unable to be written to a closed ByteOutputStream and you would have corrupted the record data.
It is safe to call it as you did in the first piece: DataOutputStream.flush, DataOutputStream.close, ByteArrayOutputStream.close, although only DataOutputStream.close is needed if we rely in the documentation.
b. The minor ones are the same as above, you should declare the streams outside a try-catch-finally to ensure that exceptions are handled properly and streams closed.
This is not an issue as big as it would be with real files or with RecordStores, as they're in-memory arrays, but it's advisable.

4. Fourth pice: the worst one :-)
a. You're wasting a lot of memory here, look at this thread for some good ways of iterating RecordStores.
b. The first thing is that you call available to determine the media size and:
b.1. It won't work since the DataInputStream has no way to determine where in the 100000 bytes of the byte array stream is the end of the current data, it will probably decide that 100000 is the available size, so you are again creating another byte array of 100000 bytes.
b.2. You're assuming that the "media" size is the same as the available data, but the available data will include also the 3 ints and the string you're writting/reading first.
c. I've found that some implementations are buggy (WTK 2.2 for example) with the read( byte[] ) methods, so it is safer to do it the hard way (with a for reading 1 byte at a time).

So to be sure you do things right, take the example of the other thread with the growing size byte array, and then make two changes to your write and read code:
First, you should write an int to the stream with the byte[] size for "media", and then the media itself.
Second, change your read code to read the int with the size, and create an array for the media with that size, then read it from the stream.

You can also save time an take a look at OpenBaseMovil, the core library has some useful things for dealing with this kind of things, such as the SerializerInputStream and SerializerOutputStream which are high level streams over DataInputStream and DataOutputStream. They provide methods for writing complex objects, including collections, custom Serializable objects, byte arrays, and other types.


-----------------------
Narciso Cerezo
CTO Elondra

OpenBaseMovil
http://www.openbasemovil.org
The Framework for enterprise class J2ME applications with a pure J2ME relational database engine and much more.
Reply With Quote

#6 Old Talking Re: RMS Compound date Update - 2008-01-29, 00:44

Join Date: Jan 2008
Posts: 57
thiagobrunoms
Offline
Regular Contributor
Hi ncerezo2, thanks one more time for helping. The problem was really the size of the media byte[]. :)
Thanks for the advices. :D

Regards.
Thiago
Reply With Quote
Reply « Previous Thread | Next Thread »
Display Modes
Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules

You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are Off
[IMG] code is Off
HTML code is Off
Forum Jump
Similar Threads
Thread Thread Starter Forum Replies Last Post
RMS record sizes. earamsey Mobile Java General 1 2007-10-18 14:51
Is it possible to update a record from a MIDlet's RMS? fightersoft Python 4 2006-12-27 17:39
update rms via ota kevin1964 Mobile Java General 2 2006-02-21 08:32
How to update calendar entrys date? kosjanne General Symbian C++ 3 2005-03-08 18:08
7210 & 7250 RMS resetting problem keithmcneill Mobile Java General 1 2003-11-13 22:04

Rate This

 
Bookmark this page: DeliciousDiggFacebookGoogleYahooStumbleUponRedditDiigoTechnocratiTwitter  Share this page Share this page Print this Page Print this page Invite a friend Invite a friend
京ICP备05048969号    Email Newsletters Press Terms & Conditions Privacy Policy Sitemap Contact Us © 2009 Nokia