/* * 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.test.api; import javax.jcr.InvalidItemStateException; import javax.jcr.ItemExistsException; import javax.jcr.Node; import javax.jcr.PathNotFoundException; import javax.jcr.Repository; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.Value; import javax.jcr.lock.LockException; import javax.jcr.nodetype.ConstraintViolationException; import org.apache.jackrabbit.test.AbstractJCRTest; import org.apache.jackrabbit.test.NotExecutableException; /** * <code>SessionTest</code> contains all test cases for the * <code>javax.jcr.Session</code> class that are level 2 (modifing repository * content). * */ public class SessionTest extends AbstractJCRTest { /** * Tries to move a node using {@link javax.jcr.Session#move(String src, String dest)} * to a location where a node already exists with * same name. * <p> * Prerequisites: * <ul> * <li><code>javax.jcr.tck.SessionTest.testMoveItemExistsException.nodetype2</code> * must contain name of a nodetype that does not allow same name sibling * child nodes.</li> * <li><code>javax.jcr.tck.SessionTest.testMoveItemExistsException.nodetype3</code> * must contain name of a valid nodetype that can be added as a child of * <code>nodetype2</code></li> * </ul> * <p> * This should throw an {@link javax.jcr.ItemExistsException}. */ public void testMoveItemExistsException() throws RepositoryException { // get default workspace test root node using superuser session Node defaultRootNode = (Node) superuser.getItem(testRootNode.getPath()); // create parent node Node srcParentNode = defaultRootNode.addNode(nodeName1, testNodeType); // create node to move Node moveNode = srcParentNode.addNode(nodeName2, getProperty("nodetype3")); // create a second node that will serve as new parent, must use a nodetype that does not allow // same name siblings Node destParentNode = defaultRootNode.addNode(nodeName3, getProperty("nodetype2")); // add a valid child Node destNode = destParentNode.addNode(nodeName2, getProperty("nodetype3")); // save the new nodes superuser.save(); try { // move the node superuser.move(moveNode.getPath(), destNode.getPath()); fail("Moving a node using Session.move() to a location where a node with same name already exists must throw ItemExistsException"); } catch (ItemExistsException e) { // ok, works as expected } } /** * Calls {@link javax.jcr.Session#move(String src, String dest)} * with invalid destination path. * <p> * Should throw a {@link javax.jcr.PathNotFoundException}. */ public void testMovePathNotFoundExceptionDestInvalid() throws RepositoryException { // get default workspace test root node using superuser session Node defaultRootNode = (Node) superuser.getItem(testRootNode.getPath()); // create parent node Node srcParentNode = defaultRootNode.addNode(nodeName1, testNodeType); // create node to move Node moveNode = srcParentNode.addNode(nodeName2, testNodeType); // save the new nodes superuser.save(); // move the node try { superuser.move(moveNode.getPath(), defaultRootNode.getPath() + "/" + nodeName2 + "/" + nodeName1); fail("Invalid destination path during Session.move() must throw PathNotFoundException"); } catch (PathNotFoundException e) { // ok, works as expected } } /** * Calls {@link javax.jcr.Session#move(String src, String dest)} with * invalid source path. * <p> * Should throw a {@link javax.jcr.PathNotFoundException}. */ public void testMovePathNotFoundExceptionSrcInvalid() throws RepositoryException { // get default workspace test root node using superuser session Node defaultRootNode = (Node) superuser.getItem(testRootNode.getPath()); // create a node that will serve as new parent Node destParentNode = defaultRootNode.addNode(nodeName3, testNodeType); // save the new nodes superuser.save(); // move the node try { superuser.move(defaultRootNode.getPath() + "/" + nodeName1, destParentNode.getPath() + "/" + nodeName2); fail("Invalid source path during Session.move() must throw PathNotFoundException"); } catch (PathNotFoundException e) { // ok. works as expected } } /** * Calls {@link javax.jcr.Session#move(String src, String dest)} * with a destination path that has an index postfixed. * <p> * This should throw a {@link javax.jcr.RepositoryException}. */ public void testMoveRepositoryException() throws RepositoryException { // get default workspace test root node using superuser session Node defaultRootNode = (Node) superuser.getItem(testRootNode.getPath()); // create parent node Node srcParentNode = defaultRootNode.addNode(nodeName1, testNodeType); // create node to be moved Node moveNode = srcParentNode.addNode(nodeName2, testNodeType); // create a node that will serve as new parent Node destParentNode = defaultRootNode.addNode(nodeName3, testNodeType); // save the new nodes superuser.save(); // move the node try { superuser.move(moveNode.getPath(), destParentNode.getPath() + "/" + nodeName2 + "[1]"); fail("If destination path of Session.move() contains an index as postfix it must throw RepositoryException"); } catch (RepositoryException e) { // ok works as expected } } /** * Moves a node using {@link javax.jcr.Session#move(String src, String dest)}, * afterwards it tries to only save the old parent node. * <p> * This should throw {@link javax.jcr.nodetype.ConstraintViolationException}. * <p> * Prerequisites: <ul> <li><code>javax.jcr.tck.nodetype</code> * must accept children of same nodetype</li> </ul> */ public void testMoveConstraintViolationExceptionSrc() throws RepositoryException { // get default workspace test root node using superuser session Node defaultRootNode = (Node) superuser.getItem(testRootNode.getPath()); // create parent node Node srcParentNode = defaultRootNode.addNode(nodeName1, testNodeType); // create node to be moved Node moveNode = srcParentNode.addNode(nodeName2, testNodeType); // create a node that will serve as new parent Node destParentNode = defaultRootNode.addNode(nodeName3, testNodeType); // save the new nodes superuser.save(); // move the node superuser.move(moveNode.getPath(), destParentNode.getPath() + "/" + nodeName2); // save only old parent node try { srcParentNode.save(); fail("Saving only the source parent node after a Session.move() operation must throw ConstraintViolationException"); } catch (ConstraintViolationException e) { // ok both work as expected } } /** * Moves a node using {@link javax.jcr.Session#move(String src, String dest)}, * afterwards it tries to only save the destination parent * node. * <p> * This should throw a {@link javax.jcr.nodetype.ConstraintViolationException}. * <p> * Prerequisites: <ul> <li><code>javax.jcr.tck.nodetype</code> * must accept children of same nodetype</li> </ul> */ public void testMoveConstraintViolationExceptionDest() throws RepositoryException { // get default workspace test root node using superuser session Node defaultRootNode = (Node) superuser.getItem(testRootNode.getPath()); // create parent node Node srcParentNode = defaultRootNode.addNode(nodeName1, testNodeType); // create node to be moved Node moveNode = srcParentNode.addNode(nodeName2, testNodeType); // create a node that will serve as new parent Node destParentNode = defaultRootNode.addNode(nodeName3, testNodeType); // save the new nodes superuser.save(); // move the node superuser.move(moveNode.getPath(), destParentNode.getPath() + "/" + nodeName2); // save only moved node try { destParentNode.save(); fail("Saving only moved node after a Session.move() operation should throw ConstraintViolationException"); } catch (ConstraintViolationException e) { // ok try to save the source } } /** * Calls {@link javax.jcr.Session#move(String src, String dest)} where * the parent node of src is locked. * <p> * Should throw a {@link LockException} immediately or on save. */ public void testMoveLockException() throws NotExecutableException, RepositoryException { Session session = superuser; if (!isSupported(Repository.OPTION_LOCKING_SUPPORTED)) { throw new NotExecutableException("Locking is not supported."); } // create a node that is lockable Node lockableNode = testRootNode.addNode(nodeName1, testNodeType); // or try to make it lockable if it is not ensureMixinType(lockableNode, mixLockable); // add a sub node (the one that is tried to move later on) Node srcNode = lockableNode.addNode(nodeName1, testNodeType); testRootNode.getSession().save(); // remove first slash of path to get rel path to root String pathRelToRoot = lockableNode.getPath().substring(1); // access node through another session to lock it Session session2 = getHelper().getSuperuserSession(); try { Node node2 = session2.getRootNode().getNode(pathRelToRoot); node2.lock(true, true); try { String destPath = testRoot + "/" + nodeName2; session.move(srcNode.getPath(), destPath); testRootNode.getSession().save(); fail("A LockException is thrown either immediately or on save if a lock prevents the move."); } catch (LockException e){ // success } } finally { session2.logout(); } } /** * Checks if {@link javax.jcr.Session#move(String src, String dest)} * works properly. To verify if node has been moved properly * it uses a second session to retrieve the moved node. * <p> * Prerequisites: <ul> <li><code>javax.jcr.tck.nodetype</code> * must accept children of same nodetype</li> </ul> */ public void testMoveNode() throws RepositoryException { // get default workspace test root node using superuser session Node defaultRootNode = (Node) superuser.getItem(testRootNode.getPath()); // create parent node Node srcParentNode = defaultRootNode.addNode(nodeName1, testNodeType); // create node to be moved Node moveNode = srcParentNode.addNode(nodeName2, testNodeType); // create a node that will serve as new parent Node destParentNode = defaultRootNode.addNode(nodeName3, testNodeType); // save the new nodes superuser.save(); //move the nodes superuser.move(moveNode.getPath(), destParentNode.getPath() + "/" + nodeName2); superuser.save(); // get moved tree root node with session 2 Session testSession = getHelper().getReadWriteSession(); try { testSession.getItem(destParentNode.getPath() + "/" + nodeName2); // node found } finally { testSession.logout(); } } /** * Checks if a newly created node gets properly saved using <code>{@link * javax.jcr.Session#save()}</code>. * <p> * It creates a new node, saves * it using <code>session.save()</code> then uses a different session to * verify if the node has been properly saved. */ public void testSaveNewNode() throws RepositoryException { // get default workspace test root node using superuser session Node defaultRootNode = (Node) superuser.getItem(testRootNode.getPath()); // create a node Node newNode = defaultRootNode.addNode(nodeName1, testNodeType); // save changes superuser.save(); // use a different session to verify if the node is there Session s = getHelper().getReadOnlySession(); try { s.getItem(newNode.getPath()); // throws PathNotFoundException if item was not saved } finally { s.logout(); } } /** * Checks if a modified node gets properly saved using <code>{@link * javax.jcr.Session#save()}</code>. * <p> * It creates a new node, saves * it using <code>session.save()</code>, modifies the node by adding a child * node, saves again and finally verifies with a different session if * changes have been stored properly. * <p> * Prerequisites: <ul> * <li><code>javax.jcr.tck.nodetype</code> must accept children of same * nodetype</li> </ul> */ public void testSaveModifiedNode() throws RepositoryException { // get default workspace test root node using superuser session Node defaultRootNode = (Node) superuser.getItem(testRootNode.getPath()); // create a node Node newNode = defaultRootNode.addNode(nodeName1, testNodeType); // save new node superuser.save(); // and add a child node newNode.addNode(nodeName2, testNodeType); // save the changes superuser.save(); // check if the child node was created properly // get a reference with a second session to the modified node Session s = getHelper().getReadOnlySession(); try { Node newNodeSession2 = (Node) s.getItem(newNode.getPath()); // check if child is there assertTrue("Modifications on a node are not save after Session.save()", newNodeSession2.hasNode(nodeName2)); } finally { s.logout(); } } /** * Tries to create and save a node using {@link javax.jcr.Session#save()} * with an mandatory property that is not set on saving time. * <p> * Prerequisites: <ul> <li><code>javax.jcr.tck.SessionTest.testSaveConstraintViolationException.nodetype2</code> * must reference a nodetype that has one at least one property that is * mandatory but not autocreated</li> </ul> */ public void testSaveConstraintViolationException() throws RepositoryException { // get default workspace test root node using superuser session Node defaultRootNode = (Node) superuser.getItem(testRootNode.getPath()); // create a node with at least one mandatory, not autocreated property defaultRootNode.addNode(nodeName1, getProperty("nodetype2")); // save changes try { superuser.save(); fail("Trying to use Session.save() with a node that has a mandatory property not set, should throw ConstraintViolationException"); } catch (ConstraintViolationException e) { // ok } } /** * Tries to save a node using {@link javax.jcr.Session#save()} that was * already deleted by an other session. * <p> * Procedure: <ul> * <li>Creates a new node with session 1, saves it, adds a child node.</li> * <li>Access new node with session 2,deletes the node, saves it.</li> * <li>session 1 tries to save modifications .</li> </ul> This should throw * an {@link javax.jcr.InvalidItemStateException}. * <p> * Prerequisites: * <ul> <li><code>javax.jcr.tck.nodetype</code> must accept children of same * nodetype</li> </ul> */ public void testSaveInvalidStateException() throws RepositoryException { // get default workspace test root node using superuser session Node defaultRootNode = (Node) superuser.getItem(testRootNode.getPath()); // create a node Node nodeSession1 = defaultRootNode.addNode(nodeName1, testNodeType); // save new node superuser.save(); // make a modification nodeSession1.addNode(nodeName2, testNodeType); // get the new node with a different session Session testSession = getHelper().getReadWriteSession(); try { Node nodeSession2 = (Node) testSession.getItem(nodeSession1.getPath()); // delete the node with the new session nodeSession2.remove(); // make node removal persistent testSession.save(); // save changes made with superuser session try { superuser.save(); fail("Saving a modified Node using Session.save() already deleted by an other session should throw InvalidItemStateException"); } catch (InvalidItemStateException e) { // ok, works as expected } } finally { testSession.logout(); } } /** * Checks if {@link javax.jcr.Session#refresh(boolean refresh)} works * properly with <code>refresh</code> set to <code>false</code>. * <p> * Procedure: <ul> <li>Creates two nodes with session 1</li> <li>Modifies * node 1 with session 1 by adding a child node</li> <li>Get node 2 with * session 2</li> <li>Modifies node 2 with session 2 by adding a child * node</li> <li>saves session 2 changes using {@link * javax.jcr.Session#save()}</li> <li>calls <code>Session.refresh(false)</code> * on session 1</li> </ul> Session 1 changes should be cleared and session 2 * changes should now be visible to session 1. * <p> * Prerequisites: <ul> * <li><code>javax.jcr.tck.nodetype</code> must accept children of same * nodetype</li> </ul> */ public void testRefreshBooleanFalse() throws RepositoryException { // get default workspace test root node using superuser session Node defaultRootNode = (Node) superuser.getItem(testRootNode.getPath()); // create a node Node testNode1Session1 = defaultRootNode.addNode(nodeName1, testNodeType); // create a second node Node testNode2Session1 = defaultRootNode.addNode(nodeName2, testNodeType); // save the new nodes superuser.save(); // add child node to test node 1 using session 1 testNode1Session1.addNode(nodeName2, testNodeType); // get session 2 Session session2 = getHelper().getReadWriteSession(); try { // get the second node Node testNode2Session2 = (Node) session2.getItem(testNode2Session1.getPath()); // adds a child node testNode2Session2.addNode(nodeName3, testNodeType); // save the changes session2.save(); // call refresh on session 1 superuser.refresh(false); // check if session 1 flag has been cleared assertFalse("Session should have no pending changes recorded after Session.refresh(false)!", superuser.hasPendingChanges()); // check if added child node for node 1 by session 1 has been removed assertFalse("Node Modifications have not been flushed after session.refresh(false)", testNode1Session1.hasNodes()); // check if added child node for node 2 by session 2 has become visible in session 1 assertTrue("Node modified by a different session has not been updated after Session.refresh(false)", testNode2Session1.hasNodes()); } finally { session2.logout(); } } /** * Checks if {@link javax.jcr.Session#refresh(boolean refresh)} works * properly with <code>refresh</code> set to <code>true</code>. * <p> * Procedure: <ul> <li>Creates two nodes with session 1</li> <li>Modifies * node 1 with session 1 by adding a child node</li> <li>Get node 2 with * session 2</li> <li>Modifies node 2 with session 2 by adding a child * node</li> <li>saves session 2 changes using {@link * javax.jcr.Session#save()}</li> <li>calls <code>Session.refresh(true)</code> * on session 1</li> </ul> Session 1 changes and session 2 changes now be * visible to session 1. * <p> * Prerequisites: <ul> * <li><code>javax.jcr.tck.nodetype</code> must accept children of same * nodetype</li> </ul> */ public void testRefreshBooleanTrue() throws RepositoryException { // get default workspace test root node using superuser session Node defaultRootNode = (Node) superuser.getItem(testRootNode.getPath()); // create a node Node testNode1Session1 = defaultRootNode.addNode(nodeName1, testNodeType); // create a second node Node testNode2Session1 = defaultRootNode.addNode(nodeName2, testNodeType); // save the new nodes superuser.save(); // add child node to test node 1 using session 1 testNode1Session1.addNode(nodeName2, testNodeType); // get session 2 Session session2 = getHelper().getReadWriteSession(); try { // get the second node Node testNode2Session2 = (Node) session2.getItem(testNode2Session1.getPath()); // adds a child node testNode2Session2.addNode(nodeName3, testNodeType); // save the changes session2.save(); // call refresh on session 1 superuser.refresh(true); // check if session 1 flag has been cleared assertTrue("Session should still have pending changes recorded after Session.refresh(true)!", superuser.hasPendingChanges()); // check if added child node for node 1 by session 1 is still there assertTrue("Node Modifications are lost after session.refresh(true)", testNode1Session1.hasNodes()); // check if added child node for node 2 by session 2 has become visible in session 1 assertTrue("Node modified by a different session has not been updated after Session.refresh(true)", testNode2Session1.hasNodes()); } finally { session2.logout(); } } /** * Checks if {@link javax.jcr.Session#hasPendingChanges()} works * properly. * <p> * Procedure: * <ul> <li>Gets a session, checks * inital flag setting</li> <li>Adds a node, checks flag</li> <li>Saves on * session, checks flag</li> <li>Adds a property, checks flag</li> <li>Saves * on session, checks flag</li> <li>Adds a child node, checks flag</li> * <li>Saves on session, checks flag</li> <li>Removes child node, checks * flag</li> <li>Saves on session, checks flag</li> <li>Removes property, * checks flag</li> <li>Saves on session, checks flag</li> </ul> * Prerequisites: <ul> <li><code>javax.jcr.tck.nodetype</code> must accept * children of same nodetype</li> <li><code>javax.jcr.tck.propertyname1</code> * must be the name of a String property that can be added to a node of type * set in <code>javax.jcr.tck.nodetype</code> </ul> */ public void testHasPendingChanges() throws RepositoryException { // get default workspace test root node using superuser session Node defaultRootNode = (Node) superuser.getItem(testRootNode.getPath()); // initial check if flag is set correctly assertFalse("Session should have no pending changes recorded!", superuser.hasPendingChanges()); // test with adding a node Node testNode1 = defaultRootNode.addNode(nodeName1, testNodeType); assertTrue("Session should have pending changes recorded after new node was added!", superuser.hasPendingChanges()); // save the node superuser.save(); // pending changes should have been cleared assertFalse("Session should have no pending changes recorded after new node was added and saved!", superuser.hasPendingChanges()); // adds a property testNode1.setProperty(propertyName1, "test"); assertTrue("Session should have pending changes recorded after a property was added!", superuser.hasPendingChanges()); // save the new prop superuser.save(); // pending changes should have been cleared assertFalse("Session should have no pending changes recorded after added property hase been saved!", superuser.hasPendingChanges()); // add child node Node testChildNode = testNode1.addNode(nodeName1, testNodeType); assertTrue("Session should have pending changes recorded after child node has been added!", superuser.hasPendingChanges()); // save the new child nodes superuser.save(); // pending changes should have been cleared assertFalse("Session should have no pending changes recorded after new child node has been added and saved!", superuser.hasPendingChanges()); // remove the child node testChildNode.remove(); assertTrue("Session should have pending changes recorded after child node has been removed", superuser.hasPendingChanges()); // save the change superuser.save(); // pending changes should have been cleared assertFalse("Session should have no pending changes recorded after child node has been removed and saved!", superuser.hasPendingChanges()); // remove the property testNode1.setProperty(propertyName1, (Value) null); assertTrue("Session should have pending changes recorded after property has been removed", superuser.hasPendingChanges()); // save the change superuser.save(); // pending changes should have been cleared assertFalse("Session should have no pending changes recorded after property has been removed and saved!", superuser.hasPendingChanges()); } /** * Checks if {@link javax.jcr.Session#hasCapability(String, Object, Object[])} * works as specified. * <p> * * @throws RepositoryException */ public void testHasCapability() throws RepositoryException { Session roSession = getHelper().getReadOnlySession(); try { Node testRoot = roSession.getNode(testRootNode.getPath()); Object[] args = new Object[] { "foo" }; if (!roSession.hasCapability("addNode", testRoot, args)) { // if hasCapability() returns false, the actual method call // is expected to fail try { testRoot.addNode("foo"); roSession.save(); fail("Node.addNode() should fail according to Session.hasCapability()"); } catch (RepositoryException e) { // expected } } else { // hasCapability() returning true doesn't guarantee that the // actual method call succeeds, it's just a best-effort. // therefore nothing to test here... } } finally { roSession.logout(); } } }