You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@jackrabbit.apache.org by Torgeir Veimo <to...@pobox.com> on 2006/06/19 14:17:20 UTC

"Unable to lock node. Node has pending changes: /counter"

I get this with the following code;

/* setup, run only once */
            
Node root = session.getRootNode();
Node counter = null;
try {
    counter = (Node) session.getItem("/counter");
} catch (javax.jcr.RepositoryException re) {
    log.debug("repository is empty, setting up default nodes");
    counter = null;
} 
if (counter == null) {
    counter = root.addNode("counter");
    counter.addMixin("mix:lockable");
    counter.setProperty("value", 0);
    session.save();
}

/* sequence fetcher */
public int generateNewResourceId()
    throws UnsupportedOperationException {
        
        try {
            Node counter = (Node) session.getItem("/counter");
            long nextValue = ((Long) new Locked() {
                protected Object run(Node counter) throws
javax.jcr.RepositoryException {
                    Property seqProp = counter.getProperty("value");
                    long value = seqProp.getLong();
                    seqProp.setValue(++value);
                    seqProp.save();
                    return new Long(value);
                }
            }.with(counter, false)).longValue();
            log.debug("sequence value: " + nextValue);
            
        } catch (Exception e) {
            log.error("unable to get new resource id; ", e);
        }
        return -1;
    }

All this is with derby DB, jackrabbit 1.01, and the Locked.java code
from current svn (as of today).

Any clues why I'm getting the following exception?

13:13:19,476 ERROR JackrabbitRepositoryDAO  - unable to get new resource
id;
javax.jcr.InvalidItemStateException: Unable to lock node. Node has
pending changes: /counter
        at org.apache.jackrabbit.core.NodeImpl.lock(NodeImpl.java:3910)
        at org.apache.jackrabbit.util.Locked.tryLock(Locked.java:251)
        at org.apache.jackrabbit.util.Locked.with(Locked.java:145)
        at org.apache.jackrabbit.util.Locked.with(Locked.java:103)
        at
com.netenviron.repository.dao.jackrabbit.JackrabbitRepositoryDAO.generateNewResourceId(JackrabbitRepositoryDAO.java:293)
        at
com.netenviron.repository.RepositoryManager.generateNewResourceId(RepositoryManager.java:164)
        at com.netenviron.repository.Resource.<init>(Resource.java:58)
        at
com.netenviron.repository.dao.jackrabbit.JackrabbitRepositoryDAO.getResource(JackrabbitRepositoryDAO.java:521)


-- 
Torgeir Veimo <to...@pobox.com>


Re: "Unable to lock node. Node has pending changes: /counter"

Posted by Stefan Guggisberg <st...@gmail.com>.
On 6/29/06, Torgeir Veimo <to...@pobox.com> wrote:
> Jukka Zitting wrote:
> > Hi,
> >
> > On 6/20/06, Torgeir Veimo <to...@pobox.com> wrote:
> >> On Tue, 2006-06-20 at 12:12 +0200, Marcel Reutegger wrote:
> >> > well, you can reuse the session, but you must not shared it among
> >> > multiple threads. Unless you synchronize access to the session.
> >>
> >> What's the recommended practice with web application? doing a login and
> >> logout for each request?
> >
> > There are a number of different patterns with different benefits and
> > drawbacks. Some examples:
> >
> > 1) Session per request. This is the simplest solution but not very
> > performant at least with the Jackrabbit architecture.
>
> I tried doing this, by doing a new login per request, but when the
> method returns a Node, yet still does a session.logout(), the Node
> itself seems to be unsable, as I get an exception;
>
> javax.jcr.RepositoryException: this session has been closed
>          at
> org.apache.jackrabbit.core.SessionImpl.sanityCheck(SessionImpl.java:340)
>          at
> org.apache.jackrabbit.core.ItemImpl.sanityCheck(ItemImpl.java:154)
>          at org.apache.jackrabbit.core.NodeImpl.getName(NodeImpl.java:1714)
> [...]
>
> So it seems that as long as any objects retrieved from a session is
> still used, one cannot log out from the session. Correct?

i'd rather put it this way:
once you've closed a session you can't use anymore any objetcs
tied to that session.

cheers
stefan


>
>
> --
> -Torgeir
>

Re: "Unable to lock node. Node has pending changes: /counter"

Posted by Torgeir Veimo <to...@pobox.com>.
Jukka Zitting wrote:
> Hi,
> 
> On 6/20/06, Torgeir Veimo <to...@pobox.com> wrote:
>> On Tue, 2006-06-20 at 12:12 +0200, Marcel Reutegger wrote:
>> > well, you can reuse the session, but you must not shared it among
>> > multiple threads. Unless you synchronize access to the session.
>>
>> What's the recommended practice with web application? doing a login and
>> logout for each request?
> 
> There are a number of different patterns with different benefits and
> drawbacks. Some examples:
> 
> 1) Session per request. This is the simplest solution but not very
> performant at least with the Jackrabbit architecture.

I tried doing this, by doing a new login per request, but when the 
method returns a Node, yet still does a session.logout(), the Node 
itself seems to be unsable, as I get an exception;

javax.jcr.RepositoryException: this session has been closed
         at 
org.apache.jackrabbit.core.SessionImpl.sanityCheck(SessionImpl.java:340)
         at 
org.apache.jackrabbit.core.ItemImpl.sanityCheck(ItemImpl.java:154)
         at org.apache.jackrabbit.core.NodeImpl.getName(NodeImpl.java:1714)
[...]

So it seems that as long as any objects retrieved from a session is 
still used, one cannot log out from the session. Correct?


-- 
-Torgeir

Re: "Unable to lock node. Node has pending changes: /counter"

Posted by Jukka Zitting <ju...@gmail.com>.
Hi,

On 6/20/06, Torgeir Veimo <to...@pobox.com> wrote:
> On Tue, 2006-06-20 at 12:12 +0200, Marcel Reutegger wrote:
> > well, you can reuse the session, but you must not shared it among
> > multiple threads. Unless you synchronize access to the session.
>
> What's the recommended practice with web application? doing a login and
> logout for each request?

There are a number of different patterns with different benefits and
drawbacks. Some examples:

1) Session per request. This is the simplest solution but not very
performant at least with the Jackrabbit architecture.

2) Session per session. This is probably the most common pattern, i.e.
you associate a JCR session with the servlet session and make sure
that the session access is synchronized.

3) Session per user. Used when it is likely that a user can have
multiple concurrent browser sessions and that there are no problems
with making transient changes visible across sessions. Requires
synchronization, but scales otherwise very well for cases like
anonymous access.

4) Session per feature. Use a separate session associated with a
specific feature, like a single servlet. Requires synchronization and
doesn't support per-user access controls, but is often more
fine-grained than pattern 3.

5) Session per application. A global session used by all parts of the
application. Requires heavy synchronization and doesn't support
per-user access controls, but is conceptually very simple.

6) Session pool. Meta-pattern that uses a pool of sessions to avoid
performance or synchronization issues in the other patterns.

It is also possible (and often useful) to combine these patterns in a
single application.

BR,

Jukka Zitting

-- 
Yukatan - http://yukatan.fi/ - info@yukatan.fi
Software craftsmanship, JCR consulting, and Java development

Re: "Unable to lock node. Node has pending changes: /counter"

Posted by Torgeir Veimo <to...@pobox.com>.
On Tue, 2006-06-20 at 12:12 +0200, Marcel Reutegger wrote:
> 
> well, you can reuse the session, but you must not shared it among
> multiple threads. Unless you synchronize access to the session. 

What's the recommended practice with web application? doing a login and
logout for each request?

-- 
Torgeir Veimo <to...@pobox.com>


Re: "Unable to lock node. Node has pending changes: /counter"

Posted by Marcel Reutegger <ma...@day.com>.
On 6/20/06, Torgeir Veimo <to...@pobox.com> wrote:
> > I copy&pasted that code into mine, and run the testSequence code on am
> > empty repository. I made some slight changes, and now that code gives
> > the same exceptions. Notice how I changed retrieving the session and the
> > lack of session logout.

I see. Now I understand why you get the exception. You have multiple
threads using the same session instance. This is not permitted. See
section 7.5 of the spec.
The reason why you get the exception is: one thread locks the node
which results in adding two properties: jcr:lockOwner and
jcr:lockIsDeep. This also results in a modification of the counter
node. Now when another thread (using the same session instance) at the
same time tries to lock the node it will see pending changes and throw
an exception.

> I can confirm that if my getNewResourceId() uses it's own session that
> it closes after retrieving the counter value, it works. The lock code
> should ideally work with the samme session being reused?

well, you can reuse the session, but you must not shared it among
multiple threads. Unless you synchronize access to the session.

regards
marcel

Re: "Unable to lock node. Node has pending changes: /counter"

Posted by Torgeir Veimo <to...@pobox.com>.
On Tue, 2006-06-20 at 09:21 +0100, Torgeir Veimo wrote:
> On Mon, 2006-06-19 at 16:56 +0200, Marcel Reutegger wrote:
> > On 6/19/06, Torgeir Veimo <to...@pobox.com> wrote:
> > > Would it be correct to call refresh(false) on the counter node in case
> > > of an exception? I only ever modify the value property when the node is
> > > created and in the generateNewResourceId() method.
> > 
> > yes, you should definitively handle that case. but after having a
> > second look at your code I realized that the counter node actually
> > doesn't get modified in the run method. It is only the property that
> > changes. So it seems there must be some code that modifies the counter
> > node outside of the Locked.run() method.
> > 
> > there are also some test cases that show how to use the utility:
> > http://svn.apache.org/repos/asf/jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/core/LockTest.java
> > 
> > Those test cases run fine on my checkout. and one of them
> > (LockTest.testSequence) does the same as your generateNewResourceId()
> > method, it increments a counter property.
> 
> I copy&pasted that code into mine, and run the testSequence code on am
> empty repository. I made some slight changes, and now that code gives
> the same exceptions. Notice how I changed retrieving the session and the
> lack of session logout. 

I can confirm that if my getNewResourceId() uses it's own session that
it closes after retrieving the counter value, it works. The lock code
should ideally work with the samme session being reused?


-- 
Torgeir Veimo <to...@pobox.com>


Re: "Unable to lock node. Node has pending changes: /counter"

Posted by Torgeir Veimo <to...@pobox.com>.
On Mon, 2006-06-19 at 16:56 +0200, Marcel Reutegger wrote:
> On 6/19/06, Torgeir Veimo <to...@pobox.com> wrote:
> > Would it be correct to call refresh(false) on the counter node in case
> > of an exception? I only ever modify the value property when the node is
> > created and in the generateNewResourceId() method.
> 
> yes, you should definitively handle that case. but after having a
> second look at your code I realized that the counter node actually
> doesn't get modified in the run method. It is only the property that
> changes. So it seems there must be some code that modifies the counter
> node outside of the Locked.run() method.
> 
> there are also some test cases that show how to use the utility:
> http://svn.apache.org/repos/asf/jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/core/LockTest.java
> 
> Those test cases run fine on my checkout. and one of them
> (LockTest.testSequence) does the same as your generateNewResourceId()
> method, it increments a counter property.

I copy&pasted that code into mine, and run the testSequence code on am
empty repository. I made some slight changes, and now that code gives
the same exceptions. Notice how I changed retrieving the session and the
lack of session logout. I guess this is where the problem lies.

Do the sequence have to run with its own session?

    protected void setupRepository() {
        try {
            // add default nen and crm namespace
            Workspace ws = session.getWorkspace();
            String [] prefixes =
ws.getNamespaceRegistry().getPrefixes();
            java.util.Arrays.sort(prefixes);
            if (java.util.Arrays.binarySearch(prefixes, "nen") < 0) {
                ws.getNamespaceRegistry().registerNamespace("nen",
"http://netenviron.com/nen/1.0");
                ws.getNamespaceRegistry().registerNamespace("crm",
"http://netenviron.com/crm/1.0");
            }
            
            testSequence();
            
        } catch (javax.jcr.RepositoryException re) {
            log.error("unable to create lock node; ", re);
        }
    }

    private static final int NUM_THREADS = 100;
    private static final int NUM_CHANGES = 10;
    private static final int NUM_VALUE_GETS = 10;
    
    public void testSequence() throws javax.jcr.RepositoryException {
        
        final String nodeName1 = "testnode";
        final String mixLockable = "mix:lockable";
        Node testRootNode = session.getRootNode();
        
        log.debug("\n\n entering testSequence..\n\n");
        
        final Node counter = testRootNode.addNode(nodeName1);
        counter.setProperty("value", 0);
        counter.addMixin(mixLockable);
        testRootNode.save();
        
        final List worker = new ArrayList();
        for (int i = 0; i < NUM_THREADS; i++) {
            worker.add(new Thread() {
                
                private final int threadNumber = worker.size();
                
                public void run() {
                    Session s;
                    //try {
                        s = session; //helper.getSuperuserSession();
                    //} catch (RepositoryException e) {
                    //    return;
                    //}
                    try {
                        for (int i = 0; i < NUM_VALUE_GETS; i++) {
                            Node n = (Node)
s.getItem(counter.getPath());
                            long currentValue = ((Long) new Locked() {
                                protected Object run(Node n) throws
javax.jcr.RepositoryException {
                                    Property seqProp =
n.getProperty("value");
                                    long value = seqProp.getLong();
                                    seqProp.setValue(++value);
                                    seqProp.save();
                                    return new Long(value);
                                }
                            }.with(n, false)).longValue();
                            log.debug("Thread" + threadNumber + ": got
sequence number: " + currentValue);
                            // do a random wait
                            Thread.sleep(new Random().nextInt(100));
                        }
                    } catch (javax.jcr.RepositoryException e) {
                        log.debug("exception while running code with
lock:" + e.getMessage());
                    } catch (InterruptedException e) {
                        log.debug(Thread.currentThread() + " interrupted
while waiting for lock");
                    } finally {
                        //s.logout();
                    }
                }
            });
        }
        
        for (Iterator it = worker.iterator(); it.hasNext(); ) {
            ((Thread) it.next()).start();
        }
        
        for (Iterator it = worker.iterator(); it.hasNext(); ) {
            try {
                ((Thread) it.next()).join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

The output from this program is;

09:20:19,473 DEBUG JackrabbitRepositoryDAO  -

 entering testSequence..


09:20:19,473 DEBUG ItemManager  - created item
f37ab406-beee-45be-b4d1-dc42d5ac94e5
09:20:19,474 DEBUG ItemManager  - caching item
f37ab406-beee-45be-b4d1-dc42d5ac94e5
09:20:19,474 DEBUG ItemManager  - created item
f37ab406-beee-45be-b4d1-dc42d5ac94e5/{http://www.jcp.org/jcr/1.0}primaryType
09:20:19,474 DEBUG ItemManager  - caching item
f37ab406-beee-45be-b4d1-dc42d5ac94e5/{http://www.jcp.org/jcr/1.0}primaryType
09:20:19,476 DEBUG HierarchyManagerImpl  - failed to build path of
f37ab406-beee-45be-b4d1-dc42d5ac94e5/{}value
09:20:19,476 DEBUG ItemManager  - created item
f37ab406-beee-45be-b4d1-dc42d5ac94e5/{}value
09:20:19,476 DEBUG ItemManager  - caching item
f37ab406-beee-45be-b4d1-dc42d5ac94e5/{}value
09:20:19,477 DEBUG NodeState  - listener already registered:
org.apache.jackrabbit.core.CachingHierarchyManager@16509fe
09:20:19,479 DEBUG ItemManager  - created item
f37ab406-beee-45be-b4d1-dc42d5ac94e5/{http://www.jcp.org/jcr/1.0}mixinTypes
09:20:19,479 DEBUG ItemManager  - caching item
f37ab406-beee-45be-b4d1-dc42d5ac94e5/{http://www.jcp.org/jcr/1.0}mixinTypes
09:20:19,493 DEBUG SharedItemStateManager  - persisting change log
{#addedStates=4, #modifiedStates=1, #deletedStates=0, #modifiedRefs=0}
took 11ms
09:20:19,494 DEBUG ObservationManagerFactory  - notifying 3 synchronous
listeners.
09:20:19,513 DEBUG SearchManager  - onEvent: indexing started
09:20:19,524 DEBUG NamespaceMappings  - adding new namespace mapping: 4
->
09:20:19,527 DEBUG SearchManager  - onEvent: indexing finished in 14 ms.
09:20:19,531 DEBUG ObservationManagerFactory  - got EventStateCollection
09:20:19,532 DEBUG ObservationManagerFactory  - event delivery to 1
consumers started...
09:20:19,532 DEBUG ObservationManagerFactory  - event delivery finished.
09:20:19,555 DEBUG HierarchyManagerImpl  - failed to build path of
f37ab406-beee-45be-b4d1-dc42d5ac94e5/{http://www.jcp.org/jcr/1.0}lockOwner
09:20:19,555 DEBUG ItemManager  - created item
f37ab406-beee-45be-b4d1-dc42d5ac94e5/{http://www.jcp.org/jcr/1.0}lockOwner
09:20:19,555 DEBUG ItemManager  - caching item
f37ab406-beee-45be-b4d1-dc42d5ac94e5/{http://www.jcp.org/jcr/1.0}lockOwner
09:20:19,556 DEBUG HierarchyManagerImpl  - failed to build path of
f37ab406-beee-45be-b4d1-dc42d5ac94e5/{http://www.jcp.org/jcr/1.0}lockIsDeep
09:20:19,556 DEBUG ItemManager  - created item
f37ab406-beee-45be-b4d1-dc42d5ac94e5/{http://www.jcp.org/jcr/1.0}lockIsDeep
09:20:19,556 DEBUG ItemManager  - caching item
f37ab406-beee-45be-b4d1-dc42d5ac94e5/{http://www.jcp.org/jcr/1.0}lockIsDeep
09:20:19,563 DEBUG SharedItemStateManager  - persisting change log
{#addedStates=2, #modifiedStates=1, #deletedStates=0, #modifiedRefs=0}
took 4ms
09:20:19,563 DEBUG ObservationManagerFactory  - notifying 3 synchronous
listeners.
09:20:19,563 DEBUG SearchManager  - onEvent: indexing started
09:20:19,565 DEBUG SearchManager  - onEvent: indexing finished in 2 ms.
09:20:19,565 DEBUG ObservationManagerFactory  - got EventStateCollection
09:20:19,565 DEBUG ObservationManagerFactory  - event delivery to 1
consumers started...
09:20:19,565 DEBUG ObservationManagerFactory  - event delivery finished.
09:20:19,569 DEBUG ItemManager  - created item
f37ab406-beee-45be-b4d1-dc42d5ac94e5/{}value
09:20:19,569 DEBUG ItemManager  - caching item
f37ab406-beee-45be-b4d1-dc42d5ac94e5/{}value
09:20:19,580 DEBUG SharedItemStateManager  - persisting change log
{#addedStates=0, #modifiedStates=1, #deletedStates=0, #modifiedRefs=0}
took 10ms
09:20:19,581 DEBUG ObservationManagerFactory  - notifying 3 synchronous
listeners.
09:20:19,581 DEBUG SearchManager  - onEvent: indexing started
09:20:19,581 DEBUG SearchManager  - onEvent: indexing finished in 0 ms.
09:20:19,581 DEBUG ObservationManagerFactory  - got EventStateCollection
09:20:19,581 DEBUG ObservationManagerFactory  - event delivery to 8
consumers started...
09:20:19,582 DEBUG ObservationManagerFactory  - event delivery finished.
09:20:19,584 DEBUG ItemManager  - invalidated item
f37ab406-beee-45be-b4d1-dc42d5ac94e5/{http://www.jcp.org/jcr/1.0}lockOwner
09:20:19,584 DEBUG ItemManager  - removing item
f37ab406-beee-45be-b4d1-dc42d5ac94e5/{http://www.jcp.org/jcr/1.0}lockOwner from cache
09:20:19,584 DEBUG ItemManager  - invalidated item
f37ab406-beee-45be-b4d1-dc42d5ac94e5/{http://www.jcp.org/jcr/1.0}lockIsDeep
09:20:19,584 DEBUG ItemManager  - removing item
f37ab406-beee-45be-b4d1-dc42d5ac94e5/{http://www.jcp.org/jcr/1.0}lockIsDeep from cache
09:20:19,585 DEBUG HierarchyManagerImpl  - failed to build path of
f37ab406-beee-45be-b4d1-dc42d5ac94e5/{http://www.jcp.org/jcr/1.0}lockOwner
09:20:19,585 DEBUG JackrabbitRepositoryDAO  - exception while running
code with lock:failed to build path of
f37ab406-beee-45be-b4d1-dc42d5ac94e5/{http://www.jcp.org/jcr/1.0}lockOwner: f37ab406-beee-45be-b4d1-dc42d5ac94e5/{http://www.jcp.org/jcr/1.0}lockOwner
09:20:19,586 DEBUG NodeImpl  - Unable to lock node. Node has pending
changes: /testnode
09:20:19,590 DEBUG JackrabbitRepositoryDAO  - exception while running
code with lock:Unable to lock node. Node has pending changes: /testnode
09:20:19,591 DEBUG NodeImpl  - Unable to lock node. Node has pending
changes: /testnode
09:20:19,591 DEBUG JackrabbitRepositoryDAO  - exception while running
code with lock:Unable to lock node. Node has pending changes: /testnode
09:20:19,589 DEBUG NodeImpl  - Unable to lock node. Node has pending
changes: /testnode
09:20:19,591 DEBUG JackrabbitRepositoryDAO  - exception while running
code with lock:Unable to lock node. Node has pending changes: /testnode
09:20:19,592 DEBUG NodeImpl  - Unable to lock node. Node has pending
changes: /testnode
09:20:19,592 DEBUG JackrabbitRepositoryDAO  - exception while running
code with lock:Unable to lock node. Node has pending changes: /testnode
09:20:19,589 DEBUG NodeImpl  - Unable to lock node. Node has pending
changes: /testnode
09:20:19,592 DEBUG JackrabbitRepositoryDAO  - exception while running
code with lock:Unable to lock node. Node has pending changes: /testnode
09:20:19,592 DEBUG NodeImpl  - Unable to lock node. Node has pending
changes: /testnode
09:20:19,593 DEBUG JackrabbitRepositoryDAO  - exception while running
code with lock:Unable to lock node. Node has pending changes: /testnode
09:20:19,588 DEBUG NodeImpl  - Unable to lock node. Node has pending
changes: /testnode

-- 
Torgeir Veimo <to...@pobox.com>


Re: "Unable to lock node. Node has pending changes: /counter"

Posted by Marcel Reutegger <ma...@day.com>.
On 6/19/06, Torgeir Veimo <to...@pobox.com> wrote:
> Would it be correct to call refresh(false) on the counter node in case
> of an exception? I only ever modify the value property when the node is
> created and in the generateNewResourceId() method.

yes, you should definitively handle that case. but after having a
second look at your code I realized that the counter node actually
doesn't get modified in the run method. It is only the property that
changes. So it seems there must be some code that modifies the counter
node outside of the Locked.run() method.

there are also some test cases that show how to use the utility:
http://svn.apache.org/repos/asf/jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/core/LockTest.java

Those test cases run fine on my checkout. and one of them
(LockTest.testSequence) does the same as your generateNewResourceId()
method, it increments a counter property.

regards
 marcel

Re: "Unable to lock node. Node has pending changes: /counter"

Posted by Torgeir Veimo <to...@pobox.com>.
On Mon, 2006-06-19 at 14:47 +0200, Marcel Reutegger wrote:
> There are two reason why this can happen (as far as I can see):
> 
> - the session actually did modify the node /counter before calling
> generateNewResourceId(), thus it cannot lock the node
> 
> or
> 
> - a previous call to generateNewResourceId() failed in the
> Locked.run() method, leaving unsaved changes.
> The Locked utility does not automatically revert changes in case of an
> exception thrown by Locked.run(). That's the responsibility of the
> caller, because only he knows what is actually done in Locked.run().
> 
> The code you provided only shows the Locked.run() method, but does not
> include a test case that shows how and when generateNewResourceId() is
> called. Do you have a simple test that reproduces the
> InvalidItemStateException? 

Hmm, it's a bit difficult as I'd have to rip it out of the framework..
I'll put something simple together.

> One other question: does this happen always
> or only sporadically?

It happens all the time.

Would it be correct to call refresh(false) on the counter node in case
of an exception? I only ever modify the value property when the node is
created and in the generateNewResourceId() method.



-- 
Torgeir Veimo <to...@pobox.com>


Re: "Unable to lock node. Node has pending changes: /counter"

Posted by Marcel Reutegger <mr...@gmail.com>.
There are two reason why this can happen (as far as I can see):

- the session actually did modify the node /counter before calling
generateNewResourceId(), thus it cannot lock the node

or

- a previous call to generateNewResourceId() failed in the
Locked.run() method, leaving unsaved changes.
The Locked utility does not automatically revert changes in case of an
exception thrown by Locked.run(). That's the responsibility of the
caller, because only he knows what is actually done in Locked.run().

The code you provided only shows the Locked.run() method, but does not
include a test case that shows how and when generateNewResourceId() is
called. Do you have a simple test that reproduces the
InvalidItemStateException? One other question: does this happen always
or only sporadically?

regards
marcel

On 6/19/06, Torgeir Veimo <to...@pobox.com> wrote:
> I get this with the following code;
>
> /* setup, run only once */
>
> Node root = session.getRootNode();
> Node counter = null;
> try {
>    counter = (Node) session.getItem("/counter");
> } catch (javax.jcr.RepositoryException re) {
>    log.debug("repository is empty, setting up default nodes");
>    counter = null;
> }
> if (counter == null) {
>    counter = root.addNode("counter");
>    counter.addMixin("mix:lockable");
>    counter.setProperty("value", 0);
>    session.save();
> }
>
> /* sequence fetcher */
> public int generateNewResourceId()
>    throws UnsupportedOperationException {
>
>        try {
>            Node counter = (Node) session.getItem("/counter");
>            long nextValue = ((Long) new Locked() {
>                protected Object run(Node counter) throws
> javax.jcr.RepositoryException {
>                    Property seqProp = counter.getProperty("value");
>                    long value = seqProp.getLong();
>                    seqProp.setValue(++value);
>                    seqProp.save();
>                    return new Long(value);
>                }
>            }.with(counter, false)).longValue();
>            log.debug("sequence value: " + nextValue);
>
>        } catch (Exception e) {
>            log.error("unable to get new resource id; ", e);
>        }
>        return -1;
>    }
>
> All this is with derby DB, jackrabbit 1.01, and the Locked.java code
> from current svn (as of today).
>
> Any clues why I'm getting the following exception?
>
> 13:13:19,476 ERROR JackrabbitRepositoryDAO  - unable to get new resource
> id;
> javax.jcr.InvalidItemStateException: Unable to lock node. Node has
> pending changes: /counter
>        at org.apache.jackrabbit.core.NodeImpl.lock(NodeImpl.java:3910)
>        at org.apache.jackrabbit.util.Locked.tryLock(Locked.java:251)
>        at org.apache.jackrabbit.util.Locked.with(Locked.java:145)
>        at org.apache.jackrabbit.util.Locked.with(Locked.java:103)
>        at
> com.netenviron.repository.dao.jackrabbit.JackrabbitRepositoryDAO.generateNewResourceId(JackrabbitRepositoryDAO.java:293)
>        at
> com.netenviron.repository.RepositoryManager.generateNewResourceId(RepositoryManager.java:164)
>        at com.netenviron.repository.Resource.<init>(Resource.java:58)
>        at
> com.netenviron.repository.dao.jackrabbit.JackrabbitRepositoryDAO.getResource(JackrabbitRepositoryDAO.java:521)
>
>
> --
> Torgeir Veimo <to...@pobox.com>
>
>