[exo-jcr-commits] exo-jcr SVN: r4683 - in jcr/trunk/exo.jcr.component.core/src: main/java/org/exoplatform/services/jcr/dataflow/persistent and 12 other directories.

do-not-reply at jboss.org do-not-reply at jboss.org
Wed Jul 27 10:23:58 EDT 2011


Author: tolusha
Date: 2011-07-27 10:23:57 -0400 (Wed, 27 Jul 2011)
New Revision: 4683

Added:
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/infinispan/CacheNodesByPageId.java
   jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/core/TestGetNodesLazily.java
Modified:
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/dataflow/ItemDataConsumer.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/dataflow/persistent/WorkspaceStorageCache.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/NodeImpl.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/SessionDataManager.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/SessionImpl.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/ACLInheritanceSupportedWorkspaceDataManager.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/CacheableWorkspaceDataManager.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/LinkedWorkspaceStorageCacheImpl.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/VersionableWorkspaceDataManager.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/WorkspacePersistentDataManager.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/infinispan/ISPNCacheWorkspaceStorageCache.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/JBossCacheWorkspaceStorageCache.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/session/LocalWorkspaceStorageDataManagerProxy.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/session/TransactionableDataManager.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/JDBCStorageConnection.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/db/MultiDbJDBCConnection.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/db/SingleDbJDBCConnection.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/CQJDBCStorageConnection.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/MultiDbJDBCConnection.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/SingleDbJDBCConnection.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/storage/WorkspaceDataContainer.java
   jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/TestCacheableWorkspaceDataManager.java
Log:
EXOJCR-1452: Implementation

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/dataflow/ItemDataConsumer.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/dataflow/ItemDataConsumer.java	2011-07-27 14:23:32 UTC (rev 4682)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/dataflow/ItemDataConsumer.java	2011-07-27 14:23:57 UTC (rev 4683)
@@ -87,6 +87,22 @@
    List<NodeData> getChildNodesData(NodeData parent) throws RepositoryException;
 
    /**
+    * Get child Nodes of the parent node.
+    * 
+    * @param parent 
+    *          the parent data
+    * @param fromOrderNum
+    *          the last order number returned in previous request
+    * @param limit   
+    *       the recommended amount of children nodes to return            
+    * @param childs
+    *          will contain the resulted children nodes
+    * @return true if there are data to retrieve for next request and false in other case 
+    */
+   boolean getChildNodesDataByPage(NodeData parent, int fromOrderNum, int limit, List<NodeData> childs)
+      throws RepositoryException;
+
+   /**
     * Get child Nodes of the parent node.ItemDataFilter used to reduce count of returned items. 
     * But not guarantee that only items matching filter will be returned.
     * 

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/dataflow/persistent/WorkspaceStorageCache.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/dataflow/persistent/WorkspaceStorageCache.java	2011-07-27 14:23:32 UTC (rev 4682)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/dataflow/persistent/WorkspaceStorageCache.java	2011-07-27 14:23:57 UTC (rev 4683)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 eXo Platform SAS.
+R * Copyright (C) 2009 eXo Platform SAS.
  *
  * This is free software; you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as
@@ -91,6 +91,18 @@
    List<NodeData> getChildNodes(NodeData parent);
 
    /**
+    * Get page of child nodes.
+    * 
+    * @param parent
+    *          the parent data
+    * @param fromOrderNum
+    *          the order number related to page of child nodes
+    * @return child nodes for parent if found; empty list if no items found; null if no items
+    *         initialized
+    */
+   List<NodeData> getChildNodesByPage(final NodeData parent, final int fromOrderNum);
+
+   /**
     * Get child nodes by pattern.
     * 
     * @param parent
@@ -166,6 +178,18 @@
    void put(ItemData item);
 
    /**
+    * Adds page of child nodes.
+    * 
+    * @param parent
+    *          the parent data
+    * @param childs
+    *          the list of child nodes
+    * @param fromOrderNum
+    *          the order number related to page of child nodes 
+    */
+   void addChildNodesByPage(NodeData parent, List<NodeData> childs, int fromOrderNum);
+
+   /**
     * Adds (update should not be the case!) list of child nodes. The list can be empty. If list is
     * null the operation is ignored.
     * 

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/NodeImpl.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/NodeImpl.java	2011-07-27 14:23:32 UTC (rev 4682)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/NodeImpl.java	2011-07-27 14:23:57 UTC (rev 4683)
@@ -1036,7 +1036,6 @@
     */
    public NodeIterator getNodes() throws RepositoryException
    {
-
       long start = 0;
       if (LOG.isDebugEnabled())
       {
@@ -1086,7 +1085,7 @@
     */
    public NodeIterator getNodesLazily() throws RepositoryException
    {
-      return null;
+      return new LazyNodeIteratorByPage(dataManager);
    }
 
    /**
@@ -3336,7 +3335,6 @@
 
       protected boolean canRead(ItemData item)
       {
-         // TODO check if deleted // if (session.getTransientNodesManager().isDeleted(item.getQPath()))
          return (filter != null ? filter.accept(item) : true)
             && session.getAccessManager().hasPermission(
                item.isNode() ? ((NodeData)item).getACL() : nodeData().getACL(), new String[]{PermissionType.READ},
@@ -3388,10 +3386,33 @@
        */
       public void skip(long skipNum)
       {
+         trySkip(skipNum, true);
+      }
+
+      /**
+       * Tries to skip some elements.
+       * 
+       * @param throwException
+       *          indicates to throw an NoSuchElementException if can't skip defined count of elements
+       * @return how many elememnts remained to skip
+       */
+      protected long trySkip(long skipNum, boolean throwException)
+      {
          pos += skipNum;
          while (skipNum-- > 1)
          {
-            iter.next();
+            try
+            {
+               iter.next();
+            }
+            catch (NoSuchElementException e)
+            {
+               if (throwException)
+               {
+                  throw e;
+               }
+               return skipNum;
+            }
          }
 
          try
@@ -3403,6 +3424,8 @@
             LOG.error(e);
             throw new NoSuchElementException(e.toString());
          }
+
+         return 0;
       }
 
       /**
@@ -3413,7 +3436,6 @@
          if (size == -1)
          {
             // calc size
-
             int sz = pos + (next != null ? 1 : 0);
             if (iter.hasNext())
             {
@@ -3454,6 +3476,201 @@
       }
    }
 
+   protected class LazyNodeIteratorByPage implements RangeIterator, NodeIterator
+   {
+      private final SessionDataManager dataManager;
+
+      private int limit = session.getLazyNodeIteratorPageSize();
+
+      private int fromOrderNum = 0;
+
+      private boolean hasNext = true;
+      
+      private int pos = 0;
+
+      private LazyNodeIterator lazyNodeItetator = new LazyNodeIterator(new ArrayList<NodeData>());
+
+      LazyNodeIteratorByPage(SessionDataManager dataManager) throws RepositoryException
+      {
+         this.dataManager = dataManager;
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      public Node nextNode()
+      {
+         return (Node)next();
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      public boolean hasNext()
+      {
+         boolean hasNext = lazyNodeItetator.hasNext();
+
+         if (!hasNext)
+         {
+            if (this.hasNext)
+            {
+               try
+               {
+                  readNextPage(0);
+                  return hasNext();
+               }
+               catch (NoSuchElementException e)
+               {
+                  return false;
+               }
+            }
+            else
+            {
+               return false;
+            }
+         }
+         else
+         {
+            return true;
+         }
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      public Object next()
+      {
+         try
+         {
+            return nextItem();
+         }
+         catch (NoSuchElementException e)
+         {
+            if (hasNext)
+            {
+               try
+               {
+                  readNextPage(0);
+                  return next();
+               }
+               catch (NoSuchElementException e1)
+               {
+                  throw e1;
+               }
+            }
+            else
+            {
+               throw e;
+            }
+         }
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      public void remove()
+      {
+         LOG.warn("remove() method is not supported in LazyNodeIteratorByPage");
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      public void skip(long skipNum)
+      {
+         pos += skipNum;
+
+         long leftToSkip = lazyNodeItetator.trySkip(skipNum, false);
+         if (leftToSkip != 0)
+         {
+            readNextPage(leftToSkip);
+         }
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      public long getSize()
+      {
+         return -1;
+      }
+
+      public ItemImpl nextItem()
+      {
+         ItemImpl item = lazyNodeItetator.nextItem();
+         pos++;
+
+         return item;
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      public long getPosition()
+      {
+         return pos;
+      }
+
+      private void readNextPage(long skip) throws NoSuchElementException
+      {
+         List<NodeData> storedNodes = new ArrayList<NodeData>();
+         try
+         {
+            hasNext = dataManager.getChildNodesDataByPage(nodeData(), fromOrderNum, limit, storedNodes);
+         }
+         catch (RepositoryException e)
+         {
+            LOG.error("There are no more elements in iterator", e);
+            throw new NoSuchElementException(e.toString());
+         }
+         
+         Collections.sort(storedNodes, new NodeDataOrderComparator());
+
+         int size = storedNodes.size();
+         if (size != 0)
+         {
+            fromOrderNum = storedNodes.get(size - 1).getOrderNumber() + 1;
+
+            while (skip > 0 && storedNodes.size() > 0)
+            {
+               storedNodes.remove(0);
+               skip--;
+            }
+         }
+
+         if (skip != 0)
+         {
+            if (hasNext)
+            {
+               readNextPage(skip);
+            }
+            else
+            {
+               throw new NoSuchElementException("There are no more elements in iterator");
+            }
+         }
+         else
+         {
+            try
+            {
+               lazyNodeItetator = new LazyNodeIterator(storedNodes);
+            }
+            catch (RepositoryException e)
+            {
+               if (hasNext)
+               {
+                  readNextPage(skip);
+               }
+               else
+               {
+                  LOG.error("There are no more elements in iterator", e);
+                  throw new NoSuchElementException(e.toString());
+               }
+            }
+         }
+      }
+   }
+
    protected class LazyNodeIterator extends LazyItemsIterator implements NodeIterator
    {
       LazyNodeIterator(List<? extends ItemData> nodes) throws RepositoryException

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/SessionDataManager.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/SessionDataManager.java	2011-07-27 14:23:32 UTC (rev 4682)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/SessionDataManager.java	2011-07-27 14:23:57 UTC (rev 4683)
@@ -946,6 +946,79 @@
    /**
     * {@inheritDoc}
     */
+   public boolean getChildNodesDataByPage(final NodeData parent, int fromOrderNum, int limit, List<NodeData> childs)
+      throws RepositoryException
+   {
+      long start = 0;
+      if (log.isDebugEnabled())
+      {
+         start = System.currentTimeMillis();
+         log.debug("getChildNodesData(" + parent.getQPath().getAsString() + " , itemDataFilter) >>>>>");
+      }
+
+      try
+      {
+         boolean hasNext = false;
+         if (!isNew(parent.getIdentifier()))
+         {
+            hasNext = transactionableManager.getChildNodesDataByPage(parent, fromOrderNum, limit, childs);
+         }
+
+         // merge data
+         Collection<ItemState> transientDescendants = changesLog.getLastChildrenStates(parent, true);
+
+         if (!transientDescendants.isEmpty())
+         {
+            // 2 get ALL persisted descendants
+            Map<String, NodeData> descendants = new LinkedHashMap<String, NodeData>();
+            for (int i = 0, length = childs.size(); i < length; i++)
+            {
+               NodeData childNode = childs.get(i);
+               descendants.put(childNode.getIdentifier(), childNode);
+            }
+
+            // merge data
+            for (ItemState state : transientDescendants)
+            {
+               NodeData data = (NodeData)state.getData();
+
+               if (state.isDeleted())
+               {
+                  descendants.remove(data.getIdentifier());
+               }
+               else if (state.isMixinChanged())
+               {
+                  NodeData removedData = descendants.remove(data.getIdentifier());
+                  if (removedData != null)
+                  {
+                     descendants.put(data.getIdentifier(), data);
+                  }
+               }
+               else if (!hasNext && (state.isAdded() || state.isRenamed() || state.isUpdated()))
+               {
+                  descendants.put(data.getIdentifier(), data);
+               }
+            }
+
+            childs.clear();
+            childs.addAll(descendants.values());
+         }
+         
+         return hasNext;
+      }
+      finally
+      {
+         if (log.isDebugEnabled())
+         {
+            log.debug("getChildNodesData(" + parent.getQPath().getAsString() + ") <<<<< "
+               + ((System.currentTimeMillis() - start) / 1000d) + "sec");
+         }
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
    public List<NodeData> getChildNodesData(NodeData parent) throws RepositoryException
    {
       long start = 0;

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/SessionImpl.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/SessionImpl.java	2011-07-27 14:23:32 UTC (rev 4682)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/SessionImpl.java	2011-07-27 14:23:57 UTC (rev 4683)
@@ -167,6 +167,8 @@
 
    private boolean triggerEventsForDescendentsOnRename;
 
+   private int lazyNodeIteatorPageSize;
+
    private final int lazyReadThreshold;
 
    private final SessionRegistry sessionRegistry;
@@ -244,9 +246,14 @@
 
       sessionRegistry.registerSession(this);
       this.lastAccessTime = System.currentTimeMillis();
+      
       this.triggerEventsForDescendentsOnRename =
          wsConfig.getContainer().getParameterBoolean(WorkspaceDataContainer.TRIGGER_EVENTS_FOR_DESCENDENTS_ON_RENAME,
             WorkspaceDataContainer.TRIGGER_EVENTS_FOR_DESCENDENTS_ON_RENAME_DEFAULT);
+
+      this.lazyNodeIteatorPageSize =
+         wsConfig.getContainer().getParameterInteger(WorkspaceDataContainer.LAZY_NODE_ITERATOR_PAGE_SIZE,
+            WorkspaceDataContainer.LAZY_NODE_ITERATOR_PAGE_SIZE_DEFAULT);
    }
 
    /**
@@ -1127,6 +1134,11 @@
       return lazyReadThreshold;
    }
 
+   int getLazyNodeIteratorPageSize()
+   {
+      return lazyNodeIteatorPageSize;
+   }
+
    @Override
    public String toString()
    {

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/ACLInheritanceSupportedWorkspaceDataManager.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/ACLInheritanceSupportedWorkspaceDataManager.java	2011-07-27 14:23:32 UTC (rev 4682)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/ACLInheritanceSupportedWorkspaceDataManager.java	2011-07-27 14:23:57 UTC (rev 4683)
@@ -73,6 +73,15 @@
    /**
     * {@inheritDoc}
     */
+   public boolean getChildNodesDataByPage(NodeData parent, int fromOrderNum, int limit, List<NodeData> childs)
+      throws RepositoryException
+   {
+      return persistentManager.getChildNodesDataByPage(parent, fromOrderNum, limit, childs);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
    public int getLastOrderNumber(final NodeData parent) throws RepositoryException
    {
       return persistentManager.getLastOrderNumber(parent);

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/CacheableWorkspaceDataManager.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/CacheableWorkspaceDataManager.java	2011-07-27 14:23:32 UTC (rev 4682)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/CacheableWorkspaceDataManager.java	2011-07-27 14:23:57 UTC (rev 4683)
@@ -513,6 +513,79 @@
    /**
     * {@inheritDoc}
     */
+   public boolean getChildNodesDataByPage(final NodeData nodeData, final int fromOrderNum, final int limit,
+      final List<NodeData> childs) throws RepositoryException
+   {
+      List<NodeData> childNodes = null;
+      if (cache.isEnabled())
+      {
+         childNodes = cache.getChildNodes(nodeData);
+         if (childNodes != null)
+         {
+            childs.addAll(childNodes);
+            return false;
+         }
+         else
+         {
+            childNodes = cache.getChildNodesByPage(nodeData, fromOrderNum);
+            if (childNodes != null)
+            {
+               childs.addAll(childNodes);
+               return true;
+            }
+         }
+      }
+      final DataRequest request = new DataRequest(nodeData.getIdentifier(), DataRequest.GET_NODES);
+
+      try
+      {
+         request.start();
+         if (cache.isEnabled())
+         {
+            // Try first to get the value from the cache since a
+            // request could have been launched just before
+            childNodes = cache.getChildNodes(nodeData);
+            if (childNodes != null)
+            {
+               childs.addAll(childNodes);
+               return false;
+            }
+            else
+            {
+               childNodes = cache.getChildNodesByPage(nodeData, fromOrderNum);
+               if (childNodes != null)
+               {
+                  childs.addAll(childNodes);
+                  return true;
+               }
+            }
+         }
+
+         return executeAction(new PrivilegedExceptionAction<Boolean>()
+         {
+            public Boolean run() throws RepositoryException
+            {
+               boolean hasNext =
+                  CacheableWorkspaceDataManager.super.getChildNodesDataByPage(nodeData, fromOrderNum, limit, childs);
+
+               if (cache.isEnabled())
+               {
+                  cache.addChildNodesByPage(nodeData, childs, fromOrderNum);
+               }
+
+               return hasNext;
+            }
+         });
+      }
+      finally
+      {
+         request.done();
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
    @Override
    public List<NodeData> getChildNodesData(NodeData parentData, List<QPathEntryFilter> patternFilters)
       throws RepositoryException
@@ -1003,21 +1076,8 @@
                List<NodeData> childNodes = CacheableWorkspaceDataManager.super.getChildNodesData(nodeData);
                if (cache.isEnabled())
                {
-                  NodeData parentData = (NodeData)getItemData(nodeData.getIdentifier());
-
-                  if (parentData != null)
-                  {
-                     cache.addChildNodes(parentData, childNodes);
-                  }
+                  cache.addChildNodes(nodeData, childNodes);
                }
-               else
-               {
-                  // ini ACL
-                  for (int i = 0; i < childNodes.size(); i++)
-                  {
-                     childNodes.set(i, (NodeData)initACL(nodeData, childNodes.get(i)));
-                  }
-               }
 
                return childNodes;
             }
@@ -1322,12 +1382,7 @@
                // TODO childProperties.size() > 0 for SDB
                if (childProperties.size() > 0 && cache.isEnabled())
                {
-                  NodeData parentData = (NodeData)getItemData(nodeData.getIdentifier());
-
-                  if (parentData != null)
-                  {
-                     cache.addChildProperties(parentData, childProperties);
-                  }
+                  cache.addChildProperties(nodeData, childProperties);
                }
                return childProperties;
             }
@@ -1644,12 +1699,7 @@
                // TODO propertiesList.size() > 0 for SDB
                if (propertiesList.size() > 0 && cache.isEnabled())
                {
-                  NodeData parentData = (NodeData)getItemData(nodeData.getIdentifier());
-
-                  if (parentData != null)
-                  {
-                     cache.addChildPropertiesList(parentData, propertiesList);
-                  }
+                  cache.addChildPropertiesList(nodeData, propertiesList);
                }
                return propertiesList;
             }

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/LinkedWorkspaceStorageCacheImpl.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/LinkedWorkspaceStorageCacheImpl.java	2011-07-27 14:23:32 UTC (rev 4682)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/LinkedWorkspaceStorageCacheImpl.java	2011-07-27 14:23:57 UTC (rev 4683)
@@ -2184,24 +2184,28 @@
 
    public void addChildProperties(NodeData parent, QPathEntryFilter pattern, List<PropertyData> childProperties)
    {
-      // TODO Auto-generated method stub
    }
 
    public List<PropertyData> getChildProperties(NodeData parent, QPathEntryFilter pattern)
    {
-      // TODO Auto-generated method stub
       return null;
    }
 
    public void addChildNodes(NodeData parent, QPathEntryFilter pattern, List<NodeData> childNodes)
    {
-      // TODO Auto-generated method stub
-
    }
 
    public List<NodeData> getChildNodes(NodeData parent, QPathEntryFilter pattern)
    {
-      // TODO Auto-generated method stub
       return null;
    }
+
+   public List<NodeData> getChildNodesByPage(NodeData parent, int fromOrderNum)
+   {
+      return null;
+   }
+
+   public void addChildNodesByPage(NodeData parent, List<NodeData> childs, int fromOrderNum)
+   {
+   }
 }

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/VersionableWorkspaceDataManager.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/VersionableWorkspaceDataManager.java	2011-07-27 14:23:32 UTC (rev 4682)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/VersionableWorkspaceDataManager.java	2011-07-27 14:23:57 UTC (rev 4683)
@@ -91,6 +91,20 @@
     * {@inheritDoc}
     */
    @Override
+   public boolean getChildNodesDataByPage(NodeData nodeData, int fromOrderNum, int limit, List<NodeData> childs)
+      throws RepositoryException
+   {
+      if (isSystemDescendant(nodeData.getQPath()) && !this.equals(versionDataManager))
+      {
+         return versionDataManager.getChildNodesDataByPage(nodeData, fromOrderNum, limit, childs);
+      }
+      return super.getChildNodesDataByPage(nodeData, fromOrderNum, limit, childs);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
    public List<NodeData> getChildNodesData(final NodeData nodeData, final List<QPathEntryFilter> patternFilters)
       throws RepositoryException
    {

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/WorkspacePersistentDataManager.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/WorkspacePersistentDataManager.java	2011-07-27 14:23:32 UTC (rev 4682)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/WorkspacePersistentDataManager.java	2011-07-27 14:23:57 UTC (rev 4683)
@@ -45,6 +45,7 @@
 import org.exoplatform.services.jcr.impl.dataflow.session.TransactionableResourceManager;
 import org.exoplatform.services.jcr.impl.dataflow.session.TransactionableResourceManagerListener;
 import org.exoplatform.services.jcr.impl.storage.SystemDataContainerHolder;
+import org.exoplatform.services.jcr.impl.storage.jdbc.JDBCStorageConnection;
 import org.exoplatform.services.jcr.storage.WorkspaceDataContainer;
 import org.exoplatform.services.jcr.storage.WorkspaceStorageConnection;
 import org.exoplatform.services.log.ExoLogger;
@@ -614,7 +615,6 @@
     */
    public List<NodeData> getChildNodesData(final NodeData nodeData) throws RepositoryException
    {
-
       final WorkspaceStorageConnection con = dataContainer.openConnection();
       try
       {
@@ -629,6 +629,32 @@
    /**
     * {@inheritDoc}
     */
+   public boolean getChildNodesDataByPage(final NodeData nodeData, int fromOrderNum, int limit, List<NodeData> childs)
+      throws RepositoryException
+   {
+      final WorkspaceStorageConnection con = dataContainer.openConnection();
+      if (con instanceof JDBCStorageConnection)
+      {
+         try
+         {
+            childs.addAll(((JDBCStorageConnection)con).getChildNodesDataByPage(nodeData, fromOrderNum, limit));
+            return childs.size() == limit;
+         }
+         finally
+         {
+            con.close();
+         }
+      }
+      else
+      {
+         throw new UnsupportedOperationException(
+            "The method getChildNodesDataLazily is supported only for JDBCStorageConnection");
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
    public List<NodeData> getChildNodesData(final NodeData nodeData, List<QPathEntryFilter> patternFilters)
       throws RepositoryException
    {

Added: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/infinispan/CacheNodesByPageId.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/infinispan/CacheNodesByPageId.java	                        (rev 0)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/infinispan/CacheNodesByPageId.java	2011-07-27 14:23:57 UTC (rev 4683)
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2011 eXo Platform SAS.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.exoplatform.services.jcr.impl.dataflow.persistent.infinispan;
+
+/**
+ * @author <a href="abazko at exoplatform.com">Anatoliy Bazko</a>
+ * @version $Id: CachePagedNodes.java 34360 2009-07-22 23:58:59Z tolusha $
+ */
+public class CacheNodesByPageId extends CacheKey
+{
+
+   CacheNodesByPageId()
+   {
+      super();
+   }
+
+   CacheNodesByPageId(String id)
+   {
+      super(id);
+   }
+
+   @Override
+   public boolean equals(Object obj)
+   {
+      if (obj instanceof CacheNodesByPageId)
+      {
+         CacheNodesByPageId cacheNodesByPageId = (CacheNodesByPageId)obj;
+         return (cacheNodesByPageId.hash == hash && cacheNodesByPageId.fullId.equals(fullId));
+      }
+      else
+      {
+         return false;
+      }
+   }
+
+}

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/infinispan/ISPNCacheWorkspaceStorageCache.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/infinispan/ISPNCacheWorkspaceStorageCache.java	2011-07-27 14:23:32 UTC (rev 4682)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/infinispan/ISPNCacheWorkspaceStorageCache.java	2011-07-27 14:23:57 UTC (rev 4683)
@@ -592,6 +592,57 @@
    /**
     * {@inheritDoc}
     */
+   public void addChildNodesByPage(NodeData parent, List<NodeData> childs, int fromOrderNum)
+   {
+      boolean inTransaction = cache.isTransactionActive();
+      try
+      {
+         if (!inTransaction)
+         {
+            cache.beginTransaction();
+         }
+
+         cache.setLocal(true);
+
+         CacheNodesByPageId cacheId = new CacheNodesByPageId(parent.getIdentifier());
+         Map<Integer, Set<String>> pages = (Map<Integer, Set<String>>)cache.get(cacheId);
+         if (pages == null)
+         {
+            pages = new HashMap<Integer, Set<String>>();
+         }
+
+         if (childs.size() > 0)
+         {
+            Set<String> set = new HashSet<String>();
+            for (NodeData child : childs)
+            {
+               putNode(child, ModifyChildOption.NOT_MODIFY);
+               set.add(child.getIdentifier());
+            }
+
+            pages.put(fromOrderNum, set);
+            cache.put(cacheId, pages);
+         }
+         else
+         {
+            // cache fact of empty childs list
+            pages.put(fromOrderNum, new HashSet<String>());
+            cache.put(cacheId, pages);
+         }
+      }
+      finally
+      {
+         cache.setLocal(false);
+         if (!inTransaction)
+         {
+            dedicatedTxCommit();
+         }
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
    public void addChildNodes(NodeData parent, List<NodeData> childs)
    {
       boolean inTransaction = cache.isTransactionActive();
@@ -611,12 +662,12 @@
                putNode(child, ModifyChildOption.NOT_MODIFY);
                set.add(child.getIdentifier());
             }
-            cache.putIfAbsent(new CacheNodesId(parent.getIdentifier()), set);
+            cache.put(new CacheNodesId(parent.getIdentifier()), set);
          }
          else
          {
             // cache fact of empty childs list
-            cache.putIfAbsent(new CacheNodesId(parent.getIdentifier()), new HashSet<Object>());
+            cache.put(new CacheNodesId(parent.getIdentifier()), new HashSet<Object>());
          }
       }
       finally
@@ -796,6 +847,43 @@
    /**
     * {@inheritDoc}
     */
+   public List<NodeData> getChildNodesByPage(final NodeData parent, final int fromOrderNum)
+   {
+      // get list of children uuids
+      final Map<Integer, Set<String>> pages =
+         (Map<Integer, Set<String>>)cache.get(new CacheNodesByPageId(parent.getIdentifier()));
+
+      if (pages == null)
+      {
+         return null;
+      }
+
+      Set<String> set = pages.get(fromOrderNum);
+      if (set == null)
+      {
+         return null;
+      }
+
+      final List<NodeData> childs = new ArrayList<NodeData>();
+      for (String childId : set)
+      {
+         NodeData child = (NodeData)cache.get(new CacheId(childId));
+         if (child == null)
+         {
+            return null;
+         }
+
+         childs.add(child);
+      }
+
+      // order children by orderNumber, as HashSet returns children in other order
+      Collections.sort(childs, new NodesOrderComparator<NodeData>());
+      return childs;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
    public List<NodeData> getChildNodes(final NodeData parent, final QPathEntryFilter pattern)
    {
       // get list of children uuids
@@ -986,6 +1074,8 @@
             cache.addToPatternList(new CachePatternNodesId(node.getParentIdentifier()), node);
             cache.addToList(new CacheNodesId(node.getParentIdentifier()), node.getIdentifier(),
                modifyListsOfChild == ModifyChildOption.FORCE_MODIFY);
+
+            cache.remove(new CacheNodesByPageId(node.getParentIdentifier()));
          }
       }
 
@@ -1122,24 +1212,27 @@
       cache.remove(new CacheId(item.getIdentifier()));
       cache.remove(new CacheQPath(item.getParentIdentifier(), item.getQPath(), ItemType.getItemType(item)));
 
-      if (item.getParentIdentifier() != null)
+      if (item.isNode())
       {
-         if (item.isNode())
+         if (item.getParentIdentifier() != null)
          {
-            cache.remove(new CacheNodesId(item.getIdentifier()));
-            cache.remove(new CachePropsId(item.getIdentifier()));
             cache.removeFromPatternList(new CachePatternNodesId(item.getParentIdentifier()), item);
-            cache.remove(new CachePatternNodesId(item.getIdentifier()));
-            cache.remove(new CachePatternPropsId(item.getIdentifier()));
             cache.removeFromList(new CacheNodesId(item.getParentIdentifier()), item.getIdentifier());
-            cache.remove(new CacheRefsId(item.getIdentifier()));
+            cache.remove(new CacheNodesByPageId(item.getParentIdentifier()));
          }
-         else
-         {
-            cache.removeFromPatternList(new CachePatternPropsId(item.getParentIdentifier()), item);
-            cache.removeFromList(new CachePropsId(item.getParentIdentifier()), item.getIdentifier());
-         }
+
+         cache.remove(new CacheNodesId(item.getIdentifier()));
+         cache.remove(new CachePropsId(item.getIdentifier()));
+         cache.remove(new CacheNodesByPageId(item.getIdentifier()));
+         cache.remove(new CachePatternNodesId(item.getIdentifier()));
+         cache.remove(new CachePatternPropsId(item.getIdentifier()));
+         cache.remove(new CacheRefsId(item.getIdentifier()));
       }
+      else
+      {
+         cache.removeFromPatternList(new CachePatternPropsId(item.getParentIdentifier()), item);
+         cache.removeFromList(new CachePropsId(item.getParentIdentifier()), item.getIdentifier());
+      }
    }
 
    /**

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/JBossCacheWorkspaceStorageCache.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/JBossCacheWorkspaceStorageCache.java	2011-07-27 14:23:32 UTC (rev 4682)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/JBossCacheWorkspaceStorageCache.java	2011-07-27 14:23:57 UTC (rev 4683)
@@ -71,9 +71,11 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Set;
 
@@ -149,6 +151,8 @@
 
    public static final String CHILD_NODES_BY_PATTERN_LIST = "$CHILD_NODES_BY_PATTERN_LIST".intern();
 
+   public static final String CHILD_NODES_BY_PAGE_LIST = "$CHILD_NODES_BY_PAGE_LIST".intern();
+
    public static final String LOCKS = "$LOCKS".intern();
 
    public static final String REFERENCE = "$REFERENCE".intern();
@@ -175,6 +179,8 @@
 
    protected final Fqn<String> childPropsList;
 
+   protected final Fqn<String> childNodesByPageList;
+
    protected final Fqn<String> childPropsByPatternList;
 
    protected final Fqn<String> childNodesByPatternList;
@@ -219,7 +225,7 @@
                {
                   NodeData node = (NodeData)cache.get(makeItemFqn((String)child), ITEM_DATA);
 
-                  if (node == null || node instanceof NullItemData)
+                  if (node == null)
                   {
                      return null;
                   }
@@ -404,7 +410,7 @@
       protected Long execute(Void arg) throws RuntimeException
       {
          // Total number of JBC nodes in the cache - the total amount of resident nodes
-         return numNodes(cache.getNode(rootFqn)) - 9;
+         return numNodes(cache.getNode(rootFqn)) - 10;
       }
    };
       
@@ -612,6 +618,7 @@
       this.childProps = Fqn.fromRelativeElements(rootFqn, CHILD_PROPS);
       this.childNodesList = Fqn.fromRelativeElements(rootFqn, CHILD_NODES_LIST);
       this.childPropsList = Fqn.fromRelativeElements(rootFqn, CHILD_PROPS_LIST);
+      this.childNodesByPageList = Fqn.fromRelativeElements(rootFqn, CHILD_NODES_BY_PAGE_LIST);
       this.childPropsByPatternList = Fqn.fromRelativeElements(rootFqn, CHILD_PROPS_BY_PATTERN_LIST);
       this.childNodesByPatternList = Fqn.fromRelativeElements(rootFqn, CHILD_NODES_BY_PATTERN_LIST);
 
@@ -669,6 +676,7 @@
       createResidentNode(childNodesList);
       createResidentNode(childProps);
       createResidentNode(childPropsList);
+      createResidentNode(childNodesByPageList);
       createResidentNode(childPropsByPatternList);
       createResidentNode(childNodesByPatternList);
       createResidentNode(itemsRoot);
@@ -899,7 +907,8 @@
          else
          {
             // cache fact of empty childs list
-            cache.putIfAbsent(makeChildListFqn(childNodesList, parent.getIdentifier()), ITEM_LIST, new HashSet<Object>());
+            cache.putIfAbsent(makeChildListFqn(childNodesList, parent.getIdentifier()), ITEM_LIST,
+               new HashSet<Object>());
          }
       }
       finally
@@ -912,17 +921,68 @@
       }
    }
 
+
    /**
     * {@inheritDoc}
     */
-   public void addChildNodes(NodeData parent, QPathEntryFilter pattern, List<NodeData> childs)
+   public void addChildNodesByPage(NodeData parent, List<NodeData> childs, int fromOrderNum)
    {
       boolean inTransaction = cache.isTransactionActive();
       try
       {
+         if (!inTransaction)
+         {
+            cache.beginTransaction();
+         }
 
+         cache.setLocal(true);
+         
+         Fqn<String> fqn = makeChildListFqn(childNodesByPageList, parent.getIdentifier());
+         Map<Integer, Set<String>> pages = (Map<Integer, Set<String>>)cache.get(fqn, ITEM_LIST);
+         if (pages == null)
+         {
+            pages = new HashMap<Integer, Set<String>>();
+         }
+         
+         if (childs.size() > 0)
+         {
+            Set<String> set = new HashSet<String>();
+            for (NodeData child : childs)
+            {
+               putNode(child, ModifyChildOption.NOT_MODIFY);
+               set.add(child.getIdentifier());
+            }
+
+            pages.put(fromOrderNum, set);
+            cache.put(fqn, ITEM_LIST, pages);
+         }
+         else
+         {
+            // cache fact of empty childs list
+            pages.put(fromOrderNum, new HashSet<String>());
+            cache.put(fqn, ITEM_LIST, pages);
+         }
+      }
+      finally
+      {
+         cache.setLocal(false);
          if (!inTransaction)
          {
+            dedicatedTxCommit();
+         }
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public void addChildNodes(NodeData parent, QPathEntryFilter pattern, List<NodeData> childs)
+   {
+      boolean inTransaction = cache.isTransactionActive();
+      try
+      {
+         if (!inTransaction)
+         {
             cache.beginTransaction();
          }
 
@@ -1077,6 +1137,43 @@
    /**
     * {@inheritDoc}
     */
+   public List<NodeData> getChildNodesByPage(final NodeData parent, final int fromOrderNum)
+   {
+      final Map<Integer, Set<String>> pages =
+         (Map<Integer, Set<String>>)cache
+            .get(makeChildListFqn(childNodesByPageList, parent.getIdentifier()), ITEM_LIST);
+
+      if (pages == null)
+      {
+         return null;
+      }
+
+      Set<String> set = pages.get(fromOrderNum);
+      if (set == null)
+      {
+         return null;
+      }
+
+      final List<NodeData> childs = new ArrayList<NodeData>();
+      for (Object child : set)
+      {
+         NodeData node = (NodeData)cache.get(makeItemFqn((String)child), ITEM_DATA);
+         if (node == null)
+         {
+            return null;
+         }
+
+         childs.add(node);
+      }
+
+      // order children by orderNumber, as HashSet returns children in other order
+      Collections.sort(childs, new NodesOrderComparator<NodeData>());
+      return childs;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
    public List<NodeData> getChildNodes(final NodeData parent, final QPathEntryFilter pattern)
    {
       // empty Set<Object> marks that there is no child nodes
@@ -1094,7 +1191,7 @@
          {
             NodeData node = (NodeData)cache.get(makeItemFqn((String)child), ITEM_DATA);
 
-            if (node == null || node instanceof NullItemData)
+            if (node == null)
             {
                return null;
             }
@@ -1390,6 +1487,8 @@
                ITEM_LIST, node);
             cache.addToList(makeChildListFqn(childNodesList, node.getParentIdentifier()), ITEM_LIST,
                node.getIdentifier(), modifyListsOfChild == ModifyChildOption.FORCE_MODIFY);
+
+            cache.removeNode(makeChildListFqn(childNodesByPageList, node.getParentIdentifier()));
          }
       }
 
@@ -1537,23 +1636,35 @@
             cache.removeFromList(makeChildListFqn(childNodesList, item.getParentIdentifier()), ITEM_LIST, item
                .getIdentifier());
 
-            // remove from CHILD_NODES as parent
-            cache.removeNode(makeChildListFqn(childNodes, item.getIdentifier()));
+            // remove from CHILD_NODES_BY_PATTERN_LIST of parent
+            cache.removeFromPatternList(makeChildListFqn(childNodesByPatternList, item.getParentIdentifier()),
+               PATTERN_OBJ, ITEM_LIST, item);
 
-            // remove from CHILD_NODES_LIST as parent
-            cache.removeNode(makeChildListFqn(childNodesList, item.getIdentifier()));
+            // remove from CHILD_NODES_BY_PAGE_LIST of parent
+            cache.removeNode(makeChildListFqn(childNodesByPageList, item.getParentIdentifier()));
+         }
 
-            // remove from CHILD_PROPS as parent
-            cache.removeNode(makeChildListFqn(childProps, item.getIdentifier()));
+         // remove from CHILD_NODES as parent
+         cache.removeNode(makeChildListFqn(childNodes, item.getIdentifier()));
 
-            // remove from CHILD_PROPS_LIST as parent
-            cache.removeNode(makeChildListFqn(childPropsList, item.getIdentifier()));
-         }
+         // remove from CHILD_NODES_LIST as parent
+         cache.removeNode(makeChildListFqn(childNodesList, item.getIdentifier()));
 
-         cache.removeFromPatternList(makeChildListFqn(childNodesByPatternList, item.getParentIdentifier()),
-            PATTERN_OBJ, ITEM_LIST, item);
+         // remove from CHILD_PROPS as parent
+         cache.removeNode(makeChildListFqn(childProps, item.getIdentifier()));
+
+         // remove from CHILD_PROPS_LIST as parent
+         cache.removeNode(makeChildListFqn(childPropsList, item.getIdentifier()));
+
+         // remove from CHILD_NODES_BY_PAGE_LIST as parent
+         cache.removeNode(makeChildListFqn(childNodesByPageList, item.getIdentifier()));
+
+         // remove from CHILD_NODES_BY_PATTERN_LIST as parent
          cache.removeNode(makeChildListFqn(childNodesByPatternList, item.getIdentifier()));
+
+         // remove from CHILD_PROPS_BY_PATTERN_LIST as parent
          cache.removeNode(makeChildListFqn(childPropsByPatternList, item.getIdentifier()));
+
          cache.removeNode(makeRefFqn(item.getIdentifier()));
       }
       else
@@ -1569,6 +1680,7 @@
          cache.removeFromPatternList(makeChildListFqn(childPropsByPatternList, item.getParentIdentifier()),
             PATTERN_OBJ, ITEM_LIST, item);
       }
+
       // remove from ITEMS
       cache.removeNode(makeItemFqn(item.getIdentifier()));
    }
@@ -1942,6 +2054,7 @@
          cache.removeNode(childProps);
          cache.removeNode(childNodesList);
          cache.removeNode(childPropsList);
+         cache.removeNode(childNodesByPageList);
          cache.removeNode(childNodesByPatternList);
          cache.removeNode(childPropsByPatternList);
 
@@ -1952,6 +2065,7 @@
          createResidentNode(childNodesList);
          createResidentNode(childProps);
          createResidentNode(childPropsList);
+         createResidentNode(childNodesByPageList);
          createResidentNode(childNodesByPatternList);
          createResidentNode(childPropsByPatternList);
          createResidentNode(itemsRoot);

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/session/LocalWorkspaceStorageDataManagerProxy.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/session/LocalWorkspaceStorageDataManagerProxy.java	2011-07-27 14:23:32 UTC (rev 4682)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/session/LocalWorkspaceStorageDataManagerProxy.java	2011-07-27 14:23:57 UTC (rev 4683)
@@ -144,6 +144,15 @@
    /**
     * {@inheritDoc}
     */
+   public boolean getChildNodesDataByPage(NodeData parent, int fromOrderNum, int limit, List<NodeData> childs)
+      throws RepositoryException
+   {
+      return storageDataManager.getChildNodesDataByPage(parent, fromOrderNum, limit, copyNodes(childs));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
    public int getLastOrderNumber(final NodeData parent) throws RepositoryException
    {
       return storageDataManager.getLastOrderNumber(parent);

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/session/TransactionableDataManager.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/session/TransactionableDataManager.java	2011-07-27 14:23:32 UTC (rev 4682)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/session/TransactionableDataManager.java	2011-07-27 14:23:57 UTC (rev 4683)
@@ -102,6 +102,45 @@
       return nodes;
    }
 
+   /**
+    * {@inheritDoc}
+    */
+   public boolean getChildNodesDataByPage(final NodeData parent, int fromOrderNum, int limit, List<NodeData> childs)
+      throws RepositoryException
+   {
+      boolean hasNext = storageDataManager.getChildNodesDataByPage(parent, fromOrderNum, limit, childs);
+
+      if (txStarted())
+      {
+         // merge data
+         List<ItemState> txChanges = transactionLog.getChildrenChanges(parent.getIdentifier(), true);
+         if (txChanges.size() > 0)
+         {
+            for (ItemState state : txChanges)
+            {
+               if (state.isDeleted())
+               {
+                  childs.remove(state.getData());
+               }
+               if (state.isMixinChanged())
+               {
+                  boolean isExists = childs.remove(state.getData());
+                  if (isExists)
+                  {
+                     childs.add((NodeData)state.getData());
+                  }
+               }
+               else if (!hasNext && (state.isAdded() || state.isRenamed() || state.isUpdated()))
+               {
+                  childs.add((NodeData)state.getData());
+               }
+            }
+         }
+      }
+
+      return hasNext;
+   }
+
    public List<NodeData> getChildNodesData(NodeData parent, List<QPathEntryFilter> patternFilters) throws RepositoryException
    {
       List<NodeData> nodes = storageDataManager.getChildNodesData(parent, patternFilters);

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/JDBCStorageConnection.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/JDBCStorageConnection.java	2011-07-27 14:23:32 UTC (rev 4682)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/JDBCStorageConnection.java	2011-07-27 14:23:57 UTC (rev 4683)
@@ -1164,6 +1164,54 @@
    }
 
    /**
+    * 
+    * @param parent
+    * @param lastOrderNum
+    * @param limit
+    * @return
+    * @throws RepositoryException
+    * @throws IllegalStateException
+    */
+   public List<NodeData> getChildNodesDataByPage(NodeData parent, int fromOrderNum, int limit) throws RepositoryException,
+      IllegalStateException
+   {
+      checkIfOpened();
+      try
+      {
+         ResultSet node = findChildNodesByParentIdentifier(getInternalId(parent.getIdentifier()), fromOrderNum, limit);
+         try
+         {
+            List<NodeData> childrens = new ArrayList<NodeData>();
+            while (node.next())
+            {
+               childrens.add((NodeData)itemData(parent.getQPath(), node, I_CLASS_NODE, parent.getACL()));
+            }
+
+            return childrens;
+         }
+         finally
+         {
+            try
+            {
+               node.close();
+            }
+            catch (SQLException e)
+            {
+               LOG.error("Can't close the ResultSet: " + e);
+            }
+         }
+      }
+      catch (SQLException e)
+      {
+         throw new RepositoryException(e);
+      }
+      catch (IOException e)
+      {
+         throw new RepositoryException(e);
+      }
+   }
+
+   /**
     * {@inheritDoc}
     */
    public List<PropertyData> listChildPropertiesData(NodeData parent) throws RepositoryException, IllegalStateException
@@ -2133,7 +2181,6 @@
    protected PersistedNodeData loadNodeRecord(QPath parentPath, String cname, String cid, String cpid, int cindex,
       int cversion, int cnordernumb, AccessControlList parentACL) throws RepositoryException, SQLException
    {
-
       try
       {
          InternalQName qname = InternalQName.parse(cname);
@@ -2851,6 +2898,9 @@
 
    protected abstract ResultSet findNodesAndProperties(String lastNodeId, int offset, int limit) throws SQLException;
 
+   protected abstract ResultSet findChildNodesByParentIdentifier(String parentCid, int fromOrderNum, int limit)
+      throws SQLException;
+
    protected abstract int addReference(PropertyData data) throws SQLException, IOException;
 
    protected abstract int renameNode(NodeData data) throws SQLException;

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/db/MultiDbJDBCConnection.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/db/MultiDbJDBCConnection.java	2011-07-27 14:23:32 UTC (rev 4682)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/db/MultiDbJDBCConnection.java	2011-07-27 14:23:57 UTC (rev 4683)
@@ -418,6 +418,15 @@
       return findPropertiesByParentId.executeQuery();
    }
 
+   /**
+    * {@inheritDoc}
+    */
+   protected ResultSet findChildNodesByParentIdentifier(String parentCid, int lastOrderNum, int limit)
+      throws SQLException
+   {
+      throw new UnsupportedOperationException("findChildNodesByParentIdentifier is not supported for old queries");
+   }
+
    // -------- values processing ------------
 
    /**

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/db/SingleDbJDBCConnection.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/db/SingleDbJDBCConnection.java	2011-07-27 14:23:32 UTC (rev 4682)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/db/SingleDbJDBCConnection.java	2011-07-27 14:23:57 UTC (rev 4683)
@@ -432,6 +432,15 @@
       return updateProperty.executeUpdate();
    }
 
+   /**
+    * {@inheritDoc}
+    */
+   protected ResultSet findChildNodesByParentIdentifier(String parentCid, int lastOrderNum, int limit)
+      throws SQLException
+   {
+      throw new UnsupportedOperationException("findChildNodesByParentIdentifier is not supported for old queries");
+   }
+
    // -------- values processing ------------
 
    /**

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/CQJDBCStorageConnection.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/CQJDBCStorageConnection.java	2011-07-27 14:23:32 UTC (rev 4682)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/CQJDBCStorageConnection.java	2011-07-27 14:23:57 UTC (rev 4683)
@@ -99,13 +99,18 @@
    protected String FIND_NODE_MAIN_PROPERTIES_BY_PARENTID_CQ;
 
    /**
-    * FIND_PROPERTIES_BY_PARENTID_AND_PATTERN_CQ_TEMPLATE;
+    * FIND_PROPERTIES_BY_PARENTID_AND_PATTERN_CQ_TEMPLATE.
     */
    protected String FIND_PROPERTIES_BY_PARENTID_AND_PATTERN_CQ_TEMPLATE;
 
    /**
-    * FIND_NODES_BY_PARENTID_AND_PATTERN_CQ_TEMPLATE;
+    * FIND_NODES_BY_PARENTID_LAZILY.
     */
+   protected String FIND_NODES_BY_PARENTID_LAZILY_CQ;
+
+   /**
+    * FIND_NODES_BY_PARENTID_AND_PATTERN_CQ_TEMPLATE.
+    */
    protected String FIND_NODES_BY_PARENTID_AND_PATTERN_CQ_TEMPLATE;
 
    /**
@@ -138,6 +143,8 @@
 
    protected PreparedStatement findPropertyById;
 
+   protected PreparedStatement findNodesByParentIdLazilyCQ;
+
    protected PreparedStatement deleteValueDataByOrderNum;
 
    protected PreparedStatement updateValue;
@@ -1123,6 +1130,11 @@
          {
             findNodesByParentIdAndComplexPatternCQ.close();
          }
+
+         if (findNodesByParentIdLazilyCQ != null)
+         {
+            findNodesByParentIdLazilyCQ.close();
+         }
       }
       catch (SQLException e)
       {

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/MultiDbJDBCConnection.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/MultiDbJDBCConnection.java	2011-07-27 14:23:32 UTC (rev 4682)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/MultiDbJDBCConnection.java	2011-07-27 14:23:57 UTC (rev 4683)
@@ -205,6 +205,13 @@
          "select I.P_TYPE, V.STORAGE_DESC from JCR_MITEM I, JCR_MVALUE V where I.ID = ? and V.PROPERTY_ID = I.ID";
       DELETE_VALUE_BY_ORDER_NUM = "delete from JCR_MVALUE where PROPERTY_ID=? and ORDER_NUM >= ?";
       UPDATE_VALUE = "update JCR_MVALUE set DATA=?, STORAGE_DESC=? where PROPERTY_ID=? and ORDER_NUM=?";
+
+      FIND_NODES_BY_PARENTID_LAZILY_CQ =
+         "select I.*, P.NAME AS PROP_NAME, V.ORDER_NUM, V.DATA"
+            + " from JCR_MITEM I, JCR_MITEM P, JCR_MVALUE V"
+            + " where I.I_CLASS=1 and I.PARENT_ID=? and I.N_ORDER_NUM >= ? and"
+            + " P.I_CLASS=2 and P.PARENT_ID=I.ID and (P.NAME='[http://www.jcp.org/jcr/1.0]primaryType' or P.NAME='[http://www.jcp.org/jcr/1.0]mixinTypes' or P.NAME='[http://www.exoplatform.com/jcr/exo/1.0]owner' or P.NAME='[http://www.exoplatform.com/jcr/exo/1.0]permissions')"
+            + " and V.PROPERTY_ID=P.ID order by I.N_ORDER_NUM, I.ID LIMIT ?";
    }
 
    /**
@@ -558,6 +565,24 @@
       }
    }
 
+   /**
+    * {@inheritDoc}
+    */
+   protected ResultSet findChildNodesByParentIdentifier(String parentCid, int fromOrderNum, int limit)
+      throws SQLException
+   {
+      if (findNodesByParentIdLazilyCQ == null)
+         findNodesByParentIdLazilyCQ = dbConnection.prepareStatement(FIND_NODES_BY_PARENTID_LAZILY_CQ);
+      else
+         findNodesByParentIdLazilyCQ.clearParameters();
+
+      findNodesByParentIdLazilyCQ.setString(1, parentCid);
+      findNodesByParentIdLazilyCQ.setInt(2, fromOrderNum);
+      findNodesByParentIdLazilyCQ.setInt(3, limit);
+
+      return findNodesByParentIdLazilyCQ.executeQuery();
+   }
+
    // -------- values processing ------------
 
    /**

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/SingleDbJDBCConnection.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/SingleDbJDBCConnection.java	2011-07-27 14:23:32 UTC (rev 4682)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/SingleDbJDBCConnection.java	2011-07-27 14:23:57 UTC (rev 4683)
@@ -194,7 +194,6 @@
 
       UPDATE_NODE = "update JCR_SITEM set VERSION=?, I_INDEX=?, N_ORDER_NUM=? where ID=?";
       UPDATE_PROPERTY = "update JCR_SITEM set VERSION=?, P_TYPE=? where ID=?";
-      //UPDATE_VALUE = "update JCR_SVALUE set DATA=?, STORAGE_DESC=? where PROPERTY_ID=?, ORDER_NUM=?";
 
       DELETE_ITEM = "delete from JCR_SITEM where ID=?";
       DELETE_VALUE = "delete from JCR_SVALUE where PROPERTY_ID=?";
@@ -205,12 +204,19 @@
             + " V.DATA, V.ORDER_NUM, V.STORAGE_DESC from JCR_SVALUE V, JCR_SITEM P"
             + " join (select I.ID, I.PARENT_ID, I.NAME, I.VERSION, I.I_INDEX, I.N_ORDER_NUM from JCR_SITEM I"
             + " where I.CONTAINER_NAME=? AND I.I_CLASS=1 AND I.ID > ? order by I.ID LIMIT ? OFFSET ?) J on P.PARENT_ID = J.ID"
-            + " where P.I_CLASS=2 and P.CONTAINER_NAME=? and V.PROPERTY_ID=P.ID  order by J.ID";
+            + " where P.I_CLASS=2 and P.CONTAINER_NAME=? and V.PROPERTY_ID=P.ID order by J.ID";
 
       FIND_PROPERTY_BY_ID =
          "select I.P_TYPE, V.STORAGE_DESC from JCR_SITEM I, JCR_SVALUE V where I.CONTAINER_NAME=? and I.ID = ? and V.PROPERTY_ID = I.ID";
       DELETE_VALUE_BY_ORDER_NUM = "delete from JCR_SVALUE where PROPERTY_ID=? and ORDER_NUM >= ?";
       UPDATE_VALUE = "update JCR_SVALUE set DATA=?, STORAGE_DESC=? where PROPERTY_ID=? and ORDER_NUM=?";
+
+      FIND_NODES_BY_PARENTID_LAZILY_CQ =
+         "select I.*, P.NAME AS PROP_NAME, V.ORDER_NUM, V.DATA"
+            + " from JCR_SITEM I, JCR_SITEM P, JCR_SVALUE V"
+            + " where I.I_CLASS=1 and I.CONTAINER_NAME=? and I.PARENT_ID=? and I.N_ORDER_NUM >= ? and "
+            + " P.I_CLASS=2 and P.CONTAINER_NAME=? and P.PARENT_ID=I.ID and (P.NAME='[http://www.jcp.org/jcr/1.0]primaryType' or P.NAME='[http://www.jcp.org/jcr/1.0]mixinTypes' or P.NAME='[http://www.exoplatform.com/jcr/exo/1.0]owner' or P.NAME='[http://www.exoplatform.com/jcr/exo/1.0]permissions')"
+            + " and V.PROPERTY_ID=P.ID order by I.N_ORDER_NUM, I.ID LIMIT ?";
    }
 
    /**
@@ -480,6 +486,26 @@
       return updateProperty.executeUpdate();
    }
 
+   /**
+    * {@inheritDoc}
+    */
+   protected ResultSet findChildNodesByParentIdentifier(String parentCid, int fromOrderNum, int limit)
+      throws SQLException
+   {
+      if (findNodesByParentIdLazilyCQ == null)
+         findNodesByParentIdLazilyCQ = dbConnection.prepareStatement(FIND_NODES_BY_PARENTID_LAZILY_CQ);
+      else
+         findNodesByParentIdLazilyCQ.clearParameters();
+
+      findNodesByParentIdLazilyCQ.setString(1, containerName);
+      findNodesByParentIdLazilyCQ.setString(2, parentCid);
+      findNodesByParentIdLazilyCQ.setInt(3, fromOrderNum);
+      findNodesByParentIdLazilyCQ.setString(4, containerName);
+      findNodesByParentIdLazilyCQ.setInt(5, limit);
+
+      return findNodesByParentIdLazilyCQ.executeQuery();
+   }
+
    // -------- values processing ------------
 
    /**

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/storage/WorkspaceDataContainer.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/storage/WorkspaceDataContainer.java	2011-07-27 14:23:32 UTC (rev 4682)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/storage/WorkspaceDataContainer.java	2011-07-27 14:23:57 UTC (rev 4683)
@@ -42,6 +42,10 @@
 
    public static final boolean TRIGGER_EVENTS_FOR_DESCENDENTS_ON_RENAME_DEFAULT = true;
 
+   public static final String LAZY_NODE_ITERATOR_PAGE_SIZE = "lazy-node-iterator-page-size";
+
+   public static final int LAZY_NODE_ITERATOR_PAGE_SIZE_DEFAULT = 100;
+
    public final static String CONTAINER_NAME = "containerName";
 
    public final static String MAXBUFFERSIZE_PROP = "max-buffer-size";

Added: jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/core/TestGetNodesLazily.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/core/TestGetNodesLazily.java	                        (rev 0)
+++ jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/core/TestGetNodesLazily.java	2011-07-27 14:23:57 UTC (rev 4683)
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2009 eXo Platform SAS.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.exoplatform.services.jcr.impl.core;
+
+import org.exoplatform.services.jcr.JcrImplBaseTest;
+import org.exoplatform.services.jcr.core.ExtendedNode;
+import org.exoplatform.services.jcr.impl.Constants;
+import org.exoplatform.services.transaction.TransactionService;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RangeIterator;
+import javax.jcr.RepositoryException;
+import javax.transaction.UserTransaction;
+
+/**
+ * Created by The eXo Platform SAS.
+ * 
+ * @author <a href="mailto:nzamosenchuk at exoplatform.com">Nikolay Zamosenchuk</a>
+ * @version $Id: TestGetNode.java 111 2009-11-11 11:11:11Z nzamosenchuk $
+ */
+public class TestGetNodesLazily extends JcrImplBaseTest
+{
+   private static String INDEX_PROPERTY = "indexNumber";
+
+   private NodeImpl testRoot;
+
+   private int nodesCount;
+
+   private TransactionService txService;
+
+   @Override
+   public void setUp() throws Exception
+   {
+      super.setUp();
+      testRoot = (NodeImpl)session.getRootNode().addNode("TestGetNodesLazily");
+
+      // add first 150 child nodes
+      nodesCount = 150;
+      for (int i = 0; i < nodesCount; i++)
+      {
+         testRoot.addNode("child" + i).setProperty(INDEX_PROPERTY, i);
+      }
+      session.save();
+      txService = (TransactionService)container.getComponentInstanceOfType(TransactionService.class);
+   }
+
+   /**
+    * Simple check, session log empty.
+    */
+   public void testGetNodesLazilyBasicUsecase() throws Exception
+   {
+      // 150 nodes added in setup
+      assertChildNodes(testRoot, nodesCount);
+   }
+
+   /**
+    * All child they reordered one by one and though must be returned in same order 
+    */
+   public void _testGetNodesLazilyReordered() throws Exception
+   {
+      // 150 nodes added in setup
+      for (int i = 0; i < nodesCount; i++)
+      {
+         NodeImpl node = (NodeImpl)testRoot.getNode("child" + i);
+         session.move(node.getPath(), node.getPath());
+      }
+      session.save();
+      assertChildNodes(testRoot, nodesCount);
+   }
+   
+   /**
+    * All child they reordered one by one and though must be returned in same order 
+    */
+   public void _testGetNodesLazilyReorderedBackwards() throws Exception
+   {
+      // 150 nodes added in setup
+      // TODO : testcase fails
+      for (int i = nodesCount - 1; i >= 0; i--)
+      {
+         NodeImpl node = (NodeImpl)testRoot.getNode("child" + i);
+         session.move(node.getPath(), node.getPath());
+      }
+      session.save();
+      assertChildNodes(testRoot, nodesCount, true);
+   }
+
+   /**
+    * Session move with save
+    */
+   public void testGetNodesLazilyRenamedParent() throws Exception
+   {
+      // 150 nodes added in setup
+      // renaming parent
+      String newName = "new Name";
+      session.move("/" + testRoot.getName(), "/" + newName);
+      NodeImpl newTestRoot = (NodeImpl)root.getNode(newName);
+      session.save();
+      assertChildNodes(newTestRoot, nodesCount);
+   }
+
+   //=============== Tests with non-empty changesLog =============== 
+
+   /**
+    * New nodes added into session log and not save
+    */
+   public void testGetNodesLazilySessionAddedNodes() throws Exception
+   {
+      // 150 nodes added in setup
+      // adding 10 more nodes without save, so it is missing in persistent layer but exist in session changes log
+      for (int i = 0; i < 10; i++)
+      {
+         testRoot.addNode("child" + nodesCount).setProperty(INDEX_PROPERTY, nodesCount);
+         nodesCount++;
+      }
+
+      assertChildNodes(testRoot, nodesCount);
+   }
+
+   /**
+    * New nodes moved into session log and not save
+    */
+   public void testGetNodesLazilySessionMovedNodes() throws Exception
+   {
+      session.getRootNode().addNode("child" + nodesCount).setProperty(INDEX_PROPERTY, nodesCount);
+      session.save();
+
+      nodesCount++;
+      
+      String newNodeName = "child" + (nodesCount - 1);
+
+      session.move("/" + newNodeName, testRoot.getPath() + "/" + newNodeName);
+      assertChildNodes(testRoot, nodesCount);
+
+      testRoot.getNode(newNodeName).remove();
+      nodesCount--;
+
+      assertChildNodes(testRoot, nodesCount);
+
+      session.save();
+      assertChildNodes(testRoot, nodesCount);
+   }
+
+   /**
+    * Change mixin in node.
+    */
+   public void testGetNodesLazilySessionMixinChanged() throws Exception
+   {
+      testRoot.getNode("child0").addMixin("mix:lockable");
+
+      NodeIterator iterator = testRoot.getNodesLazily();
+      NodeImpl node = (NodeImpl)iterator.nextNode();
+
+      assertTrue(node.isNodeType(Constants.MIX_LOCKABLE));
+   }
+
+   /**
+    * New nodes added into session log and not save
+    */
+   public void testGetNodesLazilySessionNewAddedNodes() throws Exception
+   {
+      // adding 150 new nodes
+      NodeImpl localRoot = (NodeImpl)testRoot.addNode("localRoot");
+      for (int i = 0; i < nodesCount; i++)
+      {
+         localRoot.addNode("child" + i).setProperty(INDEX_PROPERTY, i);
+      }
+      assertChildNodes(localRoot, nodesCount);
+   }
+
+   /**
+    * Last 10 nodes removed. Session log has removed states for them
+    */
+   public void testGetNodesLazilySessionRemovedNodes() throws Exception
+   {
+      // 150 nodes added in setup
+      // removing 10 nodes without save
+      for (int i = 0; i < 10; i++)
+      {
+         nodesCount--;
+         testRoot.getNode("child" + nodesCount).remove();
+      }
+      assertChildNodes(testRoot, nodesCount);
+   }
+
+   /**
+    * Session move without save
+    */
+   public void testGetNodesLazilySessionRenamedParent() throws Exception
+   {
+      // 150 nodes added in setup
+      // renaming parent
+      String newName = "new Name";
+      session.move("/" + testRoot.getName(), "/" + newName);
+      NodeImpl newTestRoot = (NodeImpl)root.getNode(newName);
+      assertChildNodes(newTestRoot, nodesCount);
+   }
+
+   /**
+    * All child nodes are in session changes log and both in persisted layer. All they reordered one by one and though must 
+    * be returned in same order
+    */
+   public void _testGetNodesLazilySessionReordered() throws Exception
+   {
+      // 150 nodes added in setup
+      // TODO : testcase fails
+      for (int i = nodesCount - 1; i >= 0; i--)
+      {
+         NodeImpl node = (NodeImpl)testRoot.getNode("child" + i);
+         session.move(node.getPath(), node.getPath());
+      }
+      assertChildNodes(testRoot, nodesCount, true);
+   }
+
+   //=============== transactions related ===============
+
+   public void testGetNodesLazilyTransaction() throws Exception
+   {
+      assertNotNull(txService);
+      UserTransaction ut = txService.getUserTransaction();
+      ut.begin();
+
+      NodeImpl localRoot = (NodeImpl)testRoot.addNode("localRoot");
+      for (int i = 0; i < nodesCount; i++)
+      {
+         localRoot.addNode("child" + i).setProperty(INDEX_PROPERTY, i);
+      }
+      // assert within session changes log
+      assertChildNodes(localRoot, nodesCount);
+      session.save();
+      // assert within transaction changes log
+      assertChildNodes(localRoot, nodesCount);
+      ut.commit();
+      // assert within persistent layer
+      assertChildNodes(localRoot, nodesCount);
+   }
+
+   public void testGetNodesLazilyTransactionRollbackAdded() throws Exception
+   {
+      assertNotNull(txService);
+      UserTransaction ut = txService.getUserTransaction();
+      ut.begin();
+
+      int txNodesCount = nodesCount;
+      // 150 nodes added in setup
+      // adding 10 more nodes without save, so it is missing in persistent layer but exist in session changes log
+      for (int i = 0; i < 10; i++)
+      {
+         testRoot.addNode("child" + txNodesCount).setProperty(INDEX_PROPERTY, txNodesCount);
+         txNodesCount++;
+      }
+      session.save();
+      // assert within transaction changes log
+      assertChildNodes(testRoot, txNodesCount);
+      ut.rollback();
+      // assert within persistent layer, nodes should be rolled back
+      assertChildNodes(testRoot, nodesCount);
+   }
+
+   public void testGetNodesLazilyTransactionRollbackRemoved() throws Exception
+   {
+      assertNotNull(txService);
+      UserTransaction ut = txService.getUserTransaction();
+      ut.begin();
+
+      // 150 nodes added in setup
+      // removing 10 nodes
+      int txNodesCount = nodesCount;
+      for (int i = 0; i < 10; i++)
+      {
+         txNodesCount--;
+         testRoot.getNode("child" + txNodesCount).remove();
+      }
+      session.save();
+
+      // assert within transaction changes log
+      assertChildNodes(testRoot, txNodesCount);
+      ut.rollback();
+      // assert within persistent layer, nodes should be rolled back
+      assertChildNodes(testRoot, nodesCount);
+   }
+
+   //=============== test iterator ===============
+
+   public void testGetNodesLazilyIterator() throws Exception
+   {
+      RangeIterator iterator = testRoot.getNodesLazily();
+
+      // there are 150 node, so it must have next
+      assertTrue(iterator.hasNext());
+      // position is before first node 
+      assertEquals(0, iterator.getPosition());
+
+      // fetch first one (/child0)
+      iterator.next();
+      assertEquals(1, iterator.getPosition());
+
+      // fetch second one (/child1)
+      iterator.next();
+      assertEquals(2, iterator.getPosition());
+
+      // skip to /child12
+      iterator.skip(10);
+      NodeImpl next = (NodeImpl)iterator.next();
+      assertEquals(13, iterator.getPosition());
+      assertEquals(12, next.getProperty(INDEX_PROPERTY).getLong());
+
+      iterator.skip(1);
+      next = (NodeImpl)iterator.next();
+      assertEquals(15, iterator.getPosition());
+      assertEquals(14, next.getProperty(INDEX_PROPERTY).getLong());
+
+      iterator.skip(100);
+      next = (NodeImpl)iterator.next();
+      assertEquals(116, iterator.getPosition());
+      assertEquals(115, next.getProperty(INDEX_PROPERTY).getLong());
+   }
+
+   //=============== stuff ===============
+
+   /**
+    * Performs complex assert, that retrieves child nodes in a lazy way and asserts it's order, content
+    * and quantity 
+    */
+   private void assertChildNodes(ExtendedNode testNode, int expectedSize) throws RepositoryException
+   {
+      assertChildNodes(testNode, expectedSize, false);
+   }
+
+   /**
+    * Performs complex assert, that retrieves child nodes in a lazy way and asserts it's order, content
+    * and quantity 
+    */
+   private void assertChildNodes(ExtendedNode testNode, int expectedSize, boolean backwardOrder)
+      throws RepositoryException
+   {
+      NodeIterator lazyIterator = testNode.getNodesLazily();
+
+      // getSize shouldn't return actual size, since it works in a lazy manner and nodes are retrieved "on demand"
+      //      int actualSize = (int)lazyIterator.getSize();
+      //      assertEquals("getSize should return -1, but returned " + actualSize, -1, actualSize);
+
+      if (expectedSize == 0)
+      {
+         assertFalse(lazyIterator.hasNext());
+      }
+      else
+      {
+         int number = 0;
+         if (!backwardOrder)
+         {
+            int i = 0;
+            while (lazyIterator.hasNext())
+            {
+               Node node = lazyIterator.nextNode();
+
+               long actualNodeIndex = node.getProperty(INDEX_PROPERTY).getLong();
+               assertEquals("Iterator must return nodes ordered by \"order num\". Occurred at: child" + actualNodeIndex
+                  + " expecting <" + i + ">", i, actualNodeIndex);
+               i++;
+               number++;
+            }
+         }
+         else
+         {
+            int i = expectedSize - 1;
+            while (lazyIterator.hasNext())
+            {
+               NodeImpl node = (NodeImpl)lazyIterator.nextNode();
+               long actualNodeIndex = node.getProperty(INDEX_PROPERTY).getLong();
+               assertEquals("Iterator must return nodes ordered by \"order num\". Occurred at: child" + actualNodeIndex
+                  + " expecting <" + i + ">", i, actualNodeIndex);
+               i--;
+               number++;
+            }
+         }
+
+         // assert all returned
+         assertEquals(
+            "Iterator returned wrong number of nodes. Expected: " + expectedSize + ", but returned " + number,
+            expectedSize, number);
+      }
+   }
+
+   @Override
+   protected void tearDown() throws Exception
+   {
+      session.refresh(false);
+      testRoot.remove();
+      session.save();
+      super.tearDown();
+   }
+
+}
\ No newline at end of file

Modified: jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/TestCacheableWorkspaceDataManager.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/TestCacheableWorkspaceDataManager.java	2011-07-27 14:23:32 UTC (rev 4682)
+++ jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/TestCacheableWorkspaceDataManager.java	2011-07-27 14:23:57 UTC (rev 4683)
@@ -422,6 +422,15 @@
          return null;
       }
 
+      public List<NodeData> getChildNodesByPage(NodeData parent, int fromOrderNum)
+      {
+         return null;
+      }
+
+      public void addChildNodesByPage(NodeData parent, List<NodeData> childs, int fromOrderNum)
+      {
+      }
+
    }
 
    private static class MyWorkspaceStorageConnection implements WorkspaceStorageConnection



More information about the exo-jcr-commits mailing list