/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jackrabbit.core.state; import org.apache.jackrabbit.commons.cnd.CndImporter; import org.apache.jackrabbit.test.AbstractJCRTest; import javax.jcr.InvalidItemStateException; import javax.jcr.Node; import javax.jcr.PropertyType; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.nodetype.NodeType; import javax.jcr.nodetype.NodeTypeIterator; import java.io.InputStreamReader; import java.io.Reader; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * <code>NodeStateMergerTest</code>... */ public class NodeStateMergerTest extends AbstractJCRTest { /** Name of the cnd nodetype file for import and namespace registration. */ private static final String TEST_NODETYPES = "org/apache/jackrabbit/core/nodetype/xml/test_nodestatemerger_nodetypes.cnd"; private List<String> testMixins = new ArrayList<String>(); private Node testNode; private Session sessionB; private Node testNodeB; @Override protected void setUp() throws Exception { super.setUp(); Reader cnd = new InputStreamReader(getClass().getClassLoader().getResourceAsStream(TEST_NODETYPES)); CndImporter.registerNodeTypes(cnd, superuser); cnd.close(); NodeTypeIterator it = superuser.getWorkspace().getNodeTypeManager().getMixinNodeTypes(); while (it.hasNext()) { NodeType nt = it.nextNodeType(); if (nt.getName().startsWith("test:")) { testMixins.add(nt.getName()); } } testNode = testRootNode.addNode(nodeName1, "nt:unstructured"); superuser.save(); sessionB = getHelper().getSuperuserSession(); testNodeB = sessionB.getNode(testNode.getPath()); } @Override protected void tearDown() throws Exception { if (sessionB != null) { sessionB.logout(); } super.tearDown(); } /** * Add the same property with both sessions but with different values. * * @throws RepositoryException */ public void testAddSamePropertiesWithDifferentValues() throws RepositoryException { assertFalse(testNode.hasProperty(propertyName2)); assertFalse(testNodeB.hasProperty(propertyName2)); testNode.setProperty(propertyName2, "value"); testNodeB.setProperty(propertyName2, "otherValue"); sessionB.save(); superuser.save(); assertEquals("value", testNode.getProperty(propertyName2).getString()); testNodeB.refresh(false); assertEquals("value", testNodeB.getProperty(propertyName2).getString()); } /** * Modify the same property with both sessions but with different values. * * @throws RepositoryException */ public void testModifySamePropertiesWithDifferentValues() throws RepositoryException { testNode.setProperty(propertyName2, "test"); superuser.save(); testNodeB.refresh(false); assertTrue(testNodeB.hasProperty(propertyName2)); try { testNode.setProperty(propertyName2, "value"); testNodeB.setProperty(propertyName2, "otherValue"); sessionB.save(); superuser.save(); fail(); } catch (InvalidItemStateException e) { // expected } } //------------------ Tests adding jcr:mixinType property in the session1 --- /** * Both node don't have any mixins assigned yet. Test if adding the same * mixin node with both sessions works. * * @throws RepositoryException */ public void testAddSameMixinToSessionsAB() throws RepositoryException { assertFalse(testNode.hasProperty("jcr:mixinTypes")); testNode.addMixin(mixReferenceable); testNodeB.addMixin(mixReferenceable); sessionB.save(); superuser.save(); } /** * Same as {@link #testAddSameMixinToSessionsAB} but in addition * adding non-conflicting properties defined by this mixin. The properties * must be merged silently. * * @throws RepositoryException */ public void testAddSameMixinToSessionsAB2() throws RepositoryException { assertFalse(testNode.hasProperty("jcr:mixinTypes")); testNode.addMixin("test:mixinProp_1"); testNode.setProperty("test:prop_double", 124); testNodeB.addMixin("test:mixinProp_1"); testNodeB.setProperty("test:prop_string", "abc"); sessionB.save(); superuser.save(); assertEquals("abc", testNode.getProperty("test:prop_string").getString()); testNodeB.refresh(false); assertEquals(124, testNodeB.getProperty("test:prop_double").getLong()); } /** * Same as {@link #testAddSameMixinToSessionsAB} but in addition * a property defined by this mixin with different value. Value of overlayed * property (from sessionB) must be merged into sessionA. * * @throws RepositoryException */ public void testAddSameMixinToSessionsAB3() throws RepositoryException { assertFalse(testNode.hasProperty("jcr:mixinTypes")); testNode.addMixin("test:mixinProp_1"); testNode.setProperty("test:prop_double", 124); testNodeB.addMixin("test:mixinProp_1"); testNodeB.setProperty("test:prop_double", 134); sessionB.save(); superuser.save(); assertEquals(124, testNode.getProperty("test:prop_double").getLong()); testNodeB.refresh(false); assertEquals(124, testNodeB.getProperty("test:prop_double").getLong()); } /** * Same as {@link #testAddSameMixinToSessionsAB3} having additional * modifications in the overlayed state (modifications by session B). * * @throws RepositoryException */ public void testAddSameMixinToSessionsAB4() throws RepositoryException { assertFalse(testNode.hasProperty("jcr:mixinTypes")); testNode.addMixin("test:mixinProp_1"); testNode.setProperty("test:prop_double", 124); testNodeB.addMixin("test:mixinProp_1"); testNodeB.setProperty("test:prop_double", 134); testNodeB.setProperty("more", "yes"); sessionB.save(); superuser.save(); assertEquals(124, testNode.getProperty("test:prop_double").getLong()); testNodeB.refresh(false); assertEquals(124, testNodeB.getProperty("test:prop_double").getLong()); } /** * Test adding mixin(s) to sessionA while the overlayed * (attached to testNode2, sessionB) doesn't have any mixins defined. * The changes should be merged as there is no conflict. * * @throws RepositoryException */ public void testMixinAddedInSessionA() throws RepositoryException { assertFalse(testNode.hasProperty("jcr:mixinTypes")); for (String mixin : testMixins) { testNode.addMixin(mixin); } testNodeB.addNode(nodeName1, "nt:unstructured"); testNodeB.setProperty(propertyName1, "anyValue"); sessionB.save(); superuser.save(); assertTrue(testNode.hasNode(nodeName1)); assertTrue(testNode.hasProperty(propertyName1)); testNodeB.refresh(false); for (String mixin : testMixins) { assertTrue(testNodeB.isNodeType(mixin)); } } /** * Test adding mixin(s) to sessionA, while the other sessionB * doesn't have any mixins defined. The changes should be merged as there * is no conflict. * * @throws RepositoryException */ public void testMixinAddedInSessionA2() throws RepositoryException { assertFalse(testNode.hasProperty("jcr:mixinTypes")); testNode.addMixin("test:mixinProp_1"); testNodeB.setProperty("test:prop_double", sessionB.getValueFactory().createValue(false)); sessionB.save(); superuser.save(); assertTrue(testNode.isNodeType("test:mixinProp_1")); assertEquals(PropertyType.BOOLEAN, testNode.getProperty("test:prop_double").getType()); testNodeB.refresh(false); assertTrue(testNodeB.isNodeType("test:mixinProp_1")); assertEquals(PropertyType.BOOLEAN, testNodeB.getProperty("test:prop_double").getType()); assertEquals(testNode.getProperty("test:prop_double").getString(), testNodeB.getProperty("test:prop_double").getString()); assertEquals("nt:unstructured", testNode.getProperty("test:prop_double").getDefinition().getDeclaringNodeType().getName()); } /** * Test adding mixin(s) to sessionA while sessionB doesn't add new mixins * but has new child items that collide with the items defined by the new mixin. * The merge should in this case fail. * * @throws RepositoryException */ public void testAddedInSessionAConflictsWithChildItemsInSessionB() throws RepositoryException { assertFalse(testNode.hasProperty("jcr:mixinTypes")); testNode.addMixin("test:mixinProp_5"); // has an autocreated property testNodeB.setProperty("test:prop_long_p", "conflict"); sessionB.save(); try { superuser.save(); fail(); } catch (InvalidItemStateException e) { // expected assertFalse(testNodeB.isNodeType("test:mixinProp_5")); testNodeB.refresh(false); assertEquals("conflict", testNodeB.getProperty("test:prop_long_p").getString()); } } /** * Similar to {@link #testAddedInSessionAConflictsWithChildItemsInSessionB} * but adding mix:referenceable in the SessionA while SessionB * gets a property jcr:uuid (with different type). * * @throws RepositoryException */ // TODO: uncomment once JCR-2779 is fixed // public void testAddedReferenceableSessionAConflictsWithPropInSessionB() throws RepositoryException { // assertFalse(testNode.hasProperty("jcr:mixinTypes")); // // testNode.addMixin(mixReferenceable); // // Property p = testNode2.setProperty("jcr:uuid", session2.getValueFactory().createValue(false)); // assertTrue(testNode2.hasProperty("jcr:uuid")); // assertTrue(p.isNew()); // session2.save(); // // assertTrue(testNode2.hasProperty("jcr:uuid")); // assertTrue(p.isNew()); // try { // superuser.save(); // fail(); // } catch (InvalidItemStateException e) { // assertTrue(testNode.isNodeType(mixReferenceable)); // assertEquals(PropertyType.STRING, testNode.getProperty("jcr:uuid").getType()); // } // } /** * Same as {@link #testAddedInSessionAConflictsWithChildItemsInSessionB} * but in addition the overlayed state gets an addition child node. * * @throws RepositoryException */ // TODO: uncomment once JCR-2779 is fixed // public void testAddedReferenceableSessionAConflictsWithPropInSessionB2() throws RepositoryException { // assertFalse(testNode.hasProperty("jcr:mixinTypes")); // // testNode.addMixin(mixReferenceable); // // testNode2.setProperty("jcr:uuid", session2.getValueFactory().createValue(false)); // testNode2.addNode("test"); // session2.save(); // // assertTrue(testNode2.hasProperty("jcr:uuid")); // assertTrue(testNode2.getProperty("jcr:uuid").isNew()); // try { // superuser.save(); // fail(); // } catch (InvalidItemStateException e) { // assertTrue(testNode.isNodeType(mixReferenceable)); // assertEquals(PropertyType.STRING, testNode.getProperty("jcr:uuid").getType()); // } // } /** * Adding different node types in the SessionA and SessionB: * Not handled and thus the merge must fail. * * @throws RepositoryException */ public void testDifferentMixinAddedInSessionAB() throws Exception { assertFalse(testNode.hasProperty("jcr:mixinTypes")); testNode.addMixin("test:mixinProp_1"); testNodeB.addMixin("test:mixinProp_3"); sessionB.save(); try { superuser.save(); fail("Different mixin types added both in SessionA and SessionB. InvalidItemStateException expected."); } catch (InvalidItemStateException e) { // expected assertFalse(testNode.isNodeType("test:mixinProp_3")); } } //---------------- Tests removing jcr:mixinType property in the SessionA --- /** * Remove the jcr:mixinType property by removing all mixin types. Merge * to changes made in the overlayed state should fail. * @throws Exception */ public void testMixinRemovedInSessionA() throws Exception { for (int i = 1; i<=5; i++) { testNode.addMixin("test:mixinProp_" + i); } superuser.save(); testNodeB.refresh(false); // remove all mixin types for (NodeType mixin : testNode.getMixinNodeTypes()) { testNode.removeMixin(mixin.getName()); } assertFalse(testNode.hasProperty("jcr:mixinTypes")); testNodeB.addNode(nodeName1, "nt:unstructured"); testNodeB.setProperty(propertyName1, "anyValue"); sessionB.save(); try { superuser.save(); fail(); } catch (InvalidItemStateException e) { // expected } } /** * Same as {@link #testMixinRemovedInSessionA} with different mixin types. * @throws Exception */ public void testMixinRemovedInSessionA2() throws Exception { for (int i = 1; i<=4; i++) { testNode.addMixin("test:mixinNode_" + i); } superuser.save(); testNodeB.refresh(false); // remove all mixin types for (NodeType mixin : testNode.getMixinNodeTypes()) { testNode.removeMixin(mixin.getName()); } assertFalse(testNode.hasProperty("jcr:mixinTypes")); testNodeB.addNode(nodeName1, "nt:unstructured"); testNodeB.setProperty(propertyName1, "anyValue"); sessionB.save(); try { superuser.save(); fail(); } catch (InvalidItemStateException e) { // expected } } //--------------- Tests modifying jcr:mixinType property in the SessionA --- /** * Modify the existing mixin property in the SessionA change without adding * conflicting modifications to SessionB. * * @throws Exception */ public void testMixinModifiedInSessionA() throws Exception { testNode.addMixin("test:mixinProp_5"); superuser.save(); testNodeB.refresh(false); assertTrue(testNodeB.hasProperty("jcr:mixinTypes")); // modify the mixin types testNode.addMixin("test:mixinProp_1"); testNodeB.addNode(nodeName1, "nt:unstructured"); testNodeB.setProperty(propertyName1, "anyValue"); sessionB.save(); superuser.save(); assertTrue(testNode.hasProperty(propertyName1)); assertTrue(testNode.hasNode(nodeName1)); assertTrue(testNodeB.isNodeType("test:mixinProp_1")); assertTrue(Arrays.asList(testNodeB.getMixinNodeTypes()).contains(sessionB.getWorkspace().getNodeTypeManager().getNodeType("test:mixinProp_1"))); } public void testMixinModifiedInSessionAB() throws Exception { testNode.addMixin("test:mixinProp_5"); superuser.save(); testNodeB.refresh(false); assertTrue(testNodeB.hasProperty("jcr:mixinTypes")); // modify the mixin types testNode.addMixin("test:mixinProp_1"); testNodeB.addMixin("test:mixinProp_1"); sessionB.save(); try { superuser.save(); fail(); } catch (InvalidItemStateException e) { // expected } } public void testMixinModifiedInSessionAB2() throws Exception { testNode.addMixin("test:mixinProp_5"); superuser.save(); testNodeB.refresh(false); assertTrue(testNodeB.hasProperty("jcr:mixinTypes")); // modify the mixin types testNode.addMixin("test:mixinProp_1"); testNodeB.removeMixin("test:mixinProp_5"); sessionB.save(); try { superuser.save(); fail(); } catch (InvalidItemStateException e) { // expected assertTrue(testNode.hasProperty("jcr:mixinTypes")); assertTrue(testNode.isNodeType("test:mixinProp_1")); assertTrue(testNode.isNodeType("test:mixinProp_5")); assertEquals(2, testNode.getMixinNodeTypes().length); } } public void testMixinModifiedInSessionAB3() throws Exception { testNode.addMixin("test:mixinProp_5"); superuser.save(); testNodeB.refresh(false); assertTrue(testNodeB.hasProperty("jcr:mixinTypes")); // modify the mixin types testNode.addMixin("test:mixinProp_1"); testNode.setProperty(propertyName1, "value"); testNodeB.removeMixin("test:mixinProp_5"); sessionB.save(); try { superuser.save(); fail(); } catch (InvalidItemStateException e) { // expected assertTrue(testNode.hasProperty("jcr:mixinTypes")); assertTrue(testNode.isNodeType("test:mixinProp_1")); assertTrue(testNode.isNodeType("test:mixinProp_5")); assertEquals(2, testNode.getMixinNodeTypes().length); } } public void testMixinModifiedInSessionAB4() throws Exception { testNode.addMixin("test:mixinProp_5"); superuser.save(); testNodeB.refresh(false); assertTrue(testNodeB.hasProperty("jcr:mixinTypes")); // modify the mixin types testNode.addMixin("test:mixinProp_1"); testNodeB.addMixin("test:mixinProp_2"); sessionB.save(); try { superuser.save(); fail(); } catch (InvalidItemStateException e) { // expected } } //-------------------------- Tests adding jcr:mixinType only in SessionB --- /** * No jcr:mixinTypes property present in the SessionA but was added * to the overlayed state while other changes were made to the SessionA. * @throws Exception */ public void testMixinAddedInSessionB() throws Exception { assertFalse(testNode.hasProperty("jcr:mixinTypes")); testNode.addNode(nodeName2); testNodeB.addMixin("test:mixinProp_1"); sessionB.save(); superuser.save(); assertTrue(testNode.isNodeType("test:mixinProp_1")); assertTrue(testNode.hasProperty("jcr:mixinTypes")); assertTrue(testNodeB.hasNode(nodeName2)); } /** * Same as {@link #testMixinAddedInSessionB} but having 2 SessionA modifications. * @throws Exception */ public void testMixinAddedInSessionB2() throws Exception { assertFalse(testNode.hasProperty("jcr:mixinTypes")); testNode.addNode(nodeName2); testNode.setProperty(propertyName1, "value"); testNodeB.addMixin("test:mixinProp_1"); sessionB.save(); superuser.save(); assertTrue(testNode.isNodeType("test:mixinProp_1")); assertTrue(testNode.hasProperty("jcr:mixinTypes")); assertTrue(testNodeB.hasNode(nodeName2)); } /** * Add the mixin property in SessionB by adding a single mixin * and create a conflicting item in the SessionA -> merge must fail. * @throws Exception */ public void testMixinAddedInSessionBWithConflictingChanges() throws Exception { assertFalse(testNode.hasProperty("jcr:mixinTypes")); testNode.addNode(nodeName2); testNode.setProperty("test:prop_long_p", "value"); testNodeB.addMixin("test:mixinProp_5"); sessionB.save(); try { superuser.save(); fail(); } catch (InvalidItemStateException e) { // expected } } //----------------------- Test jcr:mixinTypes removed in SessionB ---------- /** * Test if removing the (existing) jcr:mixinTypes property in the overlayed * state (by removing all mixins) is not merged to SessionA changes. * @throws Exception */ public void testMixinRemovedInSessionB() throws Exception { for (int i = 1; i<=5; i++) { testNode.addMixin("test:mixinProp_" + i); } superuser.save(); testNodeB.refresh(false); testNode.addNode(nodeName1, "nt:unstructured"); testNode.setProperty(propertyName1, "anyValue"); // remove all mixin types for (NodeType mixin : testNodeB.getMixinNodeTypes()) { testNodeB.removeMixin(mixin.getName()); } assertFalse(testNodeB.hasProperty("jcr:mixinTypes")); sessionB.save(); try { superuser.save(); fail(); } catch (InvalidItemStateException e) { // expected } } //----------------------- Test mixin modification (add-only) in SessionB --- /** * Existing mixins are modified in SessionB but not in the * SessionA, where the net effect is 'add-only' in the SessionA. * Changes should be merge if there are no conflicting SessionA * modifications. * * @throws Exception */ public void testMixinModifiedAddInSessionB() throws Exception { for (int i = 1; i<=5; i++) { testNode.addMixin("test:mixinProp_" + i); } superuser.save(); testNodeB.refresh(false); assertTrue(testNodeB.hasProperty("jcr:mixinTypes")); // non-conflicting modification in SessionA testNode.setProperty(propertyName1, "value"); testNodeB.addMixin("test:mixinNode_1"); sessionB.save(); superuser.save(); assertTrue(testNode.isNodeType("test:mixinNode_1")); List<NodeType> mx = Arrays.asList(testNode.getMixinNodeTypes()); assertTrue(mx.contains(superuser.getWorkspace().getNodeTypeManager().getNodeType("test:mixinNode_1"))); testNodeB.refresh(false); assertTrue(testNodeB.hasProperty(propertyName1)); } /** * Same as {@link #testMixinModifiedAddInSessionB} with different * SessionA modifications. * * @throws Exception */ public void testMixinModifiedAddInSessionB2() throws Exception { for (int i = 1; i<=5; i++) { testNode.addMixin("test:mixinProp_" + i); } superuser.save(); testNodeB.refresh(false); assertTrue(testNodeB.hasProperty("jcr:mixinTypes")); // non-conflicting modification in SessionA testNode.setProperty(propertyName1, "value"); testNode.addNode(nodeName2); testNodeB.addMixin("test:mixinNode_1"); sessionB.save(); superuser.save(); assertTrue(testNode.isNodeType("test:mixinNode_1")); List<NodeType> mx = Arrays.asList(testNode.getMixinNodeTypes()); assertTrue(mx.contains(superuser.getWorkspace().getNodeTypeManager().getNodeType("test:mixinNode_1"))); testNodeB.refresh(false); assertTrue(testNodeB.hasProperty(propertyName1)); assertTrue(testNodeB.hasNode(nodeName2)); } /** * Test if the merge of add-only mixin modification in the overlayed stated * is aborted if there are conflicting SessionA changes present. * @throws Exception */ public void testMixinModifiedAddInSessionBWithConflictingChanges() throws Exception { testNode.addMixin(mixReferenceable); superuser.save(); testNodeB.refresh(false); assertTrue(testNodeB.hasProperty("jcr:mixinTypes")); // conflicting modification in SessionA testNode.setProperty(propertyName1, "value"); testNode.addNode("test:child_1"); testNodeB.addMixin("test:mixinNode_1"); sessionB.save(); try { superuser.save(); fail(); } catch (InvalidItemStateException e) { // expected } } /** * Same as {@link #testMixinModifiedAddInSessionBWithConflictingChanges} * with different SessionA modifications. * * @throws Exception */ public void testMixinModifiedAddInSessionBWithConflictingChanges2() throws Exception { testNode.addMixin(mixReferenceable); superuser.save(); testNodeB.refresh(false); assertTrue(testNodeB.hasProperty("jcr:mixinTypes")); // conflicting modification in SessionA testNode.addNode("test:child_1"); testNodeB.addMixin("test:mixinNode_1"); sessionB.save(); try { superuser.save(); fail(); } catch (InvalidItemStateException e) { // expected } } /** * Same as {@link #testMixinModifiedAddInSessionBWithConflictingChanges} * with different mixin and SessionA modifications. * * @throws Exception */ public void testMixinModifiedAddInSessionBWithConflictingChanges3() throws Exception { testNode.addMixin(mixReferenceable); superuser.save(); testNodeB.refresh(false); assertTrue(testNodeB.hasProperty("jcr:mixinTypes")); // conflicting modification in SessionA testNode.setProperty("test:prop_long_p", "non-long-value"); testNodeB.addMixin("test:mixinProp_5"); sessionB.save(); try { superuser.save(); fail(); } catch (InvalidItemStateException e) { // expected } } /** * Same as {@link #testMixinModifiedAddInSessionBWithConflictingChanges} * with different mixin and other kind of modifications. * * @throws Exception */ public void testMixinModifiedAddInSessionBWithConflictingChanges4() throws Exception { testNode.addMixin(mixReferenceable); superuser.save(); testNodeB.refresh(false); assertTrue(testNodeB.hasProperty("jcr:mixinTypes")); // conflicting modification in session1 testNode.setProperty("test:prop_long_p", "non-long-value"); testNode.addNode("test:child_1"); testNodeB.addMixin("test:mixinProp_5"); sessionB.save(); try { superuser.save(); fail(); } catch (InvalidItemStateException e) { // expected } } /** * Same as {@link #testMixinModifiedAddInSessionBWithConflictingChanges} * but using mix:referencable as mixin to force the modification in the * overlayed state. * * @throws Exception */ // TODO: uncomment once JCR-2779 is fixed // public void testMixinModifiedReferenceableInSessionBConflicting() throws RepositoryException { // testNode.addMixin("test:mixinProp_1"); // superuser.save(); // assertTrue(testNode2.hasProperty("jcr:mixinTypes")); // // testNode.setProperty("jcr:uuid", superuser.getValueFactory().createValue(false)); // // testNode2.addMixin(mixReferenceable); // session2.save(); // // assertTrue(testNode.hasProperty("jcr:uuid")); // assertTrue(testNode.getProperty("jcr:uuid").isNew()); // try { // superuser.save(); // fail(); // } catch (InvalidItemStateException e) { // assertFalse(testNode.isNodeType(mixReferenceable)); // assertEquals(PropertyType.BOOLEAN, testNode.getProperty("jcr:uuid").getType()); // // assertTrue(testNode2.isNodeType(mixReferenceable)); // assertEquals(PropertyType.STRING, testNode2.getProperty("jcr:uuid").getType()); // } // } /** * Same as {@link #testMixinModifiedAddInSessionBWithConflictingChanges} * but using mix:referencable as mixin to force the modification in the * overlayed state. * * @throws Exception */ // TODO: uncomment once JCR-2779 is fixed // public void testMixinModifiedReferenceableInSessionBConflicting2() throws RepositoryException { // testNode.addMixin("test:mixinProp_1"); // superuser.save(); // assertTrue(testNode2.hasProperty("jcr:mixinTypes")); // // testNode.setProperty("jcr:uuid", superuser.getValueFactory().createValue(false)); // testNode.addNode(nodeName2); // // testNode2.addMixin(mixReferenceable); // session2.save(); // // assertTrue(testNode.hasProperty("jcr:uuid")); // assertTrue(testNode.getProperty("jcr:uuid").isNew()); // try { // superuser.save(); // fail(); // } catch (InvalidItemStateException e) { // assertFalse(testNode.isNodeType(mixReferenceable)); // assertEquals(PropertyType.BOOLEAN, testNode.getProperty("jcr:uuid").getType()); // // assertTrue(testNode2.isNodeType(mixReferenceable)); // assertEquals(PropertyType.STRING, testNode2.getProperty("jcr:uuid").getType()); // } // } //-------------------- Test mixin modification (remove-only) in SessionB --- /** * Test if merge fails if some mixin removal occurred in the SessionB. * * @throws Exception */ public void testMixinModifiedRemovedInSessionB() throws Exception { for (String mixin : testMixins) { testNode.addMixin(mixin); } superuser.save(); testNodeB.refresh(false); assertTrue(testNodeB.hasProperty("jcr:mixinTypes")); // modification in session1 testNode.setProperty(propertyName1, "value"); // mixin-removal in the session2 testNodeB.removeMixin("test:mixinProp_1"); testNodeB.removeMixin("test:mixinProp_2"); sessionB.save(); try { superuser.save(); fail(); } catch (InvalidItemStateException e) { // expected } } /** * Same as {@link #testMixinModifiedRemovedInSessionB} but with different * SessionA modifications. * * @throws Exception */ public void testMixinModifiedRemovedInSessionB2() throws Exception { for (String mixin : testMixins) { testNode.addMixin(mixin); } superuser.save(); testNodeB.refresh(false); assertTrue(testNodeB.hasProperty("jcr:mixinTypes")); // modification in SessionA testNode.setProperty(propertyName1, "value"); testNode.addNode(nodeName2); // mixin-removal in SessionB testNodeB.removeMixin("test:mixinProp_1"); testNodeB.removeMixin("test:mixinProp_2"); sessionB.save(); try { superuser.save(); fail(); } catch (InvalidItemStateException e) { // expected } } //---------------------------- Test other mixin modification in SessionB --- /** * Test if merge fails if the mixins of the overlayed state (sessionB) were * modified in a combination of add and removal of mixin. * * @throws Exception */ public void testMixinModifiedInSessionB() throws Exception { for (String mixin : testMixins) { testNode.addMixin(mixin); } superuser.save(); testNodeB.refresh(false); assertTrue(testNodeB.hasProperty("jcr:mixinTypes")); // modification in SessionA testNode.setProperty(propertyName1, "value"); // mixin-removal in the SessionB testNodeB.addMixin(mixReferenceable); testNodeB.removeMixin("test:mixinProp_2"); sessionB.save(); try { superuser.save(); fail(); } catch (InvalidItemStateException e) { // expected } } }