| /* |
| * 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.Node; |
| import javax.jcr.Repository; |
| import javax.jcr.RepositoryException; |
| import javax.jcr.Session; |
| import javax.jcr.Value; |
| import javax.jcr.ValueFormatException; |
| import javax.jcr.lock.LockException; |
| import javax.jcr.nodetype.NoSuchNodeTypeException; |
| import javax.jcr.nodetype.NodeType; |
| import javax.jcr.version.VersionException; |
| |
| import org.apache.jackrabbit.test.AbstractJCRTest; |
| import org.apache.jackrabbit.test.NotExecutableException; |
| |
| /** |
| * <code>NodeAddMixinTest</code> contains the test cases for the method |
| * <code>Node.AddMixin(String)</code>. |
| * |
| */ |
| public class NodeAddMixinTest extends AbstractJCRTest { |
| |
| /** |
| * Tests if <code>Node.addMixin(String mixinName)</code> adds the requested |
| * mixin and stores it in property <code>jcr:mixinTypes</code> |
| */ |
| public void testAddSuccessfully() |
| throws NotExecutableException, RepositoryException { |
| |
| Session session = testRootNode.getSession(); |
| Node node = testRootNode.addNode(nodeName1, testNodeType); |
| String mixinName = NodeMixinUtil.getAddableMixinName(session, node); |
| |
| if (mixinName == null) { |
| throw new NotExecutableException("No testable mixin node type found"); |
| } |
| |
| node.addMixin(mixinName); |
| |
| // test if mixin is written to property jcr:mixinTypes immediately |
| Value mixinValues[] = node.getProperty(jcrMixinTypes).getValues(); |
| boolean found = false; |
| for (int i = 0; i < mixinValues.length; i++) { |
| found |= mixinName.equals(mixinValues[i].getString()); |
| } |
| if (! found) { |
| fail("Mixin type must be added to property " + jcrMixinTypes + " immediately."); |
| } |
| |
| // it is implementation-specific if a added mixin is available |
| // before or after save therefore save before further tests |
| testRootNode.getSession().save(); |
| |
| // test if added mixin is available by node.getMixinNodeTypes() |
| NodeType mixins[] = node.getMixinNodeTypes(); |
| found = false; |
| for (int i = 0; i < mixins.length; i++) { |
| found |= mixinName.equals(mixins[i].getName()); |
| } |
| if (! found) { |
| fail("Mixin '" + mixinName+ "' type not added."); |
| } |
| } |
| |
| /** |
| * Tests if <code>Node.addMixin(String mixinName)</code> throws a |
| * <code>NoSuchNodeTypeException</code> if <code>mixinName</code> is not the |
| * name of an existing mixin node type |
| */ |
| public void testAddNonExisting() throws RepositoryException { |
| Session session = testRootNode.getSession(); |
| String nonExistingMixinName = NodeMixinUtil.getNonExistingMixinName(session); |
| |
| Node node = testRootNode.addNode(nodeName1, testNodeType); |
| |
| try { |
| node.addMixin(nonExistingMixinName); |
| fail("Node.addMixin(String mixinName) must throw a " + |
| "NoSuchNodeTypeException if mixinName is an unknown mixin type"); |
| } catch (NoSuchNodeTypeException e) { |
| // success |
| } |
| } |
| |
| /** |
| * Test if adding the same mixin twice works as expected. |
| * |
| * @throws RepositoryException |
| * @throws NotExecutableException |
| * @since JCR 2.0 |
| */ |
| public void testAddMixinTwice() throws RepositoryException, NotExecutableException { |
| Session session = testRootNode.getSession(); |
| Node node = testRootNode.addNode(nodeName1, testNodeType); |
| String mixinName = NodeMixinUtil.getAddableMixinName(session, node); |
| |
| if (mixinName == null) { |
| throw new NotExecutableException("No testable mixin node type found"); |
| } |
| |
| node.addMixin(mixinName); |
| // adding again must succeed |
| node.addMixin(mixinName); |
| |
| session.save(); |
| |
| node.addMixin(mixinName); |
| assertFalse(node.isModified()); |
| } |
| |
| /** |
| * Test if adding an inherited mixin type has no effect. |
| * |
| * @throws RepositoryException |
| * @since JCR 2.0 |
| */ |
| public void testAddInheritedMixin() throws RepositoryException, NotExecutableException { |
| Session session = testRootNode.getSession(); |
| Node node = testRootNode.addNode(nodeName1, testNodeType); |
| session.save(); |
| |
| String inheritedMixin = null; |
| |
| NodeType nt = node.getPrimaryNodeType(); |
| NodeType[] superTypes = nt.getSupertypes(); |
| for (int i = 0; i < superTypes.length && inheritedMixin == null; i++) { |
| if (superTypes[i].isMixin()) { |
| inheritedMixin = superTypes[i].getName(); |
| } |
| } |
| |
| if (inheritedMixin != null) { |
| node.addMixin(inheritedMixin); |
| assertFalse(node.isModified()); |
| } else { |
| throw new NotExecutableException("Primary node type does not have a mixin supertype"); |
| } |
| } |
| |
| /** |
| * Tests if <code>Node.addMixin(String mixinName)</code> throws a |
| * <code>LockException</code> if <code>Node</code> is locked |
| * <p> |
| * The test creates a node <code>nodeName1</code> of type |
| * <code>testNodeType</code> under <code>testRoot</code> and locks the node |
| * with the superuser session. Then the test tries to add a mixin to |
| * <code>nodeName1</code> with the readWrite <code>Session</code>. |
| */ |
| public void testLocked() |
| throws NotExecutableException, RepositoryException { |
| |
| Session session = testRootNode.getSession(); |
| |
| if (!isSupported(Repository.OPTION_LOCKING_SUPPORTED)) { |
| throw new NotExecutableException("Locking is not supported."); |
| } |
| |
| // create a node that is lockable |
| Node node = testRootNode.addNode(nodeName1, testNodeType); |
| // or try to make it lockable if it is not |
| ensureMixinType(node, mixLockable); |
| testRootNode.getSession().save(); |
| |
| String mixinName = NodeMixinUtil.getAddableMixinName(session, node); |
| if (mixinName == null) { |
| throw new NotExecutableException("No testable mixin node type found"); |
| } |
| |
| // remove first slash of path to get rel path to root |
| String pathRelToRoot = node.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 { |
| // implementation specific: either throw LockException upon |
| // addMixin or upon save. |
| node.addMixin(mixinName); |
| node.save(); |
| fail("Node.addMixin(String mixinName) must throw a LockException " + |
| "if the node is locked."); |
| } catch (LockException e) { |
| // success |
| } |
| |
| // unlock to remove node at tearDown() |
| node2.unlock(); |
| } finally { |
| session2.logout(); |
| } |
| } |
| |
| /** |
| * Tests if <code>Node.addMixin(String mixinName)</code> throws a |
| * <code>VersionException</code> if <code>Node</code> is checked-in. |
| * <p> |
| * The test creates a node <code>nodeName1</code> of type |
| * <code>testNodeType</code> under <code>testRoot</code> and checks it in. |
| * Then the test tries to add a mixin to <code>nodeName1</code>. |
| */ |
| public void testCheckedIn() |
| throws NotExecutableException, RepositoryException { |
| |
| Session session = testRootNode.getSession(); |
| |
| if (!isSupported(Repository.OPTION_VERSIONING_SUPPORTED)) { |
| throw new NotExecutableException("Versioning is not supported."); |
| } |
| |
| // create a node that is versionable |
| Node node = testRootNode.addNode(nodeName1, testNodeType); |
| // or try to make it versionable if it is not |
| ensureMixinType(node, mixVersionable); |
| testRootNode.getSession().save(); |
| |
| String mixinName = NodeMixinUtil.getAddableMixinName(session, node); |
| if (mixinName == null || node.isNodeType(mixinName)) { |
| throw new NotExecutableException("No testable mixin node type found"); |
| } |
| |
| node.checkin(); |
| |
| try { |
| node.addMixin(mixinName); |
| fail("Node.addMixin(String mixinName) must throw a VersionException " + |
| "if the node is checked-in."); |
| } catch (VersionException e) { |
| // success |
| } |
| } |
| |
| /** |
| * Tests if adding mix:referenceable automatically populates the jcr:uuid |
| * value. |
| */ |
| public void testAddMixinReferencable() |
| throws NotExecutableException, RepositoryException { |
| |
| // check if repository supports references |
| checkMixReferenceable(); |
| |
| // get session an create default node |
| Node node = testRootNode.addNode(nodeName1, testNodeType); |
| ensureMixinType(node, mixReferenceable); |
| // implementation specific: mixin may take effect only upon save |
| testRootNode.getSession().save(); |
| |
| // check that it did |
| assertTrue(node.isNodeType(mixReferenceable)); |
| |
| // test if jcr:uuid is not null, empty or throws a exception |
| // (format of value is not defined so we can only test if not empty) |
| try { |
| String uuid = node.getProperty(jcrUUID).getValue().getString(); |
| // default value is null so check for null |
| assertNotNull("Acessing jcr:uuid after assginment of mix:referencable returned null", uuid); |
| // check if it was not set to an empty string |
| assertTrue("Acessing jcr:uuid after assginment of mix:referencable returned an empty String!", uuid.length() > 0); |
| } catch (ValueFormatException e) { |
| // trying to access the uuid caused an exception |
| fail("Acessing jcr:uuid after assginment of mix:referencable caused an ValueFormatException!"); |
| } |
| } |
| |
| |
| /** |
| * Checks if the repository supports the mixin mix:Referenceable otherwise a |
| * {@link NotExecutableException} is thrown. |
| * |
| * @throws NotExecutableException if the repository does not support the |
| * mixin mix:referenceable. |
| */ |
| private void checkMixReferenceable() throws RepositoryException, NotExecutableException { |
| try { |
| superuser.getWorkspace().getNodeTypeManager().getNodeType(mixReferenceable); |
| } catch (NoSuchNodeTypeException e) { |
| throw new NotExecutableException("Repository does not support mix:referenceable"); |
| } |
| } |
| } |