/* * Copyright (C) 2003-2011 eXo Platform SAS. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see<http://www.gnu.org/licenses/>. */ package org.exoplatform.services.jcr.impl.dataflow.persistent.cache.infinispan; import org.exoplatform.container.configuration.ConfigurationManagerImpl; import org.exoplatform.services.jcr.config.CacheEntry; import org.exoplatform.services.jcr.config.SimpleParameterEntry; 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.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.impl.Constants; import org.exoplatform.services.jcr.impl.core.itemfilters.QPathEntryFilter; import org.exoplatform.services.jcr.impl.dataflow.persistent.ACLHolder; import org.exoplatform.services.jcr.impl.dataflow.persistent.CacheableWorkspaceDataManager; import org.exoplatform.services.jcr.impl.dataflow.persistent.ChangedSizeHandler; import org.exoplatform.services.jcr.impl.dataflow.persistent.WorkspaceStorageCacheBaseCase; import org.exoplatform.services.jcr.impl.dataflow.persistent.infinispan.ISPNCacheWorkspaceStorageCache; 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.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import javax.jcr.InvalidItemStateException; import javax.jcr.RepositoryException; /** * Created by The eXo Platform SAS. * * <br>Date: * * @author <a href="karpenko.sergiy@gmail.com">Karpenko Sergiy</a> * @version $Id: TestISPNCacheWorkspaceStorageCache.java 111 2008-11-11 11:11:11Z serg $ */ public class TestISPNCacheWorkspaceStorageCache extends WorkspaceStorageCacheBaseCase { @Override public WorkspaceStorageCache getCacheImpl() throws Exception { ArrayList<SimpleParameterEntry> list = new ArrayList<SimpleParameterEntry>(); list.add(new SimpleParameterEntry("infinispan-configuration", "jar:/conf/standalone/test-infinispan-config.xml")); CacheEntry entry = new CacheEntry(list); entry.setEnabled(true); WorkspaceEntry workspaceEntry = new WorkspaceEntry(); workspaceEntry.setUniqueName("WS_UUID"); workspaceEntry.setCache(entry); return new ISPNCacheWorkspaceStorageCache(workspaceEntry, new ConfigurationManagerImpl()); } public void testRaceConditions() throws Exception { MyWorkspaceStorageConnection con = new MyWorkspaceStorageConnection(); WorkspaceDataContainer wdc = new MyWorkspaceDataContainer(con); WorkspaceContainerFacade wsc = repository.getWorkspaceContainer("ws"); WorkspaceEntry wconf = (WorkspaceEntry)wsc.getComponent(WorkspaceEntry.class); final CacheableWorkspaceDataManager cwdm = new CacheableWorkspaceDataManager(wconf, wdc, getCacheImpl(), new SystemDataContainerHolder(wdc)); String idNode = "foo1"; executeConcurrentReadNWrite(con, cwdm, Mode.READ_FIRST, idNode); assertNotNull(cwdm.getItemData(idNode)); idNode = "foo2"; executeConcurrentReadNWrite(con, cwdm, Mode.WRITE_FIRST, idNode); assertNotNull(cwdm.getItemData(idNode)); } public void testGetChildNodesCount() throws Exception { MyWorkspaceStorageConnection con = new MyWorkspaceStorageConnection(); WorkspaceDataContainer wdc = new MyWorkspaceDataContainer(con); WorkspaceContainerFacade wsc = repository.getWorkspaceContainer("ws"); WorkspaceEntry wconf = (WorkspaceEntry)wsc.getComponent(WorkspaceEntry.class); final CacheableWorkspaceDataManager cwdm = new CacheableWorkspaceDataManager(wconf, wdc, getCacheImpl(), new SystemDataContainerHolder(wdc)); final NodeData parentNode = new PersistedNodeData("testGetChildNodesCount", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName( null, "getChildNodesCount")), null, 0, 1, null, null, null); assertEquals(0, con.getChildNodesCountCalls.get()); int result = cwdm.getChildNodesCount(parentNode); assertEquals(0, result); assertEquals(1, con.getChildNodesCountCalls.get()); result = cwdm.getChildNodesCount(parentNode); assertEquals(0, result); assertEquals(1, con.getChildNodesCountCalls.get()); 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))); cwdm.save(new TransactionChangesLog(chlog)); result = cwdm.getChildNodesCount(parentNode); assertEquals(1, result); assertEquals(2, con.getChildNodesCountCalls.get()); } public void testGetChildNodesCount2() throws Exception { MyWorkspaceStorageConnection con = new MyWorkspaceStorageConnection(); con.childNodesCount = 1; WorkspaceDataContainer wdc = new MyWorkspaceDataContainer(con); WorkspaceContainerFacade wsc = repository.getWorkspaceContainer("ws"); WorkspaceEntry wconf = (WorkspaceEntry)wsc.getComponent(WorkspaceEntry.class); final CacheableWorkspaceDataManager cwdm = new CacheableWorkspaceDataManager(wconf, wdc, getCacheImpl(), new SystemDataContainerHolder(wdc)); final NodeData parentNode = new PersistedNodeData("testGetChildNodesCount2", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName( null, "getChildNodesCount")), null, 0, 1, null, null, null); assertEquals(0, con.getChildNodesCountCalls.get()); int result = cwdm.getChildNodesCount(parentNode); assertEquals(1, result); assertEquals(1, con.getChildNodesCountCalls.get()); result = cwdm.getChildNodesCount(parentNode); assertEquals(1, result); assertEquals(1, con.getChildNodesCountCalls.get()); 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))); cwdm.save(new TransactionChangesLog(chlog)); result = cwdm.getChildNodesCount(parentNode); assertEquals(2, result); assertEquals(2, con.getChildNodesCountCalls.get()); } /** * @param con * @param cwdm * @param mode * @param idNode * @throws InterruptedException */ private void executeConcurrentReadNWrite(MyWorkspaceStorageConnection con, final CacheableWorkspaceDataManager cwdm, final Mode mode, final String idNode) throws InterruptedException { final CountDownLatch goSignal = con.setMode(mode); 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(); PlainChangesLog chlog = new PlainChangesLogImpl(); chlog.add(ItemState.createAddedState(new PersistedNodeData(idNode, Constants.ROOT_PATH, "parent-id", 1, 0, Constants.NT_UNSTRUCTURED, null, null))); if (mode == Mode.READ_FIRST) { try { goSignal.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } cwdm.save(new TransactionChangesLog(chlog)); } catch (Exception e) { ex.set(e); } finally { if (mode == Mode.WRITE_FIRST) goSignal.countDown(); doneSignal.countDown(); } } }; writer.start(); Thread reader = new Thread() { public void run() { try { startSignal.await(); cwdm.getItemData(idNode); } catch (Exception e) { ex.set(e); } finally { if (mode == Mode.READ_FIRST) goSignal.countDown(); doneSignal.countDown(); } } }; reader.start(); startSignal.countDown(); doneSignal.await(); assertNull(ex.get()); } private static enum Mode { READ_FIRST, WRITE_FIRST; } private static class MyWorkspaceStorageConnection implements WorkspaceStorageConnection { private Mode mode; private CountDownLatch goSignal; public CountDownLatch setMode(Mode mode) { this.mode = mode; this.goSignal = new CountDownLatch(1); return goSignal; } public void add(NodeData data) throws RepositoryException, UnsupportedOperationException, InvalidItemStateException, IllegalStateException { childNodesCount++; } public void add(PropertyData data, ChangedSizeHandler sizeHandler) throws RepositoryException, UnsupportedOperationException, InvalidItemStateException, IllegalStateException { } public void close() throws IllegalStateException, RepositoryException { } public void commit() throws IllegalStateException, RepositoryException { } public void delete(NodeData data) throws RepositoryException, UnsupportedOperationException, InvalidItemStateException, IllegalStateException { } public void delete(PropertyData data, ChangedSizeHandler sizeHandler) throws RepositoryException, UnsupportedOperationException, InvalidItemStateException, IllegalStateException { } public int childNodesCount = 0; public AtomicInteger getChildNodesCountCalls = new AtomicInteger(); public int getChildNodesCount(NodeData parent) throws RepositoryException { getChildNodesCountCalls.incrementAndGet(); return childNodesCount; } public List<NodeData> getChildNodesData(NodeData parent) throws RepositoryException, IllegalStateException { return null; } public List<PropertyData> getChildPropertiesData(NodeData parent) throws RepositoryException, IllegalStateException { return null; } public List<PropertyData> getChildPropertiesData(NodeData parent, List<QPathEntryFilter> pattern) throws RepositoryException, IllegalStateException { return null; } public ItemData getItemData(NodeData parentData, QPathEntry name, ItemType itemType) throws RepositoryException, IllegalStateException { return null; } public ItemData getItemData(String identifier) throws RepositoryException, IllegalStateException { if (mode == Mode.WRITE_FIRST) { try { goSignal.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } return null; } public List<PropertyData> getReferencesData(String nodeIdentifier) throws RepositoryException, IllegalStateException, UnsupportedOperationException { return null; } 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 prepare() 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 { } public int getLastOrderNumber(NodeData parent) throws RepositoryException { return -1; } public List<NodeData> getChildNodesData(NodeData parent, List<QPathEntryFilter> pattern) throws RepositoryException, IllegalStateException { return getChildNodesData(parent); } 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"; } public String getUniqueName() { return "MyWorkspaceDataContainer"; } }; }