/*
* 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;
import org.exoplatform.commons.utils.QName;
import org.exoplatform.services.jcr.JcrImplBaseTest;
import org.exoplatform.services.jcr.access.AccessControlList;
import org.exoplatform.services.jcr.config.WorkspaceEntry;
import org.exoplatform.services.jcr.core.WorkspaceContainerFacade;
import org.exoplatform.services.jcr.dataflow.ItemState;
import org.exoplatform.services.jcr.dataflow.PlainChangesLog;
import org.exoplatform.services.jcr.dataflow.PlainChangesLogImpl;
import org.exoplatform.services.jcr.dataflow.TransactionChangesLog;
import org.exoplatform.services.jcr.dataflow.persistent.PersistedNodeData;
import org.exoplatform.services.jcr.dataflow.persistent.PersistedPropertyData;
import org.exoplatform.services.jcr.dataflow.persistent.WorkspaceStorageCache;
import org.exoplatform.services.jcr.datamodel.InternalQName;
import org.exoplatform.services.jcr.datamodel.ItemData;
import org.exoplatform.services.jcr.datamodel.ItemType;
import org.exoplatform.services.jcr.datamodel.NodeData;
import org.exoplatform.services.jcr.datamodel.PropertyData;
import org.exoplatform.services.jcr.datamodel.QPath;
import org.exoplatform.services.jcr.datamodel.QPathEntry;
import org.exoplatform.services.jcr.datamodel.ValueData;
import org.exoplatform.services.jcr.impl.Constants;
import org.exoplatform.services.jcr.impl.core.itemfilters.PatternQPathEntry;
import org.exoplatform.services.jcr.impl.core.itemfilters.PatternQPathEntryFilter;
import org.exoplatform.services.jcr.impl.core.itemfilters.QPathEntryFilter;
import org.exoplatform.services.jcr.impl.storage.SystemDataContainerHolder;
import org.exoplatform.services.jcr.impl.storage.WorkspaceDataContainerBase;
import org.exoplatform.services.jcr.storage.WorkspaceDataContainer;
import org.exoplatform.services.jcr.storage.WorkspaceStorageConnection;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import javax.jcr.InvalidItemStateException;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
/**
* @author <a href="mailto:nfilotto@exoplatform.com">Nicolas Filotto</a>
* @version $Id$
*
*/
public abstract class TestWorkspaceStorageCacheInClusterMode<T extends WorkspaceStorageCache> extends JcrImplBaseTest
{
public abstract T getCacheImpl() throws Exception;
public void testRaceConditionsNConsistency() throws Exception
{
T cache1 = null, cache2 = null;
try
{
MyWorkspaceStorageConnection con = new MyWorkspaceStorageConnection();
WorkspaceDataContainer wdc = new MyWorkspaceDataContainer(con);
WorkspaceContainerFacade wsc = repository.getWorkspaceContainer("ws");
WorkspaceEntry wconf = (WorkspaceEntry)wsc.getComponent(WorkspaceEntry.class);
CacheableWorkspaceDataManager cwdmNode1 =
new CacheableWorkspaceDataManager(wconf, wdc, cache1 = getCacheImpl(), new SystemDataContainerHolder(wdc));
CacheableWorkspaceDataManager cwdmNode2 =
new CacheableWorkspaceDataManager(wconf, wdc, cache2 = getCacheImpl(), new SystemDataContainerHolder(wdc));
NodeData parentNode = new PersistedNodeData("parent-id", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent-node")), Constants.ROOT_UUID, 1, 0,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
// Test getChildNodesData
Action readAction = new Action(cwdmNode2)
{
public void execute(NodeData parentNode) throws Exception
{
cwdm.getChildNodesData(parentNode);
}
};
Action writeAction = new Action(cwdmNode1)
{
public void execute(NodeData parentNode) throws Exception
{
PlainChangesLog chlog = new PlainChangesLogImpl();
cwdm.getChildNodesData(parentNode);
chlog.add(ItemState.createAddedState(new PersistedNodeData("id-node" + parentNode.getIdentifier(), QPath.makeChildPath(parentNode.getQPath(), new InternalQName(null, "node")), parentNode.getIdentifier(), 1, 0,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null)));
cwdm.save(new TransactionChangesLog(chlog));
}
};
executeConcurrentReadNWrite(con, readAction, writeAction, Mode.READ_FIRST, parentNode);
assertNotNull(cwdmNode1.getChildNodesData(parentNode));
assertEquals(2, cwdmNode1.getChildNodesData(parentNode).size());
assertNotNull(cwdmNode2.getChildNodesData(parentNode));
assertEquals(2, cwdmNode2.getChildNodesData(parentNode).size());
parentNode = new PersistedNodeData("parent-id2", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent-node2")), Constants.ROOT_UUID, 1, 0,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
executeConcurrentReadNWrite(con, readAction, writeAction, Mode.WRITE_FIRST, parentNode);
assertNotNull(cwdmNode1.getChildNodesData(parentNode));
assertEquals(2, cwdmNode1.getChildNodesData(parentNode).size());
assertNotNull(cwdmNode2.getChildNodesData(parentNode));
assertEquals(2, cwdmNode2.getChildNodesData(parentNode).size());
// Test getChildPropertiesData
readAction = new Action(cwdmNode2)
{
public void execute(NodeData parentNode) throws Exception
{
cwdm.getChildPropertiesData(parentNode);
}
};
writeAction = new Action(cwdmNode1)
{
public void execute(NodeData parentNode) throws Exception
{
PlainChangesLog chlog = new PlainChangesLogImpl();
cwdm.getChildPropertiesData(parentNode);
chlog.add(ItemState.createAddedState(new PersistedPropertyData("id-property"
+ parentNode.getIdentifier(), QPath.makeChildPath(parentNode.getQPath(), new InternalQName(null,
"property")), parentNode.getIdentifier(), 0, PropertyType.STRING, false, Arrays
.asList((ValueData)new ByteArrayPersistedValueData(0, "some data".getBytes("UTF-8"))),
new SimplePersistedSize(0))));
cwdm.save(new TransactionChangesLog(chlog));
}
};
parentNode = new PersistedNodeData("parent-id3", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent-node3")), Constants.ROOT_UUID, 1, 0,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
executeConcurrentReadNWrite(con, readAction, writeAction, Mode.READ_FIRST, parentNode);
assertNotNull(cwdmNode1.getChildPropertiesData(parentNode));
assertEquals(2, cwdmNode1.getChildPropertiesData(parentNode).size());
assertNotNull(cwdmNode2.getChildPropertiesData(parentNode));
assertEquals(2, cwdmNode2.getChildPropertiesData(parentNode).size());
parentNode = new PersistedNodeData("parent-id4", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent-node4")), Constants.ROOT_UUID, 1, 0,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
executeConcurrentReadNWrite(con, readAction, writeAction, Mode.WRITE_FIRST, parentNode);
assertNotNull(cwdmNode1.getChildPropertiesData(parentNode));
assertEquals(2, cwdmNode1.getChildPropertiesData(parentNode).size());
assertNotNull(cwdmNode2.getChildPropertiesData(parentNode));
assertEquals(2, cwdmNode2.getChildPropertiesData(parentNode).size());
// Test getReferencesData
readAction = new Action(cwdmNode2)
{
public void execute(NodeData parentNode) throws Exception
{
cwdm.getReferencesData(parentNode.getIdentifier(), false);
}
};
writeAction = new Action(cwdmNode1)
{
public void execute(NodeData parentNode) throws Exception
{
PlainChangesLog chlog = new PlainChangesLogImpl();
cwdm.getReferencesData(parentNode.getIdentifier(), false);
chlog.add(ItemState.createAddedState(new PersistedPropertyData("id-reference"
+ parentNode.getIdentifier(), QPath.makeChildPath(parentNode.getQPath(), new InternalQName(null,
"reference")), parentNode.getIdentifier(), 0, PropertyType.REFERENCE, false, Arrays
.asList((ValueData)new ByteArrayPersistedValueData(0, parentNode.getIdentifier().getBytes("UTF-8"))),
new SimplePersistedSize(0))));
cwdm.save(new TransactionChangesLog(chlog));
}
};
parentNode =
new PersistedNodeData("parent-id5", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null,
"parent-node5")), Constants.ROOT_UUID, 1, 0, Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
executeConcurrentReadNWrite(con, readAction, writeAction, Mode.READ_FIRST, parentNode);
assertNotNull(cwdmNode1.getReferencesData(parentNode.getIdentifier(), false));
assertEquals(2, cwdmNode1.getReferencesData(parentNode.getIdentifier(), false).size());
assertNotNull(cwdmNode2.getReferencesData(parentNode.getIdentifier(), false));
assertEquals(2, cwdmNode2.getReferencesData(parentNode.getIdentifier(), false).size());
parentNode = new PersistedNodeData("parent-id6", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent-node6")), Constants.ROOT_UUID, 1, 0,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
executeConcurrentReadNWrite(con, readAction, writeAction, Mode.WRITE_FIRST, parentNode);
assertNotNull(cwdmNode1.getReferencesData(parentNode.getIdentifier(), false));
assertEquals(2, cwdmNode1.getReferencesData(parentNode.getIdentifier(), false).size());
assertNotNull(cwdmNode2.getReferencesData(parentNode.getIdentifier(), false));
assertEquals(2, cwdmNode2.getReferencesData(parentNode.getIdentifier(), false).size());
// Test getItemData by Id
readAction = new Action(cwdmNode2)
{
public void execute(NodeData parentNode) throws Exception
{
cwdm.getItemData(parentNode.getIdentifier());
}
};
writeAction = new Action(cwdmNode1)
{
public void execute(NodeData parentNode) throws Exception
{
PlainChangesLog chlog = new PlainChangesLogImpl();
cwdm.getItemData(parentNode.getIdentifier());
chlog.add(ItemState.createUpdatedState(new PersistedNodeData(parentNode.getIdentifier(), parentNode.getQPath(), Constants.ROOT_UUID, 2, 1,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null)));
cwdm.save(new TransactionChangesLog(chlog));
}
};
parentNode = new PersistedNodeData("parent-id7", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent-node7")), Constants.ROOT_UUID, 1, 0,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
executeConcurrentReadNWrite(con, readAction, writeAction, Mode.READ_FIRST, parentNode);
assertNotNull(cwdmNode1.getItemData(parentNode.getIdentifier()));
assertEquals(2, cwdmNode1.getItemData(parentNode.getIdentifier()).getPersistedVersion());
assertNotNull(cwdmNode2.getItemData(parentNode.getIdentifier()));
assertEquals(2, cwdmNode2.getItemData(parentNode.getIdentifier()).getPersistedVersion());
parentNode = new PersistedNodeData("parent-id8", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent-node8")), Constants.ROOT_UUID, 1, 0,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
executeConcurrentReadNWrite(con, readAction, writeAction, Mode.WRITE_FIRST, parentNode);
assertNotNull(cwdmNode1.getItemData(parentNode.getIdentifier()));
assertEquals(2, cwdmNode1.getItemData(parentNode.getIdentifier()).getPersistedVersion());
assertNotNull(cwdmNode2.getItemData(parentNode.getIdentifier()));
assertEquals(2, cwdmNode2.getItemData(parentNode.getIdentifier()).getPersistedVersion());
// Test getItemData by Path
final QPathEntry qpe = new QPathEntry(null, "my-property", 1);
readAction = new Action(cwdmNode2)
{
public void execute(NodeData parentNode) throws Exception
{
cwdm.getItemData(parentNode, qpe, ItemType.PROPERTY);
}
};
writeAction = new Action(cwdmNode1)
{
public void execute(NodeData parentNode) throws Exception
{
PlainChangesLog chlog = new PlainChangesLogImpl();
cwdm.getItemData(parentNode, qpe, ItemType.PROPERTY);
chlog.add(ItemState.createUpdatedState(new PersistedPropertyData("property-by-path"
+ parentNode.getIdentifier(), QPath.makeChildPath(parentNode.getQPath(), qpe), parentNode
.getIdentifier(), 2, PropertyType.STRING, false, Arrays
.asList((ValueData)new ByteArrayPersistedValueData(0, "some new data".getBytes("UTF-8"))),
new SimplePersistedSize(0))));
cwdm.save(new TransactionChangesLog(chlog));
}
};
parentNode =
new PersistedNodeData("parent-id9", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null,
"parent-node9")), Constants.ROOT_UUID, 1, 0,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
executeConcurrentReadNWrite(con, readAction, writeAction, Mode.READ_FIRST, parentNode);
assertNotNull(cwdmNode1.getItemData(parentNode, qpe, ItemType.PROPERTY));
assertEquals(2, cwdmNode1.getItemData(parentNode, qpe, ItemType.PROPERTY).getPersistedVersion());
assertNotNull(cwdmNode2.getItemData(parentNode, qpe, ItemType.PROPERTY));
assertEquals(2, cwdmNode2.getItemData(parentNode, qpe, ItemType.PROPERTY).getPersistedVersion());
parentNode = new PersistedNodeData("parent-id10", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent-node10")), Constants.ROOT_UUID, 1, 0,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
executeConcurrentReadNWrite(con, readAction, writeAction, Mode.WRITE_FIRST, parentNode);
assertNotNull(cwdmNode1.getItemData(parentNode, qpe, ItemType.PROPERTY));
assertEquals(2, cwdmNode1.getItemData(parentNode, qpe, ItemType.PROPERTY).getPersistedVersion());
assertNotNull(cwdmNode2.getItemData(parentNode, qpe, ItemType.PROPERTY));
assertEquals(2, cwdmNode2.getItemData(parentNode, qpe, ItemType.PROPERTY).getPersistedVersion());
// Test getChildNodesData by patterns
final List<QPathEntryFilter> nodePatterns =
Collections.singletonList((QPathEntryFilter)new PatternQPathEntryFilter(new PatternQPathEntry("",
"my-node")));
readAction = new Action(cwdmNode2)
{
public void execute(NodeData parentNode) throws Exception
{
cwdm.getChildNodesData(parentNode, nodePatterns);
}
};
writeAction = new Action(cwdmNode1)
{
public void execute(NodeData parentNode) throws Exception
{
PlainChangesLog chlog = new PlainChangesLogImpl();
cwdm.getChildNodesData(parentNode, nodePatterns);
chlog.add(ItemState.createAddedState(new PersistedNodeData("my-node2" + parentNode.getIdentifier(),
QPath.makeChildPath(parentNode.getQPath(), new QName("", "my-node"), 2), parentNode.getIdentifier(), 1, 2,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null)));
cwdm.save(new TransactionChangesLog(chlog));
}
};
con.setCanModify(true);
parentNode = new PersistedNodeData("parent-id11", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent-node11")), Constants.ROOT_UUID, 1, 0,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
executeConcurrentReadNWrite(con, readAction, writeAction, Mode.READ_FIRST, parentNode);
assertNotNull(cwdmNode1.getChildNodesData(parentNode, nodePatterns));
assertEquals(2, cwdmNode1.getChildNodesData(parentNode, nodePatterns).size());
assertNotNull(cwdmNode2.getChildNodesData(parentNode, nodePatterns));
assertEquals(2, cwdmNode2.getChildNodesData(parentNode, nodePatterns).size());
parentNode = new PersistedNodeData("parent-id12", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent-node12")), Constants.ROOT_UUID, 1, 0,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
executeConcurrentReadNWrite(con, readAction, writeAction, Mode.WRITE_FIRST, parentNode);
assertNotNull(cwdmNode1.getChildNodesData(parentNode, nodePatterns));
assertEquals(2, cwdmNode1.getChildNodesData(parentNode, nodePatterns).size());
assertNotNull(cwdmNode2.getChildNodesData(parentNode, nodePatterns));
assertEquals(2, cwdmNode2.getChildNodesData(parentNode, nodePatterns).size());
// Test getChildPropertiesData by patterns
final List<QPathEntryFilter> propPatterns =
Collections.singletonList((QPathEntryFilter)new PatternQPathEntryFilter(new PatternQPathEntry("",
"my-property*")));
readAction = new Action(cwdmNode2)
{
public void execute(NodeData parentNode) throws Exception
{
cwdm.getChildPropertiesData(parentNode, propPatterns);
}
};
writeAction = new Action(cwdmNode1)
{
public void execute(NodeData parentNode) throws Exception
{
PlainChangesLog chlog = new PlainChangesLogImpl();
cwdm.getChildPropertiesData(parentNode, propPatterns);
chlog.add(ItemState.createAddedState(new PersistedPropertyData("id-property"
+ parentNode.getIdentifier(), QPath.makeChildPath(parentNode.getQPath(), new InternalQName(null,
"my-property1")), parentNode.getIdentifier(), 0, PropertyType.STRING, false, Arrays
.asList((ValueData)new ByteArrayPersistedValueData(0, "some data".getBytes("UTF-8"))),
new SimplePersistedSize(0))));
cwdm.save(new TransactionChangesLog(chlog));
}
};
parentNode = new PersistedNodeData("parent-id13", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent-node13")), Constants.ROOT_UUID, 1, 0,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
executeConcurrentReadNWrite(con, readAction, writeAction, Mode.READ_FIRST, parentNode);
assertNotNull(cwdmNode1.getChildPropertiesData(parentNode, propPatterns));
assertEquals(2, cwdmNode1.getChildPropertiesData(parentNode, propPatterns).size());
assertNotNull(cwdmNode2.getChildPropertiesData(parentNode, propPatterns));
assertEquals(2, cwdmNode2.getChildPropertiesData(parentNode, propPatterns).size());
parentNode = new PersistedNodeData("parent-id14", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent-node14")), Constants.ROOT_UUID, 1, 0,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
executeConcurrentReadNWrite(con, readAction, writeAction, Mode.WRITE_FIRST, parentNode);
assertNotNull(cwdmNode1.getChildPropertiesData(parentNode, propPatterns));
assertEquals(2, cwdmNode1.getChildPropertiesData(parentNode, propPatterns).size());
assertNotNull(cwdmNode2.getChildPropertiesData(parentNode, propPatterns));
assertEquals(2, cwdmNode2.getChildPropertiesData(parentNode, propPatterns).size());
// testConsistency
con = new MyWorkspaceStorageConnection(true);
wdc = new MyWorkspaceDataContainer(con);
wsc = repository.getWorkspaceContainer("ws");
wconf = (WorkspaceEntry)wsc.getComponent(WorkspaceEntry.class);
cwdmNode1 =
new CacheableWorkspaceDataManager(wconf, wdc, cache1, new SystemDataContainerHolder(wdc));
cwdmNode2 =
new CacheableWorkspaceDataManager(wconf, wdc, cache2, new SystemDataContainerHolder(wdc));
parentNode = new PersistedNodeData("parent2-id", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent2-node")), Constants.ROOT_UUID, 1, 0,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
// Test getChildNodesData
con.setParentNode(parentNode);
cwdmNode2.getChildNodesData(parentNode);
PlainChangesLog chlog = new PlainChangesLogImpl();
chlog.add(ItemState.createAddedState(new PersistedNodeData("id-node" + parentNode.getIdentifier(), QPath.makeChildPath(parentNode.getQPath(), new InternalQName(null, "node")), parentNode.getIdentifier(), 1, 0,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null)));
cwdmNode1.save(new TransactionChangesLog(chlog));
assertNotNull(cwdmNode1.getChildNodesData(parentNode));
assertEquals(2, cwdmNode1.getChildNodesData(parentNode).size());
assertNotNull(cwdmNode2.getChildNodesData(parentNode));
assertEquals(2, cwdmNode2.getChildNodesData(parentNode).size());
parentNode = new PersistedNodeData("parent2-id2", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent2-node2")), Constants.ROOT_UUID, 1, 0,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
con.setParentNode(parentNode);
cwdmNode2.getChildNodesData(parentNode);
chlog = new PlainChangesLogImpl();
chlog.add(ItemState.createDeletedState(new PersistedNodeData("id-node2" + parentNode.getIdentifier(), QPath.makeChildPath(parentNode.getQPath(), new InternalQName(null, "node2")), parentNode.getIdentifier(), 1, 0,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null)));
cwdmNode1.save(new TransactionChangesLog(chlog));
assertNotNull(cwdmNode1.getChildNodesData(parentNode));
assertEquals(0, cwdmNode1.getChildNodesData(parentNode).size());
assertNotNull(cwdmNode2.getChildNodesData(parentNode));
assertEquals(0, cwdmNode2.getChildNodesData(parentNode).size());
// Test getChildPropertiesData
parentNode = new PersistedNodeData("parent2-id3", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent2-node3")), Constants.ROOT_UUID, 1, 0,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
con.setParentNode(parentNode);
cwdmNode2.getChildPropertiesData(parentNode);
chlog = new PlainChangesLogImpl();
chlog.add(ItemState.createAddedState(new PersistedPropertyData("id-property" + parentNode.getIdentifier(),
QPath.makeChildPath(parentNode.getQPath(), new InternalQName(null, "property")),
parentNode.getIdentifier(), 0, PropertyType.STRING, false, Arrays
.asList((ValueData)new ByteArrayPersistedValueData(0, "some data".getBytes("UTF-8"))),
new SimplePersistedSize(0))));
cwdmNode1.save(new TransactionChangesLog(chlog));
assertNotNull(cwdmNode1.getChildPropertiesData(parentNode));
assertEquals(2, cwdmNode1.getChildPropertiesData(parentNode).size());
assertNotNull(cwdmNode2.getChildPropertiesData(parentNode));
assertEquals(2, cwdmNode2.getChildPropertiesData(parentNode).size());
parentNode = new PersistedNodeData("parent2-id4", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent2-node4")), Constants.ROOT_UUID, 1, 0,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
con.setParentNode(parentNode);
cwdmNode2.getChildPropertiesData(parentNode);
chlog = new PlainChangesLogImpl();
chlog.add(ItemState.createDeletedState(new PersistedPropertyData("id-property2" + parentNode.getIdentifier(),
QPath.makeChildPath(parentNode.getQPath(), new InternalQName(null, "property2")), parentNode
.getIdentifier(), 0, PropertyType.STRING, false, Arrays
.asList((ValueData)new ByteArrayPersistedValueData(0, "some data".getBytes("UTF-8"))),
new SimplePersistedSize(0))));
cwdmNode1.save(new TransactionChangesLog(chlog));
assertNotNull(cwdmNode1.getChildPropertiesData(parentNode));
assertEquals(0, cwdmNode1.getChildPropertiesData(parentNode).size());
assertNotNull(cwdmNode2.getChildPropertiesData(parentNode));
assertEquals(0, cwdmNode2.getChildPropertiesData(parentNode).size());
// Test getReferencesData
parentNode = new PersistedNodeData("parent2-id5", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent2-node5")), Constants.ROOT_UUID, 1, 0,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
con.setParentNode(parentNode);
cwdmNode2.getReferencesData(parentNode.getIdentifier(), false);
chlog = new PlainChangesLogImpl();
chlog.add(ItemState.createAddedState(new PersistedPropertyData("id-reference" + parentNode.getIdentifier(),
QPath.makeChildPath(parentNode.getQPath(), new InternalQName(null, "reference")), parentNode
.getIdentifier(), 0, PropertyType.REFERENCE, false, Arrays
.asList((ValueData)new ByteArrayPersistedValueData(0, parentNode.getIdentifier().getBytes("UTF-8"))),
new SimplePersistedSize(0))));
cwdmNode1.save(new TransactionChangesLog(chlog));
assertNotNull(cwdmNode1.getReferencesData(parentNode.getIdentifier(), false));
assertEquals(2, cwdmNode1.getReferencesData(parentNode.getIdentifier(), false).size());
assertNotNull(cwdmNode2.getReferencesData(parentNode.getIdentifier(), false));
assertEquals(2, cwdmNode2.getReferencesData(parentNode.getIdentifier(), false).size());
parentNode = new PersistedNodeData("parent2-id6", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent2-node6")), Constants.ROOT_UUID, 1, 0,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
con.setParentNode(parentNode);
cwdmNode2.getReferencesData(parentNode.getIdentifier(), false);
chlog = new PlainChangesLogImpl();
chlog.add(ItemState.createDeletedState(new PersistedPropertyData("id-reference2" + parentNode.getIdentifier(),
QPath.makeChildPath(parentNode.getQPath(), new InternalQName(null, "reference2")), parentNode
.getIdentifier(), 0, PropertyType.REFERENCE, false, Arrays
.asList((ValueData)new ByteArrayPersistedValueData(0, parentNode.getIdentifier().getBytes("UTF-8"))),
new SimplePersistedSize(0))));
cwdmNode1.save(new TransactionChangesLog(chlog));
assertNotNull(cwdmNode1.getReferencesData(parentNode.getIdentifier(), false));
assertEquals(0, cwdmNode1.getReferencesData(parentNode.getIdentifier(), false).size());
assertNotNull(cwdmNode2.getReferencesData(parentNode.getIdentifier(), false));
assertEquals(0, cwdmNode2.getReferencesData(parentNode.getIdentifier(), false).size());
// Test getChildNodesData by patterns
final List<QPathEntryFilter> patterns =
Collections.singletonList((QPathEntryFilter)new PatternQPathEntryFilter(new PatternQPathEntry("",
"my-node")));
parentNode = new PersistedNodeData("parent2-id11", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent2-node11")), Constants.ROOT_UUID, 1, 0,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
con.setParentNode(parentNode);
cwdmNode2.getChildNodesData(parentNode, patterns);
chlog = new PlainChangesLogImpl();
chlog.add(ItemState.createAddedState(new PersistedNodeData("my-node2" + parentNode.getIdentifier(),
QPath.makeChildPath(parentNode.getQPath(), new QName("", "my-node"), 2), parentNode.getIdentifier(), 1, 2,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null)));
cwdmNode1.save(new TransactionChangesLog(chlog));
assertNotNull(cwdmNode1.getChildNodesData(parentNode, patterns));
assertEquals(2, cwdmNode1.getChildNodesData(parentNode, patterns).size());
assertNotNull(cwdmNode2.getChildNodesData(parentNode, patterns));
assertEquals(2, cwdmNode2.getChildNodesData(parentNode, patterns).size());
parentNode = new PersistedNodeData("parent2-id12", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent2-node12")), Constants.ROOT_UUID, 1, 0,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
con.setParentNode(parentNode);
cwdmNode2.getChildNodesData(parentNode, patterns);
chlog = new PlainChangesLogImpl();
chlog.add(ItemState.createDeletedState(new PersistedNodeData("my-node" + parentNode.getIdentifier(), QPath
.makeChildPath(parentNode.getQPath(), new QName("", "my-node"), 1), parentNode.getIdentifier(), 1, 1,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null)));
cwdmNode1.save(new TransactionChangesLog(chlog));
assertNotNull(cwdmNode1.getChildNodesData(parentNode, patterns));
assertEquals(0, cwdmNode1.getChildNodesData(parentNode, patterns).size());
assertNotNull(cwdmNode2.getChildNodesData(parentNode, patterns));
assertEquals(0, cwdmNode2.getChildNodesData(parentNode, patterns).size());
// Test getChildPropertiesData by patterns
final List<QPathEntryFilter> propPattern2s =
Collections.singletonList((QPathEntryFilter)new PatternQPathEntryFilter(new PatternQPathEntry("",
"my-property*")));
parentNode = new PersistedNodeData("parent2-id13", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent2-node13")), Constants.ROOT_UUID, 1, 0,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
con.setParentNode(parentNode);
cwdmNode2.getChildPropertiesData(parentNode, propPattern2s);
chlog = new PlainChangesLogImpl();
chlog.add(ItemState.createAddedState(new PersistedPropertyData("id-property" + parentNode.getIdentifier(),
QPath.makeChildPath(parentNode.getQPath(), new InternalQName(null, "my-property1")), parentNode
.getIdentifier(), 0, PropertyType.STRING, false, Arrays
.asList((ValueData)new ByteArrayPersistedValueData(0, "some data".getBytes("UTF-8"))),
new SimplePersistedSize(0))));
cwdmNode1.save(new TransactionChangesLog(chlog));
assertNotNull(cwdmNode1.getChildPropertiesData(parentNode, propPattern2s));
assertEquals(2, cwdmNode1.getChildPropertiesData(parentNode, propPattern2s).size());
assertNotNull(cwdmNode2.getChildPropertiesData(parentNode, propPattern2s));
assertEquals(2, cwdmNode2.getChildPropertiesData(parentNode, propPattern2s).size());
parentNode = new PersistedNodeData("parent2-id14", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent2-node14")), Constants.ROOT_UUID, 1, 0,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
con.setParentNode(parentNode);
cwdmNode2.getChildPropertiesData(parentNode, propPattern2s);
chlog = new PlainChangesLogImpl();
chlog.add(ItemState.createDeletedState(new PersistedPropertyData("id-property2" + parentNode.getIdentifier(),
QPath.makeChildPath(parentNode.getQPath(), new InternalQName(null, "my-property2")), parentNode
.getIdentifier(), 0, PropertyType.STRING, false, Arrays
.asList((ValueData)new ByteArrayPersistedValueData(0, "some data".getBytes("UTF-8"))),
new SimplePersistedSize(0))));
cwdmNode1.save(new TransactionChangesLog(chlog));
assertNotNull(cwdmNode1.getChildPropertiesData(parentNode, propPattern2s));
assertEquals(0, cwdmNode1.getChildPropertiesData(parentNode, propPattern2s).size());
assertNotNull(cwdmNode2.getChildPropertiesData(parentNode, propPattern2s));
assertEquals(0, cwdmNode2.getChildPropertiesData(parentNode, propPattern2s).size());
}
finally
{
if (cache1 != null)
{
try
{
finalize(cache1);
}
catch (Exception e)
{
// ignore me
}
}
if (cache2 != null)
{
try
{
finalize(cache2);
}
catch (Exception e)
{
// ignore me
}
}
}
}
protected void finalize(T cache)
{
}
/**
* @param con
* @param cwdm
* @param mode
* @param idNode
* @throws InterruptedException
*/
private void executeConcurrentReadNWrite(final MyWorkspaceStorageConnection con, final Action readAction,
final Action writeAction, final Mode mode, final NodeData parentNode) throws InterruptedException
{
final CountDownLatch goSignal = con.initCountDownLatch();
con.setParentNode(parentNode);
final AtomicReference<Exception> ex = new AtomicReference<Exception>();
final CountDownLatch startSignal = new CountDownLatch(1);
final CountDownLatch doneSignal = new CountDownLatch(2);
Thread writer = new Thread()
{
public void run()
{
try
{
startSignal.await();
con.wait.set(mode != Mode.WRITE_FIRST);
writeAction.execute(parentNode);
}
catch (Exception e)
{
e.printStackTrace();
ex.set(e);
}
finally
{
if (mode == Mode.WRITE_FIRST) goSignal.countDown();
doneSignal.countDown();
con.wait.remove();
}
}
};
writer.start();
Thread reader = new Thread()
{
public void run()
{
try
{
startSignal.await();
con.wait.set(mode != Mode.READ_FIRST);
readAction.execute(parentNode);
}
catch (Exception e)
{
e.printStackTrace();
ex.set(e);
}
finally
{
if (mode == Mode.READ_FIRST) goSignal.countDown();
doneSignal.countDown();
con.wait.remove();
}
}
};
reader.start();
startSignal.countDown();
doneSignal.await();
assertNull(ex.get());
}
private abstract class Action
{
protected final CacheableWorkspaceDataManager cwdm;
public Action(CacheableWorkspaceDataManager cwdm)
{
this.cwdm = cwdm;
}
protected abstract void execute(NodeData parentNode) throws Exception;
}
private static enum Mode
{
READ_FIRST, WRITE_FIRST;
}
public static class MyWorkspaceStorageConnection implements WorkspaceStorageConnection
{
public ThreadLocal<Boolean> wait = new ThreadLocal<Boolean>();
private NodeData parentNode;
private CountDownLatch goSignal;
private ItemData itemAdded;
private boolean canModify;
private boolean itemDeleted;
public MyWorkspaceStorageConnection()
{
}
public MyWorkspaceStorageConnection(boolean canModify)
{
this.canModify = canModify;
}
/**
* @param canModify the canModify to set
*/
public void setCanModify(boolean canModify)
{
this.canModify = canModify;
}
public CountDownLatch initCountDownLatch()
{
return this.goSignal = new CountDownLatch(1);
}
public void setParentNode(NodeData parentNode)
{
this.parentNode = parentNode;
this.itemAdded = null;
this.itemDeleted = false;
}
public void add(NodeData data) throws RepositoryException, UnsupportedOperationException,
InvalidItemStateException, IllegalStateException
{
this.itemAdded = data;
}
public void add(PropertyData data, ChangedSizeHandler sizeHandler) throws RepositoryException,
UnsupportedOperationException,
InvalidItemStateException, IllegalStateException
{
this.itemAdded = data;
}
public void close() throws IllegalStateException, RepositoryException
{
}
public void prepare() throws IllegalStateException, RepositoryException
{
}
public void commit() throws IllegalStateException, RepositoryException
{
if (wait.get() != null && wait.get())
{
try
{
goSignal.await();
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
}
}
}
public void delete(NodeData data) throws RepositoryException, UnsupportedOperationException,
InvalidItemStateException, IllegalStateException
{
this.itemDeleted = true;
}
public void delete(PropertyData data, ChangedSizeHandler sizeHandler) throws RepositoryException,
UnsupportedOperationException, InvalidItemStateException, IllegalStateException
{
this.itemDeleted = true;
}
public int getChildNodesCount(NodeData parent) throws RepositoryException
{
return -1;
}
public List<NodeData> getChildNodesData(NodeData parent) throws RepositoryException, IllegalStateException
{
if (wait.get() != null && wait.get())
{
try
{
goSignal.await();
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
}
}
List<NodeData> children = new ArrayList<NodeData>();
if (!canModify || !itemDeleted)
{
children.add(new PersistedNodeData("id-node2" + parentNode.getIdentifier(), QPath.makeChildPath(parent.getQPath(), new InternalQName(null, "node2")), parent.getIdentifier(), 1, 0,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null));
}
if (itemAdded != null)
{
children.add((NodeData)itemAdded);
}
return children;
}
public List<PropertyData> getChildPropertiesData(NodeData parent) throws RepositoryException,
IllegalStateException
{
if (wait.get() != null && wait.get())
{
try
{
goSignal.await();
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
}
}
List<PropertyData> children = new ArrayList<PropertyData>();
try
{
if (!canModify || !itemDeleted)
{
children.add(new PersistedPropertyData("id-property2" + parentNode.getIdentifier(), QPath.makeChildPath(
parentNode.getQPath(), new InternalQName(null, "property2")), parentNode.getIdentifier(), 0,
PropertyType.STRING, false, Arrays.asList((ValueData)new ByteArrayPersistedValueData(0, "some data"
.getBytes("UTF-8"))), new SimplePersistedSize(0)));
}
if (itemAdded != null)
{
children.add((PropertyData)itemAdded);
}
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
return children;
}
public List<PropertyData> getChildPropertiesData(NodeData parent, List<QPathEntryFilter> pattern) throws RepositoryException,
IllegalStateException
{
if (wait.get() != null && wait.get())
{
try
{
goSignal.await();
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
}
}
List<PropertyData> children = new ArrayList<PropertyData>();
try
{
if (!canModify || !itemDeleted)
{
children.add(new PersistedPropertyData("id-property2" + parentNode.getIdentifier(), QPath.makeChildPath(
parentNode.getQPath(), new InternalQName(null, "my-property2")), parentNode.getIdentifier(), 0,
PropertyType.STRING, false, Arrays.asList((ValueData)new ByteArrayPersistedValueData(0, "some data"
.getBytes("UTF-8"))), new SimplePersistedSize(0)));
}
if (canModify && itemAdded != null)
{
children.add((PropertyData)itemAdded);
}
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
return children;
}
public ItemData getItemData(NodeData parentData, QPathEntry name, ItemType itemType) throws RepositoryException,
IllegalStateException
{
if (wait.get() != null && wait.get())
{
try
{
goSignal.await();
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
}
}
if (itemType == ItemType.NODE)
{
if (name.equals(Constants.ROOT_PATH.getEntries()[0]))
{
return new PersistedNodeData(Constants.ROOT_UUID, Constants.ROOT_PATH, Constants.ROOT_PARENT_UUID, 1, 1,
Constants.NT_UNSTRUCTURED, new InternalQName[0], new AccessControlList());
}
else
{
return new PersistedNodeData("my-node" + parentNode.getIdentifier(), QPath.makeChildPath(
parentNode.getQPath(), name), Constants.ROOT_UUID, 1, 1, Constants.NT_UNSTRUCTURED,
new InternalQName[0], null);
}
}
try
{
return new PersistedPropertyData("property-by-path" + parentNode.getIdentifier(), QPath.makeChildPath(
parentNode.getQPath(), name), parentNode.getIdentifier(), 1, PropertyType.STRING, false,
Arrays.asList((ValueData)new ByteArrayPersistedValueData(0, "some new data".getBytes("UTF-8"))),
new SimplePersistedSize(0));
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
return null;
}
public ItemData getItemData(String identifier) throws RepositoryException, IllegalStateException
{
if (wait.get() != null && wait.get())
{
try
{
goSignal.await();
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
}
}
if (identifier.equals(Constants.ROOT_UUID))
{
return new PersistedNodeData(Constants.ROOT_UUID, Constants.ROOT_PATH, Constants.ROOT_PARENT_UUID, 1, 1,
Constants.NT_UNSTRUCTURED, new InternalQName[0], new AccessControlList());
}
else
{
return parentNode;
}
}
public List<PropertyData> getReferencesData(String nodeIdentifier) throws RepositoryException,
IllegalStateException, UnsupportedOperationException
{
if (wait.get() != null && wait.get())
{
try
{
goSignal.await();
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
}
}
List<PropertyData> children = new ArrayList<PropertyData>();
try
{
if (!canModify || !itemDeleted)
{
children.add(new PersistedPropertyData("id-reference2" + parentNode.getIdentifier(), QPath
.makeChildPath(parentNode.getQPath(), new InternalQName(null, "reference2")), parentNode
.getIdentifier(), 0, PropertyType.REFERENCE, false, Arrays
.asList((ValueData)new ByteArrayPersistedValueData(0, parentNode.getIdentifier().getBytes("UTF-8"))),
new SimplePersistedSize(0)));
}
if (itemAdded != null)
{
children.add((PropertyData)itemAdded);
}
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
return children;
}
public boolean isOpened()
{
return true;
}
public List<PropertyData> listChildPropertiesData(NodeData parent) throws RepositoryException,
IllegalStateException
{
return null;
}
public void rename(NodeData data) throws RepositoryException, UnsupportedOperationException,
InvalidItemStateException, IllegalStateException
{
}
public void rollback() throws IllegalStateException, RepositoryException
{
}
public void update(NodeData data) throws RepositoryException, UnsupportedOperationException,
InvalidItemStateException, IllegalStateException
{
}
public void update(PropertyData data, ChangedSizeHandler sizeHandler) throws RepositoryException,
UnsupportedOperationException, InvalidItemStateException, IllegalStateException
{
}
/**
* @see org.exoplatform.services.jcr.storage.WorkspaceStorageConnection#getLastOrderNumber(org.exoplatform.services.jcr.datamodel.NodeData)
*/
public int getLastOrderNumber(NodeData parent) throws RepositoryException
{
return 0;
}
public List<NodeData> getChildNodesData(NodeData parent, List<QPathEntryFilter> pattern) throws RepositoryException,
IllegalStateException
{
if (wait.get() != null && wait.get())
{
try
{
goSignal.await();
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
}
}
List<NodeData> children = new ArrayList<NodeData>();
if (!canModify || !itemDeleted)
{
children.add(new PersistedNodeData("my-node" + parent.getIdentifier(), QPath.makeChildPath(parent.getQPath(), new QName("","my-node"), 1), parent.getIdentifier(), 1, 1,
Constants.NT_UNSTRUCTURED, new InternalQName[0], null));
}
if (canModify && itemAdded != null)
{
children.add((NodeData)itemAdded);
}
return children;
}
public boolean getChildNodesDataByPage(NodeData parent, int fromOrderNum, int offset, int pageSize, List<NodeData> childs)
throws RepositoryException
{
return false;
}
/**
* @see org.exoplatform.services.jcr.storage.WorkspaceStorageConnection#getACLHolders()
*/
public List<ACLHolder> getACLHolders() throws RepositoryException, IllegalStateException,
UnsupportedOperationException
{
return null;
}
/**
* @see org.exoplatform.services.jcr.storage.WorkspaceStorageConnection#getNodesCount()
*/
public long getNodesCount() throws RepositoryException
{
throw new UnsupportedOperationException();
}
public boolean hasItemData(NodeData parentData, QPathEntry name, ItemType itemType) throws RepositoryException,
IllegalStateException
{
return getItemData(parentData, name, itemType) != null;
}
public long getWorkspaceDataSize() throws RepositoryException
{
return 0;
}
public long getNodeDataSize(String parentId) throws RepositoryException
{
return 0;
}
};
private static class MyWorkspaceDataContainer extends WorkspaceDataContainerBase
{
private WorkspaceStorageConnection con;
public MyWorkspaceDataContainer(WorkspaceStorageConnection con)
{
this.con = con;
}
public boolean isCheckSNSNewConnection()
{
return false;
}
public boolean isSame(WorkspaceDataContainer another)
{
return false;
}
public WorkspaceStorageConnection openConnection() throws RepositoryException
{
return con;
}
public WorkspaceStorageConnection openConnection(boolean readOnly) throws RepositoryException
{
return con;
}
public WorkspaceStorageConnection reuseConnection(WorkspaceStorageConnection original) throws RepositoryException
{
return con;
}
public String getInfo()
{
return "MyWorkspaceDataContainer";
}
public String getName()
{
return "MyWorkspaceDataContainer";
}
public String getStorageVersion()
{
return "0";
}
/**
* @see org.exoplatform.services.jcr.storage.DataContainer#getUniqueName()
*/
public String getUniqueName()
{
return "MyWorkspaceDataContainer";
}
};
}