/*
* (C) Copyright 2006-2014 Nuxeo SA (http://nuxeo.com/) and others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Contributors:
* Florent Guillaume
*/
package org.nuxeo.ecm.core.opencmis.impl;
import static org.apache.chemistry.opencmis.commons.BasicPermissions.ALL;
import static org.apache.chemistry.opencmis.commons.BasicPermissions.READ;
import static org.apache.chemistry.opencmis.commons.BasicPermissions.WRITE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Serializable;
import java.math.BigInteger;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import javax.servlet.http.HttpServletResponse;
import org.apache.chemistry.opencmis.client.api.CmisObject;
import org.apache.chemistry.opencmis.client.api.Document;
import org.apache.chemistry.opencmis.client.api.Folder;
import org.apache.chemistry.opencmis.client.api.ItemIterable;
import org.apache.chemistry.opencmis.client.api.ObjectId;
import org.apache.chemistry.opencmis.client.api.OperationContext;
import org.apache.chemistry.opencmis.client.api.Policy;
import org.apache.chemistry.opencmis.client.api.Property;
import org.apache.chemistry.opencmis.client.api.QueryResult;
import org.apache.chemistry.opencmis.client.api.Relationship;
import org.apache.chemistry.opencmis.client.api.Rendition;
import org.apache.chemistry.opencmis.client.api.Session;
import org.apache.chemistry.opencmis.client.api.SessionFactory;
import org.apache.chemistry.opencmis.client.bindings.CmisBindingFactory;
import org.apache.chemistry.opencmis.client.runtime.SessionFactoryImpl;
import org.apache.chemistry.opencmis.commons.PropertyIds;
import org.apache.chemistry.opencmis.commons.SessionParameter;
import org.apache.chemistry.opencmis.commons.data.Ace;
import org.apache.chemistry.opencmis.commons.data.Acl;
import org.apache.chemistry.opencmis.commons.data.AllowableActions;
import org.apache.chemistry.opencmis.commons.data.ContentStream;
import org.apache.chemistry.opencmis.commons.data.Principal;
import org.apache.chemistry.opencmis.commons.data.RenditionData;
import org.apache.chemistry.opencmis.commons.data.RepositoryInfo;
import org.apache.chemistry.opencmis.commons.enums.Action;
import org.apache.chemistry.opencmis.commons.enums.BindingType;
import org.apache.chemistry.opencmis.commons.enums.DateTimeFormat;
import org.apache.chemistry.opencmis.commons.enums.RelationshipDirection;
import org.apache.chemistry.opencmis.commons.enums.VersioningState;
import org.apache.chemistry.opencmis.commons.exceptions.CmisConstraintException;
import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException;
import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException;
import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
import org.apache.chemistry.opencmis.commons.impl.Base64;
import org.apache.chemistry.opencmis.commons.impl.dataobjects.AccessControlEntryImpl;
import org.apache.chemistry.opencmis.commons.impl.dataobjects.AccessControlPrincipalDataImpl;
import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl;
import org.apache.commons.httpclient.util.DateUtil;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.nuxeo.ecm.core.api.CoreSession;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.IdRef;
import org.nuxeo.ecm.core.api.Lock;
import org.nuxeo.ecm.core.api.PathRef;
import org.nuxeo.ecm.core.api.RecoverableClientException;
import org.nuxeo.ecm.core.api.security.ACE;
import org.nuxeo.ecm.core.api.security.ACL;
import org.nuxeo.ecm.core.api.security.ACP;
import org.nuxeo.ecm.core.api.security.SecurityConstants;
import org.nuxeo.ecm.core.api.security.impl.ACLImpl;
import org.nuxeo.ecm.core.api.security.impl.ACPImpl;
import org.nuxeo.ecm.core.event.EventService;
import org.nuxeo.ecm.core.opencmis.impl.client.NuxeoSession;
import org.nuxeo.ecm.core.opencmis.impl.server.NuxeoContentStream;
import org.nuxeo.ecm.core.opencmis.tests.Helper;
import org.nuxeo.ecm.core.opencmis.tests.StatusLoggingDefaultHttpInvoker;
import org.nuxeo.ecm.core.test.CoreFeature;
import org.nuxeo.ecm.core.test.TransactionalFeature;
import org.nuxeo.ecm.core.test.annotations.Granularity;
import org.nuxeo.ecm.core.test.annotations.RepositoryConfig;
import org.nuxeo.runtime.api.Framework;
import org.nuxeo.runtime.test.runner.Deploy;
import org.nuxeo.runtime.test.runner.Features;
import org.nuxeo.runtime.test.runner.FeaturesRunner;
import org.nuxeo.runtime.test.runner.LocalDeploy;
import org.nuxeo.runtime.test.runner.RuntimeHarness;
import org.nuxeo.runtime.transaction.TransactionHelper;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* Test the high-level session using a local connection.
*/
@RunWith(FeaturesRunner.class)
@Features(CmisFeature.class)
@Deploy({ "org.nuxeo.ecm.webengine.core" })
@LocalDeploy({ //
"org.nuxeo.ecm.core.opencmis.tests.tests:OSGI-INF/types-contrib.xml", //
"org.nuxeo.ecm.core.opencmis.tests.tests:OSGI-INF/throw-exception-listener.xml", //
})
@RepositoryConfig(cleanup = Granularity.METHOD)
public class CmisSuiteSession {
private static final Log log = LogFactory.getLog(CmisSuiteSession.class);
public static final String BASE_RESOURCE = "jetty-test";
public static final String NUXEO_ROOT_TYPE = "Root"; // from Nuxeo
public static final String NUXEO_ROOT_NAME = ""; // NuxeoPropertyDataName;
public static final String USERNAME = "Administrator";
public static final String PASSWORD = "test";
// stream content with non-ASCII characters
public static final String STREAM_CONTENT = "Caf\u00e9 Diem\none\0two";
public static final String NOT_NULL = "CONSTRAINT_NOT_NULL";
@Inject
protected RuntimeHarness harness;
@Inject
protected CoreFeature coreFeature;
@Inject
protected CmisFeatureSession cmisFeatureSession;
@Inject
protected CoreSession coreSession;
@Inject
protected EventService eventService;
@Inject
protected Session session;
protected String rootFolderId;
protected boolean isHttp;
protected boolean isAtomPub;
protected boolean isBrowser;
protected Map<String, String> repoDetails;
@Before
public void setUp() throws Exception {
setUpData();
session.clear(); // clear cache
RepositoryInfo rid = session.getBinding().getRepositoryService().getRepositoryInfo(
coreSession.getRepositoryName(), null);
assertNotNull(rid);
rootFolderId = rid.getRootFolderId();
assertNotNull(rootFolderId);
isHttp = cmisFeatureSession.isHttp;
isAtomPub = cmisFeatureSession.isAtomPub;
isBrowser = cmisFeatureSession.isBrowser;
}
protected void setUpData() throws Exception {
repoDetails = Helper.makeNuxeoRepository(coreSession);
txFeature.nextTransaction();
coreFeature.getStorageConfiguration().sleepForFulltext();
}
@Inject
TransactionalFeature txFeature;
protected void waitForAsyncCompletion() {
txFeature.nextTransaction();
}
@Test
public void testRoot() {
Folder root = session.getRootFolder();
assertNotNull(root);
assertNotNull(root.getId());
assertNotNull(root.getType());
assertEquals(NUXEO_ROOT_TYPE, root.getType().getId());
assertEquals(rootFolderId, root.getPropertyValue(PropertyIds.OBJECT_ID));
assertEquals(NUXEO_ROOT_TYPE, root.getPropertyValue(PropertyIds.OBJECT_TYPE_ID));
assertEquals(NUXEO_ROOT_NAME, root.getName());
List<Property<?>> props = root.getProperties();
assertNotNull(props);
assertTrue(props.size() > 0);
assertEquals("/", root.getPath());
assertEquals(Collections.singletonList("/"), root.getPaths());
assertNull(root.getFolderParent());
assertEquals(Collections.emptyList(), root.getParents());
}
@Test
public void testDefaultProperties() throws Exception {
Folder root = session.getRootFolder();
CmisObject child = root.getChildren().iterator().next();
assertNotNull(child.getProperty("dc:coverage"));
assertNull(child.getPropertyValue("dc:coverage"));
Document doc = (Document) session.getObjectByPath("/testfolder1/testfile1");
List<String> subjects = doc.getPropertyValue("dc:subjects");
assertEquals(Arrays.asList("foo", "gee/moo"), subjects);
}
@Test
public void testPath() throws Exception {
Folder folder = (Folder) session.getObjectByPath("/testfolder1");
assertEquals("/testfolder1", folder.getPath());
assertEquals(Collections.singletonList("/testfolder1"), folder.getPaths());
Document doc = (Document) session.getObjectByPath("/testfolder1/testfile1");
assertEquals(Collections.singletonList("/testfolder1/testfile1"), doc.getPaths());
}
@Test
public void testParent() throws Exception {
Folder folder = (Folder) session.getObjectByPath("/testfolder1");
assertEquals(rootFolderId, folder.getFolderParent().getId());
List<Folder> parents = folder.getParents();
assertEquals(1, parents.size());
assertEquals(rootFolderId, parents.get(0).getId());
Document doc = (Document) session.getObjectByPath("/testfolder1/testfile1");
parents = doc.getParents();
assertEquals(1, parents.size());
assertEquals(folder.getId(), parents.get(0).getId());
}
@Test
public void testCreateObject() {
Folder root = session.getRootFolder();
ContentStream contentStream = null;
VersioningState versioningState = null;
List<Policy> policies = null;
List<Ace> addAces = null;
List<Ace> removeAces = null;
OperationContext context = NuxeoSession.DEFAULT_CONTEXT;
Map<String, Serializable> properties = new HashMap<String, Serializable>();
properties.put(PropertyIds.OBJECT_TYPE_ID, "Note");
properties.put(PropertyIds.NAME, "mynote");
properties.put("note", "bla bla");
Document doc = root.createDocument(properties, contentStream, versioningState, policies, addAces, removeAces,
context);
assertNotNull(doc.getId());
assertEquals("mynote", doc.getName());
assertEquals("mynote", doc.getPropertyValue("dc:title"));
assertEquals("bla bla", doc.getPropertyValue("note"));
// list children
ItemIterable<CmisObject> children = root.getChildren();
assertEquals(3, children.getTotalNumItems());
CmisObject note = null;
for (CmisObject child : children) {
if (child.getName().equals("mynote")) {
note = child;
}
}
assertNotNull("Missing child", note);
assertEquals("Note", note.getType().getId());
assertEquals("bla bla", note.getPropertyValue("note"));
}
@Test
public void testCreateDocumentWithContentStream() throws Exception {
Folder root = session.getRootFolder();
ContentStream cs = new ContentStreamImpl("myfile", "text/plain", Helper.FILE1_CONTENT);
OperationContext context = NuxeoSession.DEFAULT_CONTEXT;
VersioningState versioningState = null;
List<Policy> policies = null;
List<Ace> addAces = null;
List<Ace> removeAces = null;
Map<String, Serializable> properties = new HashMap<String, Serializable>();
properties.put(PropertyIds.OBJECT_TYPE_ID, "File");
properties.put(PropertyIds.NAME, "myfile");
Document doc = root.createDocument(properties, cs, versioningState, policies, addAces, removeAces, context);
cs = doc.getContentStream();
assertNotNull(cs);
assertEquals("text/plain", cs.getMimeType());
assertEquals("myfile", cs.getFileName());
if (!(isAtomPub || isBrowser)) {
assertEquals(Helper.FILE1_CONTENT.length(), cs.getLength());
}
assertEquals(Helper.FILE1_CONTENT, Helper.read(cs.getStream(), "UTF-8"));
}
@Test
public void testCreateDocumentThenSetContentStream() throws Exception {
Folder root = session.getRootFolder();
OperationContext context = NuxeoSession.DEFAULT_CONTEXT;
VersioningState versioningState = null;
List<Policy> policies = null;
List<Ace> addAces = null;
List<Ace> removeAces = null;
Map<String, Serializable> properties = new HashMap<String, Serializable>();
properties.put(PropertyIds.OBJECT_TYPE_ID, "File");
properties.put(PropertyIds.NAME, "myfile");
Document doc = root.createDocument(properties, null, versioningState, policies, addAces, removeAces, context);
ContentStream cs = new ContentStreamImpl("myfile", "text/plain", Helper.FILE1_CONTENT);
doc.setContentStream(cs, true);
cs = doc.getContentStream();
assertNotNull(cs);
assertEquals("text/plain", cs.getMimeType());
assertEquals("myfile", cs.getFileName());
if (!(isAtomPub || isBrowser)) {
assertEquals(Helper.FILE1_CONTENT.length(), cs.getLength());
}
assertEquals(Helper.FILE1_CONTENT, Helper.read(cs.getStream(), "UTF-8"));
}
@Test
public void testCreateDocumentWithoutName() throws Exception {
Map<String, Serializable> properties = new HashMap<>();
properties.put(PropertyIds.OBJECT_TYPE_ID, "File");
try {
session.getRootFolder().createDocument(properties, null, null, null, null, null,
NuxeoSession.DEFAULT_CONTEXT);
fail("Creation without cmis:name should fail");
} catch (CmisConstraintException e) {
// ok
}
}
@Test
public void testCreateRelationship() throws Exception {
if (!(isAtomPub || isBrowser)) {
// createRelationship admin user only empowered for AtomPub &
// Browser tests
return;
}
String id1 = session.getObjectByPath("/testfolder1/testfile1").getId();
String id2 = session.getObjectByPath("/testfolder1/testfile2").getId();
Map<String, Serializable> properties = new HashMap<String, Serializable>();
properties.put(PropertyIds.OBJECT_TYPE_ID, "Relation");
properties.put(PropertyIds.NAME, "rel");
properties.put(PropertyIds.SOURCE_ID, id1);
properties.put(PropertyIds.TARGET_ID, id2);
ObjectId relid = session.createRelationship(properties);
ItemIterable<Relationship> rels = session.getRelationships(session.createObjectId(id1), false,
RelationshipDirection.SOURCE, null, session.createOperationContext());
assertEquals(1, rels.getTotalNumItems());
for (Relationship r : rels) {
assertEquals(relid.getId(), r.getId());
}
Relationship rel = (Relationship) session.getObject(relid);
assertNotNull(rel);
assertEquals(id1, rel.getSourceId().getId());
assertEquals(id2, rel.getTargetId().getId());
}
// HiddenRelation, like the standard DefaultRelation, is marked HiddenInNavigation
@Test
public void testCreateHiddenRelation() throws Exception {
if (!(isAtomPub || isBrowser)) {
// createRelationship admin user only empowered for AtomPub &
// Browser tests
return;
}
String id1 = session.getObjectByPath("/testfolder1/testfile1").getId();
String id2 = session.getObjectByPath("/testfolder1/testfile2").getId();
Map<String, Serializable> properties = new HashMap<String, Serializable>();
properties.put(PropertyIds.OBJECT_TYPE_ID, "HiddenRelation");
properties.put(PropertyIds.NAME, "rel");
properties.put(PropertyIds.SOURCE_ID, id1);
properties.put(PropertyIds.TARGET_ID, id2);
ObjectId relid = session.createRelationship(properties);
ItemIterable<Relationship> rels = session.getRelationships(session.createObjectId(id1), false,
RelationshipDirection.SOURCE, null, session.createOperationContext());
assertEquals(1, rels.getTotalNumItems());
for (Relationship r : rels) {
assertEquals(relid.getId(), r.getId());
}
Relationship rel = (Relationship) session.getObject(relid);
assertNotNull(rel);
assertEquals(id1, rel.getSourceId().getId());
assertEquals(id2, rel.getTargetId().getId());
}
@Test
public void testUpdate() throws Exception {
Document doc;
doc = (Document) session.getObjectByPath("/testfolder1/testfile1");
Map<String, Object> map = new HashMap<String, Object>();
map.put("dc:title", "new title");
map.put("dc:subjects", Arrays.asList("a", "b", "c"));
doc.updateProperties(map);
doc = (Document) session.getObjectByPath("/testfolder1/testfile1");
assertEquals("new title", doc.getPropertyValue("dc:title"));
assertEquals(Arrays.asList("a", "b", "c"), doc.getPropertyValue("dc:subjects"));
// TODO test transient object API
map.clear();
map.put("dc:title", "other title");
map.put("dc:subjects", Arrays.asList("foo"));
doc.updateProperties(map);
doc.refresh(); // reload
assertEquals("other title", doc.getPropertyValue("dc:title"));
assertEquals(Arrays.asList("foo"), doc.getPropertyValue("dc:subjects"));
}
@Test
public void testUpdateDescription() throws Exception {
Document doc;
doc = (Document) session.getObjectByPath("/testfolder1/testfile1");
doc.updateProperties(Collections.singletonMap("cmis:description", "desc1"));
doc = (Document) session.getObjectByPath("/testfolder1/testfile1");
assertEquals("desc1", doc.getPropertyValue("cmis:description"));
assertEquals("desc1", doc.getPropertyValue("dc:description"));
doc.updateProperties(Collections.singletonMap("dc:description", "desc2"));
doc = (Document) session.getObjectByPath("/testfolder1/testfile1");
assertEquals("desc2", doc.getPropertyValue("cmis:description"));
assertEquals("desc2", doc.getPropertyValue("dc:description"));
}
@Test
public void testPropertyFromSecondaryType() throws Exception {
DocumentModel doc = coreSession.getDocument(new PathRef("/testfolder1/testfile1"));
doc.addFacet("CustomFacetWithMySchema2");
doc.setPropertyValue("my2:string", "foo");
coreSession.saveDocument(doc);
coreSession.save();
TransactionHelper.commitOrRollbackTransaction();
TransactionHelper.startTransaction();
Document file = (Document) session.getObjectByPath("/testfolder1/testfile1");
Property<?> p = file.getProperty("cmis:secondaryObjectTypeIds");
assertNotNull(p);
@SuppressWarnings("unchecked")
List<String> stl = (List<String>) p.getValues();
assertNotNull(stl);
assertTrue(stl.contains("facet:CustomFacetWithMySchema2"));
assertEquals("foo", file.getPropertyValue("my2:string"));
// change secondary prop
file.updateProperties(Collections.singletonMap("my2:string", "bar"), true); // refresh
// check updated
assertEquals("bar", file.getPropertyValue("my2:string"));
}
@Test
public void testContentStream() throws Exception {
Document file = (Document) session.getObjectByPath("/testfolder1/testfile1");
// check Cache Response Headers (eTag and Last-Modified)
if (isAtomPub || isBrowser) {
RepositoryInfo ri = session.getRepositoryInfo();
String uri = ri.getThinClientUri() + ri.getId() + "/";
uri += isAtomPub ? "content?id=" : "root?objectId=";
uri += file.getId();
String eTag = file.getPropertyValue("nuxeo:contentStreamDigest");
GregorianCalendar lastModifiedCalendar = file.getPropertyValue("dc:modified");
String lastModified = DateUtil.formatDate(lastModifiedCalendar.getTime());
String encoding = Base64.encodeBytes(new String(USERNAME + ":" + PASSWORD).getBytes());
DefaultHttpClient client = new DefaultHttpClient();
HttpGet request = new HttpGet(uri);
HttpResponse response = null;
request.setHeader("Authorization", "Basic " + encoding);
try {
request.setHeader("If-None-Match", eTag);
response = client.execute(request);
assertEquals(HttpServletResponse.SC_NOT_MODIFIED, response.getStatusLine().getStatusCode());
request.removeHeaders("If-None-Match");
request.setHeader("If-Modified-Since", lastModified);
response = client.execute(request);
String debug = "lastModified=" + lastModifiedCalendar.getTimeInMillis() + " If-Modified-Since="
+ lastModified + " NuxeoContentStream last=" + NuxeoContentStream.LAST_MODIFIED;
// TODO NXP-16198 there are still timezone issues here
// @Ignore
// assertEquals(debug, HttpServletResponse.SC_NOT_MODIFIED, response.getStatusLine().getStatusCode());
} finally {
client.getConnectionManager().shutdown();
}
}
// get stream
ContentStream cs = file.getContentStream();
assertNotNull(cs);
assertEquals("text/plain", cs.getMimeType());
assertEquals("testfile.txt", cs.getFileName());
if (!(isAtomPub || isBrowser)) {
// TODO fix AtomPub/Browser case where the length is unknown
// (streaming)
assertEquals(Helper.FILE1_CONTENT.length(), cs.getLength());
}
assertEquals(Helper.FILE1_CONTENT, Helper.read(cs.getStream(), "UTF-8"));
// set stream
// TODO convenience constructors for ContentStreamImpl
byte[] streamBytes = STREAM_CONTENT.getBytes("UTF-8");
ByteArrayInputStream stream = new ByteArrayInputStream(streamBytes);
cs = new ContentStreamImpl("foo.txt", BigInteger.valueOf(streamBytes.length), "text/plain; charset=UTF-8",
stream);
file.setContentStream(cs, true);
// refetch stream
file = (Document) session.getObject(file);
cs = file.getContentStream();
assertNotNull(cs);
// AtomPub lowercases charset -> TODO proper mime type comparison
String mimeType = cs.getMimeType().toLowerCase().replace(" ", "");
assertEquals("text/plain;charset=utf-8", mimeType);
// TODO fix AtomPub case where the filename is null
assertEquals("foo.txt", cs.getFileName());
if (!(isAtomPub || isBrowser)) {
// TODO fix AtomPub/Browser case where the length is unknown
// (streaming)
assertEquals(streamBytes.length, cs.getLength());
}
assertEquals(STREAM_CONTENT, Helper.read(cs.getStream(), "UTF-8"));
// delete
file.deleteContentStream();
file.refresh();
assertEquals(null, file.getContentStream());
}
@Test
public void testAllowableActions() throws Exception {
CmisObject ob;
AllowableActions aa;
Set<Action> expected;
ob = session.getObjectByPath("/testfolder1");
aa = ob.getAllowableActions();
assertNotNull(aa);
expected = EnumSet.of( //
Action.CAN_GET_OBJECT_PARENTS, //
Action.CAN_GET_PROPERTIES, //
Action.CAN_GET_DESCENDANTS, //
Action.CAN_GET_FOLDER_PARENT, //
Action.CAN_GET_FOLDER_TREE, //
Action.CAN_GET_CHILDREN, //
Action.CAN_CREATE_DOCUMENT, //
Action.CAN_CREATE_FOLDER, //
Action.CAN_CREATE_RELATIONSHIP, //
Action.CAN_DELETE_TREE, //
Action.CAN_GET_RENDITIONS, //
Action.CAN_UPDATE_PROPERTIES, //
Action.CAN_MOVE_OBJECT, //
Action.CAN_DELETE_OBJECT);
assertEquals(expected, aa.getAllowableActions());
ob = session.getObjectByPath("/testfolder1/testfile1");
aa = ob.getAllowableActions();
assertNotNull(aa);
expected = EnumSet.of( //
Action.CAN_GET_OBJECT_PARENTS, //
Action.CAN_GET_PROPERTIES, //
Action.CAN_GET_CONTENT_STREAM, //
Action.CAN_SET_CONTENT_STREAM, //
Action.CAN_DELETE_CONTENT_STREAM, //
Action.CAN_UPDATE_PROPERTIES, //
Action.CAN_MOVE_OBJECT, //
Action.CAN_DELETE_OBJECT, //
Action.CAN_ADD_OBJECT_TO_FOLDER, //
Action.CAN_REMOVE_OBJECT_FROM_FOLDER, //
Action.CAN_GET_RENDITIONS, //
Action.CAN_GET_ALL_VERSIONS, //
Action.CAN_CANCEL_CHECK_OUT, //
Action.CAN_CHECK_IN);
assertEquals(expected, aa.getAllowableActions());
String q = "SELECT cmis:objectId FROM cmis:document WHERE cmis:name = 'testfile1_Title'";
OperationContext oc = session.createOperationContext();
oc.setIncludeAllowableActions(true);
ItemIterable<QueryResult> results = session.query(q, true, oc);
assertEquals(1, results.getTotalNumItems());
aa = results.iterator().next().getAllowableActions();
assertNotNull(aa);
assertEquals(expected, aa.getAllowableActions());
}
public static final Comparator<RenditionData> RENDITION_CMP = new Comparator<RenditionData>() {
@Override
public int compare(RenditionData a, RenditionData b) {
return a.getStreamId().compareTo(b.getStreamId());
};
};
private static final int THUMBNAIL_SIZE = 394;
@Test
public void testRenditions() throws Exception {
boolean checkStream = !(isAtomPub || isBrowser);
CmisObject ob = session.getObjectByPath("/testfolder1/testfile1");
List<Rendition> renditions = ob.getRenditions();
assertTrue(renditions == null || renditions.isEmpty());
// no renditions by default with object
session.clear();
OperationContext oc = session.createOperationContext();
ob = session.getObject(session.createObjectId(ob.getId()), oc);
renditions = ob.getRenditions();
assertTrue(renditions == null || renditions.isEmpty());
// check rendition content stream requested directly
// even though the doc has no renditions requested
ContentStream cs = ((Document) ob).getContentStream("nuxeo:icon");
assertNotNull(cs);
assertEquals("image/png", cs.getMimeType());
assertTrue(cs.getFileName().endsWith(".png"));
if (!(isAtomPub || isBrowser)) {
assertEquals(THUMBNAIL_SIZE, cs.getLength());
}
// get renditions with object
session.clear();
oc = session.createOperationContext();
oc.setRenditionFilterString("*");
ob = session.getObject(session.createObjectId(ob.getId()), oc);
renditions = ob.getRenditions();
assertEquals(4, renditions.size());
Collections.sort(renditions, RENDITION_CMP);
check(renditions.get(0), checkStream);
// get renditions with query
String q = "SELECT cmis:objectId FROM cmis:document WHERE cmis:name = 'testfile1_Title'";
ItemIterable<QueryResult> results = session.query(q, true, oc);
assertEquals(1, results.getTotalNumItems());
renditions = results.iterator().next().getRenditions();
assertEquals(4, renditions.size());
Collections.sort(renditions, RENDITION_CMP);
check(renditions.get(0), false);
// no rendition stream, Chemistry deficiency (QueryResultImpl
// constructor call to of.convertRendition with null)
}
protected void check(Rendition ren, boolean checkStream) {
assertEquals("cmis:thumbnail", ren.getKind());
assertEquals("nuxeo:icon", ren.getStreamId());
assertEquals("image/png", ren.getMimeType());
assertTrue(ren.getTitle().endsWith(".png"));
assertEquals(THUMBNAIL_SIZE, ren.getLength());
if (checkStream) {
// get rendition stream
ContentStream cs = ren.getContentStream();
assertEquals("image/png", cs.getMimeType());
assertTrue(cs.getFileName().endsWith(".png"));
assertEquals(THUMBNAIL_SIZE, cs.getLength());
}
}
@Test
public void testDeletedInTrash() throws Exception {
String file5id = repoDetails.get("file5id");
try {
session.getObjectByPath("/testfolder1/testfile5");
fail("file 5 should be in trash");
} catch (CmisObjectNotFoundException e) {
// ok
}
try {
session.getObject(session.createObjectId(file5id));
fail("file 5 should be in trash");
} catch (CmisObjectNotFoundException e) {
// ok
}
Folder folder = (Folder) session.getObjectByPath("/testfolder1");
ItemIterable<CmisObject> children = folder.getChildren();
assertEquals(3, children.getTotalNumItems());
for (CmisObject child : children) {
if (child.getName().equals("title5")) {
fail("file 5 should be in trash");
}
}
// TODO
// String query =
// "SELECT cmis:objectId FROM cmis:document WHERE dc:title = 'title5'";
// ItemIterable<QueryResult> col = session.query(query, false);
// assertEquals("file 5 should be in trash", 0, col.getTotalNumItems());
// cannot delete folder, has children
try {
folder.delete(true);
fail("Should not be able to delete non-empty folder");
} catch (CmisConstraintException e) {
// ok
}
// test trashed child doesn't block folder delete
for (CmisObject child : folder.getChildren()) {
child.delete(true);
}
folder.delete(true);
}
@Test
public void testDelete() throws Exception {
Document doc = (Document) session.getObjectByPath("/testfolder1/testfile1");
doc.delete(true);
session.clear();
try {
session.getObjectByPath("/testfolder1/testfile1");
fail("Document should be deleted");
} catch (CmisObjectNotFoundException e) {
// ok
}
}
@Test
public void testDeleteTree() throws Exception {
Folder folder = (Folder) session.getObjectByPath("/testfolder1");
List<String> failed = folder.deleteTree(true, null, true);
assertTrue(failed == null || failed.isEmpty());
session.clear();
try {
session.getObjectByPath("/testfolder1");
fail("Folder should be deleted");
} catch (CmisObjectNotFoundException e) {
// ok
}
try {
session.getObjectByPath("/testfolder1/testfile1");
fail("Folder should be deleted");
} catch (CmisObjectNotFoundException e) {
// ok
}
folder = (Folder) session.getObjectByPath("/testfolder2");
assertNotNull(folder);
}
@Test
public void testCopy() throws Exception {
if (isAtomPub) {
// copy not implemented by AtomPub bindings
return;
}
Document doc = (Document) session.getObjectByPath("/testfolder1/testfile1");
assertEquals("testfile1_Title", doc.getPropertyValue("dc:title"));
Document copy = doc.copy(session.createObjectId(rootFolderId),
Collections.singletonMap("dc:title", "new title"), null, null, null, null, session.getDefaultContext());
assertNotSame(doc.getId(), copy.getId());
assertEquals("new title", copy.getPropertyValue("dc:title"));
// copy is also available from the folder
Document copy2 = session.getRootFolder().createDocumentFromSource(doc,
Collections.singletonMap("dc:title", "other title"), null);
assertNotSame(copy.getId(), copy2.getId());
assertNotSame(doc.getId(), copy2.getId());
assertEquals("other title", copy2.getPropertyValue("dc:title"));
}
@Test
public void testMove() throws Exception {
Folder folder = (Folder) session.getObjectByPath("/testfolder1");
Document doc = (Document) session.getObjectByPath("/testfolder2/testfolder3/testfile4");
String docId = doc.getId();
// TODO add move(target) convenience method
doc.move(doc.getParents().get(0), folder);
assertEquals(docId, doc.getId());
session.clear();
try {
session.getObjectByPath("/testfolder2/testfolder3/testfile4");
fail("Object should be moved away");
} catch (CmisObjectNotFoundException e) {
// ok
}
Document doc2 = (Document) session.getObjectByPath("/testfolder1/testfile4");
assertEquals(docId, doc2.getId());
}
@Test
public void testVersioning() throws Exception {
CmisObject ob = session.getObjectByPath("/testfolder1/testfile1");
String id = ob.getId();
// checked out
checkValue(PropertyIds.IS_LATEST_VERSION, Boolean.FALSE, ob);
checkValue(PropertyIds.IS_MAJOR_VERSION, Boolean.FALSE, ob);
checkValue(PropertyIds.IS_LATEST_MAJOR_VERSION, Boolean.FALSE, ob);
checkValue(PropertyIds.VERSION_LABEL, null, ob);
checkValue(PropertyIds.VERSION_SERIES_ID, NOT_NULL, ob);
checkValue(PropertyIds.IS_VERSION_SERIES_CHECKED_OUT, Boolean.TRUE, ob);
checkValue(PropertyIds.VERSION_SERIES_CHECKED_OUT_ID, id, ob);
checkValue(PropertyIds.VERSION_SERIES_CHECKED_OUT_BY, USERNAME, ob);
checkValue(PropertyIds.CHECKIN_COMMENT, null, ob);
String series = ob.getPropertyValue(PropertyIds.VERSION_SERIES_ID);
// check in major -> version 1.0
ObjectId vid = ((Document) ob).checkIn(true, null, null, "comment");
CmisObject ver = session.getObject(vid);
checkValue(PropertyIds.IS_LATEST_VERSION, Boolean.TRUE, ver);
checkValue(PropertyIds.IS_MAJOR_VERSION, Boolean.TRUE, ver);
checkValue(PropertyIds.IS_LATEST_MAJOR_VERSION, Boolean.TRUE, ver);
checkValue(PropertyIds.VERSION_LABEL, "1.0", ver);
checkValue(PropertyIds.VERSION_SERIES_ID, series, ver);
checkValue(PropertyIds.IS_VERSION_SERIES_CHECKED_OUT, Boolean.FALSE, ver);
checkValue(PropertyIds.VERSION_SERIES_CHECKED_OUT_ID, null, ver);
checkValue(PropertyIds.VERSION_SERIES_CHECKED_OUT_BY, null, ver);
checkValue(PropertyIds.CHECKIN_COMMENT, "comment", ver);
// look at the checked in document to verify
// that CMIS views it as a version
session.clear(); // clear cache
CmisObject ci = session.getObject(ob);
checkValue(PropertyIds.IS_LATEST_VERSION, Boolean.TRUE, ci);
checkValue(PropertyIds.IS_MAJOR_VERSION, Boolean.TRUE, ci);
checkValue(PropertyIds.IS_LATEST_MAJOR_VERSION, Boolean.TRUE, ci);
checkValue(PropertyIds.VERSION_LABEL, "1.0", ci);
checkValue(PropertyIds.VERSION_SERIES_ID, series, ci);
checkValue(PropertyIds.IS_VERSION_SERIES_CHECKED_OUT, Boolean.FALSE, ci);
checkValue(PropertyIds.VERSION_SERIES_CHECKED_OUT_ID, null, ci);
checkValue(PropertyIds.VERSION_SERIES_CHECKED_OUT_BY, null, ci);
checkValue(PropertyIds.CHECKIN_COMMENT, "comment", ci);
// check out
ObjectId coid = ((Document) ci).checkOut();
session.clear(); // clear cache
CmisObject co = session.getObject(coid);
assertEquals(id, coid.getId()); // Nuxeo invariant
checkValue(PropertyIds.IS_LATEST_VERSION, Boolean.FALSE, co);
checkValue(PropertyIds.IS_MAJOR_VERSION, Boolean.FALSE, co);
checkValue(PropertyIds.IS_LATEST_MAJOR_VERSION, Boolean.FALSE, co);
checkValue(PropertyIds.VERSION_LABEL, null, co);
checkValue(PropertyIds.VERSION_SERIES_ID, series, co);
checkValue(PropertyIds.IS_VERSION_SERIES_CHECKED_OUT, Boolean.TRUE, co);
checkValue(PropertyIds.VERSION_SERIES_CHECKED_OUT_ID, coid.getId(), co);
checkValue(PropertyIds.VERSION_SERIES_CHECKED_OUT_BY, USERNAME, co);
checkValue(PropertyIds.CHECKIN_COMMENT, null, co);
}
@Test
public void testVersionBasedLocking() throws Exception {
CmisObject ob = session.getObjectByPath("/testfolder1/testfile1");
// implicitly checked out after create - unlocked
assertFalse(isDocumentLocked(ob));
((Document) ob).checkIn(true, null, null, "comment");
// checked in - unlocked
assertFalse(isDocumentLocked(ob));
CmisObject ci = session.getObject(ob);
ObjectId coid = ((Document) ci).checkOut();
session.clear(); // clear cache
CmisObject co = session.getObject(coid);
// explicitly checked out - locked
assertTrue(isDocumentLocked(co));
// wait for fulltext before a cancelCheckOut, which does a copy of the fulltext rows as well
waitForAsyncCompletion();
((Document) co).cancelCheckOut();
session.clear(); // clear cache
CmisObject cco = session.getObject(ob);
// cancelled check out - unlocked
assertFalse(isDocumentLocked(cco));
// cannot check out a locked document
lockDocument(cco);
try {
((Document) cco).checkOut();
fail("Cannot check out a locked document");
} catch (CmisConstraintException e) {
// ok
}
}
@Test
public void testDeleteObjectOrCancelCheckOut() throws Exception {
// test cancelCheckOut
CmisObject ob = session.getObjectByPath("/testfolder1/testfile1");
((Document) ob).checkIn(true, null, null, "comment");
((Document) ob).checkOut();
Map<String, Object> map = new HashMap<String, Object>();
map.put("dc:title", "new title");
map.put("dc:subjects", Arrays.asList("a", "b", "c"));
ob.updateProperties(map);
// wait for fulltext before a cancelCheckOut, which does a copy of the fulltext rows as well
waitForAsyncCompletion();
((Document) ob).cancelCheckOut();
session.clear();
ob = session.getObjectByPath("/testfolder1/testfile1");
assertFalse("new title".equals(ob.getPropertyValue("dc:title")));
// test deleteObject
ob = session.getObjectByPath("/testfolder1/testfile2");
map = new HashMap<String, Object>();
map.put("dc:title", "new title");
map.put("dc:subjects", Arrays.asList("a", "b", "c"));
ob.updateProperties(map);
// wait for fulltext before a cancelCheckOut, which does a copy of the fulltext rows as well
waitForAsyncCompletion();
((Document) ob).cancelCheckOut();
session.clear();
try {
ob = session.getObjectByPath("/testfolder1/testfile2");
fail("Document should be deleted");
} catch (CmisObjectNotFoundException e) {
// ok
}
}
@Test
public void testCheckInWithChanges() throws Exception {
CmisObject ob = session.getObjectByPath("/testfolder1/testfile1");
// check in with data
Map<String, Serializable> props = new HashMap<String, Serializable>();
props.put("dc:title", "newtitle");
byte[] bytes = "foo-bar".getBytes("UTF-8");
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
ContentStream cs = session.getObjectFactory().createContentStream("test.pdf", bytes.length, "application/pdf",
in);
ObjectId vid = ((Document) ob).checkIn(true, props, cs, "comment");
CmisObject ver = session.getObject(vid);
checkValue(PropertyIds.IS_LATEST_VERSION, Boolean.TRUE, ver);
checkValue(PropertyIds.VERSION_LABEL, "1.0", ver);
checkValue(PropertyIds.CHECKIN_COMMENT, "comment", ver);
// check changes applied
checkValue("dc:title", "newtitle", ver);
ContentStream cs2 = ((Document) ver).getContentStream();
assertEquals("application/pdf", cs2.getMimeType());
if (!(isAtomPub || isBrowser)) {
assertEquals(bytes.length, cs2.getLength());
assertEquals("test.pdf", cs2.getFileName());
}
ByteArrayOutputStream os = new ByteArrayOutputStream();
IOUtils.copy(cs2.getStream(), os);
assertEquals("foo-bar", os.toString("UTF-8"));
}
@Test
public void testUserWorkspace() {
String wsPath = Helper.createUserWorkspace(coreSession, (isAtomPub || isBrowser) ? USERNAME : "Administrator");
Folder ws = (Folder) session.getObjectByPath(wsPath);
assertNotNull(ws);
}
@Test
public void testLastModifiedServiceWrapper() throws Exception {
if (!(isAtomPub || isBrowser)) {
// test only makes sense in the context of REST HTTP
return;
}
cmisFeatureSession.tearDownCmisSession();
Thread.sleep(1000); // otherwise sometimes fails to set up again
// deploy the LastModifiedServiceWrapper
harness.deployContrib("org.nuxeo.ecm.core.opencmis.tests.tests",
"OSGI-INF/test-servicefactorymanager-contrib.xml");
session = cmisFeatureSession.setUpCmisSession(coreSession.getRepositoryName());
Document doc = (Document) session.getObjectByPath("/testfolder1/testfile1");
Calendar lastModifiedCalendar = doc.getPropertyValue("dc:modified");
// check Last-Modified Cache Response Header
RepositoryInfo ri = session.getRepositoryInfo();
String uri = ri.getThinClientUri() + ri.getId() + "/";
uri += isAtomPub ? "content?id=" : "root?objectId=";
uri += doc.getId();
String lastModified = DateUtil.formatDate(lastModifiedCalendar.getTime());
String encoding = Base64.encodeBytes(new String(USERNAME + ":" + PASSWORD).getBytes());
DefaultHttpClient client = new DefaultHttpClient();
HttpGet request = new HttpGet(uri);
HttpResponse response = null;
request.setHeader("Authorization", "Basic " + encoding);
try {
response = client.execute(request);
assertEquals(HttpServletResponse.SC_OK, response.getStatusLine().getStatusCode());
// TODO NXP-18731 there are still timezone issues here
// @Ignore
// assertEquals(lastModified, response.getLastHeader("Last-Modified").getValue());
} finally {
client.getConnectionManager().shutdown();
}
}
protected void checkValue(String prop, Object expected, CmisObject ob) {
Object value = ob.getPropertyValue(prop);
if (expected == NOT_NULL) {
assertNotNull(value);
} else {
assertEquals(expected, value);
}
}
private boolean isDocumentLocked(CmisObject ob) {
return coreSession.getDocument(new IdRef(ob.getId())).isLocked();
}
private Lock lockDocument(CmisObject ob) {
return coreSession.getDocument(new IdRef(ob.getId())).setLock();
}
protected static Set<String> set(String... strings) {
return new HashSet<String>(Arrays.asList(strings));
}
/** Get ACL, using * suffix on username to denote non-direct. */
protected static Map<String, Set<String>> getActualAcl(Acl acl) {
Map<String, Set<String>> actual = new HashMap<>();
for (Ace ace : acl.getAces()) {
actual.put(ace.getPrincipalId() + (ace.isDirect() ? "" : "*"), new HashSet<String>(ace.getPermissions()));
}
return actual;
}
@Test
public void testGetACLBase() throws Exception {
String file1Id = session.getObjectByPath("/testfolder1/testfile1").getId();
Acl acl = session.getAcl(session.createObjectId(file1Id), false);
if (!(isAtomPub || isBrowser)) { // OpenCMIS 0.12 bug
assertEquals(Boolean.TRUE, acl.isExact());
}
Map<String, Set<String>> actual = getActualAcl(acl);
Map<String, Set<String>> expected = new HashMap<>();
expected.put("bob", set("Browse"));
expected.put("members*", set(READ, "Read"));
expected.put("Administrator*", set(READ, WRITE, ALL, "Everything"));
assertEquals(expected, actual);
// with only basic permissions
acl = session.getAcl(session.createObjectId(file1Id), true);
if (!(isAtomPub || isBrowser)) { // OpenCMIS 0.12 bug
assertEquals(Boolean.FALSE, acl.isExact());
}
actual = getActualAcl(acl);
expected = new HashMap<>();
expected.put("members*", set(READ));
expected.put("Administrator*", set(READ, WRITE, ALL));
assertEquals(expected, actual);
}
@Test
public void testGetACL() throws Exception {
String folder1Id = coreSession.getDocument(new PathRef("/testfolder1")).getId();
String file1Id = coreSession.getDocument(new PathRef("/testfolder1/testfile1")).getId();
String file4Id = coreSession.getDocument(new PathRef("/testfolder2/testfolder3/testfile4")).getId();
// set more complex ACLs
{
// file1
ACP acp = new ACPImpl();
ACL acl = new ACLImpl();
acl.add(new ACE("pete", SecurityConstants.READ_WRITE, true));
acl.add(new ACE("john", SecurityConstants.WRITE, true));
acp.addACL(acl);
// other ACL
acl = new ACLImpl("workflow");
acl.add(new ACE("steve", SecurityConstants.READ, true));
acp.addACL(acl);
coreSession.setACP(new IdRef(file1Id), acp, true);
// folder1
acp = new ACPImpl();
acl = new ACLImpl();
acl.add(new ACE("mary", SecurityConstants.READ, true));
acp.addACL(acl);
coreSession.setACP(new IdRef(folder1Id), acp, true);
// block on testfile4
acp = new ACPImpl();
acl = new ACLImpl();
acl.add(new ACE(SecurityConstants.ADMINISTRATOR, SecurityConstants.READ, true));
acl.add(new ACE(SecurityConstants.EVERYONE, SecurityConstants.EVERYTHING, false));
acp.addACL(acl);
coreSession.setACP(new IdRef(file4Id), acp, true);
coreSession.save();
TransactionHelper.commitOrRollbackTransaction();
TransactionHelper.startTransaction();
}
Acl acl = session.getAcl(session.createObjectId(file1Id), false);
if (!(isAtomPub || isBrowser)) { // OpenCMIS 0.12 bug
assertEquals(Boolean.TRUE, acl.isExact());
}
Map<String, Set<String>> actual = getActualAcl(acl);
Map<String, Set<String>> expected = new HashMap<>();
expected.put("pete", set(READ, WRITE, "ReadWrite"));
expected.put("john", set("Write"));
// * for inherited or not local acl
expected.put("steve*", set(READ, "Read"));
expected.put("mary*", set(READ, "Read"));
expected.put("members*", set(READ, "Read"));
expected.put("Administrator*", set(READ, WRITE, ALL, "Everything"));
assertEquals(expected, actual);
// direct Object API
OperationContext oc = session.createOperationContext();
oc.setIncludeAcls(true);
Document ob = (Document) session.getObjectByPath("/testfolder1/testfile1", oc);
acl = ob.getAcl();
if (!(isAtomPub || isBrowser)) { // OpenCMIS 0.12 bug
assertEquals(Boolean.TRUE, acl.isExact());
}
actual = getActualAcl(acl);
assertEquals(expected, actual);
// check blocking
acl = session.getAcl(session.createObjectId(file4Id), false);
if (!(isAtomPub || isBrowser)) { // OpenCMIS 0.12 bug
assertEquals(Boolean.TRUE, acl.isExact());
}
actual = getActualAcl(acl);
expected = new HashMap<>();
expected.put("Administrator", set(READ, "Read"));
expected.put("Everyone", set("Nothing"));
assertEquals(expected, actual);
}
@Test
public void testApplyACL() throws Exception {
String file1Id = session.getObjectByPath("/testfolder1/testfile1").getId();
// file1 already has a bob -> Browse permission from setUp
// add
Principal p = new AccessControlPrincipalDataImpl("mary");
Ace ace = new AccessControlEntryImpl(p, Arrays.asList(READ));
List<Ace> addAces = Arrays.asList(ace);
List<Ace> removeAces = null;
Acl acl = session.applyAcl(session.createObjectId(file1Id), addAces, removeAces, null);
if (!(isAtomPub || isBrowser)) { // OpenCMIS 0.12 bug
assertEquals(Boolean.TRUE, acl.isExact());
}
Map<String, Set<String>> actual = getActualAcl(acl);
Map<String, Set<String>> expected = new HashMap<>();
expected.put("bob", set("Browse"));
expected.put("mary", set(READ, "Read"));
expected.put("members*", set(READ, "Read"));
expected.put("Administrator*", set(READ, WRITE, ALL, "Everything"));
assertEquals(expected, actual);
// remove
ace = new AccessControlEntryImpl(p, Arrays.asList(READ, "Read"));
addAces = null;
removeAces = Arrays.asList(ace);
acl = session.applyAcl(session.createObjectId(file1Id), addAces, removeAces, null);
if (!(isAtomPub || isBrowser)) { // OpenCMIS 0.12 bug
assertEquals(Boolean.TRUE, acl.isExact());
}
actual = getActualAcl(acl);
expected = new HashMap<>();
expected.put("bob", set("Browse"));
expected.put("members*", set(READ, "Read"));
expected.put("Administrator*", set(READ, WRITE, ALL, "Everything"));
assertEquals(expected, actual);
}
@Test
public void testRecoverableException() throws Exception {
cmisFeatureSession.tearDownCmisSession();
Thread.sleep(1000); // otherwise sometimes fails to set up again
// listener that will cause a RecoverableClientException to be thrown
// when a doc whose name starts with "throw" is created
harness.deployContrib("org.nuxeo.ecm.core.opencmis.tests.tests",
"OSGI-INF/recoverable-exc-listener-contrib.xml");
session = cmisFeatureSession.setUpCmisSession(coreSession.getRepositoryName());
Map<String, Serializable> properties = new HashMap<>();
properties.put(PropertyIds.OBJECT_TYPE_ID, "File");
properties.put(PropertyIds.NAME, "throw_foo");
try {
session.getRootFolder().createDocument(properties, null, null, null, null, null,
NuxeoSession.DEFAULT_CONTEXT);
fail("should throw RecoverableClientException");
} catch (CmisInvalidArgumentException e) {
// ok, this is what we get for a 400
// check message
assertEquals("bad name", e.getMessage());
} catch (CmisRuntimeException e) {
// check status code
if (isHttp) {
fail("should have thrown CmisInvalidArgumentException");
// int status = StatusLoggingDefaultHttpInvoker.lastStatus;
// assertEquals(400, status);
} else {
Throwable cause = e.getCause();
if (!(cause instanceof RecoverableClientException)) {
throw e;
}
}
// check message
assertEquals("bad name", e.getMessage());
}
}
@Test
public void testComplexProperties() throws Exception {
// Enable complex properties
String ENABLE_COMPLEX_PROPERTIES = "org.nuxeo.cmis.enableComplexProperties";
Framework.getProperties().setProperty(ENABLE_COMPLEX_PROPERTIES, "true");
cmisFeatureSession.tearDownCmisSession();
Thread.sleep(1000); // otherwise sometimes fails to set up again
session = cmisFeatureSession.setUpCmisSession(coreSession.getRepositoryName());
ObjectMapper om = new ObjectMapper();
Map<String, Object> inputMap = new HashMap<>();
inputMap.put("stringProp", "testString1");
Long dateAsLong = Long.valueOf(1234500000000L);
String dateAsString = "2009-02-13T04:40:00.000Z";
Map<String, Serializable> properties = new HashMap<>();
properties.put(PropertyIds.OBJECT_TYPE_ID, "ComplexFile");
properties.put(PropertyIds.NAME, "complexfile");
Document doc;
List<String> docIds = new ArrayList<String>();
// date as long timestamp
inputMap.put("dateProp", dateAsLong);
properties.put("complexTest:complexItem", om.writeValueAsString(inputMap));
doc = session.getRootFolder().createDocument(properties, null, null, null, null, null,
NuxeoSession.DEFAULT_CONTEXT);
docIds.add(doc.getId());
// date as w3c string
inputMap.put("dateProp", dateAsString);
properties.put("complexTest:complexItem", om.writeValueAsString(inputMap));
doc = session.getRootFolder().createDocument(properties, null, null, null, null, null,
NuxeoSession.DEFAULT_CONTEXT);
docIds.add(doc.getId());
Map<String, Object> expectedMap = new HashMap<>();
expectedMap.put("stringProp", "testString1");
expectedMap.put("dateProp", isBrowser ? dateAsLong : dateAsString);
expectedMap.put("boolProp", null);
expectedMap.put("enumProp", null);
expectedMap.put("arrayProp", new ArrayList<String>(0));
expectedMap.put("intProp", null);
expectedMap.put("floatProp", null);
for (String docId : docIds) {
doc = (Document) session.getObject(docId);
String res = (String) doc.getPropertyValue("complexTest:complexItem");
assertEquals(expectedMap, om.readValue(res, Map.class));
}
if (!isBrowser) {
return;
}
expectedMap.put("dateProp", dateAsString);
session = createBrowserCmisSession(coreSession.getRepositoryName(),
((CmisFeatureSessionHttp) cmisFeatureSession).serverURI);
try {
for (String docId : docIds) {
doc = (Document) session.getObject(docId);
String res = (String) doc.getPropertyValue("complexTest:complexItem");
assertEquals(expectedMap, om.readValue(res, Map.class));
}
} finally {
session.clear();
}
}
private Session createBrowserCmisSession(String repositoryName, URI serverURI) {
SessionFactory sf = SessionFactoryImpl.newInstance();
Map<String, String> params = new HashMap<String, String>();
params.put(SessionParameter.AUTHENTICATION_PROVIDER_CLASS, CmisBindingFactory.STANDARD_AUTHENTICATION_PROVIDER);
params.put(SessionParameter.CACHE_SIZE_REPOSITORIES, "10");
params.put(SessionParameter.CACHE_SIZE_TYPES, "100");
params.put(SessionParameter.CACHE_SIZE_OBJECTS, "100");
params.put(SessionParameter.REPOSITORY_ID, repositoryName);
params.put(SessionParameter.USER, USERNAME);
params.put(SessionParameter.PASSWORD, PASSWORD);
params.put(SessionParameter.HTTP_INVOKER_CLASS, StatusLoggingDefaultHttpInvoker.class.getName());
params.put(SessionParameter.BINDING_TYPE, BindingType.BROWSER.value());
params.put(SessionParameter.BROWSER_URL, serverURI.toString());
params.put(SessionParameter.BROWSER_DATETIME_FORMAT, DateTimeFormat.EXTENDED.value());
session = sf.createSession(params);
return session;
}
@Test
public void testRollbackOnException() throws Exception {
Folder root = session.getRootFolder();
Map<String, Serializable> properties = new HashMap<>();
properties.put(PropertyIds.OBJECT_TYPE_ID, "File");
properties.put(PropertyIds.NAME, ThrowExceptionListener.CRASHME_NAME); // listener on this throws exception
try {
root.createDocument(properties, null, null, null, null, null, NuxeoSession.DEFAULT_CONTEXT);
fail("creation should fail");
} catch (CmisRuntimeException e) {
// ok
}
// for local binding, rollback here
if (TransactionHelper.isTransactionActiveOrMarkedRollback()) {
TransactionHelper.commitOrRollbackTransaction();
TransactionHelper.startTransaction();
}
try {
session.getObjectByPath("/" + ThrowExceptionListener.CRASHME_NAME);
fail("doc should not have been created");
} catch (CmisObjectNotFoundException e) {
// ok
}
try {
session.getObjectByPath("/" + ThrowExceptionListener.AFTERCRASH_NAME);
fail("second doc should not have been created");
} catch (CmisObjectNotFoundException e) {
// ok
}
}
}