jackrabbit-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Tobias Bocanegra" <tobias.bocane...@day.com>
Subject Re: Reordering of Child Nodes
Date Wed, 12 Dec 2007 20:54:59 GMT
hi,

> We think we have discovered a problem in reordering of nodes. I apologize
> upfront for the long post, but we are hoping the information will help.
i just swept over your post without reading it closer - you are using
same name siblings. and this will always cause problems. especially
with moving/reordering.

i suggest you create nodes that have distinct names and use the
nodetypes for your 'types'
regards, toby


>
> Background
>
> We have been attempting to use JackRabbit for meta-data management.
>
> We have adopted a model where we have meta-meta artifacts, for example like
>
>
> Model
> Atom
> Implementation
>
>
> so that the node names will actually be "Model" and "Atom" and
> "Implementation". One reason for this is that this helps us retrieve "all
> models in the repository". The othery way of doing this is of course, to
> store nodes with the actual name and then include a type property that is
> one of "Model", "Atom" or "Implementation".
>
> Attributes are likewise stored as nodes instead of as properties (so that we
> can reorder them or look for all defined atttributes across all models and
> so on).
>
> Problem
>
> Assume we have a node "Parent" as a child of the root node. Let us assume
> further that "Parent" has three children "Child", "Child", "Child" with the
> name property of each set to "1", "2" and "3". Thus we have
>
>
> root
>   |
>   + parent
>         |
>         + child [name = 1]
>         |
>         + child [name = 2]
>         |
>         + child [name = 3]
>
>
> Now we want to reorder the "child" nodes in reverse order of "name"
> property. That is, we want to change the above to the following
>
>
> root
>   |
>   + parent
>         |
>         + child [name = 3]
>         |
>         + child [name = 2]
>         |
>         + child [name = 1]
>
>
> We figured that this is equivalent to an algorithm for doing the following
>
> Given an input list {1, 2, ... , N} and a required output list {a1, a2, ...
> , aN}, generate a sequence of operations using only the primitive operation
>
> orderBefore (y, x): x,y ---> y,x
>
> That is orderBefore will place element y before x. I think this is a good
> enough abstraction of using the Node::orderBefore method to achieve this.
>
> We wrote some tests to test our assumptions. Our first program was:-
>
>
> public class OrderAttributes
> {
>         /**
>          * @param args
>          */
>         public static void main(String[] args)
>                 throws Exception
>         {
>                 Repository repository = new TransientRepository();
>                 Session session = repository.login(new SimpleCredentials("username",
> "password".toCharArray()));
>
>             try
>             {
>                 Node root = session.getRootNode();
>
>                 Node node1 = root.addNode("node1");
>                 Node node2 = root.addNode("node2");
>                 Node node3 = root.addNode("node3");
>
>                 System.out.println(node1.getName() + " @ " + node1.getIndex());
>                 System.out.println(node2.getName() + " @ " + node2.getIndex());
>                 System.out.println(node3.getName() + " @ " + node3.getIndex());
>
>                 printChildren(root, System.out);
>
>                 root.orderBefore("node2", "node1");
>
>                 printChildren(root, System.out);
>
>                 root.orderBefore("node3", "node2");
>
>                 printChildren(root, System.out);
>             }
>             finally
>             {
>                 session.logout();
>             }
>         }
>
>     /**
>      *
>      * @param node
>      * @param target
>      * @throws RepositoryException
>      */
>     public static void printChildren (Node node, PrintStream target)
>         throws RepositoryException
>     {
>         if (node == null || target == null)
>         {
>             return;
>         }
>
>         target.println(node.getName());
>
>         NodeIterator iter = node.getNodes();
>
>         while (iter.hasNext())
>         {
>             Node child = iter.nextNode();
>
>             target.println(child.getName());
>         }
>     }
> }
>
>
> This worked correctly and produce the following output:-
>
>
> node1 @ 1
> node2 @ 1
> node3 @ 1
>
> jcr:system
> node1
> node2
> node3
>
> jcr:system
> node2
> node1
> node3
>
> jcr:system
> node3
> node2
> node1
>
>
> We then attempted to do what we wanted to do:-
>
>
> public class ReOrderAttributes
> {
>         /**
>          * @param args
>          */
>         public static void main(String[] args)
>                 throws Exception
>         {
>                 Repository repository = new TransientRepository();
>                 Session session = repository.login(new
> SimpleCredentials("username", "password".toCharArray()));
>
>             try
>             {
>                 Node root = session.getRootNode();
>
>                 Node parent = root.addNode("parent");
>
>                 Node node1 = createNode(parent, 1);
>                 Node node2 = createNode(parent, 2);
>                 Node node3 = createNode(parent, 3);
>
>                 session.save();
>
>                      printChildren(parent, System.out);
>
>                 String path1 = getRelativePath(parent, node1.getPath());
>                 String path2 = getRelativePath(parent, node2.getPath());
>                 String path3 = getRelativePath(parent, node3.getPath());
>
>                 printChildren(parent, System.out);
>
>                 parent.orderBefore(getRelativePath(parent, node2.getPath()),
> getRelativePath(parent, node1.getPath()));
>
>                 System.out.println(node1.getPath());
>                     System.out.println(node2.getPath());
>                     System.out.println(node3.getPath());
>
>                 printChildren(parent, System.out);
>
>                 parent.orderBefore(getRelativePath(parent, node3.getPath()),
> getRelativePath(parent, node2.getPath()));
>
>                 printChildren(parent, System.out);
>             }
>             finally
>             {
>                 session.logout();
>             }
>         }
>
>         /**
>          *
>          * @param index
>          * @return
>          * @throws RepositoryException
>          */
>         private static Node createNode (Node parent, int index)
>             throws RepositoryException
>         {
>             Node node = parent.addNode("ChildNode");
>
>             node.setProperty("name", String.valueOf(index));
>
>             return node;
>         }
>
>     /**
>      *
>      * @param parent
>      * @param target
>      * @throws RepositoryException
>      */
>     private static void printChildren (Node parent, PrintStream target)
>         throws RepositoryException
>     {
>         if (parent == null || target == null)
>         {
>             return;
>         }
>
>         target.println(parent.getName());
>
>         NodeIterator iter = parent.getNodes();
>
>         while (iter.hasNext())
>         {
>             Node child = iter.nextNode();
>
>             target.print(child.getName());
>
>             try
>             {
>                 target.println("[name = " +
> child.getProperty("name").getString() + "]");
>             }
>             catch (Exception ex)
>             {
>                 ex.printStackTrace(System.out);
>             }
>         }
>     }
>
>     /**
>      * Given an absolute path and a node, this function computes the
> relative path
>      * from that node to the node pointed to by the given absolute path.
> Note that
>      * this function does not handle namespace resolution. Hence namespace
> qualified
>      * paths will not be properly handled by calls to this function.
>      *
>      * @param node A {@link javax.jcr.Node} instance relative to which we
> want the
>      *             node pointed to by the given absolute path.
>      * @param absolutePath The absolute path to convert into a relative
> path.
>      * @return A relative path string.
>      * @throws RepositoryException
>      * @throws IllegalArgumentException
>      * @throws MalformedPathException
>      * @throws NoPrefixDeclaredException
>      */
>     public static String getRelativePath (Node node, String absolutePath)
>         throws RepositoryException, MalformedPathException,
> NoPrefixDeclaredException
>     {
>         if (absolutePath.length() <= 0)
>         {
>             throw new IllegalArgumentException("Invalid absolute path
> argument.");
>         }
>
>         Path mainNodePath = PathFormat.parse(node.getPath(),
> DummyNamespaceResolver.getInstance());
>         Path relNodePath = PathFormat.parse(absolutePath,
> DummyNamespaceResolver.getInstance());
>
>         Path relPath = mainNodePath.computeRelativePath(relNodePath);
>
>         return PathFormat.format(relPath,
> DummyNamespaceResolver.getInstance());
>     }
>
>     /**
>      * @author Vijai Kalyan
>      */
>     private static class DummyNamespaceResolver extends
> AbstractNamespaceResolver
>     {
>         /** Required by PROS coding standards. Do not remove. */
>         public static final String SOURCE_FILE_REVISION = "$ Revision: $";
>
>         static final NamespaceResolver instance = new
> DummyNamespaceResolver();
>
>         static final NamespaceResolver getInstance()
>         {
>             return instance;
>         }
>
>         /**
>          *
>          */
>         private DummyNamespaceResolver()
>         {
>             super(false);
>         }
>
>         /* (non-Javadoc)
>          * @see
> org.apache.jackrabbit.name.NamespaceResolver#getPrefix(java.lang.String)
>          */
>         public String getPrefix(String uri) throws NamespaceException
>         {
>             return "";
>         }
>
>         /* (non-Javadoc)
>          * @see
> org.apache.jackrabbit.name.NamespaceResolver#getURI(java.lang.String)
>          */
>         public String getURI(String prefix) throws NamespaceException
>         {
>             return "";
>         }
>     }
> }
>
>
> This produced the following output:-
>
>
> parent
> ChildNode[name = 1]
> ChildNode[name = 2]
> ChildNode[name = 3]
> parent
> ChildNode[name = 1]
> ChildNode[name = 2]
> ChildNode[name = 3]
> /parent/ChildNode
> /parent/ChildNode[2]
> /parent/ChildNode[3]
> parent
> ChildNode[name = 2]
> ChildNode[name = 1]
> ChildNode[name = 3]
> parent
> ChildNode[name = 2]
> ChildNode[name = 3]
> ChildNode[name = 1]
>
>
> When we debugged, we found that in the following code
> (NodeImpl::orderBefore):-
>
>
>     /**
>      * Same as {@link Node#orderBefore(String, String)} except that
>      * this method takes a Path.PathElement arguments instead of
>      * Strings.
>      *
>      * @param srcName
>      * @param dstName
>      * @throws UnsupportedRepositoryOperationException
>      * @throws VersionException
>      * @throws ConstraintViolationException
>      * @throws ItemNotFoundException
>      * @throws LockException
>      * @throws RepositoryException
>      */
>     public synchronized void orderBefore(Path.PathElement srcName,
>                                          Path.PathElement dstName)
>             throws UnsupportedRepositoryOperationException,
> VersionException,
>             ConstraintViolationException, ItemNotFoundException,
> LockException,
>             RepositoryException {
>
>         // check state of this instance
>         sanityCheck();
>
>         if (!getPrimaryNodeType().hasOrderableChildNodes()) {
>             throw new UnsupportedRepositoryOperationException(
>                     "child node ordering not supported on node "
>                     + safeGetJCRPath());
>         }
>
>         // check arguments
>         if (srcName.equals(dstName)) {
>             // there's nothing to do
>             return;
>         }
>
>         // check existence
>         if (!hasNode(srcName.getName(), srcName.getIndex())) {
>             String name;
>             try {
>                 Path.PathElement[] path = new Path.PathElement[] { srcName
> };
>                 name = session.getJCRPath(new
> Path.PathBuilder(path).getPath());
>             } catch (NameException e) {
>                 name = srcName.toString();
>             } catch (NamespaceException e) {
>                 name = srcName.toString();
>             }
>             throw new ItemNotFoundException(safeGetJCRPath()
>                     + " has no child node with name " + name);
>         }
>         if (dstName != null && !hasNode(dstName.getName(),
> dstName.getIndex())) {
>             String name;
>             try {
>                 Path.PathElement[] path = new Path.PathElement[] { dstName
> };
>                 name = session.getJCRPath(new
> Path.PathBuilder(path).getPath());
>             } catch (NameException e) {
>                 name = dstName.toString();
>             } catch (NamespaceException e) {
>                 name = dstName.toString();
>             }
>             throw new ItemNotFoundException(safeGetJCRPath()
>                     + " has no child node with name " + name);
>         }
>
>         // make sure this node is checked-out
>         if (!internalIsCheckedOut()) {
>             String msg = safeGetJCRPath()
>                     + ": cannot change child node ordering of a checked-in
> node";
>             log.debug(msg);
>             throw new VersionException(msg);
>         }
>
>         // check protected flag
>         if (definition.isProtected()) {
>             String msg = safeGetJCRPath()
>                     + ": cannot change child node ordering of a protected
> node";
>             log.debug(msg);
>             throw new ConstraintViolationException(msg);
>         }
>
>         // check lock status
>         checkLock();
>
>         ArrayList list = new ArrayList(((NodeState)
> state).getChildNodeEntries());
>         int srcInd = -1, destInd = -1;
>         for (int i = 0; i < list.size(); i++) {
>             NodeState.ChildNodeEntry entry = (NodeState.ChildNodeEntry)
> list.get(i);
>             if (srcInd == -1) {
>                 if (entry.getName().equals(srcName.getName())
>                         && (entry.getIndex() == srcName.getIndex()
>                         || srcName.getIndex() == 0 && entry.getIndex() ==
> 1)) {
>                     srcInd = i;
>                 }
>             }
>             if (destInd == -1 && dstName != null) {
>                 if (entry.getName().equals(dstName.getName())
>                         && (entry.getIndex() == dstName.getIndex()
>                         || dstName.getIndex() == 0 && entry.getIndex() ==
> 1)) {
>                     destInd = i;
>                     if (srcInd != -1) {
>                         break;
>                     }
>                 }
>             } else {
>                 if (srcInd != -1) {
>                     break;
>                 }
>             }
>         }
>
>         // check if resulting order would be different to current order
>         if (destInd == -1) {
>             if (srcInd == list.size() - 1) {
>                 // no change, we're done
>                 return;
>             }
>         } else {
>             if ((destInd - srcInd) == 1) {
>                 // no change, we're done
>                 return;
>             }
>         }
>
>         // reorder list
>         if (destInd == -1) {
>             list.add(list.remove(srcInd));
>         } else {
>             if (srcInd < destInd) {
>                 list.add(destInd, list.get(srcInd));
>                 list.remove(srcInd);
>             } else {
>                 list.add(destInd, list.remove(srcInd));
>             }
>         }
>
>         // modify the state of 'this', i.e. the parent node
>         NodeState thisState = (NodeState) getOrCreateTransientItemState();
>         thisState.setChildNodeEntries(list);
>     }
>
>
> The value of "destInd" and "srcInd" are such that it looks like the original
> order of the child nodes is being used. That is, when the second
> "orderBefore" call is done, destInd should be 0 and srcInd should be 1.
> However, we found that in the above, destInd was 1 and srcInd was 2 (which
> is what it would be if the original order of child node entries was being
> used).
>
> So, although it looks like the state change is being changed at the end of
> the "orderBefore" method, it doesn't seem to be reflected in the next
> iteration.
>
> Looking at the documentation, it indicates that calls to "orderBefore" may
> not be persisted until a "save" method call is done. So, changing the
> program slighlty, we introduced a line "session.save()" just after the first
> call to "orderBefore".
>
> That ended up with the following output:-
>
>
> parent
> ChildNode[name = 1]
> ChildNode[name = 2]
> ChildNode[name = 3]
> parent
> ChildNode[name = 1]
> ChildNode[name = 2]
> ChildNode[name = 3]
> /parent[2]/ChildNode[2]
> /ChildNode[2]
> /parent[2]/ChildNode[3]
> parent
> ChildNode[name = 2]
> ChildNode[name = 1]
> ChildNode[name = 3]
> Exception in thread "main" javax.jcr.RepositoryException: invalid name:
> ../ChildNode[2]
>         at org.apache.jackrabbit.core.NodeImpl.orderBefore(NodeImpl.java:2044)
>         at ReOrderAttributes.main(ReOrderAttributes.java:69)
>
>
> Note in the above that one of the "child" nodes has lost its parent.
>
> Can someone point out to us what we may be doing wrong?
>
> Thanks,
>
> Vijai.
> --
> View this message in context: http://www.nabble.com/Reordering-of-Child-Nodes-tp14300056p14300056.html
> Sent from the Jackrabbit - Users mailing list archive at Nabble.com.
>


-- 
-----------------------------------------< tobias.bocanegra@day.com >---
Tobias Bocanegra, Day Management AG, Barfuesserplatz 6, CH - 4001 Basel
T +41 61 226 98 98, F +41 61 226 98 97
-----------------------------------------------< http://www.day.com >---

Mime
View raw message