7

I use a function to get access to a configuration document:

private Document lookupDoc(String key1) {
    try {
        Session sess = ExtLibUtil.getCurrentSession();
        Database wDb = sess.getDatabase(sess.getServerName(), this.dbname1);
        View wView = wDb.getView(this.viewname1);
        Document wDoc = wView.getDocumentByKey(key1, true);
        this.debug("Got a doc for key: [" + key1 + "]");
        return wDoc;
    } catch (NotesException ne) {
        if (this.DispLookupErrors)
            ne.printStackTrace();
        this.lastErrorMsg = ne.text;
        this.debug(this.lastErrorMsg, "error");
    }
    return null;
}

In another method I use this function to get the document:

Document wDoc = this.lookupDoc(key1);

if (wdoc != null) {
    // do things with the document
    wdoc.recycle();
}

Should I be recycling the Database and View objects when I recycle the Document object? Or should those be recycled before the function returns the Document?

halfer
  • 19,824
  • 17
  • 99
  • 186
Newbs
  • 1,632
  • 11
  • 16
  • 2
    One additional comment besides Tim's excellent response: a (much) faster way to retrieve a specific document is by using a db.getDocumentByUNID() call. So if you need to retrieve the same document multiple times, at the first call you can retrieve it from the view and store its UNID in a private variable. In subsuquent calls you can then use that UNID to retrieve it. – Mark Leusink Jun 23 '12 at 06:39
  • @Mark: I have already implemented caching of the data in appropriate scope variables once the document is read, so there is rarely the need to re-read the document until the scoped variables are gone... In some cases the info in the scoped variable includes the UNID so that re accessing the document is as fast as possible. /Newbs – Newbs Jun 23 '12 at 09:44

3 Answers3

21

The best practice is to recycle all Domino objects during the scope within which they are created. However, recycling any object automatically recycles all objects "beneath" it. Hence, in your example method, you can't recycle wDb, because that would cause wDoc to be recycled as well, so you'd be returning a recycled Document handle.

So if you want to make sure that you're not leaking memory, it's best to recycle objects in reverse order (e.g., document first, then view, then database). This tends to require structuring your methods such that you do whatever you need to/with a Domino object inside whatever method obtains the handle on it.

For instance, I'm assuming the reason you defined a method to get a configuration document is so that you can pull the value of configuration settings from it. So, instead of a method to return the document, perhaps it would be better to define a method to return an item value:

private Object lookupItemValue(String configKey, itemName) {
    Object result = null;
    Database wDb = null;
    View wView = null;
    Document wDoc = null;
    try {
        Session sess = ExtLibUtil.getCurrentSession();
        wDb = sess.getDatabase(sess.getServerName(), this.dbname1);
        wView = wDb.getView(this.viewname1);
        wDoc = wView.getDocumentByKey(configKey, true);
        this.debug("Got a doc for key: [" + configKey + "]");
        result = wDoc.getItemValue(itemName);
    } catch (NotesException ne) {
        if (this.DispLookupErrors)
            ne.printStackTrace();
        this.lastErrorMsg = ne.text;
        this.debug(this.lastErrorMsg, "error");
    } finally {
        incinerate(wDoc, wView, wDb);
    }
    return result;
}

There are a few things about the above that merit an explanation:

  • Normally in Java, we declare variables at first use, not Table of Contents style. But with Domino objects, it's best to revert to TOC so that, whether or not an exception was thrown, we can try to recycle them when we're done... hence the use of finally.
  • The return Object (which should be an item value, not the document itself) is also declared in the TOC, so we can return that Object at the end of the method - again, whether or not an exception was encountered (if there was an exception, presumably it will still be null).
  • This example calls a utility method that allows us to pass all Domino objects to a single method call for recycling.

Here's the code of that utility method:

private void incinerate(Object... dominoObjects) {
    for (Object dominoObject : dominoObjects) {
        if (null != dominoObject) {
            if (dominoObject instanceof Base) {
                try {
                    ((Base)dominoObject).recycle();
                } catch (NotesException recycleSucks) {
                    // optionally log exception
                }
            }
        }
    }
}

It's private, as I'm assuming you'll just define it in the same bean, but lately I tend to define this as a public static method of a Util class, allowing me to follow this same pattern from pretty much anywhere.

One final note: if you'll be retrieving numerous item values from a config document, obviously it would be expensive to establish a new database, view, and document handle for every item value you want to return. So I'd recommend overriding this method to accept a List<String> (or String[ ]) of item names and return a Map<String, Object> of the resulting values. That way you can establish a single handle for the database, view, and document, retrieve all the values you need, then recycle the Domino objects before actually making use of the item values returned.

Eric McCormick
  • 2,716
  • 2
  • 19
  • 37
Tim Tripcony
  • 8,056
  • 1
  • 23
  • 34
  • Tim, you are, as always, the go to man for this technology. Thanks for both the excellence and completeness of this answer. Very, very helpful. /Newbs – Newbs Jun 22 '12 at 23:21
  • My pleasure. I'm glad you found it useful. – Tim Tripcony Jun 23 '12 at 03:40
  • Current database should not be recycled because any View or other child objects will then be gone in the calling code. The same applies of course to all Domino objects. If you happen to have a View object pointing to this same view in calling code recycling the view here will destroy that view object too. In most cases it's enough not to recycle current Database and Session. – Panu Haaramo Dec 14 '13 at 10:20
  • That's very true, and is mentioned in this answer: http://stackoverflow.com/a/17216904/1171761 This can be a bit confusing, because it's possible to have multiple Java pointers to the same back end object, each with a separate C++ handle. So if you get the current database the way Newbs does here, it should be recycled even though it's the current database, because even though the `database` variable refers to the same Domino database, the pointer obtained here has a separate C++ handle.. which is why you should always use the variable resolver to get the current session and database. – Tim Tripcony Dec 14 '13 at 17:51
  • ...or just use the OpenNTF Domino API so you never have to worry about recycling again. ;) – Tim Tripcony Dec 14 '13 at 17:52
  • One additional thought: the code in this question (and my answer) refers to `this.dbname1`. We're assuming that identifies the current database, but it could be any valid filepath, because it's a property of the class, and we're not shown here how that property was initialized. So the core principle holds: if you obtain a new handle in your routine, recycle that handle in your routine, but don't recycle handles obtained elsewhere... and never, ever, ever, recycle a session yourself. – Tim Tripcony Dec 14 '13 at 18:03
  • OK that's interesting information. I would think using variable resolver always is heavy and having multiple objects in C++ level consumes much more memory. Need to take a look at OpenNTF Domino API. – Panu Haaramo Dec 15 '13 at 17:01
  • You might find this article interesting: http://nathantfreeman.wordpress.com/2013/03/21/recycle-and-the-retail-experience/ The stock variable resolver is actually quite efficient... if it weren't, the whole platform would be unacceptably slow, because any EL reference (including SSJS) to any variable touches the variable resolver. For instance, the expression `#{contact.firstName}` asks the resolver what `contact` is, and depending on what is bound to that expression, it may have to hit the resolver a dozen times in a single request just to evaluate that one expression. – Tim Tripcony Dec 15 '13 at 18:18
  • So imagine how many times the resolver is invoked on a page with dozens of components bound to a document data source... it has to keep asking what `contact` is, over and over again. The good news is, the stock data sources are highly optimized to do as much in RAM as possible, and only touch disk and the Domino API when truly necessary. Well-structured managed beans offer a similar advantage. Ultimately, consuming more RAM isn't intrinsically "bad", because RAM is both faster and cheaper than disk... – Tim Tripcony Dec 15 '13 at 18:24
  • ...but Domino enforces a finite limit on handles to back end objects, no matter how much RAM the server has, so if we don't recycle, Domino locks itself down, even if the JVM still has gigabytes of RAM available. The OpenNTF API makes all of this a non-issue, because it listens for Java's built-in garbage collection and automatically recycles any Domino handles before they can be orphaned... something IBM should have just done for us natively in the platform long ago. – Tim Tripcony Dec 15 '13 at 18:30
0

Here's an idea i'm experimenting with. Tim's answer is excellent however for me I really needed the document for other purposes so I've tried this..

Document doc = null;
            View view = null;
            try {
                Database database = ExtLibUtil.getCurrentSessionAsSigner().getCurrentDatabase();
                view = database.getView("LocationsByLocationCode");
                doc = view.getDocumentByKey(code, true);
                //need to get this via the db object directly so we can safely recycle the view
                String id = doc.getNoteID();
                doc.recycle();
                doc = database.getDocumentByID(id);

            } catch (Exception e) {
                log.error(e);
            } finally {
                JSFUtils.incinerate(view);
            }
            return doc;

You then have to make sure you're recycling the doc object safely in whatever method calls this one

Martin Holland
  • 291
  • 1
  • 14
  • You really should use the [OpenNTF Domino API](http://www.openntf.org/main.nsf/project.xsp?r=project/OpenNTF%20Domino%20API) if you can as it will handle all this for you ;-) ... you will need to be on 9.0.x though. And actually - in your current example I don't see that you gain anything? You release a handle and use a new one right after? – John Dalsgaard Jan 02 '15 at 15:19
  • Yep am using ODA in 9 environments but still have to use 8.53 for some clients :( In my example the second doc handle isn't a child of the view so the view obj can be recycled safely. And you don't have to worry about recycling the db object as it's retrieved via ExtLibUtils – Martin Holland Jan 02 '15 at 15:30
  • I know that someone (Paul Withers, I think - or he will know) has "backported" ODA to 8.5.3. Possibly not the newest version - but it will still give you the recycling magic. Perhaps a try worth? ;-) – John Dalsgaard Jan 02 '15 at 17:15
0

I have some temporary documents which exist for a while as config docs then no longer needed, so get deleted. This is kind of enforced by an existing Notes client application: they have to exist to keep that happy.

I've written a class which has a HashMap of Java Date, String and Doubles with the item name as the key. So now I have a serializable representation of the document, plus the original doc noteID so that it can be found quickly and amended/deleted when it's not needed anymore.

That means the config doc can be collected, a standard routine creates a map of all items for the Java representation, taking into account the item type. The doc object can then be recycled right away.

The return object is the Java class representation of the document, with getValue(String name) and setValue(String name, val) where val can be Double String or Date. NB: this structure has no need for rich text or attachments, so it's kept to simple field values.

It works well although if the config doc has lots of Items, it could mean holding a lot of info in memory unnecessarily. Not so in my particular case though.

The point is: the Java object is now serializable so it can remain in memory, and as Tim's brilliant reply suggests, the document can be recycled right away.

halfer
  • 19,824
  • 17
  • 99
  • 186
user2808054
  • 1,376
  • 1
  • 11
  • 19