The classic recycle design pattern is flawed in some document iterations


As we all know you need to recycle your Domino Objects in Java to free up the back-end C++ objects, or your server will crash in the end.


People in general use the same pattern when iterating through documents.

Something like this:

Document doc = view.getFirstDocument();
while (doc != null) {
     Document tmpdoc = view.getNextDocument(doc);
     doc.recycle();
     doc = tmpdoc;
}


This will work in must situations, but in at least one situation you will get a serious NotesException:
"Object has been removed or recycled"

An example showing the issue
To prove this point I will show you an example.

I have created a simple demo database containing a few documents to illustrate the issues.
The documents contain only 2 items Country and Region.
Country is a multi-value field since a region can contain several countries.
The database contains a simple view with 3 columns


The first column has the property "Show multiple values as separate values" set so each entry in the documents item will show up in its own row
This is handy and a VERY useful feature, especially for lookups in the view.


There are only 4 documents in the database, but the view has 6 entries since one document has an Country item with 3 values.
The very important thing to notice is Norway and Sweden are both the same document.
To illustrate this I have added a third column which shows the NoteID for the document.

So what happens if the normal recycle pattern is used?

....


Document doc = view.getFirstDocument();
int counter = 1;
System.out.println("\nTest View. Getting columnValues from documents using getNextDocumnt iteration");
System.out.println("-----------------------------------------------------------------------");
while (doc != null) {
Vector cv = doc.getColumnValues();
if (cv.get(0).getClass().getName().endsWith("String")) {
System.out.println(counter++ +". String doc: " + cv.get(0)
+ "-" + cv.get(1));
} else if (cv.get(0).getClass().getName().endsWith("Vector")) {
Vector fieldVector = ((Vector) cv.get(0));
System.out.println(counter++ + ". Vector doc: " + fieldVector.get(0)+"-" + cv.get(1));
}
Document tmpDoc = view.getNextDocument(doc);
doc.recycle();
doc = tmpDoc;
}
....


You will get a NotesException! Surprisingly since this pattern has always worked.

When the document iteration gets to the Sweden document it fails Why?

The problem is that Norway and Sweden are the same document.
So instead of getting hold of a new document as it normally would it, getNextdocument assigns tmpDoc to the same doc once again.

Document tmpDoc = view.getNextDocument(doc);


doc.recycle();
doc = tmpDoc;


Since doc now gets recycled, so does tmpdoc since they point to the same backend document.
So when I try to use the doc document it fails with a NotesException "Object has been removed or recyled" ...which is true :-)

The fix
The workaround to this is very simple, do not recycle if two consecutive documents are the same document.

if (doc!=tmpDoc) {


doc.recycle();
}


What about Entries?
You may be safe here but as I will show in my next blog, there is a bug in ViewEntryCollections that will give you wrong values!

Lesson learned
So the lesson learned here is that if a column has "Show multiple values as separate values" set, you need to use the recycle workaround if you iterate with documents
Yes, the example here is of course a special case where two consecutive entries are the same document, and you may never have run in to it, ....but you might the next time :-)

UPDATE
In the comments to this blorentry Karsten Lehmann (who's work I respect a lot!) has questioned the workaround claiming that the recycle will never be called.
So to show it IS actually being called I have inserted two simple print statements.

if (tmpDoc != null) {


if (doc!=tmpDoc) {
System.out.println("Recycling");
doc.recycle();
}else{
System.out.println("No Recycling");
}
}


and this gives


Which shows it recycles exactly as intended.


Posted on 04/19/2013 04:34:35 PM CEDT