Merge pull request #26 from Alfresco/fix/REPO-2926_fix_cmis_sys_updates

REPO-2926: CMIS update now ignores aspects in the sys namespace (MNT-18340)
This commit is contained in:
Matt Ward
2017-09-27 14:51:33 +01:00
committed by GitHub
2 changed files with 104 additions and 80 deletions

View File

@@ -238,12 +238,14 @@ import org.springframework.context.event.ApplicationContextEvent;
import org.springframework.extensions.surf.util.AbstractLifecycleBean; import org.springframework.extensions.surf.util.AbstractLifecycleBean;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import static java.util.Collections.singletonList;
/** /**
* Bridge connecting Alfresco and OpenCMIS. * Bridge connecting Alfresco and OpenCMIS.
* <p/> * <p/>
* This class provides many of the typical services that the {@link CmisService} implementation * This class provides many of the typical services that the {@link CmisService} implementation
* will need to use Alfresco. * will need to use Alfresco.
* *
* @author florian.mueller * @author florian.mueller
* @author Derek Hulley * @author Derek Hulley
* @author steveglover * @author steveglover
@@ -254,7 +256,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
// mappings from cmis property names to their Alfresco property name counterparts (used by getChildren) // mappings from cmis property names to their Alfresco property name counterparts (used by getChildren)
private static Map<String, QName> SORT_PROPERTY_MAPPINGS = new HashMap<String, QName>(); private static Map<String, QName> SORT_PROPERTY_MAPPINGS = new HashMap<String, QName>();
static static
{ {
SORT_PROPERTY_MAPPINGS.put(PropertyIds.LAST_MODIFICATION_DATE, ContentModel.PROP_MODIFIED); SORT_PROPERTY_MAPPINGS.put(PropertyIds.LAST_MODIFICATION_DATE, ContentModel.PROP_MODIFIED);
@@ -338,13 +340,13 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
private StoreRef storeRef; private StoreRef storeRef;
private String rootPath; private String rootPath;
private Map<String, List<String>> kindToRenditionNames; private Map<String, List<String>> kindToRenditionNames;
// note: cache is tenant-aware (if using TransctionalCache impl) // note: cache is tenant-aware (if using TransctionalCache impl)
private SimpleCache<String, Object> singletonCache; // eg. for cmisRootNodeRef, cmisRenditionMapping private SimpleCache<String, Object> singletonCache; // eg. for cmisRootNodeRef, cmisRenditionMapping
private final String KEY_CMIS_ROOT_NODEREF = "key.cmisRoot.noderef"; private final String KEY_CMIS_ROOT_NODEREF = "key.cmisRoot.noderef";
private final String KEY_CMIS_RENDITION_MAPPING_NODEREF = "key.cmisRenditionMapping.noderef"; private final String KEY_CMIS_RENDITION_MAPPING_NODEREF = "key.cmisRenditionMapping.noderef";
private String proxyUser; private String proxyUser;
private boolean openHttpSession = false; private boolean openHttpSession = false;
@@ -360,11 +362,11 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
private ObjectFilter objectFilter; private ObjectFilter objectFilter;
// Bulk update properties // Bulk update properties
private int bulkMaxItems = 1000; private int bulkMaxItems = 1000;
private int bulkBatchSize = 20; private int bulkBatchSize = 20;
private int bulkWorkerThreads = 2; private int bulkWorkerThreads = 2;
// -------------------------------------------------------------- // --------------------------------------------------------------
// Configuration // Configuration
// -------------------------------------------------------------- // --------------------------------------------------------------
@@ -376,7 +378,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
/** /**
* Sets the root store. * Sets the root store.
* *
* @param store * @param store
* store_type://store_id * store_type://store_id
*/ */
@@ -394,7 +396,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
{ {
this.activityPoster = activityPoster; this.activityPoster = activityPoster;
} }
public CmisActivityPoster getActivityPoster() public CmisActivityPoster getActivityPoster()
{ {
return activityPoster; return activityPoster;
@@ -413,7 +415,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
/** /**
* Sets the root path. * Sets the root path.
* *
* @param path * @param path
* path within default store * path within default store
*/ */
@@ -500,7 +502,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
{ {
this.thumbnailService = thumbnailService; this.thumbnailService = thumbnailService;
} }
public void setServiceRegistry(ServiceRegistry serviceRegistry) public void setServiceRegistry(ServiceRegistry serviceRegistry)
{ {
this.serviceRegistry = serviceRegistry; this.serviceRegistry = serviceRegistry;
@@ -601,7 +603,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
{ {
return contentService; return contentService;
} }
/** /**
* Sets the event publisher * Sets the event publisher
*/ */
@@ -609,7 +611,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
{ {
this.eventPublisher = eventPublisher; this.eventPublisher = eventPublisher;
} }
/** /**
* Sets the rendition service. * Sets the rendition service.
*/ */
@@ -638,12 +640,12 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
{ {
this.tenantAdminService = tenantAdminService; this.tenantAdminService = tenantAdminService;
} }
public void setSingletonCache(SimpleCache<String, Object> singletonCache) public void setSingletonCache(SimpleCache<String, Object> singletonCache)
{ {
this.singletonCache = singletonCache; this.singletonCache = singletonCache;
} }
/** /**
* Sets the transaction service. * Sets the transaction service.
*/ */
@@ -721,7 +723,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
{ {
this.cmisQueryService11 = cmisQueryService; this.cmisQueryService11 = cmisQueryService;
} }
public CMISQueryService getOpenCMISQueryService() public CMISQueryService getOpenCMISQueryService()
{ {
CmisVersion cmisVersion = getRequestCmisVersion(); CmisVersion cmisVersion = getRequestCmisVersion();
@@ -809,38 +811,38 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
{ {
bulkMaxItems = size; bulkMaxItems = size;
} }
public int getBulkMaxItems() public int getBulkMaxItems()
{ {
return bulkMaxItems; return bulkMaxItems;
} }
public void setBulkBatchSize(int size) public void setBulkBatchSize(int size)
{ {
bulkBatchSize = size; bulkBatchSize = size;
} }
public int getBulkBatchSize() public int getBulkBatchSize()
{ {
return bulkBatchSize; return bulkBatchSize;
} }
public void setBulkWorkerThreads(int threads) public void setBulkWorkerThreads(int threads)
{ {
bulkWorkerThreads = threads; bulkWorkerThreads = threads;
} }
public int getBulkWorkerThreads() public int getBulkWorkerThreads()
{ {
return bulkWorkerThreads; return bulkWorkerThreads;
} }
// -------------------------------------------------------------- // --------------------------------------------------------------
// Lifecycle methods // Lifecycle methods
// -------------------------------------------------------------- // --------------------------------------------------------------
private File tmp; private File tmp;
public void setup() public void setup()
{ {
File tempDir = TempFileProvider.getTempDir(); File tempDir = TempFileProvider.getTempDir();
@@ -850,7 +852,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
throw new AlfrescoRuntimeException("Failed to create CMIS temporary directory"); throw new AlfrescoRuntimeException("Failed to create CMIS temporary directory");
} }
} }
public void init() public void init()
{ {
// register as tenant deployer // register as tenant deployer
@@ -916,12 +918,12 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
/* /*
* For the given cmis property name get the corresponding Alfresco property name. * For the given cmis property name get the corresponding Alfresco property name.
* *
* Certain CMIS properties (e.g. cmis:creationDate and cmis:lastModifiedBy) don't * Certain CMIS properties (e.g. cmis:creationDate and cmis:lastModifiedBy) don't
* have direct mappings to Alfresco properties through the CMIS dictionary, because * have direct mappings to Alfresco properties through the CMIS dictionary, because
* these mappings should not be exposed outside the repository through CMIS. For these, * these mappings should not be exposed outside the repository through CMIS. For these,
* however, this method provides the mapping so that the sort works. * however, this method provides the mapping so that the sort works.
* *
*/ */
public Pair<QName, Boolean> getSortProperty(String cmisPropertyName, String direction) public Pair<QName, Boolean> getSortProperty(String cmisPropertyName, String direction)
{ {
@@ -966,7 +968,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
/** /**
* Asynchronously generates thumbnails for the given node. * Asynchronously generates thumbnails for the given node.
* *
* @param nodeRef NodeRef * @param nodeRef NodeRef
*/ */
public void createThumbnails(NodeRef nodeRef, Set<String> thumbnailNames) public void createThumbnails(NodeRef nodeRef, Set<String> thumbnailNames)
@@ -997,7 +999,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
ThumbnailDefinition details = registry.getThumbnailDefinition(thumbnailName); ThumbnailDefinition details = registry.getThumbnailDefinition(thumbnailName);
if(details == null) if(details == null)
{ {
// Throw exception // Throw exception
logger.warn("The thumbnail name '" + thumbnailName + "' is not registered"); logger.warn("The thumbnail name '" + thumbnailName + "' is not registered");
continue; continue;
} }
@@ -1006,7 +1008,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
if(registry.isThumbnailDefinitionAvailable(contentData.getContentUrl(), mimeType, size, nodeRef, details)) if(registry.isThumbnailDefinitionAvailable(contentData.getContentUrl(), mimeType, size, nodeRef, details))
{ {
org.alfresco.service.cmr.action.Action action = ThumbnailHelper.createCreateThumbnailAction(details, serviceRegistry); org.alfresco.service.cmr.action.Action action = ThumbnailHelper.createCreateThumbnailAction(details, serviceRegistry);
// Queue async creation of thumbnail // Queue async creation of thumbnail
actionService.executeAction(action, nodeRef, true, true); actionService.executeAction(action, nodeRef, true, true);
} }
@@ -1021,7 +1023,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
/** /**
* Extracts metadata for the node. * Extracts metadata for the node.
* *
* @param nodeRef NodeRef * @param nodeRef NodeRef
*/ */
public void extractMetadata(NodeRef nodeRef) public void extractMetadata(NodeRef nodeRef)
@@ -1034,7 +1036,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
{ {
return siteService.getSite(nodeRef); return siteService.getSite(nodeRef);
} }
/** /**
* Should the node be filtered? * Should the node be filtered?
*/ */
@@ -1052,7 +1054,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
} }
return wasEnabled; return wasEnabled;
} }
public boolean enableBehaviour(QName className) public boolean enableBehaviour(QName className)
{ {
boolean isEnabled = behaviourFilter.isEnabled(className); boolean isEnabled = behaviourFilter.isEnabled(className);
@@ -1063,7 +1065,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
return isEnabled; return isEnabled;
} }
public boolean disableBehaviour(QName className, NodeRef nodeRef) public boolean disableBehaviour(QName className, NodeRef nodeRef)
{ {
boolean wasEnabled = behaviourFilter.isEnabled(nodeRef, className); boolean wasEnabled = behaviourFilter.isEnabled(nodeRef, className);
@@ -1073,7 +1075,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
} }
return wasEnabled; return wasEnabled;
} }
public boolean enableBehaviour(QName className, NodeRef nodeRef) public boolean enableBehaviour(QName className, NodeRef nodeRef)
{ {
boolean isEnabled = behaviourFilter.isEnabled(nodeRef, className); boolean isEnabled = behaviourFilter.isEnabled(nodeRef, className);
@@ -1235,7 +1237,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
return id; return id;
} }
} }
/* /*
* Construct an object id based on the incoming assocRef and versionLabel. The object id will always * Construct an object id based on the incoming assocRef and versionLabel. The object id will always
* be the assocRef guid. * be the assocRef guid.
@@ -1244,7 +1246,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
{ {
return constructObjectId(assocRef, versionLabel, isPublicApi()); return constructObjectId(assocRef, versionLabel, isPublicApi());
} }
public String constructObjectId(AssociationRef assocRef, String versionLabel, boolean dropStoreRef) public String constructObjectId(AssociationRef assocRef, String versionLabel, boolean dropStoreRef)
{ {
StringBuilder sb = new StringBuilder(CMISConnector.ASSOC_ID_PREFIX); StringBuilder sb = new StringBuilder(CMISConnector.ASSOC_ID_PREFIX);
@@ -1265,7 +1267,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
} }
return sb.toString(); return sb.toString();
} }
/* /*
* Construct an object id based on the incoming incomingObjectId. The object id will always * Construct an object id based on the incoming incomingObjectId. The object id will always
* be the node guid. * be the node guid.
@@ -1283,7 +1285,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
{ {
return constructObjectId(incomingNodeId, versionLabel, isPublicApi()); return constructObjectId(incomingNodeId, versionLabel, isPublicApi());
} }
public String constructObjectId(String incomingNodeId, String versionLabel, boolean dropStoreRef) public String constructObjectId(String incomingNodeId, String versionLabel, boolean dropStoreRef)
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
@@ -1348,7 +1350,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
{ {
return constructObjectId(incomingNodeRef, versionLabel, isPublicApi()); return constructObjectId(incomingNodeRef, versionLabel, isPublicApi());
} }
public String constructObjectId(NodeRef incomingNodeRef, String versionLabel, boolean dropStoreRef) public String constructObjectId(NodeRef incomingNodeRef, String versionLabel, boolean dropStoreRef)
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
@@ -1368,22 +1370,22 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
{ {
return createObjectId(nodeRef, isPublicApi()); return createObjectId(nodeRef, isPublicApi());
} }
public String createObjectId(NodeRef nodeRef, boolean dropStoreRef) public String createObjectId(NodeRef nodeRef, boolean dropStoreRef)
{ {
QName typeQName = nodeService.getType(nodeRef); QName typeQName = nodeService.getType(nodeRef);
TypeDefinitionWrapper type = getOpenCMISDictionaryService().findNodeType(typeQName); TypeDefinitionWrapper type = getOpenCMISDictionaryService().findNodeType(typeQName);
if(type instanceof ItemTypeDefinitionWrapper) if(type instanceof ItemTypeDefinitionWrapper)
{ {
return constructObjectId(nodeRef, null); return constructObjectId(nodeRef, null);
} }
if(type instanceof FolderTypeDefintionWrapper) if(type instanceof FolderTypeDefintionWrapper)
{ {
return constructObjectId(nodeRef, null, dropStoreRef); return constructObjectId(nodeRef, null, dropStoreRef);
} }
Serializable versionLabel = getNodeService() Serializable versionLabel = getNodeService()
.getProperty(nodeRef, ContentModel.PROP_VERSION_LABEL); .getProperty(nodeRef, ContentModel.PROP_VERSION_LABEL);
if (versionLabel == null) if (versionLabel == null)
@@ -1393,7 +1395,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
return constructObjectId(nodeRef, (String)versionLabel, dropStoreRef); return constructObjectId(nodeRef, (String)versionLabel, dropStoreRef);
} }
private boolean isFolder(NodeRef nodeRef) private boolean isFolder(NodeRef nodeRef)
{ {
return getType(nodeRef) instanceof FolderTypeDefintionWrapper; return getType(nodeRef) instanceof FolderTypeDefintionWrapper;
@@ -1455,7 +1457,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
case CMIS_POLICY: case CMIS_POLICY:
throw new CmisConstraintException("Type is not a policy type!"); throw new CmisConstraintException("Type is not a policy type!");
case CMIS_ITEM: case CMIS_ITEM:
throw new CmisConstraintException("Type is not an item type!"); throw new CmisConstraintException("Type is not an item type!");
} }
} }
@@ -1517,7 +1519,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
{ {
return; return;
} }
if (!childTypes.contains(childType)) if (!childTypes.contains(childType))
{ {
throw new CmisConstraintException("Objects of type '" + childType + "' cannot be added to this folder!"); throw new CmisConstraintException("Objects of type '" + childType + "' cannot be added to this folder!");
@@ -1728,7 +1730,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
result.setMimeType(contentReader.getMimetype()); result.setMimeType(contentReader.getMimetype());
long contentSize = contentReader.getSize(); long contentSize = contentReader.getSize();
if ((offset == null) && (length == null)) if ((offset == null) && (length == null))
{ {
result.setStream(contentReader.getContentInputStream()); result.setStream(contentReader.getContentInputStream());
@@ -1774,7 +1776,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
/** /**
* Notifies listeners that a read has taken place. * Notifies listeners that a read has taken place.
* *
* @param nodeRef NodeRef * @param nodeRef NodeRef
* @param name String * @param name String
* @param mimeType String * @param mimeType String
@@ -1785,7 +1787,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
protected void publishReadEvent(final NodeRef nodeRef, final String name, final String mimeType, final long contentSize, final String encoding, final String range) protected void publishReadEvent(final NodeRef nodeRef, final String name, final String mimeType, final long contentSize, final String encoding, final String range)
{ {
final QName nodeType = nodeRef==null?null:nodeService.getType(nodeRef); final QName nodeType = nodeRef==null?null:nodeService.getType(nodeRef);
eventPublisher.publishEvent(new EventPreparator(){ eventPublisher.publishEvent(new EventPreparator(){
@Override @Override
public Event prepareEvent(String user, String networkId, String transactionId) public Event prepareEvent(String user, String networkId, String transactionId)
@@ -1793,16 +1795,16 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
if (StringUtils.hasText(range)) if (StringUtils.hasText(range))
{ {
return new ContentReadRangeEvent(user, networkId, transactionId, return new ContentReadRangeEvent(user, networkId, transactionId,
nodeRef.getId(), null, nodeType.toString(), Client.asType(ClientType.cmis), name, mimeType, contentSize, encoding, range); nodeRef.getId(), null, nodeType.toString(), Client.asType(ClientType.cmis), name, mimeType, contentSize, encoding, range);
} }
else else
{ {
return new ContentEventImpl(ContentEvent.DOWNLOAD, user, networkId, transactionId, return new ContentEventImpl(ContentEvent.DOWNLOAD, user, networkId, transactionId,
nodeRef.getId(), null, nodeType.toString(), Client.asType(ClientType.cmis), name, mimeType, contentSize, encoding); nodeRef.getId(), null, nodeType.toString(), Client.asType(ClientType.cmis), name, mimeType, contentSize, encoding);
} }
} }
}); });
} }
@@ -1811,7 +1813,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
NodeRef nodeRef = nodeInfo.getNodeRef(); NodeRef nodeRef = nodeInfo.getNodeRef();
this.disableBehaviour(ContentModel.ASPECT_VERSIONABLE, nodeRef); this.disableBehaviour(ContentModel.ASPECT_VERSIONABLE, nodeRef);
if(!nodeService.hasAspect(nodeRef, ContentModel.ASPECT_CMIS_UPDATE_CONTEXT)) if(!nodeService.hasAspect(nodeRef, ContentModel.ASPECT_CMIS_UPDATE_CONTEXT))
{ {
Map<QName, Serializable> props = new HashMap<QName, Serializable>(); Map<QName, Serializable> props = new HashMap<QName, Serializable>();
@@ -1825,7 +1827,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true); ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true);
OutputStream out = new BufferedOutputStream(writer.getContentOutputStream()); OutputStream out = new BufferedOutputStream(writer.getContentOutputStream());
InputStream in = null; InputStream in = null;
if(existingContentInput != null) if(existingContentInput != null)
{ {
@@ -1856,9 +1858,9 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
} }
if(out != null) if(out != null)
{ {
out.close(); out.close();
} }
this.enableBehaviour(ContentModel.ASPECT_VERSIONABLE, nodeRef); this.enableBehaviour(ContentModel.ASPECT_VERSIONABLE, nodeRef);
} }
} }
@@ -2071,7 +2073,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
{ {
attributes.put("localName", propertyDefinition.getLocalName()); attributes.put("localName", propertyDefinition.getLocalName());
} }
List<CmisExtensionElement> propertyValues = new ArrayList<CmisExtensionElement>(); List<CmisExtensionElement> propertyValues = new ArrayList<CmisExtensionElement>();
if (value != null) if (value != null)
@@ -2127,7 +2129,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
// MNT-12496 MNT-15044 // MNT-12496 MNT-15044
// Filter for AtomPub and Web services bindings only. Browser/json binding already encodes. // Filter for AtomPub and Web services bindings only. Browser/json binding already encodes.
if (AlfrescoCmisServiceCall.get() != null && if (AlfrescoCmisServiceCall.get() != null &&
(CallContext.BINDING_ATOMPUB.equals(AlfrescoCmisServiceCall.get().getBinding()) || (CallContext.BINDING_ATOMPUB.equals(AlfrescoCmisServiceCall.get().getBinding()) ||
CallContext.BINDING_WEBSERVICES.equals(AlfrescoCmisServiceCall.get().getBinding()))) CallContext.BINDING_WEBSERVICES.equals(AlfrescoCmisServiceCall.get().getBinding())))
{ {
@@ -2233,7 +2235,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
{ {
// MNT-12496 MNT-15044 // MNT-12496 MNT-15044
// Filter for AtomPub and Web services bindings only. Browser/json binding already encodes. // Filter for AtomPub and Web services bindings only. Browser/json binding already encodes.
if (AlfrescoCmisServiceCall.get() != null && if (AlfrescoCmisServiceCall.get() != null &&
(CallContext.BINDING_ATOMPUB.equals(AlfrescoCmisServiceCall.get().getBinding()) || (CallContext.BINDING_ATOMPUB.equals(AlfrescoCmisServiceCall.get().getBinding()) ||
CallContext.BINDING_WEBSERVICES.equals(AlfrescoCmisServiceCall.get().getBinding()))) CallContext.BINDING_WEBSERVICES.equals(AlfrescoCmisServiceCall.get().getBinding())))
{ {
@@ -2270,7 +2272,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
return result; return result;
} }
private String filterXmlRestrictedCharacters(String origValue) private String filterXmlRestrictedCharacters(String origValue)
{ {
if (origValue == null) if (origValue == null)
@@ -2285,7 +2287,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
boolean restricted = (ch < '\u0020') && !(ch == '\t' || ch == '\n' || ch == '\r'); boolean restricted = (ch < '\u0020') && !(ch == '\t' || ch == '\n' || ch == '\r');
sb.append(restricted ? REPLACEMENT_CHAR : ch); sb.append(restricted ? REPLACEMENT_CHAR : ch);
} }
return sb.toString(); return sb.toString();
} }
@@ -2767,7 +2769,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
/** /**
* Converts Acl to map and ignore the indirect ACEs * Converts Acl to map and ignore the indirect ACEs
* *
* @param acl Acl * @param acl Acl
* @return Map * @return Map
*/ */
@@ -2814,7 +2816,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
/** /**
* Filter acl to ignore inherited ACEs * Filter acl to ignore inherited ACEs
* *
* @param nodeRef NodeRef * @param nodeRef NodeRef
* @param acl Acl * @param acl Acl
* @return Acl * @return Acl
@@ -2989,18 +2991,18 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
for (CMISResultSetRow row : rs) for (CMISResultSetRow row : rs)
{ {
NodeRef nodeRef = row.getNodeRef(); NodeRef nodeRef = row.getNodeRef();
if(!nodeService.exists(nodeRef) || filter(nodeRef)) if(!nodeService.exists(nodeRef) || filter(nodeRef))
{ {
continue; continue;
} }
TypeDefinitionWrapper type = getType(nodeRef); TypeDefinitionWrapper type = getType(nodeRef);
if (type == null) if (type == null)
{ {
continue; continue;
} }
ObjectDataImpl hit = new ObjectDataImpl(); ObjectDataImpl hit = new ObjectDataImpl();
PropertiesImpl properties = new PropertiesImpl(); PropertiesImpl properties = new PropertiesImpl();
hit.setProperties(properties); hit.setProperties(properties);
@@ -3076,7 +3078,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
{ {
return; return;
} }
Map<String, PropertyData<?>> incomingPropsMap = properties.getProperties(); Map<String, PropertyData<?>> incomingPropsMap = properties.getProperties();
if (incomingPropsMap == null) if (incomingPropsMap == null)
{ {
@@ -3171,7 +3173,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
for(Object o : secondaryTypes) for(Object o : secondaryTypes)
{ {
String secondaryType = (String)o; String secondaryType = (String)o;
TypeDefinitionWrapper wrapper = getOpenCMISDictionaryService().findType(secondaryType); TypeDefinitionWrapper wrapper = getOpenCMISDictionaryService().findType(secondaryType);
if(wrapper != null) if(wrapper != null)
{ {
@@ -3184,28 +3186,34 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen
} }
} }
Set<QName> ignore = new HashSet<QName>(); Set<QName> aspectsToIgnore = new HashSet<>();
ignore.add(ContentModel.ASPECT_REFERENCEABLE); aspectsToIgnore.add(ContentModel.ASPECT_REFERENCEABLE);
ignore.add(ContentModel.ASPECT_LOCALIZED); aspectsToIgnore.add(ContentModel.ASPECT_LOCALIZED);
ignore.add(ContentModel.ASPECT_WORKING_COPY); aspectsToIgnore.add(ContentModel.ASPECT_WORKING_COPY);
Set<String> namespacesToIgnore = new HashSet<>(singletonList(NamespaceService.SYSTEM_MODEL_1_0_URI));
// aspects to add == the list of secondary types - existing aspects - ignored aspects // aspects to add == the list of secondary types - existing aspects - ignored aspects
Set<QName> toAdd = new HashSet<QName>(secondaryTypeAspects); Set<QName> toAdd = new HashSet<QName>(secondaryTypeAspects);
toAdd.removeAll(existingAspects); toAdd.removeAll(existingAspects);
toAdd.removeAll(ignore); toAdd.removeAll(aspectsToIgnore);
toAdd.removeIf(a -> namespacesToIgnore.contains(a.getNamespaceURI()));
// aspects to remove == existing aspects - secondary types // aspects to remove == existing aspects - secondary types
Set<QName> aspectsToRemove = new HashSet<QName>(); Set<QName> aspectsToRemove = new HashSet<QName>();
aspectsToRemove.addAll(existingAspects); aspectsToRemove.addAll(existingAspects);
aspectsToRemove.removeAll(ignore); aspectsToRemove.removeAll(aspectsToIgnore);
Iterator<QName> it = aspectsToRemove.iterator(); Iterator<QName> it = aspectsToRemove.iterator();
while(it.hasNext()) while(it.hasNext())
{ {
QName aspectQName = it.next(); QName aspectQName = it.next();
TypeDefinitionWrapper w = getOpenCMISDictionaryService().findNodeType(aspectQName); TypeDefinitionWrapper w = getOpenCMISDictionaryService().findNodeType(aspectQName);
if(w == null || secondaryTypeAspects.contains(aspectQName)) if(w == null || secondaryTypeAspects.contains(aspectQName) || namespacesToIgnore.contains(aspectQName.getNamespaceURI()))
{ {
// the type is not exposed or is in the secondary types to set, so remove it from the to remove set // the type is not exposed,
// or is in the secondary types to set,
// or is in the set of namespaces to ignore,
// so remove it from the "to remove" set
it.remove(); it.remove();
} }
} }

View File

@@ -1754,13 +1754,25 @@ public class CMISTest
} }
}, CmisVersion.CMIS_1_1); }, CmisVersion.CMIS_1_1);
List secondaryTypeIds = currentProperties.getProperties().get(PropertyIds.SECONDARY_OBJECT_TYPE_IDS).getValues(); List<String> secondaryTypeIds = (List<String>) currentProperties.getProperties().get(PropertyIds.SECONDARY_OBJECT_TYPE_IDS).getValues();
assertTrue(secondaryTypeIds.contains(aspectName)); assertTrue(secondaryTypeIds.contains(aspectName));
// We don't actually want to add these! (REPO-2926)
final Set<String> sysAspectsToAdd = new HashSet<>(Arrays.asList(
"P:sys:undeletable",
"P:sys:hidden"));
// Pre-condition of further test is that these aspects are not present
assertEquals(0, secondaryTypeIds.stream().filter(sysAspectsToAdd::contains).count());
// We also want to check that existing sys aspects aren't accidentally removed
assertTrue(secondaryTypeIds.contains("P:sys:localized"));
// Check we can remove an aspect - through its absence
secondaryTypeIds.remove(aspectName); secondaryTypeIds.remove(aspectName);
// Check that attempts to update/add sys:* aspects are ignored
secondaryTypeIds.addAll(sysAspectsToAdd);
final PropertiesImpl newProperties = new PropertiesImpl(); final PropertiesImpl newProperties = new PropertiesImpl();
newProperties.addProperty(new PropertyStringImpl(PropertyIds.SECONDARY_OBJECT_TYPE_IDS, secondaryTypeIds)); newProperties.addProperty(new PropertyStringImpl(PropertyIds.SECONDARY_OBJECT_TYPE_IDS, secondaryTypeIds));
final String updatedName = "My_new_name_"+UUID.randomUUID().toString(); final String updatedName = "My_new_name_"+UUID.randomUUID().toString();
newProperties.replaceProperty(new PropertyStringImpl(PropertyIds.NAME, updatedName)); newProperties.replaceProperty(new PropertyStringImpl(PropertyIds.NAME, updatedName));
@@ -1787,10 +1799,14 @@ public class CMISTest
return properties; return properties;
} }
}, CmisVersion.CMIS_1_1); }, CmisVersion.CMIS_1_1);
secondaryTypeIds = currentProperties1.getProperties().get(PropertyIds.SECONDARY_OBJECT_TYPE_IDS).getValues(); secondaryTypeIds = (List<String>) currentProperties1.getProperties().get(PropertyIds.SECONDARY_OBJECT_TYPE_IDS).getValues();
assertFalse(secondaryTypeIds.contains(aspectName)); assertFalse(secondaryTypeIds.contains(aspectName));
assertEquals(updatedName, currentProperties1.getProperties().get(PropertyIds.NAME).getFirstValue()); assertEquals(updatedName, currentProperties1.getProperties().get(PropertyIds.NAME).getFirstValue());
// sys aspects must not be added through CMIS (REPO-2926)
assertEquals(0, secondaryTypeIds.stream().filter(sysAspectsToAdd::contains).count());
// Check pre-existing sys aspects aren't accidentally removed
assertTrue(secondaryTypeIds.contains("P:sys:localized"));
} }
/** /**