/*
* 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.storage.fs;
import org.exoplatform.services.jcr.JcrImplBaseTest;
import org.exoplatform.services.jcr.dataflow.DataManager;
import org.exoplatform.services.jcr.dataflow.ItemState;
import org.exoplatform.services.jcr.dataflow.TransactionChangesLog;
import org.exoplatform.services.jcr.datamodel.IllegalPathException;
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.NodeImpl;
import org.exoplatform.services.jcr.impl.dataflow.SpoolConfig;
import org.exoplatform.services.jcr.impl.dataflow.TransientNodeData;
import org.exoplatform.services.jcr.impl.dataflow.TransientPropertyData;
import org.exoplatform.services.jcr.impl.dataflow.TransientValueData;
import org.exoplatform.services.jcr.impl.dataflow.session.SessionChangesLog;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Value;
import javax.jcr.ValueFormatException;
/**
* Created by The eXo Platform SAS 10.07.2007
*
* @author <a href="mailto:peter.nedonosko@exoplatform.com.ua">Peter Nedonosko</a>
* @version $Id: TestJCRVSReadWrite.java 11907 2008-03-13 15:36:21Z ksm $
*/
public class TestJCRVSReadWrite extends JcrImplBaseTest
{
private static Log log = ExoLogger.getLogger("exo.jcr.component.core.TestJCRVSReadWrite");
public static final int FILES_COUNT = 1000;
// public static final int FILE1_SIZE_KB = 1;
// public static final int FILE1_SIZE = FILE1_SIZE_KB * 1024;
public static int FILE1_SIZE = 0;
// public static final int FILE2_SIZE_KB = 2;
// public static final int FILE2_SIZE = FILE2_SIZE_KB * 1024;
public static int FILE2_SIZE = 0;
protected Node testRoot = null;
protected List<String> properties = null;
protected BufferedInputStream fBLOB1 = null;
protected BufferedInputStream fBLOB2 = null;
@Override
public void setUp() throws Exception
{
super.setUp();
if (fBLOB1 == null)
{
// fBLOB1 = createBLOBTempFile("treeVSTest_", FILE1_SIZE_KB);
// fBLOB1.deleteOnExit();
fBLOB1 = new BufferedInputStream(new ByteArrayInputStream("qazws".getBytes()));
FILE1_SIZE = fBLOB1.available();
}
if (fBLOB2 == null)
{
// fBLOB2 = createBLOBTempFile("treeVSTest_", FILE2_SIZE_KB);
// fBLOB2.deleteOnExit();
fBLOB2 = new BufferedInputStream(new ByteArrayInputStream("qazwsxedcr".getBytes()));
FILE2_SIZE = fBLOB2.available();
}
testRoot = root.addNode("tree_vs_test");
root.save();
}
@Override
protected void tearDown() throws Exception
{
long time = System.currentTimeMillis();
if (root.hasNode(testRoot.getName()))
{
testRoot.remove();
root.save();
}
if (log.isDebugEnabled())
{
log.debug("Tear down of " + getName() + ",\t" + (System.currentTimeMillis() - time));
}
super.tearDown();
}
protected List<String> createJCRAPICase() throws Exception
{
List<String> props = new ArrayList<String>();
String rootPath = testRoot.getPath();
fBLOB1.mark(FILE1_SIZE);
fBLOB2.mark(FILE2_SIZE);
for (int i = 0; i < FILES_COUNT; i++)
{
try
{
Node resource = testRoot.addNode("blob" + i, "nt:file").addNode("jcr:content", "nt:unstructured"); // ,
// "nt:resource"
String path = "";
if (i % 10 == 0)
{
Value[] vals =
new Value[]{session.getValueFactory().createValue(fBLOB1),
session.getValueFactory().createValue(fBLOB2),};
path = resource.setProperty("jcr:data", vals).getPath();
}
else
{
path = resource.setProperty("jcr:data", fBLOB1).getPath();
}
resource.setProperty("jcr:mimeType", "application/x-octet-stream");
resource.setProperty("jcr:lastModified", Calendar.getInstance());
testRoot.save();
props.add(path.substring(rootPath.length() + 1));
}
catch (RepositoryException e)
{
log.warn("Can't create test case, " + e);
throw new Exception("Can't create test case, " + e, e);
}
finally
{
fBLOB1.reset();
fBLOB2.reset();
}
}
return props;
}
protected void deleteJCRAPICase() throws RepositoryException
{
for (NodeIterator iter = testRoot.getNodes(); iter.hasNext();)
{
iter.nextNode().remove();
}
testRoot.save();
}
protected List<QPathEntry[]> createInternalAPICase() throws Exception
{
DataManager dm =
((NodeImpl)testRoot).getSession().getTransientNodesManager().getTransactManager().getStorageDataManager();
List<QPathEntry[]> props = new ArrayList<QPathEntry[]>();
NodeData rootData = (NodeData)((NodeImpl)testRoot).getData();
fBLOB1.mark(FILE1_SIZE);
fBLOB2.mark(FILE2_SIZE);
for (int i = 0; i < FILES_COUNT; i++)
{
try
{
SessionChangesLog changes = new SessionChangesLog(((NodeImpl)testRoot).getSession());
TransientNodeData ntfile =
TransientNodeData.createNodeData(rootData, InternalQName.parse("[]blob" + i), Constants.NT_FILE);
changes.add(ItemState.createAddedState(ntfile));
TransientPropertyData ntfilePrimaryType =
TransientPropertyData.createPropertyData(ntfile, Constants.JCR_PRIMARYTYPE, PropertyType.NAME, false,
new TransientValueData(Constants.NT_FILE));
changes.add(ItemState.createAddedState(ntfilePrimaryType));
TransientNodeData res =
TransientNodeData.createNodeData(ntfile, Constants.JCR_CONTENT, Constants.NT_UNSTRUCTURED);
changes.add(ItemState.createAddedState(res));
TransientPropertyData resPrimaryType =
TransientPropertyData.createPropertyData(res, Constants.JCR_PRIMARYTYPE, PropertyType.NAME, false,
new TransientValueData(Constants.NT_UNSTRUCTURED));
changes.add(ItemState.createAddedState(resPrimaryType));
List<ValueData> data = new ArrayList<ValueData>();
if (i % 10 == 0)
{
data.add(new TransientValueData(fBLOB1, SpoolConfig.getDefaultSpoolConfig()));
data.add(new TransientValueData(fBLOB2, SpoolConfig.getDefaultSpoolConfig()));
}
else
{
data.add(new TransientValueData(fBLOB1, SpoolConfig.getDefaultSpoolConfig()));
}
TransientPropertyData resData =
TransientPropertyData.createPropertyData(res, Constants.JCR_DATA, PropertyType.BINARY, data.size() > 1,
data);
changes.add(ItemState.createAddedState(resData));
TransientPropertyData resMimeType =
TransientPropertyData.createPropertyData(res, Constants.JCR_MIMETYPE, PropertyType.STRING, false,
new TransientValueData("application/x-octet-stream"));
changes.add(ItemState.createAddedState(resMimeType));
TransientPropertyData resLastModified =
TransientPropertyData.createPropertyData(res, Constants.JCR_LASTMODIFIED, PropertyType.DATE, false,
new TransientValueData(Calendar.getInstance()));
changes.add(ItemState.createAddedState(resLastModified));
QPath path = resData.getQPath();
dm.save(new TransactionChangesLog(changes));
props.add(path.getRelPath(path.getEntries().length - rootData.getQPath().getEntries().length));
}
catch (RepositoryException e)
{
log.warn("Can't create test case, " + e);
throw new Exception("Can't create test case, " + e, e);
}
finally
{
fBLOB1.reset();
fBLOB2.reset();
}
}
return props;
}
protected void deleteInternalAPICase() throws RepositoryException
{
final DataManager dm =
((NodeImpl)testRoot).getSession().getTransientNodesManager().getTransactManager().getStorageDataManager();
final SessionChangesLog changes = new SessionChangesLog(((NodeImpl)testRoot).getSession());
class Remover
{
void delete(NodeData node) throws RepositoryException
{
for (NodeData nd : dm.getChildNodesData(node))
{
new Remover().delete(nd);
}
for (PropertyData pd : dm.getChildPropertiesData(node))
{
changes.add(ItemState.createDeletedState(pd));
}
changes.add(ItemState.createDeletedState(node));
}
}
NodeData rootData = (NodeData)((NodeImpl)testRoot).getData();
new Remover().delete(rootData);
dm.save(new TransactionChangesLog(changes));
}
// copied from SessionDataManager
protected ItemData getItemData(DataManager manager, NodeData parent, QPathEntry[] relPathEntries, ItemType itemType)
throws RepositoryException
{
ItemData item = parent;
for (int i = 0; i < relPathEntries.length; i++)
{
if (i == relPathEntries.length - 1)
{
item = manager.getItemData(parent, relPathEntries[i], itemType);
}
else
{
item = manager.getItemData(parent, relPathEntries[i], ItemType.UNKNOWN);
}
if (item == null)
{
break;
}
if (item.isNode())
{
parent = (NodeData)item;
}
else if (i < relPathEntries.length - 1)
{
throw new IllegalPathException("Path can not contains a property as the intermediate element");
}
}
return item;
}
public void testname() throws Exception
{
}
public void _testReadWriteJCRAPI() throws Exception
{
long time = System.currentTimeMillis();
List<String> props = createJCRAPICase();
log.info(getName() + " ADD -- " + (System.currentTimeMillis() - time));
time = System.currentTimeMillis();
// read randomize
Set<String> caseProps = new HashSet<String>(props);
for (String prop : caseProps)
{
try
{
InputStream stream = testRoot.getProperty(prop).getStream();
assertEquals("Value has wrong length", FILE1_SIZE, stream.available());
}
catch (ValueFormatException e)
{
Value[] vs = testRoot.getProperty(prop).getValues();
for (int i = 0; i < vs.length; i++)
{
assertEquals("Value has wrong length", i == 0 ? FILE1_SIZE : FILE2_SIZE, vs[i].getStream().available());
}
}
}
log.info(getName() + " READ -- " + (System.currentTimeMillis() - time));
time = System.currentTimeMillis();
deleteJCRAPICase();
log.info(getName() + " DELETE -- " + (System.currentTimeMillis() - time));
}
public void _testReadWriteInternalAPI() throws Exception
{
DataManager manager =
((NodeImpl)testRoot).getSession().getTransientNodesManager().getTransactManager().getStorageDataManager();
NodeData parent = (NodeData)((NodeImpl)testRoot).getData();
long time = System.currentTimeMillis();
List<QPathEntry[]> props = createInternalAPICase();
log.info(getName() + " ADD -- " + (System.currentTimeMillis() - time));
time = System.currentTimeMillis();
// read randomize
Set<QPathEntry[]> caseProps = new HashSet<QPathEntry[]>(props);
for (QPathEntry[] prop : caseProps)
{
PropertyData p = (PropertyData)getItemData(manager, parent, prop, ItemType.PROPERTY);
List<ValueData> vals = p.getValues();
for (int i = 0; i < vals.size(); i++)
{
assertEquals("Value has wrong length", i == 0 ? FILE1_SIZE : FILE2_SIZE, vals.get(i).getAsStream()
.available());
}
}
log.info(getName() + " READ -- " + (System.currentTimeMillis() - time));
// time = System.currentTimeMillis();
// deleteInternalAPICase();
// log.info(getName() + " DELETE -- " + (System.currentTimeMillis() - time));
}
}