javax.jcr.InvalidItemStateException: Item seems to have been removed externally


#1

Hello all,

One and a half year ago a predecessor of me had created the following ticket in the former community:
https://groups.google.com/forum/#!topic/hippo-community/3dknHFH95Z4

As this is currently still an issue, I would like to follow this up. Woonsan asked to provide the snippet which provides the session:

private Session session;

@Override
public void initialize(Session session) throws RepositoryException {
    this.session = session;
    HippoServiceRegistry.registerService(this, HippoEventBus.class);
}

In the code that invokes the save() method, this is session instance variable is directly accessed. As a result, from a certain point in time, the save() method continuously keeps throwing exceptions for a node with an UUID for every event, which is nowhere to be found in the repository (please see the description in the link).

In another implementation the session gets impersonated in the initialize method for every implementation of the DaemonModule. As far as I know impersonating the session object is to make the DaemonModule thread-safe. Does anyone know if not impersonating the session object in a DaemonModule implementation could lead to such kind of problems as reported in the former google group community?

In parallel to this we’ve also executed the repository checker tool, in an effort to solve it. Although this is still running.

Thanks in advance,

Philippe.


#2

probably related to:

https://issues.onehippo.com/browse/REPO-493

“A cluster node reacts to external update events by reindexing changed nodes. However, in the meantime the changed node was removed again, leading to this warning. I don’t readily see how we can improve the code here. The warning is valid from one point of view and redundant from another. In any case, it does not indicate anything serious in this case.”


#3

Hi Machak,

Thanks for your response! It does look similar, although I’m not quite sure how to deal with it.

I’m going to implement session impersonation as soon as possible. Besides that I’m thinking to at least restart the CMS applications to get rid of the existing session object.

Thanks in advance,

Philippe.


#4

I am not sure why you want/need to get rid of the session.
Normally you surround your code bits with try/catch statement for repository exceptions
and use session.refresh(false) method to update your session in cases like one above.


#5

DaemonModule is a singleton. So, instantiation of new JCR session through impersonation in #initialize() doesn’t make the event listener thread-safe. But if it does every time whenever the listener handles an event notification–for example, #getSession() returns an impersonated one–then it will thread-safe in regarding JCR session usage. In this usage, the caller of #getSession() is responsible for closing the session (#logout()) to avoid session leaks.


#6

Hi Machak,

Cleaning up the session, using the refresh method, is exactly what isn’t happening when a RepositoryException is thrown, so I’ll start implementing this.

In the meantime, in an effort to solve the problem today, so that the content managers can move on doing their work, I assume that restarting the CMS application would drop the current session(s). This way we can temporarily work around the fact that the sessions aren’t treated properly in our DaemonModule implementation until my changes are ready.

Does this makes sense?

Thanks, Philippe.


#7

Hi Woonsan,

Would this bring any value:

when this impersonated session object is stored in an instance variable of the implementing class of the DaemonModule?


#8

Having an impersonated session object as an instance member doesn’t make it thread-safe because the listener object using the session – which seems actually to be the same as the DaemonModule object itself – is singleton.
The repository will populate events asynchronously in parallel, invoking a registered listener. If the listener is singleton, you are using the shared JCR session in parallel.

I just wanted to point out the thread-safety issue here. I don’t know if this is the cause and if using a new session for each event handling thread would cure your problem.
But in general, the following is true:

  • A JCR session is not thread-safe, therefore it is not recommended to use a shared JCR session in multiple threads.
  • Event handling in JCR repositories is mostly asynchronous and so causes multi-threaded executions in applications. So, it is safer to use an exclusive JCR session on each event handling thread as well.
  • When you create a new session (impersonation is one way to do it), it must be closed after use.

#9

Thanks Woonsanko and Machak! I think I can move on from here.


#10

My remarks were not so precise. Most of all hold true with JCR Observation Event Listeners, but not so true in HippoEvent listeners.

If your event listener is in the same lifecycle of the DaemonModule and the JCR session of your DaemonModule given in #initialize() method is used only in the event listener not shared with any other threads in any case, you don’t have to do anything more, in the perspective of JCR session thread-safety issue. The reason is:

  • Hippo Repository already creates a new dedicated jcr session for each DaemonModule and passes it to #initialize() method. So, the JCR session is really for the module only, not shared by anything else.

Sorry for the confusion.

Woonsan


#11

I made a mistake in the above. Sorry again.
My original remarks hold true whether you’re implementing either JCR Observation event listener or Hippo EventBus event listener.
In Hippo EventBus use cases, multiple users can possibly cause events; so the event listener may be invoked in multiple threads at the same time using the same JCR session.

Therefore, I think it is best to create (or impersonate) a new JCR session every time whenever handling an event in your listener.

It’s been a long day. :wink:

Woonsan