/*
* Copyright (c) 2006-2014 Nuxeo SA (http://nuxeo.com/) and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Anahide Tchertchian
* Florent Guillaume
*/
package org.nuxeo.ecm.core.storage.mem;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.nuxeo.ecm.core.api.Blob;
import org.nuxeo.ecm.core.api.CoreSession;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.DocumentRef;
import org.nuxeo.ecm.core.api.IdRef;
import org.nuxeo.ecm.core.api.PathRef;
import org.nuxeo.ecm.core.api.impl.DocumentModelImpl;
import org.nuxeo.ecm.core.api.impl.blob.ByteArrayBlob;
import org.nuxeo.ecm.core.api.impl.blob.StringBlob;
import org.nuxeo.ecm.core.api.model.DeltaLong;
import org.nuxeo.ecm.core.api.model.DocumentPart;
import org.nuxeo.ecm.core.api.model.Property;
import org.nuxeo.ecm.core.api.model.PropertyNotFoundException;
import org.nuxeo.ecm.core.api.model.impl.primitives.ExternalBlobProperty;
import org.nuxeo.ecm.core.schema.SchemaManager;
import org.nuxeo.ecm.core.schema.types.ComplexTypeImpl;
import org.nuxeo.ecm.core.schema.types.Field;
import org.nuxeo.ecm.core.schema.types.ListType;
import org.nuxeo.ecm.core.schema.types.Type;
import org.nuxeo.runtime.api.Framework;
import org.nuxeo.runtime.reload.ReloadService;
@SuppressWarnings("unchecked")
public class TestMemRepositoryProperties extends MemRepositoryTestCase {
DocumentModel doc;
@Override
protected void initRepository() throws Exception {
deployContrib("org.nuxeo.ecm.core.storage.mem.tests",
"OSGI-INF/test-repo-types.xml");
deployContrib("org.nuxeo.ecm.core.storage.mem.tests",
"OSGI-INF/test-restriction-contrib.xml");
super.initRepository();
// deploy specific adapter for testing external blobs: files are stored
// in temporary directory
// deployContrib("org.nuxeo.ecm.core.storage.mem.tests",
// "OSGI-INF/test-externalblob-adapters-contrib.xml");
// // set container to temp directory here in case that depends on the
// OS
// // or machine configuration and add funny characters to avoid
// problems
// // due to xml parsing
// BlobHolderAdapterService service =
// Framework.getService(BlobHolderAdapterService.class);
// assertNotNull(service);
// ExternalBlobAdapter adapter =
// service.getExternalBlobAdapterForPrefix("fs");
// Map<String, String> props = new HashMap<String, String>();
// props.put(FileSystemExternalBlobAdapter.CONTAINER_PROPERTY_NAME, "\n"
// + System.getProperty("java.io.tmpdir") + " ");
// adapter.setProperties(props);
}
@Before
public void createDoc() {
doc = session.createDocumentModel("TestDocument");
doc.setPathInfo("/", "doc");
doc = session.createDocument(doc);
}
protected File createTempFile() throws Exception {
File file = File.createTempFile("testExternalBlob", ".txt");
FileWriter fstream = new FileWriter(file);
BufferedWriter out = new BufferedWriter(fstream);
out.write("Hello External Blob");
out.close();
Framework.trackFile(file, file);
return file;
}
@Test
public void testUnknownProperty() throws Exception {
try {
doc.getPropertyValue("nosuchprop");
fail("Should throw PropertyNotFoundException");
} catch (PropertyNotFoundException e) {
assertEquals("nosuchprop", e.getPath());
assertNull(e.getDetail());
}
try {
doc.getPropertyValue("tp:nosuchprop");
fail("Should throw PropertyNotFoundException");
} catch (PropertyNotFoundException e) {
assertEquals("tp:nosuchprop", e.getPath());
assertEquals("segment nosuchprop cannot be resolved", e.getDetail());
}
try {
doc.getPropertyValue("nosuchschema:nosuchprop");
fail("Should throw PropertyNotFoundException");
} catch (PropertyNotFoundException e) {
assertEquals("nosuchschema:nosuchprop", e.getPath());
assertEquals("No such schema", e.getDetail());
}
try {
doc.getPropertyValue("tp:complexChain/nosuchprop");
fail("Should throw PropertyNotFoundException");
} catch (PropertyNotFoundException e) {
assertEquals("tp:complexChain/nosuchprop", e.getPath());
assertEquals("segment nosuchprop cannot be resolved", e.getDetail());
}
try {
doc.getPropertyValue("tp:complexChain/complex/nosuchprop");
fail("Should throw PropertyNotFoundException");
} catch (PropertyNotFoundException e) {
assertEquals("tp:complexChain/complex/nosuchprop", e.getPath());
assertEquals("segment nosuchprop cannot be resolved", e.getDetail());
}
try {
doc.getPropertyValue("tp:complexList/notaninteger/foo");
fail("Should throw PropertyNotFoundException");
} catch (PropertyNotFoundException e) {
assertEquals("tp:complexList/notaninteger/foo", e.getPath());
assertEquals("segment notaninteger cannot be resolved",
e.getDetail());
}
try {
doc.getPropertyValue("tp:complexList/0/foo");
fail("Should throw PropertyNotFoundException");
} catch (PropertyNotFoundException e) {
assertEquals("tp:complexList/0/foo", e.getPath());
assertEquals("segment 0 cannot be resolved", e.getDetail());
}
}
// NXP-2467
@Test
public void testCreationWithDefaultPrefetch() throws Exception {
DocumentModel doc = session.createDocumentModel("TestDocumentWithDefaultPrefetch");
doc.setPathInfo("/", "docwithDefaultPrefetch");
session.createDocument(doc);
}
@Test
public void testStringArray() throws Exception {
assertNotNull(doc.getPropertyValue("tp:stringArray"));
String[] values = { "foo", "bar" };
doc.setPropertyValue("tp:stringArray", values);
doc = session.saveDocument(doc);
assertTrue(Arrays.equals(values,
(Object[]) doc.getPropertyValue("tp:stringArray")));
}
// NXP-2454
@Test
public void testDateArray() throws Exception {
assertNotNull(doc.getPropertyValue("tp:dateArray"));
Calendar cal = Calendar.getInstance();
cal.set(2008, 6, 10);
Calendar[] values = { cal };
doc.setPropertyValue("tp:dateArray", values);
doc = session.saveDocument(doc);
// currently returning long[] instead of Calendar[]
assertTrue(Arrays.equals(values,
(Object[]) doc.getPropertyValue("tp:dateArray")));
}
// NXP-2454
@Test
public void testIntArray() throws Exception {
assertNotNull(doc.getPropertyValue("tp:intArray"));
Long[] values = { 1L, 2L, 3L };
doc.setPropertyValue("tp:intArray", values);
doc = session.saveDocument(doc);
// currently returning long[], maybe this is the wanted behaviour (?)
assertTrue(Arrays.equals(values,
(Object[]) doc.getPropertyValue("tp:intArray")));
}
@Test
public void testComplexList() throws Exception {
// not null on list
assertTrue(doc.getPropertyValue("tp:complexList") instanceof List);
assertEquals(0, ((List) doc.getPropertyValue("tp:complexList")).size());
ArrayList<Map<String, Serializable>> values = new ArrayList<>();
Map<String, Serializable> item = new HashMap<>();
item.put("string", "foo");
Calendar cal = Calendar.getInstance();
cal.set(2008, 6, 10);
item.put("date", cal);
item.put("int", 3L);
values.add(item);
doc.setPropertyValue("tp:complexList", values);
doc = session.saveDocument(doc);
Serializable actual = doc.getPropertyValue("tp:complexList");
assertTrue(actual instanceof List);
assertEquals(1, ((List) actual).size());
assertEquals(item, ((List) actual).get(0));
}
@Test
public void testComplexListChange() throws Exception {
ArrayList<Map<String, Serializable>> values = new ArrayList<>();
Map<String, Serializable> item1 = new HashMap<>();
Map<String, Serializable> item2 = new HashMap<>();
Map<String, Serializable> item3 = new HashMap<>();
List<?> actual;
item1.put("string", "foo");
item1.put("int", Long.valueOf(123));
values.add(item1);
doc.setPropertyValue("tp:complexList", values);
doc = session.saveDocument(doc);
session.save();
closeSession();
openSession();
doc = session.getDocument(new PathRef("/doc"));
actual = (List<?>) doc.getPropertyValue("tp:complexList");
assertEquals(1, actual.size());
assertComplexListElements(actual, 0, "foo", 123);
// add to list
item2.put("string", "bar");
item2.put("int", Long.valueOf(999));
values.add(item2);
item3.put("string", "baz");
item3.put("int", Long.valueOf(314));
values.add(item3);
doc.setPropertyValue("tp:complexList", values);
doc = session.saveDocument(doc);
session.save();
closeSession();
openSession();
doc = session.getDocument(new PathRef("/doc"));
actual = (List<?>) doc.getPropertyValue("tp:complexList");
assertEquals(3, actual.size());
assertComplexListElements(actual, 0, "foo", 123);
assertComplexListElements(actual, 1, "bar", 999);
assertComplexListElements(actual, 2, "baz", 314);
// change list
item1.put("int", Long.valueOf(111));
item2.put("int", Long.valueOf(222));
item3.put("int", Long.valueOf(333));
doc.setPropertyValue("tp:complexList", values);
doc = session.saveDocument(doc);
session.save();
closeSession();
openSession();
doc = session.getDocument(new PathRef("/doc"));
actual = (List<?>) doc.getPropertyValue("tp:complexList");
assertEquals(3, actual.size());
assertComplexListElements(actual, 0, "foo", 111);
assertComplexListElements(actual, 1, "bar", 222);
assertComplexListElements(actual, 2, "baz", 333);
// remove from list
values.remove(0);
values.remove(0);
doc.setPropertyValue("tp:complexList", values);
doc = session.saveDocument(doc);
session.save();
closeSession();
openSession();
doc = session.getDocument(new PathRef("/doc"));
actual = (List<?>) doc.getPropertyValue("tp:complexList");
assertEquals(1, actual.size());
assertComplexListElements(actual, 0, "baz", 333);
}
protected static void assertComplexListElements(List<?> list, int i,
String string, int theint) {
Map<String, Serializable> map = (Map<String, Serializable>) list.get(i);
assertEquals(string, map.get("string"));
assertEquals(Long.valueOf(theint), map.get("int"));
}
// NXP-912
@Test
public void testNewBlob() throws Exception {
// simple
Object value = null;
SchemaManager tm = Framework.getService(SchemaManager.class);
Field field = tm.getField("tp:fileList");
Type type = field.getType();
Type itemType = ((ListType) type).getFieldType();
value = itemType.newInstance();
assertNull(value);
// complex
field = tm.getField("tp:fileComplexList");
type = field.getType();
itemType = ((ListType) type).getFieldType();
Map<String, Serializable> map = (Map) itemType.newInstance();
assertEquals(2, map.size());
assertTrue(map.containsKey("filename"));
assertTrue(map.containsKey("blob"));
assertNull(map.get("filename"));
assertNull(map.get("blob"));
}
// NXP-2468
@Test
public void testBlobListValue() throws Exception {
// not null on list
assertTrue(doc.getPropertyValue("tp:fileList") instanceof List);
assertEquals(0, ((List) doc.getPropertyValue("tp:fileList")).size());
ArrayList<Blob> values = new ArrayList<>();
StringBlob blob = new StringBlob("My content");
values.add(blob);
doc.setPropertyValue("tp:fileList", values);
doc = session.saveDocument(doc);
Serializable actual = doc.getPropertyValue("tp:fileList");
assertTrue(actual instanceof List);
List<Blob> blobs = (List) actual;
assertEquals(1, blobs.size());
assertNotNull(blobs.get(0));
}
// NXP-2301
@Test
public void testSubBlobValue() throws Exception {
// not null on list
assertTrue(doc.getPropertyValue("tp:fileComplexList") instanceof List);
assertEquals(0,
((List) doc.getPropertyValue("tp:fileComplexList")).size());
ArrayList<Map<String, Serializable>> values = new ArrayList<>();
Map<String, Serializable> item = new HashMap<>();
StringBlob blob = new StringBlob("My content");
item.put("blob", blob);
item.put("filename", "My filename");
values.add(item);
doc.setPropertyValue("tp:fileComplexList", values);
doc = session.saveDocument(doc);
Object actual = doc.getPropertyValue("tp:fileComplexList");
assertTrue(actual instanceof List);
List<Map<String, Serializable>> items = (List) actual;
assertEquals(1, items.size());
assertNotNull(items.get(0));
Map<String, Serializable> actualItem = items.get(0);
assertEquals("My filename", actualItem.get("filename"));
assertTrue(actualItem.get("blob") instanceof Blob);
Object actualBlob = doc.getProperty("tp:fileComplexList/0/blob").getValue(
Blob.class);
assertTrue(actualBlob instanceof Blob);
}
@Test
public void testComplexParallelFetch() throws Exception {
DocumentModel doc2 = session.createDocumentModel("/", "doc2",
"TestDocument2");
doc2.setPropertyValue("dc:title", "doc2");
doc2 = session.createDocument(doc2);
session.save();
// has not created the complex properties at that point
CoreSession s1 = openSessionAs("Administrator");
CoreSession s2 = openSessionAs("Administrator");
DocumentModel d1 = s1.getDocument(new IdRef(doc2.getId()));
DocumentModel d2 = s2.getDocument(new IdRef(doc2.getId()));
// read the complex prop to trigger documentpart fetch
// and node creation (SQLSession.makeProperties)
d1.getProperty("tp:complex");
d2.getProperty("tp:complex");
// write an unrelated property, to trigger flush()
d1.setPropertyValue("dc:title", "d1");
d2.setPropertyValue("dc:title", "d2");
s1.saveDocument(d1);
s2.saveDocument(d2);
s1.save();
// without the fix the following save would cause a second insert
s2.save();
closeSession(s1);
closeSession(s2);
}
@Test
public void testComplex2() throws Exception {
doc = session.createDocumentModel("/", "doc2", "TestDocument2");
doc = session.createDocument(doc);
session.save();
// has not created the complex properties at that point
// read the a complex propy to trigger documentpart fetch
// and node creation (SQLSession.makeProperties)
doc.getProperty("tp:complex");
// write an unrelated property, to trigger flush()
doc.setPropertyValue("dc:title", "doc2bis");
doc = session.saveDocument(doc);
session.save();
}
@Test
public void testComplexNotDirtyOnRead() throws Exception {
doc = session.createDocumentModel("/", "doc2", "TestDocument2");
doc = session.createDocument(doc);
session.save();
// reread doc
doc = session.getDocument(doc.getRef());
assertFalse(doc.isDirty());
// read a complex prop
Property prop = doc.getProperty("tp:complex");
// check that this does not mark the doc dirty
assertFalse(doc.isDirty());
// but changing the property does
prop.setValue(Collections.singletonMap("string", "abc"));
assertTrue(doc.isDirty());
}
@Test
public void testComplexNotDirtyOnVersionRead() throws Exception {
doc = session.createDocumentModel("/", "doc2", "TestDocument2");
doc = session.createDocument(doc);
DocumentRef verRef = doc.checkIn(null, null);
// reread doc
DocumentModel ver = session.getDocument(verRef);
assertFalse(ver.isDirty());
// read a complex prop
Property prop = ver.getProperty("tp:complex");
// check that this does not mark the doc dirty
assertFalse(ver.isDirty());
assertFalse(prop.isDirty());
// modify the version (allowed property)
ver.setPropertyValue("dc:issued", new Date());
// try to re-save the version
// works if the complex document part is not dirty
ver = session.saveDocument(ver);
}
@Test
public void testComplexPropertyChain() throws Exception {
Property p = doc.getProperty("tp:complexChain");
assertTrue(p.getValue() instanceof Map);
assertEquals(2, ((Map) p.getValue()).size());
p.setValue("string", "test");
Map<String, Serializable> map = new HashMap<>();
map.put("string", "test2");
p.setValue("complex", map);
doc = session.saveDocument(doc);
p = doc.getProperty("tp:complexChain");
assertTrue(p.getValue() instanceof Map);
assertEquals("test", p.getValue("string"));
assertEquals("test2", p.getValue("complex/string"));
p = p.get("complex");
assertTrue(p.getValue() instanceof Map);
assertEquals("test2", p.getValue("string"));
}
@Test
public void testComplexPropertySubValue() throws Exception {
doc.setPropertyValue("tp:complex/string", "test");
doc = session.saveDocument(doc);
assertEquals("test", doc.getPropertyValue("tp:complex/string"));
}
@Test
public void testComplexPropertySchemaUpdate() throws Exception {
deployBundle("org.nuxeo.runtime.reload");
// create a doc
doc.setPropertyValue("tp:complex/string", "test");
doc = session.saveDocument(doc);
session.save();
closeSession();
waitForAsyncCompletion();
// add complexschema to TestDocument
deployContrib("org.nuxeo.ecm.core.storage.mem.tests",
"OSGI-INF/test-schema-update.xml");
// reload repo with new doctype
Framework.getService(ReloadService.class).reloadRepository();
openSession();
doc = session.getDocument(new IdRef(doc.getId()));
// this property did not exist on document creation, after updating the
// doctype it should not fail
doc.getProperty("cmpf:attachedFile");
}
// NXP-2318: i don't get what's supposed to be answered to these questions
@Test
@Ignore
public void testArrayOrListProperties() throws Exception {
Property prop = doc.getProperty("tp:stringArray");
assertFalse(prop.isContainer());
assertFalse(prop.isList());
assertTrue(prop.isScalar());
prop = doc.getProperty("tp:dateArray");
assertFalse(prop.isContainer());
assertFalse(prop.isList());
assertTrue(prop.isScalar());
prop = doc.getProperty("tp:intArray");
assertFalse(prop.isContainer());
assertFalse(prop.isList());
assertTrue(prop.isScalar());
prop = doc.getProperty("tp:complex");
assertTrue(prop.isContainer());
assertFalse(prop.isList());
assertFalse(prop.isScalar());
prop = doc.getProperty("tp:complexList");
assertTrue(prop.isContainer());
assertTrue(prop.isList());
assertFalse(prop.isScalar());
}
// ignore external blob for MemRepository
@Ignore
@Test
public void testExternalBlobDocumentProperty() throws Exception {
File file = createTempFile();
HashMap<String, String> map = new HashMap<>();
String uri = String.format("fs:%s", file.getName());
map.put(ExternalBlobProperty.URI, uri);
map.put(ExternalBlobProperty.FILE_NAME, "hello.txt");
doc.setPropertyValue("tp:externalcontent", map);
doc = session.saveDocument(doc);
Object blob = doc.getPropertyValue("tp:externalcontent");
assertNotNull(blob);
assertTrue(blob instanceof Blob);
assertEquals("Hello External Blob", ((Blob) blob).getString());
assertEquals("hello.txt", ((Blob) blob).getFilename());
assertEquals("hello.txt",
doc.getPropertyValue("tp:externalcontent/name"));
assertEquals(uri, doc.getPropertyValue("tp:externalcontent/uri"));
}
// ignore external blob for MemRepository
@Ignore
// this time only set the uri
@Test
public void testExternalBlobDocumentProperty2() throws Exception {
File file = createTempFile();
String uri = String.format("fs:%s", file.getName());
doc.setPropertyValue("tp:externalcontent/uri", uri);
doc = session.saveDocument(doc);
Object blob = doc.getPropertyValue("tp:externalcontent");
assertNotNull(blob);
assertTrue(blob instanceof Blob);
assertEquals("Hello External Blob", ((Blob) blob).getString());
assertEquals(file.getName(), ((Blob) blob).getFilename());
assertNull(doc.getPropertyValue("tp:externalcontent/name"));
assertEquals(uri, doc.getPropertyValue("tp:externalcontent/uri"));
}
// ignore external blob for MemRepository
@Ignore
@Test
public void testExternalBlobListValue() throws Exception {
// not null on list
String propName = "tp:externalFileList";
assertTrue(doc.getPropertyValue(propName) instanceof List);
assertEquals(0, ((List) doc.getPropertyValue(propName)).size());
File file = createTempFile();
ArrayList<Map> values = new ArrayList<>();
Map<String, String> map = new HashMap<>();
String uri = String.format("fs:%s", file.getName());
map.put(ExternalBlobProperty.URI, uri);
map.put(ExternalBlobProperty.FILE_NAME, "hello.txt");
values.add(map);
doc.setPropertyValue(propName, values);
doc = session.saveDocument(doc);
Serializable actual = doc.getPropertyValue(propName);
assertTrue(actual instanceof List);
List<Blob> blobs = (List) actual;
assertEquals(1, blobs.size());
assertNotNull(blobs.get(0));
assertTrue(blobs.get(0) instanceof Blob);
Blob actualBlob = blobs.get(0);
assertEquals("Hello External Blob", actualBlob.getString());
assertEquals("hello.txt", actualBlob.getFilename());
assertEquals("hello.txt", doc.getPropertyValue(propName + "/0/name"));
assertEquals(uri, doc.getPropertyValue(propName + "/0/uri"));
}
// ignore external blob for MemRepository
@Ignore
@Test
public void testSubExternalBlobValue() throws Exception {
String propName = "tp:externalFileComplexList";
// not null on list
assertTrue(doc.getPropertyValue(propName) instanceof List);
assertEquals(0, ((List) doc.getPropertyValue(propName)).size());
ArrayList<Map<String, Serializable>> values = new ArrayList<>();
Map<String, Serializable> item = new HashMap<>();
File file = createTempFile();
HashMap<String, String> blobMap = new HashMap<>();
String uri = String.format("fs:%s", file.getName());
blobMap.put(ExternalBlobProperty.URI, uri);
blobMap.put(ExternalBlobProperty.FILE_NAME, "hello.txt");
item.put("blob", blobMap);
item.put("filename", "My filename");
values.add(item);
doc.setPropertyValue(propName, values);
doc = session.saveDocument(doc);
Object actual = doc.getPropertyValue(propName);
assertTrue(actual instanceof List);
List<Map<String, Serializable>> items = (List) actual;
assertEquals(1, items.size());
assertNotNull(items.get(0));
Map<String, Serializable> actualItem = items.get(0);
assertEquals("My filename", actualItem.get("filename"));
assertTrue(actualItem.get("blob") instanceof Blob);
Object actualBlob = doc.getProperty(propName + "/0/blob").getValue();
assertTrue(actualBlob instanceof Blob);
assertEquals("Hello External Blob", ((Blob) actualBlob).getString());
assertEquals("hello.txt", ((Blob) actualBlob).getFilename());
assertEquals("hello.txt",
doc.getPropertyValue(propName + "/0/blob/name"));
assertEquals(uri, doc.getPropertyValue(propName + "/0/blob/uri"));
}
@Test
public void testSaveComplexTwice() throws Exception {
testComplexList();
doc.setPropertyValue("tp:stringArray", new String[] {}); // dirty dp
doc = session.saveDocument(doc); // rewrites complex list again
session.save();
}
// not many tests, logs have to be looked at to confirm behavior
@Test
public void testUpdateMinimalChanges() throws Exception {
// populate some properties
testStringArray();
testDateArray();
testComplexList();
testBlobListValue();
session.save();
closeSession();
openSession();
// change just one of the collection properties
doc.setPropertyValue("tp:stringArray", new String[] { "baz" });
doc = session.saveDocument(doc);
session.save();
// check that the minimal number of updates are done in the db
}
// toplevel complex list
@Test
public void testXPath1() throws Exception {
DocumentModel doc = new DocumentModelImpl("/", "doc", "File");
List<Object> files = new ArrayList<>(2);
Map<String, Object> f = new HashMap<>();
f.put("filename", "f1");
files.add(f);
doc.setProperty("files", "files", files);
assertEquals("f1", doc.getPropertyValue("files/0/filename"));
assertEquals("f1", doc.getPropertyValue("files/item[0]/filename"));
}
// other complex list
@Test
public void testXPath2() throws Exception {
DocumentModel doc = new DocumentModelImpl("/", "doc", "ComplexDoc");
HashMap<String, Object> attachedFile = new HashMap<>();
List<Map<String, Object>> vignettes = new ArrayList<>();
attachedFile.put("vignettes", vignettes);
Map<String, Object> vignette = new HashMap<>();
Long width = Long.valueOf(123);
vignette.put("width", width);
vignettes.add(vignette);
doc.setPropertyValue("cmpf:attachedFile", attachedFile);
assertEquals(width,
doc.getPropertyValue("cmpf:attachedFile/vignettes/0/width"));
assertEquals(
width,
doc.getPropertyValue("cmpf:attachedFile/vignettes/vignette[0]/width"));
}
private static String canonXPath(String xpath) {
return ComplexTypeImpl.canonicalXPath(xpath);
}
@Test
public void testCanonicalizeXPath() throws Exception {
assertEquals("foo", canonXPath("foo"));
assertEquals("foo", canonXPath("/foo"));
assertEquals("foo", canonXPath("//foo"));
assertEquals("foo/bar", canonXPath("foo/bar"));
assertEquals("foo/bar", canonXPath("/foo/bar"));
assertEquals("foo/bar/baz", canonXPath("foo/bar/baz"));
assertEquals("foo/0/bar", canonXPath("foo/0/bar"));
assertEquals("foo/bar/0", canonXPath("foo/bar/0"));
assertEquals("foo/0/bar", canonXPath("foo/gee[0]/bar"));
assertEquals("foo/*/bar", canonXPath("foo/gee[*]/bar"));
assertEquals("foo/bar/0", canonXPath("foo/bar/gee[0]"));
assertEquals("foo/0/bar/123/moo",
canonXPath("foo/gee[0]/bar/baz[123]/moo"));
assertEquals("foo/0/bar/*/moo", canonXPath("foo/gee[0]/bar/baz[*]/moo"));
}
@Test
public void testPrefetchDefault() throws Exception {
doc = session.createDocument(session.createDocumentModel("/", "doc2",
"TestDocumentWithDefaultPrefetch"));
assertTrue(doc.isPrefetched("dc:title"));
assertTrue(doc.isPrefetched("dc:description"));
assertTrue(doc.isPrefetched("dc:created"));
assertTrue(doc.isPrefetched("dc:modified"));
assertTrue(doc.isPrefetched("dc:creator"));
assertTrue(doc.isPrefetched("dc:lastContributor"));
assertTrue(doc.isPrefetched("icon"));
assertTrue(doc.isPrefetched("dublincore", "title"));
assertTrue(doc.isPrefetched("common", "icon"));
assertFalse(doc.isPrefetched("dc:contributors"));
assertNull(doc.getPropertyValue("dc:title"));
assertNull(doc.getProperty("dublincore", "title"));
doc.setPropertyValue("dc:title", "foo");
assertFalse(doc.isPrefetched("dc:title"));
assertFalse(doc.isPrefetched("dublincore", "title"));
assertEquals("foo", doc.getPropertyValue("dc:title"));
assertEquals("foo", doc.getProperty("dublincore", "title"));
// set using schema + name
Calendar cal = Calendar.getInstance();
doc.setProperty("dublincore", "modified", cal);
assertFalse(doc.isPrefetched("dc:modified"));
assertFalse(doc.isPrefetched("dublincore", "modified"));
assertEquals(cal, doc.getPropertyValue("dc:modified"));
assertEquals(cal, doc.getProperty("dublincore", "modified"));
// with no schema prefix
doc.setPropertyValue("icon", "myicon");
assertFalse(doc.isPrefetched("icon"));
assertFalse(doc.isPrefetched("common", "icon"));
assertEquals("myicon", doc.getPropertyValue("icon"));
assertEquals("myicon", doc.getProperty("common", "icon"));
}
@Test
public void testPrefetchComplexProperty() throws Exception {
doc = session.createDocumentModel("/", "doc2", "MyDocType");
doc.setPropertyValue("book:author/pJob", "somejob");
doc.setPropertyValue("dc:subjects", new String[] { "bar" });
StringBlob blob = new StringBlob("foo");
blob.setFilename("fooname");
LinkedList<Object> blobs = new LinkedList<>();
blobs.add(blob);
doc.setPropertyValue("attachments", blobs);
doc = session.createDocument(doc);
doc = session.getDocument(doc.getRef());
assertTrue(doc.isPrefetched("dc:title"));
assertTrue(doc.isPrefetched("dc:subjects"));
// assertTrue(doc.isPrefetched("attachments/0/name"));
assertTrue(doc.isPrefetched("book:author/pJob"));
// assertEquals("fooname", doc.getPropertyValue("attachments/0/name"));
assertEquals("somejob", doc.getPropertyValue("book:author/pJob"));
Serializable subjects = doc.getPropertyValue("dc:subjects");
assertNotNull(subjects);
assertTrue(subjects.getClass().getName(), subjects instanceof String[]);
String[] array = (String[]) subjects;
assertEquals(Arrays.asList("bar"), Arrays.asList(array));
// array mutability
array[0] = "moo";
// different mutable array returned each time
subjects = doc.getPropertyValue("dc:subjects");
array = (String[]) subjects;
assertEquals(Arrays.asList("bar"), Arrays.asList(array));
// set another prop in same schema
doc.setPropertyValue("book:author/pAge", null);
// not prefetched anymore as schema was loaded
assertFalse(doc.isPrefetched("book:author/pJob"));
}
@Test
public void testRestriction() throws Exception {
doc = session.createDocumentModel("/", "doc2", "Restriction");
doc.setPropertyValue("restr:shortstring", "foo");
doc = session.createDocument(doc);
doc = session.getDocument(doc.getRef());
String value = doc.getProperty("restr:shortstring").getValue(
String.class);
assertEquals("foo", value);
}
@Test
public void testPropertyIsSameAsBlob() throws Exception {
doc = session.createDocumentModel("/", "file", "File");
doc = session.createDocument(doc);
DocumentPart part = doc.getPart("file");
assertTrue(part.isSameAs(part));
DocumentModel doc2 = session.createDocumentModel("/", "file2", "File");
Blob blob2 = new ByteArrayBlob("hello world!".getBytes(), "text/plain",
"UTF-8");
doc2.setPropertyValue("file:content", (Serializable) blob2);
doc2 = session.createDocument(doc2);
DocumentPart part2 = doc2.getPart("file");
assertTrue(part2.isSameAs(part2));
assertFalse(part2.isSameAs(part));
assertFalse(part.isSameAs(part2));
// same blob content, should compare equal
DocumentModel doc3 = session.createDocumentModel("/", "file3", "File");
Blob blob3 = new StringBlob("hello world!", "text/plain");
doc3.setPropertyValue("file:content", (Serializable) blob3);
doc3 = session.createDocument(doc3);
DocumentPart part3 = doc3.getPart("file");
assertTrue(part2.isSameAs(part3));
// different blob content
DocumentModel doc4 = session.createDocumentModel("/", "file3", "File");
Blob blob4 = new StringBlob("this is goodbye", "text/plain");
doc4.setPropertyValue("file:content", (Serializable) blob4);
doc4 = session.createDocument(doc4);
DocumentPart part4 = doc4.getPart("file");
assertFalse(part2.isSameAs(part4));
// compare directly two StorageBlobs
// same
assertEquals(doc2.getPropertyValue("file:content"),
doc3.getPropertyValue("file:content"));
// different
assertFalse(doc2.getPropertyValue("file:content").equals(
doc4.getPropertyValue("file:content")));
// compare a ByteArrayBlob and a StorageBlob
assertEquals(blob2, doc3.getPropertyValue("file:content"));
assertEquals(doc3.getPropertyValue("file:content"), blob2);
// compare a StringBlob and a StorageBlob
assertEquals(blob3, doc3.getPropertyValue("file:content"));
assertEquals(doc3.getPropertyValue("file:content"), blob3);
// compare a ByteArrayBlob and a StringBlob
assertEquals(blob2, blob3);
assertEquals(blob3, blob2);
// compare a StringBlob with a different StringBlob or ByteArrayBlob
assertFalse(blob2.equals(blob4));
assertFalse(blob4.equals(blob2));
assertFalse(blob3.equals(blob4));
assertFalse(blob4.equals(blob3));
}
@Test
public void testPropertyDelta() throws Exception {
int base = 100;
int fakebase = 1000;
int delta = 123;
doc = session.createDocumentModel("/", "doc", "MyDocType");
doc.setPropertyValue("my:integer", Long.valueOf(base));
doc = session.createDocument(doc);
session.save();
doc.setPropertyValue("my:integer", new DeltaLong(fakebase, delta));
// re-reading the property before saveDocument() returns the Delta
Serializable value = doc.getPropertyValue("my:integer");
assertTrue(value.getClass().getName(), value instanceof DeltaLong);
assertEquals(fakebase + delta, ((DeltaLong) value).longValue());
doc = session.saveDocument(doc);
// write another property in the same schema
// to make sure the delta is not applied twice
doc.setPropertyValue("my:string", "foo");
doc = session.saveDocument(doc);
// after saveDocument() we now read a Long
value = doc.getPropertyValue("my:integer");
assertTrue(value.getClass().getName(), value instanceof Long);
assertEquals(fakebase + delta, ((Long) value).longValue());
session.save();
// after save() but before refetch still a Long
value = doc.getPropertyValue("my:integer");
assertTrue(value.getClass().getName(), value instanceof Long);
assertEquals(fakebase + delta, ((Long) value).longValue());
// DBS: save() purged the transient space so doc state was reread
// DBS: with the incremented integer, and this saveDocument
// DBS: will overwrite it with the full value based on the fake base
// // write another property in the same schema
// // to make sure the delta is not applied twice
// doc.setPropertyValue("my:string", "bar");
// doc = session.saveDocument(doc);
// session.save();
closeSession();
openSession();
// after refetch it's a Long with the correct incremented value
doc = session.getDocument(new IdRef(doc.getId()));
value = doc.getPropertyValue("my:integer");
assertTrue(value.getClass().getName(), value instanceof Long);
assertEquals(base + delta, ((Long) value).longValue());
}
@Test
public void testPropertyDeltaTwice() throws Exception {
int base = 100;
int fakebase = 1000;
int delta = 123;
doc = session.createDocumentModel("/", "doc", "MyDocType");
doc.setPropertyValue("my:integer", Long.valueOf(base));
doc = session.createDocument(doc);
session.save();
doc.setPropertyValue("my:integer", new DeltaLong(fakebase, delta));
doc = session.saveDocument(doc);
doc.setPropertyValue("my:integer", new DeltaLong(fakebase, delta));
doc = session.saveDocument(doc);
session.save();
closeSession();
openSession();
// after refetch it's a Long with the correct incremented value
doc = session.getDocument(new IdRef(doc.getId()));
Serializable value = doc.getPropertyValue("my:integer");
assertTrue(value.getClass().getName(), value instanceof Long);
assertEquals(base + delta * 2, ((Long) value).longValue());
}
/**
* Checks that writing several documents using batching with some of them
* having Delta and some not doesn't fail.
*/
@Test
public void testPropertyDeltaBatching() throws Exception {
int n = 10;
int base = 100;
int fakebase = 1000;
for (int i = 0; i < n; i++) {
DocumentModel doc = session.createDocumentModel("/", "doc" + i,
"MyDocType");
doc.setPropertyValue("my:integer", Long.valueOf(base));
doc = session.createDocument(doc);
}
session.save();
// updates
for (int i = 0; i < n; i++) {
DocumentModel doc = session.getDocument(new PathRef("/doc" + i));
Serializable value;
if (i < n / 2) {
value = Long.valueOf(i);
} else {
// delta whose base is not the actual base, to check
// that we really do an increment instead of setting
// the full value
value = new DeltaLong(fakebase, i);
}
doc.setPropertyValue("my:integer", value);
if (i % 2 == 0) {
// also sometimes change another property
doc.setPropertyValue("my:string", "foo" + i);
}
doc = session.saveDocument(doc);
}
session.save();
// check result after re-reading from database
closeSession();
openSession();
for (int i = 0; i < n; i++) {
DocumentModel doc = session.getDocument(new PathRef("/doc" + i));
Serializable value = doc.getPropertyValue("my:integer");
Serializable expected;
if (i < n / 2) {
expected = Long.valueOf(i);
} else {
expected = Long.valueOf(base + i);
}
assertEquals(doc.getName(), expected, value);
}
}
/**
* Checks that even on document creation using a Delta doesn't fail.
*/
@Test
public void testPropertyDeltaOnCreate() throws Exception {
doc = session.createDocumentModel("/", "doc", "MyDocType");
doc.setPropertyValue("my:integer", new DeltaLong(100, 123));
doc = session.createDocument(doc);
session.save();
Serializable value = doc.getPropertyValue("my:integer");
assertTrue(value.getClass().getName(), value instanceof Long);
assertEquals(223, ((Long) value).longValue());
}
}