-
1. Re: Cannot delete workspace after merge.
hchiorean Dec 14, 2016 10:09 AM (in response to mfed)When you create the new workspace you're cloning the old workspace, meaning that all node IDs and therefore references are preserved. When you do the merge operation, depending on your data (node types and state) I suspect some reference properties are being changed and this change is reflected both in the old and new workspace. This particular change is most likely causing the error you're seeing.
However, the merge algorithm is so complex (https://docs.adobe.com/content/docs/en/spec/jcr/2.0/15_Versioning.html) that it's impossible to tell if this is a bug or it's working as designed by the spec. If you can create a simple test case for this (simple being the operative keyword) I can look at it some more. You can use our unit tests as example: modeshape/JcrVersioningTest.java at master · ModeShape/modeshape · GitHub
-
2. Re: Cannot delete workspace after merge.
mfed Dec 14, 2016 10:23 AM (in response to hchiorean)Ok i'll see if i can setup a simple test case. FWIW, forcing the versions to the latest after merge like so:
mergedNode.update(newBranch)
versionManager.checkout(nodePath)If done for each versionable node in the tree, allows me to delete the new workspace and the original workspace ends in the state which i'd expect post-merge.
-
3. Re: Cannot delete workspace after merge.
mfed Dec 18, 2016 2:58 PM (in response to hchiorean)Ok so I haven't put it in the exact format of your existing test cases but I wasn't taking that as the big deal. This case uses the following CND:
<jcr='http://www.jcp.org/jcr/1.0'>
<nt='http://www.jcp.org/jcr/nt/1.0'>
<mix='http://www.jcp.org/jcr/mix/1.0'>
<sc='http:///www.foo.com/foo/sc/1.0'>
[sc:simpleContent] > nt:hierarchyNode, mix:created, mix:referenceable, mix:lastModified, mix:versionable, mix:lockable
- sc:foo (string)
- sc:gooRef (reference)
[sc:simpleReference] > nt:hierarchyNode, mix:created, mix:referenceable, mix:lastModified, mix:versionable, mix:lockable
- sc:goo (string)
The key part above is the reference. Without it, everything works fine (ie if gooRef was a string, everything merges and deletes just fine but obviously i don't get proper referential integrity). Conceptually this is the case:
- Create workspace "working".
- Create a sc:simpleContent and a sc:simpleReference and have the former refer to the latter via the "gooRef" property. Give "foo" and "goo" default values of "bar".
- Create a workspace "newWorkspaceA" from "working".
- Set the "foo" property on the sc:simpleContent to "dar".
- Merge "newWorkspaceA" into "working".
- The "foo" value is now "dar" as expected.
- The "goo" property in the "gooRef" reference still results in the original "bar" as expected.
- Attempt to delete "newWorkspaceA". Expect it to delete just fine, but get
Cannot remove some nodes because they are still strongly referred by existing nodes: {/{}Test/{}TheGoo=[/{}Test/{}TheFoo]}
org.modeshape.jcr.cache.ReferentialIntegrityException: Cannot remove some nodes because they are still strongly referred by existing nodes: {/{}Test/{}TheGoo=[/{}Test/{}TheFoo]}
at org.modeshape.jcr.cache.document.WritableSessionCache.persistChanges(WritableSessionCache.java:1477)
at org.modeshape.jcr.cache.document.WritableSessionCache.save(WritableSessionCache.java:538)
at org.modeshape.jcr.cache.document.WritableSessionCache.save(WritableSessionCache.java:500)
at org.modeshape.jcr.cache.RepositoryCache.lambda$destroyWorkspace$17(RepositoryCache.java:933)
at org.modeshape.jcr.cache.document.LocalDocumentStore.runInTransaction(LocalDocumentStore.java:297)
at org.modeshape.jcr.cache.RepositoryCache.destroyWorkspace(RepositoryCache.java:926)
at org.modeshape.jcr.JcrWorkspace.deleteWorkspace(JcrWorkspace.java:873)
------------------------------------------------------------------------------------------------
My local modeshape source isn't working right now and I don't have the time just now to figure out why so as to write this case in your unit test format. Below is roughly my code using org.modeshape.bom:modeshape-bom-embedded:5.2.0.Final (it's scala. sorry if that is a problem). Any thoughts about what I might be doing incorrectly or if this is a bug would be greatly appreciated. Thanks.
/*********** just helpers, possibly not relevant to the case but included here in case they are ****************/
val tools = new JcrTools()
def put(value: String, path: String, property: String, typ: String)(implicit session: Session) = {
val node = tools.findOrCreateNode(session.getRootNode, path, "nt:folder", typ)
val versionManager = session.getWorkspace.getVersionManager
val absPath = node.getPath
if (!versionManager.isCheckedOut(absPath))
versionManager.checkout(absPath)
node.setProperty(property, value)
session.save()
versionManager.checkin(absPath)
}
def merge(nodeToMerge: String, mergeFrom: String)(implicit session: Session) = {
val versionManager = session.getWorkspace.getVersionManager
versionManager.merge(nodeToMerge, mergeFrom, false, false)
println(s"Workspace '$mergeFrom' merged into '$nodeToMerge'.")
}
def delete(workspaceName: String) (implicit session: Session) = {
session.getWorkspace.deleteWorkspace(workspaceName)
if(session.getWorkspace.getAccessibleWorkspaceNames.contains(workspaceName))
throw new Exception(s"'$workspaceName' was not actually deleted.")
println(s"Workspace '$workspaceName' was deleted.")
}
/*********** end helpers, begin case ****************/
def runShouldNotThrowButDoes() = {
val modeshape = getModeshape()
//this "withSession" function is just some simple wrapper which does login, gets the session and logs out at the end. nothing fancy.
modeshape.withSession("working")(session => {
implicit val s: Session = session
put("bar", "/Test/TheFoo", "sc:foo", "sc:simpleContent")
put("bar", "/Test/TheGoo", "sc:goo", "sc:simpleReference")
val referencedNode = session.getNode("/Test/TheGoo")
val result = session.getValueFactory.createValue(referencedNode)
put(result.toString, "/Test/TheFoo", "sc:gooRef", "sc:simpleContent")
session.getWorkspace.createWorkspace("newWorkspaceA", "working")
})
modeshape.withSession("newWorkspaceA")(session => {
implicit val s: Session = session
put("dar", "/Test/TheFoo", "sc:foo", "sc:simpleContent")
})
modeshape.withSession("working")(session => {
implicit val s: Session = session
merge("/Test", "newWorkspaceA")
})
modeshape.withSession("working")(session => {
implicit val s: Session = session
delete("newWorkspaceA") //This throws the given exception.
})
}
runShouldNotThrowButDoes()
-
4. Re: Cannot delete workspace after merge.
hchiorean Dec 20, 2016 3:31 AM (in response to mfed)I was able to reproduce your test case locally and I will argue that this is not a bug, but rather the behavior is according to the JCR 2.0 spec:
15.9 Merge
The merge test is performed by comparing N with its corresponding node in
srcWorkspace, call it N'.
The merge test is done by comparing the base version of N (call it V) and the base
version of N' (call it V').
...
If N is currently checked-in then:
• If V' is an eventual successor of V, then the merge result for N is update.
...
doupdate(n, n')
replace set of properties of n with those of n'.
So in your test case you're merging your updated workspace ("newWorkspaceA") into the original workspace ("working"). According to the algorithm from the spec, the properties of the sc:simpleContent node from the "working" workspace will be updated to have the same value as the properties of the sc:simpleContent node from the "newWorkspaceA" workspace. This means that that strong reference property sc:gooRef belonging to this node will point to the sc:goo node from the "newWorkspaceA" workspace (remember that when you cloned the workspace the property sc:gooRef from the newWorkspaceA points to the node from within the same workspace).
In other words, because you're merging corresponding versionable nodes with strong reference properties you're creating strong references across nodes from different workspaces. This is why you cannot remove the "newWorkspaceA" - removing it would break a strong referenceable constraint, where a node from "working" strongly refers a node from "newWorkspaceA".
Your options are to either not use strong references (use weak or simple references instead) or manually change the value of the reference property after performing the merge.