1
votes

I've been having a lot of problems with disappearing Java class level Domino objects lately. For example I place a lotus.domino.Session to a (non-static) class level variable and when I try to use it in the next code line I get:

NotesException: Object has been removed or recycled

I didn't have these problems before I started using managed beans but now I seem to get them all the time with request scoped beans and also in plain Java objects. I've been adding isRecycled() checks to many places and been wondering why I didn't have to do it before. I know Domino objects are not serialized but before they stayed for the duration of the request or agent.

Today I copied the code giving this exception to another db and there the exception did not occur. Then I copied the xsp.properties from that db to the original db and the exception did not occur there either. By removing one line at a time I found out that if I have this:

xsp.application.timeout=10

I don't get the exception and if I remove it I get the exception. Does anyone understand why? The default should be 30 minutes but my session object seems to dissappear in nanoseconds unless I set the application timeout. I'm passing the session from SSJS to Java and store it in the constructor code:

private Session session;    

public Domino(Session session) {
    this.session = session;
}

As you can see this is not a managed bean. The Domino version I tested with is 9.0.1 but I need to use this code also with 8.5.2. The code is running in beforePageLoad event.

It seems that my problem is solved but I'd like understand what's happening here.

Update1

I still get the error in the main db if I wait for a while (probabaly more than 10 minutes) and then reload the XPage. In the other db I've never gotten the error.

Update2

Yesterday I added back the full xsp.properties from the db where it has worked all the time. Now after 8 hours it still works fine also in my original db. Looks like I need also this:

xsp.persistence.mode=basic

which means "Keeps pages in memory". It seems the XPage is serialized immediately (within a single HTTP request) without this setting.

3
> As you can see this is not a managed bean. - What is it then? How do you instantiate this object and where you keep references to it?Frantisek Kossuth
It's plain Java object. I instantiate it with new MyClass(sessionAsSigner). In my sample I don't even keep reference to it. I just call new MyClass(sessionAsSigner).getStuff() and when getStuff() is called the session is already recycled! The session is stored in the parent object of the object I'm using.Panu Haaramo
Do you really need to store that session? What about static method getStuff(Session)?Frantisek Kossuth
This is a very simplified case. Often things are much more complex and there can be quite many references to the session in one request. I can always test for isRecycled() and re-get it but I don't understand why I have to do that. And the same applies to all Domino objects. In some cases it's costly to re-get them (f.e. DocumentCollection). As said the problem is solved by using "Keep pages in memory" but of course I'd like my code to work also with other persistence settings.Panu Haaramo
I do not allow developers to use that (keep pages in memory). Perfect smoke test. But what you experience is very odd. Could you give more code examples to resolve that?Frantisek Kossuth

3 Answers

8
votes

Quick answer: don't ever, ever, ever, for any reason whatsoever, store a handle to a Domino object for any longer than a single HTTP request. :)

I suspect that, because a Domino session is essentially a singleton, storing it in application scope was preventing it from being recycled at the end of each request, as it normally would. Under normal circumstances, any Domino object the XPage engine is aware of at the end of any HTTP request is automatically recycled. So in order to prevent the error you're receiving, it's best practice to not store handles in any scope higher than request.

The good news is, at least for the current Session and Database, you never need to: you can ask the variable resolver for it.

SSJS has intrinsic access to the variable resolver, since all SSJS code has to be evaluated at runtime; so any reference to session, for example, has to ask the variable resolver what "session" currently evaluates to. This happens automatically when the entire expression is evaluated, but we can manually access the variable resolver from our own Java code:

FacesContext context = FacesContext.getCurrentInstance();
Application app = context.getApplication();
VariableResolver resolver = app.getVariableResolver();
Session currentSession = (Session) resolver.resolveVariable("session", context);

That's a lot of nonsense to type each time we want to get a handle on a variable, and resolving contextual variables is often useful (because this works for any variables that would also be valid within SSJS, not just for Domino objects), so I recommend wrapping this in a static utility method:

public class VariableUtils {

 public static Object getVariableValue(String variable) {
  FacesContext context = FacesContext.getCurrentInstance();
  Application app = context.getApplication();
  VariableResolver resolver = app.getVariableResolver();
  return resolver.resolveVariable(variable, context);
 }

}

Then you can easily resolve any variable from any of your Java code:

Session currentSession = (Session) VariableUtils.resolveVariable("session");
Database currentDatabase = (Database) VariableUtils.resolveVariable("database");
DominoDocument currentDocument = (DominoDocument) VariableUtils.resolveVariable("currentDocument");

If your app loads the Extension Library, there's already a static method available for those first two:

Session currentSession = (Session) ExtLibUtils.getCurrentSession();
Database currentDatabase = (Database) ExtLibUtils.getCurrentDatabase();

That class has a bunch of useful variable resolution methods you might want to check out. But having your own convenience method for resolving variables is useful for any contextual inspection -- for instance, retrieving the current row for a view panel, data table, or repeat; the query string parameter map (param); any data source. Just like SSJS, your Java code is typically triggered within the context of a given component (for instance, the onclick event handler of a button), so any variables that are valid for that component can be resolved this way.

One last note about storage of Domino objects: just store metadata instead. So, if you would otherwise be storing a database (other than the current), store instead its filepath or replica ID, and when you need to access it, use the stored metadata to ask the current session for a handle to the database. Similarly, store the name of a view, but not the View itself; store a document's NoteID or UNID, but not the actual Document. If you find you're having to obtain these handles repeatedly, and therefore wishing they were cached, revisit the way your logic is structured... the code should be refactored to establish a handle once, do whatever needs to be done with / to that object, then discard it (and, if it's not a session, database, or an object that you also have a data source bound to, manually recycle it).

Keep in mind that any Java object that implements Serializable and does not store pointers to any Domino objects can be stored as long as you want. So create "model" objects for your data (and populate their properties by reading the corresponding Domino handles), store those objects in scope, and then write back to the corresponding Domino objects whenever appropriate. If IBM hadn't tried to simplify XPages for us by creating the document and view data sources for us, this is what we'd already be doing (for instance, we'd be binding all of our editable fields to properties of bean classes like Contact, ExpenseReport, Facility, etc., not directly to note items). But because these data sources do exist, we're still thinking in terms of "documents" and "views", instead of thinking in terms of the people, physical objects, and business processes that the data represents. As a result, we maintain unnecessary links between our user interfaces and back end data, instead of only touching the back end data when we need to do an initial query and an eventual update (or create). Sorry to get so philosophical, but so much of what we try to do in XPages gets so much easier when we transition beyond thinking in documents. :)

1
votes

I think this can be caused by known bug of 8.5.2. We have had the same problems, only workaround was to remove recycle code where not really necessary. The example in APAR is not very helpful, it covers only loop avoiding to recycle single document, but other objects (database, view) need different approach.

Edit: There can be another issue related to internal object cache. Not sure if it was fixed: when you ask for some object, you may get one from internal cache, which was already recycled (intentionally). This can be proved by printing Java object ID. It was big issue in 8.5.1 and 8.5.2, and I doubt it happens in R9. Did not test, tho.

0
votes

It turned out that when I made a small change to the parent of my parent class and built the application it started to work. To make sure I copy pasted the original code back and it still worked.

Looks like it was some kind of build problem. This was not the only case when I've seen this problem but next time I will try some re-saving, cleaning and rebuilding.