You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@jackrabbit.apache.org by "Thomas Mueller (JIRA)" <ji...@apache.org> on 2013/08/28 10:32:51 UTC

[jira] [Comment Edited] (JCR-3652) Bundle serialization broken

    [ https://issues.apache.org/jira/browse/JCR-3652?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13752202#comment-13752202 ] 

Thomas Mueller edited comment on JCR-3652 at 8/28/13 8:32 AM:
--------------------------------------------------------------

A simpler test case:

{code}
Node node = root.addNode(nodeName);
superuser.save();
try {
    node.setProperty("jcr:isCheckedOut", true);
} catch (ArrayIndexOutOfBoundsException e) {
    System.out.println("set failed strangely: " + e);
}
System.out.println(node.getProperty("jcr:isCheckedOut").getValue().getString());
{code}

I think what happens is: the property is half-way added (in NodeImpl.SetPropertyOperation.perform, PropertyImpl property = getOrCreateProperty(name, type, false, enforceType, status)). The property is non-multi-valued, and the values array is 0. This seems to be always done like that. Then it tries to set the value, with 

{code}
try { 
  property.setValue(value); 
} catch(RepositoryException) { 
  ... revert ... 
}
{code}

The setValue checks if setting the value is allowed (via checkSetValue, which calls ItemValidator.checkModify, which calls ItemValidator.checkCondition). At some point this verification calls NodeImpl.isCheckedOut(). There is a "// FIXME should not only rely on existence of jcr:isCheckedOut property" in the code, but for performance reason it just gets the value of the jcr:isCheckedOut property, using "return ps.getValues()[0].getBoolean();". Now, as the property is half-way set, this throws an ArrayOutOfBoundsException (half-way set properties have an empty values array). Because this isn't a RepositoryException, this is thrown back to the user, and the property remains half-way set. The internal state of the node is corrupt.

One solution is to change NodeImpl.isCheckedOut() to deal with a half-way set property, and throw a RepositoryException instead of a ArrayOutOfBoundsException. However, there might be more checks that don't deal with half-way set properties.

In addition to the above, instead of just catching RepositoryException, the code could catch Exception or even Throwable (that is, also Error). Usually catching Throwable isn't a good idea, however the alternative (having a corrupt node) is much worse, so I guess this should also be done.

The best solution would be not to have half-way set properties at all, but that would be quite a big change, so I will not try to do that.

                
      was (Author: tmueller):
    A simpler test case:

{code}
Node node = root.addNode(nodeName);
superuser.save();
try {
    node.setProperty("jcr:isCheckedOut", true);
} catch (ArrayIndexOutOfBoundsException e) {
    System.out.println("set failed strangely: " + e);
}
System.out.println(node.getProperty("jcr:isCheckedOut").getValue().getString());
{code}

I think what happens is: the property is half-way added (in NodeImpl.SetPropertyOperation.perform, PropertyImpl property = getOrCreateProperty(name, type, false, enforceType, status);). The property is non-multi-valued, and the values array is 0. This seems to be always done like that. Then it tries to set the value, with a try { property.setValue(value); } catch(RepositoryException) { ... revert ... }. The setValue checks if setting the value is allowed (via checkSetValue, which calls ItemValidator.checkModify, which calls ItemValidator.checkCondition). At some point this verification calls NodeImpl.isCheckedOut(). There is a "// FIXME should not only rely on existence of jcr:isCheckedOut property" in the code, but for performance reason it just gets the value of the jcr:isCheckedOut property, using "return ps.getValues()[0].getBoolean();". Now, as the property is half-way set, this throws an ArrayOutOfBoundsException (half-way set properties have an empty values array). Because this isn't a RepositoryException, this is thrown back to the user, and the property remains half-way set. The internal state of the node is corrupt.

One solution is to change NodeImpl.isCheckedOut() to deal with a half-way set property, and throw a RepositoryException instead of a ArrayOutOfBoundsException. However, there might be more checks that don't deal with half-way set properties.

In addition to the above, instead of just catching RepositoryException, the code could catch Exception or even Throwable (that is, also Error). Usually catching Throwable isn't a good idea, however the alternative (having a corrupt node) is much worse, so I guess this should also be done.

The best solution would be not to have half-way set properties at all, but that would be quite a big change, so I will not try to do that.

                  
> Bundle serialization broken
> ---------------------------
>
>                 Key: JCR-3652
>                 URL: https://issues.apache.org/jira/browse/JCR-3652
>             Project: Jackrabbit Content Repository
>          Issue Type: New Feature
>          Components: jackrabbit-core
>            Reporter: Thomas Mueller
>            Assignee: Thomas Mueller
>            Priority: Minor
>             Fix For: 2.4.4, 2.7.1
>
>         Attachments: JCR-3652.patch, JCR-3652-test-case.patch
>
>
> I have got a strange case where some node bundle is broken, seemingly because a byte is missing. I can't explain the missing byte, but it is reproducible, meaning that writing the bundles again will break them again. There are 11 broken bundles, 10 of them have the size 480 bytes and one is slightly larger. It is always a boolean property value that is missing, always the value for the property jcr:isCheckedOut.
> As a (temporary) solution, and to help analyze what the problem might be, I will create a patch that does the following:
> * When serializing a bundle, check if the byte array can be de-serialized. If not, then try again. Starting with the 3th try, use a slower variant where before and after writing the boolean value the buffer is flushed. I'm aware that ByteArrayOutputStream.flush doesn't do much, but maybe it solves the problem (let's see) if the problem is related to a JVM issue.
> * If de-serializing a bundle fails, check if it's because of a missing boolean property value. If yes, insert the missing byte.
> I have also added some log messages (warning / error) to help analyze the problem.

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators
For more information on JIRA, see: http://www.atlassian.com/software/jira