Merging BRANCHES/DEV/CMIS_10 to HEAD:

17717: This check-in contains changes in Java and .NET TCK tests related to CMIS-43  and CMIS-44 JIRA tasks. Also some bugs were faced out and fixed in 
   17727: CMIS-69: Alfresco to CMIS ACL mapping: Part 1: API
   17732: Merge HEAD to DEV/CMIS10
   17756: MOB-563: SQL Tests - Lexer
   17764: CMIS-69: Alfresco to CMIS ACL mapping: get ACL support
   17802: More for CMIS-69: Alfresco to CMIS ACL mapping. Implementation for applyAcl.
   17830: Fixes for CMIS lexer and parser tests
   17838: Access fix ups for access by the WS/Rest layers
   17869: 1) remote-api:
   17874: SAIL-146: Alfresco to CMIS ACL mapping: Support to group ACEs by principal id
   17883: Adjust version properties for dev/cmis10 branch.
   17885: Update OASIS CMIS TC status.
   17889: Fix issue where objectid is not rendered correctly for CMIS private working copies.
   17890: SAIL-146: Alfresco to CMIS ACL mapping: Fixes for ACL merging when reporting and ordering of ACEs. Report full permissions and not unique short names.
   17902: Fix issue where CMIS queries via GET used incorrect defaults for paging.
   17909: Fix CMIS link relations for folder tree.
   17912: Fix CMIS type descendants atompub link
   17922: Update AtomPub binding to CMIS 1.0 CD05 XSDs.
   17924: SAIL-146: Alfresco to CMIS ACL mapping: Test set using full permissions (as opposed to short unique names)
   17927: Fix content stream create/update status to comply with CMIS 1.0 CD05.
   17934: Resolve encoding issues in CMIS AtomPub binding.
   17973: SAIL-171: CMIS Renditions REST binding
   17975: SAIL-146: Alfresco to CMIS ACL mapping: Completed AllowedAction and Permissions mapping. Added missing canDeleteTree.
   17990: Update CMIS AtomPub to CD06
   17996: Updates for cmis.alfresco.com for CD06 in prep for public review 2.
   18007: WS-Bindings were updated with CMIS 1.0 cd06 changes.
   18016: CMIS web services: Add missing generated files from WSDL
   18018: CMIS index page updates for cmis.alfresco.com
   18041: Merged HEAD to DEV/CMIS_10
   18059: SAIL-227:
   18067: SAIL-157: Strict vs Non-Strict Query Language: Enforce restrictions on the use of SCORE() and CONTAINS()
   18080: Fix for SAIL-213:Bug: Query engine does not check that select list properties are valid for selectors
   18131: SAIL-156: Query Language Compliance: Fix support for LIKE, including escaping of '%' and '_' with '\'.
   18132: SAIL-156: Query Language Compliance: Fix support for LIKE, including escaping of '%' and '_' with '\': Fix underlying lucene impl for prefix and fuzzy queries to match wildcard/like
   18143: SAIL-156: Query Language Compliance: Fix and check qualifiers in IN_TREE and IN_FOLDER. Improved scoring for CONTAINS()
   18173: SAIL-245: Exclude thumbnails from normal query results
   18179: SAIL 214: Query Language Compliance: Check for valid object ids in IN_FOLDER and IN_TREE
   18210: SAIL-156:  Query Language Compliance: Support for simple column aliases in predicates/function arguments/embedded FTS. Check property/selector binding in embedded FTS.
   18211: SAIL-156:  Query Language Compliance: Support for simple column aliases in predicates/function arguments/embedded FTS. Check property/selector binding in embedded FTS.
   18215: SAIL 156: Query Language Compliance: Fix CMIS type info to reflect the underlying settings of the Alfresco type for includeInSuperTypeQuery
   18244: SAIL 156: Query Language Compliance: includeInSuperTypeQuery -> includedInSuperTypeQuery: First cut of cmis query test model. Fixed modelSchema.xml to validate
   18255: SAIL 156: Query Language Compliance: First set of tests for predicates using properties mapped to CMIS Strings.
   18261: CMIS-49 SAIL-163: Alfresco to CMIS Change Log mapping - New CMIS Audit mapping is implemented. ChangeLogDataExtractor was added.
   18263: Build Fix
   18285: SAIL 156: Query Language Compliance: Restrictions on predicates that may be used by single-valued and multi-valued properties
   18287: SAIL-186: Changes to make CMIS Rendition REST bindings pass new TCK tests
   18291: Fix Eclipse classpath problems
   18323: CMIS-44 SAIL-187: Change Log tests (WS) – Java and .NET tests for change log were implemented.
   18325: SAIL 156: Query Language Compliance: Fixes and tests for d:mltext mappings
   18329: Updated Chemistry TCK jar including Dave W's rendition tests.
   18333: Fix compile error - spurious imports.
   18334: Fix issue where absurl web script method failed when deployed to root context.
   18339: Update CMIS index page for start of public review 2.
   18387: SAIL-147: CMIS ACL REST bindings + framework fixes
   18392: Fix typo
   18394: SAIL 156: Query Language Compliance: Fixes and tests for d:<numeric>
   18406: SAIL 156: Query Language Compliance: Remaining type/predicate combinations. Restriction of In/Comparisons for ID/Boolean
   18408: CMIS Query language - remove (pointless) multi-valued column from language definition
   18409: Formatting change for CMIS.g
   18410: Formatting change for FTS.g
   18411: CMIS TCK tests were updated to CMIS 1.0 cd06 schemas.
   18412: SAIL 156: Query Language Compliance: Tests and fixes for aliases for all data types in simple predicates (they behave as the direct column reference)
   18417: Update Chemistry TCK which now incorporates Dave W's ACL tests.
   18419: Update CMIS index page to include public review end date.
   18427: SAIL 156: Query Language Compliance: Expose multi-valued properties in queries. Tests for all accessors. Fix content length to be long.
   18435: SAIL 156: Query Language Compliance: Use queryable correctly and fix up model mappings. Add tests for baseTypeId, contentStreamId and path.
   18472: SAIL 156: Query Language Compliance: Tests and fixes for FTS/Contains expressions. Adhere strictly to the spec - no extensions available by default. Improved FTS error reporting (and stop any recovery).
   18477: SAIL-164: CMIS change log REST bindings
   18495: SAIL 156: Query Language Compliance: Tests and fixes for escaping in string literals, LIKE and FTS expressions.
   18537: SAIL 156: Query Language Compliance: Sorting support. Basic sort test for all orderable/indexed CMIS properties.
   18538: SAIL-164: CMIS change log fixes for TCK compliance
   18547: SAIL 156: Query Language Compliance: Ordering tests for all datatypes, including null values. 
   18582: Incorporate latest Chemistry TCK
   18583: Update list of supported CMIS capabilities in index page.
   18606: SAIL-156, SAIL-157, SAIL-158: Query Language Compliance: Respect all query options including locale. Fixes and tests for MLText cross language support.
   18608: SAIL-159: Java / Javascript API access to CMIS Query Language
   18617: SAIL-158: Query Tests: Check policy and relationship types are not queryable.
   18636: SAIL-184: ACL tests (WS) 
   18663: ACL tests were updated in accordance with last requirements by David Caruana.
   18680: Update to CMIS CD07
   18681: Fix CMIS ContentStreamId property when document has no content.
   18700: CMIS: Head merge problem resolution.

Phase 1: Merge up to and including revision 18700, as this the point where both AtomPub and Web Services TCK tests succeed completely on dev branch.

Note: includes CMIS rendition support ready for integration and testing with DM renditions.

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@18790 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
David Caruana
2010-02-23 17:23:42 +00:00
parent 8cbdacb18b
commit 575c970565
155 changed files with 33014 additions and 17420 deletions

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="source/java"/>
<classpathentry kind="src" path="config"/>
<classpathentry kind="src" path="source/test-resources"/>
<classpathentry exported="true" kind="lib" path="config"/>
<classpathentry exported="true" kind="lib" path="source/test-resources"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="src" path="/Core"/>
<classpathentry kind="src" path="/3rd Party"/>

View File

@@ -117,19 +117,16 @@
<property name="nodeService" ref="nodeService" />
</bean>
<!-- Models -->
<bean id="auditModel.modelRegistry" class="org.alfresco.repo.audit.model.AuditModelRegistry">
<property name="transactionService" ref="transactionService"/>
<property name="auditDAO" ref="auditDAO"/>
<property name="dataExtractors" ref="auditModel.extractorRegistry"/>
<property name="dataGenerators" ref="auditModel.generatorRegistry"/>
</bean>
<bean id="auditModel.repository" class="org.alfresco.repo.audit.model.AuditModelReader">
<property name="auditModelUrl">
<value>classpath:alfresco/audit/alfresco-audit-repository.xml</value>
<!-- Mount registry from Audit subsystem -->
<bean id="auditModel.modelRegistry" class="org.alfresco.repo.management.subsystems.SubsystemProxyFactory">
<property name="sourceApplicationContextFactory">
<ref bean="Audit" />
</property>
<property name="interfaces">
<list>
<value>org.alfresco.repo.audit.model.AuditModelRegistry</value>
</list>
</property>
<property name="auditModelRegistry" ref="auditModel.modelRegistry"/>
</bean>
</beans>

View File

@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<Audit xmlns="http://www.alfresco.org/repo/audit/model/3.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.alfresco.org/repo/audit/model/3.2 alfresco-audit-3.2.xsd">
<DataExtractors>
<DataExtractor name="simpleValue" registeredName="auditModel.extractor.simpleValue"/>
<DataExtractor name="nullValue" registeredName="auditModel.extractor.nullValue"/>
<DataExtractor name="changeLog" registeredName="CMISChangeLogDataExtractor"/>
</DataExtractors>
<PathMappings>
<PathMap source="/CMISChangeLog" target="/CMISChangeLog" />
<PathMap source="/alfresco-api/post/FileFolderService/create" target="/CMISChangeLog/CREATED" />
<PathMap source="/alfresco-api/post/NodeService/setProperties" target="/CMISChangeLog/UPDATED" />
<PathMap source="/alfresco-api/post/NodeService/addProperties" target="/CMISChangeLog/UPDATED" />
<PathMap source="/alfresco-api/post/NodeService/setProperty" target="/CMISChangeLog/UPDATED" />
<PathMap source="/alfresco-api/post/NodeService/removeProperty" target="/CMISChangeLog/UPDATED" />
<PathMap source="/alfresco-node/beforeDeleteNode" target="/CMISChangeLog/DELETED" />
<PathMap source="/alfresco-api/post/PermissionService/setPermission" target="/CMISChangeLog/SECURITY" />
<PathMap source="/alfresco-api/post/PermissionService/setInheritParentPermissions" target="/CMISChangeLog/SECURITY" />
<PathMap source="/alfresco-api/post/PermissionService/clearPermission" target="/CMISChangeLog/SECURITY" />
<PathMap source="/alfresco-api/post/PermissionService/deletePermission" target="/CMISChangeLog/SECURITY" />
</PathMappings>
<Application name="CMISChangeLog" key="CMISChangeLog">
<AuditPath key="CREATED">
<AuditPath key="result">
<RecordValue key="value" dataExtractor="changeLog" />
</AuditPath>
<AuditPath key="error">
<RecordValue key="value" dataExtractor="nullValue" />
</AuditPath>
</AuditPath>
<AuditPath key="UPDATED">
<AuditPath key="args">
<AuditPath key="nodeRef">
<RecordValue key="value" dataExtractor="changeLog" />
</AuditPath>
</AuditPath>
<AuditPath key="error">
<RecordValue key="value" dataExtractor="nullValue" />
</AuditPath>
</AuditPath>
<AuditPath key="DELETED">
<AuditPath key="node">
<RecordValue key="value" dataExtractor="changeLog" />
</AuditPath>
</AuditPath>
<AuditPath key="SECURITY">
<AuditPath key="args">
<AuditPath key="nodeRef">
<RecordValue key="value" dataExtractor="simpleValue" />
</AuditPath>
</AuditPath>
<AuditPath key="error">
<RecordValue key="value" dataExtractor="nullValue" />
</AuditPath>
</AuditPath>
</Application>
</Audit>

View File

@@ -552,9 +552,10 @@
</bean>
<!-- Start Auditing -->
<bean id="audit.bootstrap" class="org.alfresco.repo.audit.AuditBootstrap">
<property name="transactionService" ref="transactionService"/>
<property name="auditModelRegistry" ref="auditModel.modelRegistry"/>
<bean id="Audit" class="org.alfresco.repo.management.subsystems.ChildApplicationContextFactory" parent="abstractPropertyBackedBean">
<property name="autoStart">
<value>true</value>
</property>
</bean>
<!-- Repository helper class -->

View File

@@ -14,11 +14,12 @@
<bean id="CMISService" class="org.alfresco.cmis.mapping.CMISServicesImpl">
<property name="CMISSpecVersion"><value>1.0</value></property>
<property name="CMISSpecTitle"><value>Version 1.0 Committee Draft 04</value></property>
<property name="CMISSpecTitle"><value>Version 1.0 Committee Draft 07</value></property>
<property name="defaultStore"><value>workspace://SpacesStore</value></property>
<property name="defaultRootPath"><value>/Company Home</value></property>
<property name="dictionaryService" ref="dictionaryService" />
<property name="CMISDictionaryService" ref="CMISDictionaryService" />
<property name="CMISRenditionService" ref="CMISRenditionService" />
<property name="searchService" ref="SearchService" />
<property name="transactionHelper" ref="retryingTransactionHelper" />
<property name="tenantAdminService" ref="tenantAdminService" />
@@ -68,11 +69,79 @@
</property>
</bean>
<bean id="CMISRenditionService" class="org.alfresco.cmis.renditions.CMISRenditionServiceImpl">
<property name="renditionKindMapping">
<map>
<entry key="cmis:thumbnail">
<list><value>doclib</value></list>
</entry>
<entry key="alf:webpreview">
<list><value>webpreview</value><value>imgpreview</value></list>
</entry>
</map>
</property>
<property name="customRenditions">
<list>
<bean class="org.alfresco.cmis.renditions.CMISRenditionImpl">
<property name="streamId"><value>alf:icon16</value></property>
<property name="mimeType"><value>image/gif</value></property>
<property name="kind"><value>ICON16</value></property>
<property name="width"><value>16</value></property>
<property name="height"><value>16</value></property>
</bean>
<bean class="org.alfresco.cmis.renditions.CMISRenditionImpl">
<property name="streamId"><value>alf:icon32</value></property>
<property name="mimeType"><value>image/gif</value></property>
<property name="kind"><value>ICON32</value></property>
<property name="width"><value>32</value></property>
<property name="height"><value>32</value></property>
</bean>
</list>
</property>
<property name="thumbnailService">
<ref bean="ThumbnailService" />
</property>
<property name="nodeService">
<ref bean="NodeService" />
</property>
</bean>
<bean id="CMISChangeLogService" class="org.alfresco.cmis.changelog.CMISChangeLogServiceImpl">
<property name="cmisAuditApplicationName" value="CMISChangeLog" />
<property name="changesOnTypeCapability">
<list>
<value>FOLDER</value>
<value>DOCUMENT</value>
</list>
</property>
<property name="auditService" ref="auditService" />
</bean>
<bean id="CMISChangeLogDataExtractor" class="org.alfresco.cmis.changelog.CMISChangeLogDataExtractor">
<property name="cmisService" ref="CMISService" />
<property name="fileFolderService" ref="FileFolderService" />
<property name="registry" ref="auditModel.extractorRegistry" />
</bean>
<bean id="CMISReferenceFactory" class="org.alfresco.cmis.reference.ReferenceFactory">
<property name="CMISService" ref="CMISService" />
<property name="CMISDictionaryService" ref="CMISDictionaryService" />
</bean>
<bean id="CMISAccessControlService" class="org.alfresco.cmis.acl.CMISAccessControlServiceImpl" >
<!-- Valid values are: NONE, DISCOVER, MANAGE -->
<property name="aclCapabilityEnum" value="MANAGE" />
<!-- Valid values are: BASIC, REPOSITORY, BOTH -->
<property name="aclSupportedPermissionEnum" value="BOTH" />
<!-- Alfrecso only supports PROPAGATE -->
<property name="aclPropagationEnum" value="PROPAGATE" />
<property name="permissionModelDao" ref="permissionsModelDAO" />
<property name="permissionService" ref="PermissionService" />
<property name="CMISMapping" ref="CMISMapping" />
<property name="CMISDictionaryService" ref="CMISDictionaryService" />
<property name="nodeService" ref="NodeService" />
</bean>
<bean id="lucene.sql.cmis.strict" class="org.alfresco.repo.search.impl.lucene.LuceneCmisStrictSqlQueryLanguage" >
<property name="cmisQueryService">
<ref bean="CMISQueryService" />

View File

@@ -9,7 +9,7 @@
</imports>
<namespaces>
<namespace uri="http://www.alfresco.org/model/cmis/1.0/cd04" prefix="cmis" />
<namespace uri="http://www.alfresco.org/model/cmis/1.0/cd07" prefix="cmis" />
</namespaces>
<data-types>
@@ -78,9 +78,7 @@
<protected>true</protected>
<mandatory enforced="true">true</mandatory>
<multiple>false</multiple>
<index enabled="true">
<tokenised>false</tokenised>
</index>
<index enabled="false" />
</property>
<property name="cmis:createdBy">
<title>Created by</title>
@@ -215,9 +213,7 @@
<protected>true</protected>
<mandatory enforced="true">true</mandatory>
<multiple>false</multiple>
<index enabled="true">
<tokenised>false</tokenised>
</index>
<index enabled="false"/>
</property>
<property name="cmis:isVersionSeriesCheckedOut">
<title>Is Version Series Checked Out</title>
@@ -258,7 +254,7 @@
<property name="cmis:contentStreamLength">
<title>Content Stream Length</title>
<description>The length of the content stream</description>
<type>d:int</type>
<type>d:long</type>
<protected>true</protected>
<mandatory enforced="false">false</mandatory>
<multiple>false</multiple>
@@ -295,9 +291,7 @@
<protected>true</protected>
<mandatory enforced="false">false</mandatory>
<multiple>false</multiple>
<index enabled="true">
<tokenised>false</tokenised>
</index>
<index enabled="false" />
</property>
</properties>
</type>
@@ -325,9 +319,7 @@
<protected>true</protected>
<mandatory enforced="true">true</mandatory>
<multiple>false</multiple>
<index enabled="true">
<tokenised>false</tokenised>
</index>
<index enabled="false"/>
</property>
<property name="cmis:allowedChildObjectTypeIds">
<title>Allowed Child Object Types Ids</title>

View File

@@ -1,4 +1,8 @@
<model name="cm:contentmodel" xmlns="http://www.alfresco.org/model/dictionary/1.0">
<?xml version="1.0" encoding="UTF-8"?>
<!-- xsi:schemaLocation="http://www.alfresco.org/model/dictionary/1.0 modelSchema.xsd" -->
<model name="cm:contentmodel"
xmlns="http://www.alfresco.org/model/dictionary/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<description>Alfresco Content Domain Model</description>
<author>Alfresco</author>
@@ -419,6 +423,7 @@
<title>Thumbnail</title>
<parent>cm:content</parent>
<archive>true</archive>
<includedInSuperTypeQuery>false</includedInSuperTypeQuery>
<properties>
<property name="cm:thumbnailName">
<title>Thumbnail Name</title>

View File

@@ -104,6 +104,8 @@
maxOccurs="1" minOccurs="0" />
<xs:element name="archive" type="xs:boolean"
maxOccurs="1" minOccurs="0" />
<xs:element name="includedInSuperTypeQuery" type="xs:boolean"
maxOccurs="1" minOccurs="0" />
<xs:element name="properties" maxOccurs="1"
minOccurs="0">
<xs:complexType>
@@ -257,7 +259,8 @@
<xs:complexType name="association">
<xs:sequence>
<xs:group ref="dd:TextualDescription"></xs:group>
<xs:element name="source" maxOccurs="1" minOccurs="0">
<xs:element name="source" maxOccurs="1" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="role" type="xs:string" maxOccurs="1"
minOccurs="0" />
@@ -266,7 +269,7 @@
<xs:element name="many" type="xs:boolean"
maxOccurs="1" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:complexType>
</xs:element>
<xs:element name="target">
<xs:complexType>
@@ -293,17 +296,6 @@
<xs:element name="duplicate" type="xs:boolean"
maxOccurs="1" minOccurs="0" />
<xs:element name="propagateTimestamps" type="xs:boolean"
maxOccurs="1" minOccurs="0" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:group name="role">
<xs:sequence>
<xs:element name="role" type="xs:string" maxOccurs="1"
minOccurs="0" />
<xs:element name="mandatory" type="xs:boolean"
maxOccurs="1" minOccurs="0" />
</xs:sequence>
</xs:extension>

View File

@@ -160,7 +160,7 @@
on - if required permission must be present on the node, its primary parent, or its children
imples - false - the permission wil be checked
implies - false - the permission wil be checked
- true - the permission is effectively granted along with this one.
This will normally be false. For example, to require read permission on the

View File

@@ -235,4 +235,13 @@
</property>
</bean>
<bean id="nodeAuditor" class="org.alfresco.repo.node.NodeAuditor">
<property name="policyComponent">
<ref bean="policyComponent"/>
</property>
<property name="auditComponent">
<ref bean="auditComponent"/>
</property>
</bean>
</beans>

View File

@@ -40,9 +40,6 @@
<property name="transactionService">
<ref bean="transactionService"/>
</property>
<property name="enabled">
<value>${audit.enabled}</value>
</property>
<property name="useNewConfig">
<value>${audit.useNewConfig}</value>
</property>

View File

@@ -250,7 +250,9 @@ db.pool.abandoned.time=300
# Audit configuration
audit.enabled=false
audit.useNewConfig=false
audit.repository.enabled=true
audit.cmis.enabled=true
audit.useNewConfig=true
# Email configuration
mail.host=

View File

@@ -0,0 +1,43 @@
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
<beans>
<!-- Start Auditing -->
<bean id="audit.bootstrap" class="org.alfresco.repo.audit.AuditBootstrap">
<property name="active">
<value>${audit.enabled}</value>
</property>
<property name="transactionService" ref="transactionService"/>
<property name="auditModelRegistry" ref="auditModel.modelRegistry"/>
</bean>
<!-- Models -->
<bean id="auditModel.modelRegistry" class="org.alfresco.repo.audit.model.AuditModelRegistryImpl">
<property name="transactionService" ref="transactionService"/>
<property name="auditDAO" ref="auditDAO"/>
<property name="dataExtractors" ref="auditModel.extractorRegistry"/>
<property name="dataGenerators" ref="auditModel.generatorRegistry"/>
</bean>
<bean id="auditModel.repository" class="org.alfresco.repo.audit.model.AuditModelReader">
<property name="active">
<value>${audit.repository.enabled}</value>
</property>
<property name="auditModelUrl">
<value>classpath:alfresco/audit/alfresco-audit-repository.xml</value>
</property>
<property name="auditModelRegistry" ref="auditModel.modelRegistry"/>
</bean>
<bean id="auditModel.cmis" class="org.alfresco.repo.audit.model.AuditModelReader">
<property name="active">
<value>${audit.cmis.enabled}</value>
</property>
<property name="auditModelUrl">
<value>classpath:alfresco/audit/alfresco-audit-cmis.xml</value>
</property>
<property name="auditModelRegistry" ref="auditModel.modelRegistry"/>
</bean>
</beans>

View File

@@ -0,0 +1,3 @@
audit.enabled=false
audit.repository.enabled=true
audit.cmis.enabled=true

View File

@@ -0,0 +1,54 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis;
import java.util.List;
/**
* @author andyh
*
*/
public interface CMISAccessControlEntriesGroupedByPrincipalId
{
/**
* Get the principal id.
* @return principal id
*/
public String getPrincipalId();
/**
* Get the direct permissions
* @return the direct permissions
*/
public List<String> getDirectPermissions();
/**
* Get the indirect permissions
* @return the indirect permissions
*/
public List<String> getIndirectPermissions();
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis;
/**
* An Access control entry
*
* Note: we report one permission at a time rather than group them
*
* @author andyh
*
*/
public interface CMISAccessControlEntry
{
/**
* Get the principal id.
* @return principal id
*/
public String getPrincipalId();
/**
* Get the unique permission id
* @return the unique permission id
*/
public String getPermission();
/**
* Is the assignment direct on the object
* @return <code>true</code> if directly assigned, <code>false</code> otherwise.
*/
public boolean getDirect();
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis;
/**
* @author andyh
*
*/
public enum CMISAccessControlFormatEnum implements EnumLabel
{
/**
* Report only CMIS basic permissions
*/
CMIS_BASIC_PERMISSIONS("onlyBasicPermissions"),
/**
* May report CMIS basic permission, repository specific permissions or a mixture of both.
*/
REPOSITORY_SPECIFIC_PERMISSIONS("repositorySpecificPermissions");
private String label;
/**
* Construct
*
* @param label
*/
CMISAccessControlFormatEnum(String label)
{
this.label = label;
}
/*
* (non-Javadoc)
*
* @see org.alfresco.cmis.EnumLabel#label()
*/
public String getLabel()
{
return label;
}
/**
* Factory for CMISAclPropagationEnum
*/
public static EnumFactory<CMISAccessControlFormatEnum> FACTORY = new EnumFactory<CMISAccessControlFormatEnum>(CMISAccessControlFormatEnum.class, CMIS_BASIC_PERMISSIONS, true);
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis;
import java.util.List;
/**
* An Access control report.
* This is an ACL.
*
* @author andyh
*
*/
public interface CMISAccessControlReport
{
/**
* Get the list of ACEs.
* @return the list of ACEs.
*/
public List<? extends CMISAccessControlEntry> getAccessControlEntries();
/**
* Is this report incomplete?
* If so there are other other security constraints that apply
*
* @return <code>false</code> means the report fully describes security access, <code>true</code> means other
* security constraints apply but are not reported.
*/
public boolean isExtract();
/**
* Get ACEs grouped by principal id
* @return
*/
public List<? extends CMISAccessControlEntriesGroupedByPrincipalId> getAccessControlEntriesGroupedByPrincipalId();
}

View File

@@ -0,0 +1,131 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis;
import java.util.List;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Service for CMIS access control support.
*
* @author andyh
*/
public interface CMISAccessControlService
{
/**
* CMIS Read (properties and content)
*/
public static final String CMIS_READ_PERMISSION = "cmis:read";
/**
* CMIS Write (properties and content)
*/
public static final String CMIS_WRITE_PERMISSION = "cmis:write";
/**
* CMIS ALL permissions (includes all permissions defined within the repository)
*/
public static final String CMIS_ALL_PERMISSION = "cmis:all";
/**
* Get the ACL capability enum.
* @return the ACL capability enum.
*/
public CMISAclCapabilityEnum getAclCapability();
/**
* Gets the supported permission types
* @return the supported permission types
*/
public CMISAclSupportedPermissionEnum getSupportedPermissions();
/**
* Get the ACL propagation enum.
* @return the ACL propagation enum.
*/
public CMISAclPropagationEnum getAclPropagation();
/**
* Get all the permissions defined by the repository.
* @return a list of permissions
*/
public List<CMISPermissionDefinition> getRepositoryPermissions();
/**
* Get the list of permission mappings.
* @return get the permission mapping as defined by the CMIS specification.
*/
public List<? extends CMISPermissionMapping> getPermissionMappings();
/**
* Gets the name of the principal who is used for anonymous access. This principal can then be passed to the ACL
* services to specify what permissions anonymous users should have.
*
* @return name of the principal who is used for anonymous access
*/
public String getPrincipalAnonymous();
/**
* Gets the name of the principal who is used to indicate any authenticated user. This principal can then be passed
* to the ACL services to specify what permissions any authenticated user should have.
*
* @return name of the principal who is used to indicate any authenticated user
*/
public String getPrincipalAnyone();
/**
* Get the ACLs set on a node.
* @param nodeRef
* @param format
* @return an access control report
*/
public CMISAccessControlReport getAcl(NodeRef nodeRef, CMISAccessControlFormatEnum format);
/**
* Update the ACEs on a node.
* Those already existing, are preserved, those new are added and those missing are removed.
*
* @param nodeRef
* @param acesToApply
* @return an access control report of the final state
*/
public CMISAccessControlReport applyAcl(NodeRef nodeRef, List<CMISAccessControlEntry> acesToApply);
/**
* Update the ACEs on a node.
* The deletions are applied before the additions.
* Existing ACEs not deleted or added remain unchanged.
*
* @param nodeRef
* @param acesToRemove
* @param acesToAdd
* @param propagation
* @param format
* @return an access control report of the final state
* @exception UnsupportedCMISAclPropagationMode will be thrown for unsupported propagation modes.
*/
public CMISAccessControlReport applyAcl(NodeRef nodeRef, List<CMISAccessControlEntry> acesToRemove, List<CMISAccessControlEntry> acesToAdd, CMISAclPropagationEnum propagation, CMISAccessControlFormatEnum format);
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis;
/**
* CMIS ACL capability
*
* @author andyh
*/
public enum CMISAclCapabilityEnum implements EnumLabel
{
/**
* ACLs are not supported
*/
NONE("none"),
/**
* ACLs can be read but not set
*/
DISCOVER("discover"),
/**
* ACLs can be read and set
*/
MANAGE("manage");
private String label;
/**
* Construct
*
* @param label
*/
CMISAclCapabilityEnum(String label)
{
this.label = label;
}
/*
* (non-Javadoc)
*
* @see org.alfresco.cmis.EnumLabel#label()
*/
public String getLabel()
{
return label;
}
/**
* Factory for CMISAclCapabilityEnum
*/
public static EnumFactory<CMISAclCapabilityEnum> FACTORY = new EnumFactory<CMISAclCapabilityEnum>(CMISAclCapabilityEnum.class, MANAGE, true);
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis;
/**
* CMIS ACL propagation
*
* Used to request a particular behaviour or report back behaviour.
*
* @author andyh
*
*/
public enum CMISAclPropagationEnum implements EnumLabel
{
/**
* The ACL only applies to the object
* (not yet supported in Alfresco)
*/
OBJECT_ONLY("objectonly"),
/**
* ACLs are applied to all inheriting objects
* (the default in Alfresco)
*/
PROPAGATE("propagate"),
/**
* Some other mechanism by which ACL changes influence other ACL's non-direct ACEs.
*/
REPOSITORY_DETERMINED("repositorydetermined");
private String label;
/**
* Construct
*
* @param label
*/
CMISAclPropagationEnum(String label)
{
this.label = label;
}
/*
* (non-Javadoc)
*
* @see org.alfresco.cmis.EnumLabel#label()
*/
public String getLabel()
{
return label;
}
/**
* Factory for CMISAclPropagationEnum
*/
public static EnumFactory<CMISAclPropagationEnum> FACTORY = new EnumFactory<CMISAclPropagationEnum>(CMISAclPropagationEnum.class, PROPAGATE, true);
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis;
/**
* Specifies which types of permissions are supported..
*
* @author dward
*/
public enum CMISAclSupportedPermissionEnum
{
/**
* CMIS Basic permissions are supported.
*/
BASIC("basic"),
/**
* Repository specific permissions are supported.
*/
REPOSITORY("repository"),
/**
* both CMIS basic permissions and repository specific permissions are supported. (the default in Alfresco)
*/
BOTH("both");
private String label;
/**
* Construct
*
* @param label
*/
CMISAclSupportedPermissionEnum(String label)
{
this.label = label;
}
/*
* (non-Javadoc)
* @see org.alfresco.cmis.EnumLabel#label()
*/
public String getLabel()
{
return label;
}
/**
* Factory for CMISAclSupportedPermissionEnum
*/
public static EnumFactory<CMISAclSupportedPermissionEnum> FACTORY = new EnumFactory<CMISAclSupportedPermissionEnum>(
CMISAclSupportedPermissionEnum.class, BOTH, true);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -18,12 +18,17 @@
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* CMIS Allowed Action Enum
*
@@ -31,60 +36,76 @@ package org.alfresco.cmis;
*/
public enum CMISAllowedActionEnum implements EnumLabel
{
// navigation services
CAN_GET_DESCENDANTS("canGetDescendants"),
CAN_GET_FOLDER_TREE("canGetFolderTree"),
CAN_GET_CHILDREN("canGetChildren"),
CAN_GET_FOLDER_PARENT("canGetFolderParent"),
CAN_GET_OBJECT_PARENTS("canGetObjectParents"),
CAN_GET_DESCENDANTS("canGetDescendants", "canGetDescendents.Folder", CMISAccessControlService.CMIS_READ_PERMISSION, "canGetDescendents.Folder", "{http://www.alfresco.org/model/system/1.0}base.ReadChildren"),
CAN_GET_FOLDER_TREE("canGetFolderTree"/*, "canGetFolderTree.Folder", CMISAccessControlService.CMIS_READ_PERMISSION, "canGetFolderTree.Folder", "{http://www.alfresco.org/model/system/1.0}base.ReadChildren"*/),
CAN_GET_CHILDREN("canGetChildren", "canGetChildren.Folder", CMISAccessControlService.CMIS_READ_PERMISSION, "canGetChildren.Folder", "{http://www.alfresco.org/model/system/1.0}base.ReadChildren"),
CAN_GET_FOLDER_PARENT("canGetFolderParent", "canGetParents.Folder", CMISAccessControlService.CMIS_READ_PERMISSION, "canGetParents.Folder", "{http://www.alfresco.org/model/system/1.0}base.ReadProperties"),
CAN_GET_OBJECT_PARENTS("canGetObjectParents", "canGetFolderParent.Object", CMISAccessControlService.CMIS_READ_PERMISSION, "canGetFolderParent.Object", "{http://www.alfresco.org/model/system/1.0}base.ReadProperties"),
// object services
CAN_CREATE_DOCUMENT("canCreateDocument"),
CAN_CREATE_FOLDER("canCreateFolder"),
CAN_CREATE_DOCUMENT("canCreateDocument", "canCreateDocument.Folder", CMISAccessControlService.CMIS_ALL_PERMISSION, "canCreateDocument.Folder", "{http://www.alfresco.org/model/system/1.0}base.CreateChildren"),
CAN_CREATE_FOLDER("canCreateFolder", "canCreateFolder.Folder", CMISAccessControlService.CMIS_ALL_PERMISSION, "canCreateFolder.Folder", "{http://www.alfresco.org/model/system/1.0}base.CreateChildren"),
CAN_CREATE_RELATIONSHIP("canCreateRelationship"),
CAN_CREATE_POLICY("canCreatePolicy"),
CAN_GET_PROPERTIES("canGetProperties"),
CAN_GET_RENDITIONS("canGetRenditions"),
CAN_GET_CONTENT_STREAM("canGetContentStream"),
CAN_UPDATE_PROPERTIES("canUpdateProperties"),
CAN_MOVE_OBJECT("canMoveObject"),
CAN_DELETE_OBJECT("canDeleteObject"),
CAN_SET_CONTENT_STREAM("canSetContentStream"),
CAN_DELETE_CONTENT_STREAM("canDeleteContentStream"),
CAN_GET_PROPERTIES("canGetProperties", "canGetProperties.Object", CMISAccessControlService.CMIS_READ_PERMISSION, "canGetProperties.Object", "{http://www.alfresco.org/model/system/1.0}base.ReadProperties"),
CAN_GET_RENDITIONS("canGetRenditions"/*, "canGetRenditions.Object", CMISAccessControlService.CMIS_READ_PERMISSION, "canGetRenditions.Object", "{http://www.alfresco.org/model/system/1.0}base.ReadProperties"*/),
CAN_GET_CONTENT_STREAM("canGetContentStream", "canViewContent.Object", CMISAccessControlService.CMIS_READ_PERMISSION, "canViewContent.Object", "{http://www.alfresco.org/model/system/1.0}base.ReadContent"),
CAN_UPDATE_PROPERTIES("canUpdateProperties", "canUpdateProperties.Object", CMISAccessControlService.CMIS_WRITE_PERMISSION, "canUpdateProperties.Object", "{http://www.alfresco.org/model/system/1.0}base.WriteProperties"),
CAN_MOVE_OBJECT("canMoveObject", "canMove.Object", CMISAccessControlService.CMIS_ALL_PERMISSION, "canMove.Target", CMISAccessControlService.CMIS_ALL_PERMISSION, "canMove.Object", "{http://www.alfresco.org/model/system/1.0}base.DeleteNode", "canMove.Target", "{http://www.alfresco.org/model/system/1.0}base.CreateChildren"),
CAN_DELETE_OBJECT("canDeleteObject", "canDelete.Object", CMISAccessControlService.CMIS_ALL_PERMISSION, "canDelete.Object", "{http://www.alfresco.org/model/system/1.0}base.DeleteNode"),
CAN_SET_CONTENT_STREAM("canSetContentStream", "canSetContent.Document", CMISAccessControlService.CMIS_WRITE_PERMISSION, "canSetContent.Document", "{http://www.alfresco.org/model/system/1.0}base.WriteContent"),
CAN_DELETE_CONTENT_STREAM("canDeleteContentStream", "canDeleteContent.Document", CMISAccessControlService.CMIS_WRITE_PERMISSION, "canDeleteContent.Document", "{http://www.alfresco.org/model/system/1.0}base.WriteContent"),
CAN_DELETE_TREE("canDeleteTree", "canDeleteTree.Folder", CMISAccessControlService.CMIS_ALL_PERMISSION, "canDeleteTree.Folder", "{http://www.alfresco.org/model/system/1.0}base.DeleteNode"),
// multi-filing services
CAN_ADD_OBJECT_TO_FOLDER("canAddObjectToFolder"),
CAN_REMOVE_OBJECT_FROM_FOLDER("canRemoveObjectFromFolder"),
CAN_ADD_OBJECT_TO_FOLDER("canAddObjectToFolder", "canAddToFolder.Object", CMISAccessControlService.CMIS_READ_PERMISSION, "canAddToFolder.Folder", CMISAccessControlService.CMIS_ALL_PERMISSION, "canAddToFolder.Object", "{http://www.alfresco.org/model/system/1.0}base.ReadProperties", "canAddToFolder.Folder", "{http://www.alfresco.org/model/system/1.0}base.CreateChildren"),
CAN_REMOVE_OBJECT_FROM_FOLDER("canRemoveObjectFromFolder", "canRemoveFromFolder.Object", CMISAccessControlService.CMIS_ALL_PERMISSION, "canRemoveFromFolder.Object", "{http://www.alfresco.org/model/system/1.0}base.DeleteNode"),
// versioning services
CAN_CHECKOUT("canCheckOut"),
CAN_CANCEL_CHECKOUT("canCancelCheckOut"),
CAN_CHECKIN("canCheckIn"),
CAN_GET_ALL_VERSIONS("canGetAllVersions"),
CAN_CHECKOUT("canCheckOut", "canCheckout.Document", CMISAccessControlService.CMIS_ALL_PERMISSION, "canCheckout.Document", "{http://www.alfresco.org/model/content/1.0}lockable.CheckOut"),
CAN_CANCEL_CHECKOUT("canCancelCheckOut", "canCancelCheckout.Document", CMISAccessControlService.CMIS_ALL_PERMISSION, "canCancelCheckout.Document", "{http://www.alfresco.org/model/content/1.0}lockable.CancelCheckOut"),
CAN_CHECKIN("canCheckIn", "canCheckin.Document", CMISAccessControlService.CMIS_ALL_PERMISSION, "canCheckin.Document", "{http://www.alfresco.org/model/content/1.0}lockable.CheckIn"),
CAN_GET_ALL_VERSIONS("canGetAllVersions", "canGetAllVersions.VersionSeries", CMISAccessControlService.CMIS_READ_PERMISSION, "canGetAllVersions.VersionSeries", "{http://www.alfresco.org/model/system/1.0}base.Read"),
// relationship services
CAN_GET_OBJECT_RELATIONSHIPS("canGetObjectRelationships"),
// policy services
CAN_APPLY_POLICY("canApplyPolicy"),
CAN_REMOVE_POLICY("canRemovePolicy"),
CAN_GET_APPLIED_POLICIES("canGetAppliedPolicies"),
CAN_APPLY_POLICY("canApplyPolicy", "canAddPolicy.Object", CMISAccessControlService.CMIS_WRITE_PERMISSION, "canAddPolicy.Policy", CMISAccessControlService.CMIS_READ_PERMISSION, "canAddPolicy.Object", "{http://www.alfresco.org/model/system/1.0}base.Write"),
CAN_REMOVE_POLICY("canRemovePolicy", "canRemovePolicy.Object", CMISAccessControlService.CMIS_WRITE_PERMISSION, "canRemovePolicy.Policy", CMISAccessControlService.CMIS_READ_PERMISSION, "canRemovePolicy.Object", "{http://www.alfresco.org/model/system/1.0}base.Write"),
CAN_GET_APPLIED_POLICIES("canGetAppliedPolicies", "canGetAppliedPolicies.Object", CMISAccessControlService.CMIS_READ_PERMISSION, "canGetAppliedPolicies.Object", "{http://www.alfresco.org/model/system/1.0}base.ReadProperties"),
// acl services
CAN_GET_ACL("canGetACL"),
CAN_APPLY_ACL("canApplyACL");
CAN_GET_ACL("canGetACL", "canGetACL.Object", CMISAccessControlService.CMIS_ALL_PERMISSION, "canGetACL.Object", "{http://www.alfresco.org/model/system/1.0}base.ReadPermissions"),
CAN_APPLY_ACL("canApplyACL", "canApplyACL.Object", CMISAccessControlService.CMIS_ALL_PERMISSION, "canApplyACL.Object", "{http://www.alfresco.org/model/system/1.0}base.ChangePermissions");
private String label;
private Map<String, List<String>> mapping = new HashMap<String, List<String>>();
/**
* Construct
*
* @param label
*/
CMISAllowedActionEnum(String label)
CMISAllowedActionEnum(String label, String ... keysAndPermissions)
{
this.label = label;
assert(keysAndPermissions.length % 2 == 0);
for(int i = 0; i < keysAndPermissions.length; i++)
{
String key = keysAndPermissions[i];
String permission = keysAndPermissions[++i];
List<String> permissions = mapping.get(key);
if(permissions == null)
{
permissions = new ArrayList<String>(1);
mapping.put(key, permissions);
}
permissions.add(permission);
}
}
/* (non-Javadoc)
@@ -95,5 +116,10 @@ public enum CMISAllowedActionEnum implements EnumLabel
return label;
}
public Map<String, List<String>> getPermissionMapping()
{
return mapping;
}
public static EnumFactory<CMISAllowedActionEnum> FACTORY = new EnumFactory<CMISAllowedActionEnum>(CMISAllowedActionEnum.class);
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis;
/**
* Enumeration of base Alfresco Object type Ids. Possible values:<br />
* <b>DOCUMENT</b>,<br />
* <b>FOLDER</b>,<br />
* <b>RELATIONSHIP</b>,<br />
* <b>POLICY</b>
*
* @author Dmitry Velichkevich
*/
public enum CMISBaseObjectTypeIds implements EnumLabel
{
DOCUMENT("cmis:document"), FOLDER("cmis:folder"), RELATIONSHIP("cmis:relationship"), POLICY("cmis:policy");
private String label;
CMISBaseObjectTypeIds(String label)
{
this.label = label;
}
public String getLabel()
{
return label;
}
public static final EnumFactory<CMISBaseObjectTypeIds> FACTORY = new EnumFactory<CMISBaseObjectTypeIds>(CMISBaseObjectTypeIds.class);
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis;
/**
* Enumeration of the <b>CMIS Changes Capability</b>
*
* @author Dmitry Velichkevich
*/
public enum CMISCapabilityChanges implements EnumLabel
{
NONE("none"), OBJECTIDSONLY("objectidsonly");
private String label;
CMISCapabilityChanges(String label)
{
this.label = label;
}
public String getLabel()
{
return label;
}
public static final EnumFactory<CMISCapabilityChanges> FACTORY = new EnumFactory<CMISCapabilityChanges>(CMISCapabilityChanges.class);
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis;
import java.util.Date;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* This class describes entry record for some <b>Change Log</b> descriptor
*
* @author Dmitry Velichkevich
*/
public interface CMISChangeEvent
{
/**
* @return {@link NodeRef} instance that represents <b>Id</b> of one of the <b>Change Event</b> objects for some <b>Change Log Token</b>
*/
public NodeRef getNode();
/**
* @return {@link CMISChangeType} <b>enum</b> value that determines the type of current <b>Change Event</b>
*/
public CMISChangeType getChangeType();
/**
* @return {@link Date} value that represents time of current <b>Change Event</b>
*/
public Date getChangeTime();
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis;
import java.util.List;
/**
* This class represents descriptor for some <b>Change Log Token</b>
*
* @author Dmitry Velichkevich
*/
public interface CMISChangeLog
{
/**
* @return {@link List}&lt;{@link CMISChangeEvent}&gt; collection that contains all available for some <b>Change Log Token</b> <b>Change Events</b> descriptors
*/
public List<CMISChangeEvent> getChangeEvents();
/**
* @return {@link Boolean} value that determines whether repository contains any more <b>Change Events</b> after some {@link CMISChangeLogService}.{@link #getChangeEvents()}
* invocation
*/
public boolean hasMoreItems();
/**
* <b>Note:</b> this is optional operation and actual <b>Change Events</b> amount in collection may not be equal to result of this method invocation
*
* @return {@link Integer} value that determines amount of <b>Change Events</b> those were returned
*/
public Integer getEventCount();
/**
* @return {@link String} value that represents the next generated <b>Change Log Token</b>
*/
public String getNextChangeToken();
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis;
import java.util.List;
/**
* Service for manipulating with <b>Change Log Events</b> by <b>Change Log Tokens</b>. Also this service exposes some methods for describing <b>Auditing</b> features
*
* @author Dmitry Velichkevich
*/
public interface CMISChangeLogService
{
/**
* @param changeLogToken - {@link String} value that represents some <b>Change Log Token</b>
* @param maxItems - {@link Integer} value that determines required amount of entries
* @return {@link CMISChangeLog} instance that describes entry mapped to specified <b>Change Log Token</b>
*/
public CMISChangeLog getChangeLogEvents(String changeLogToken, Integer maxItems);
/**
* @return {@link String} value that represents <b>Change Log Token</b> which is currently actual
*/
public String getLastChangeLogToken();
/**
* Gets the change log token maxItems entries before the given one
*
* @param currentPageToken
* a change log token
* @param maxItems
* the number of entries to skip backwards
* @return the change log token maxItems entries before currentPageToken
*/
public String getPreviousPageChangeLogToken(String currentPageToken, Integer maxItems);
/**
* Assuming that currentPageToken begins a page of maxItems entries, gets the token at the start of the final page.
*
* @param currentPageToken
* a change log token
* @param maxItems
* the number of entries per page
* @return the change log token at the start of the final page
*/
public String getLastPageChangeLogToken(String currentPageToken, Integer maxItems);
/**
* @return {@link CMISCapabilityChanges} <b>enum</b> value that specifies currently configured <b>Auditing</b> feature mode
*/
public CMISCapabilityChanges getCapability();
/**
* @return {@link List}&lt;{@link CMISBaseObjectTypeIds}&gt; collection instance that contains configured value for Changes On Type capability
* @see CMISBaseObjectTypeIds
*/
public List<CMISBaseObjectTypeIds> getChangesOnTypeCapability();
/**
* @return always <b>true</b>
*/
public boolean getChangesIncomplete();
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis;
/**
* Enumeration of the <b>CMIS Change Type</b>. Possible values:<br />
* <b>CREATED</b><br />
* <b>UPDATED</b><br />
* <b>DELETED</b><br />
* <b>SECURITY</b>
*
* @author Dmitry Velichkevich
*/
public enum CMISChangeType implements EnumLabel
{
CREATED("created"), UPDATED("updated"), DELETED("deleted"), SECURITY("security");
private String label;
CMISChangeType(String label)
{
this.label = label;
}
public String getLabel()
{
return label;
}
public static final EnumFactory<CMISChangeType> FACTORY = new EnumFactory<CMISChangeType>(CMISChangeType.class);
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis;
/**
* @author andyh
*
*/
public class CMISConstraintException extends CMISRuntimeException
{
/**
*
*/
private static final long serialVersionUID = 575343231113393902L;
/**
* @param msgId
*/
public CMISConstraintException(String msgId)
{
super(msgId);
// TODO Auto-generated constructor stub
}
/**
* @param msgId
* @param msgParams
*/
public CMISConstraintException(String msgId, Object[] msgParams)
{
super(msgId, msgParams);
// TODO Auto-generated constructor stub
}
/**
* @param msgId
* @param cause
*/
public CMISConstraintException(String msgId, Throwable cause)
{
super(msgId, cause);
// TODO Auto-generated constructor stub
}
/**
* @param msgId
* @param msgParams
* @param cause
*/
public CMISConstraintException(String msgId, Object[] msgParams, Throwable cause)
{
super(msgId, msgParams, cause);
// TODO Auto-generated constructor stub
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis;
/**
* A permission definition
*
* @author andyh
*
*/
public interface CMISPermissionDefinition
{
/**
* The unique permission string (used for assignment)
* @return the permission.
*/
public String getPermission();
/**
* An optional permission description.
* If not set, this defaults to the permission.
*
* @return the optional description.
*/
public String getDescription();
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis;
import java.util.List;
/**
* A CMIS permission mapping.
*
* @author andyh
*
*/
public interface CMISPermissionMapping
{
/**
* Get the allowed action key.
* @return the key.
*/
public String getKey();
/**
* Get the required permissions.
* If the list is of zero length the action is unprotected and allowed.
* (Disallowed actions never appear at all)
* For one or more entries a match against any permission allws.
*
* @return the list of required permissions.
*/
public List<String> getPermissions();
}

View File

@@ -128,9 +128,10 @@ public interface CMISPropertyLuceneBuilder
public Query buildLuceneLike(LuceneQueryParser lqp, Serializable value, Boolean not) throws ParseException;
/**
* @param lqp TODO
* @return the sort field
*/
public String getLuceneSortField();
public String getLuceneSortField(LuceneQueryParser lqp);
/**
* @return the field name

View File

@@ -24,13 +24,12 @@
*/
package org.alfresco.cmis;
import org.alfresco.error.AlfrescoRuntimeException;
/**
* @author andyh
*
*/
public class CMISQueryException extends AlfrescoRuntimeException
public class CMISQueryException extends CMISRuntimeException
{
/**

View File

@@ -0,0 +1,90 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* CMIS Document Rendition
*
* @author Stas Sokolovsky
*/
public interface CMISRendition
{
/**
* Gets the thumbnail node reference
*/
public NodeRef getNodeRef();
/**
* Get the rendition stream id
* @return
*/
public String getStreamId();
/**
* Get the MIME type of the rendition stream.
* @return
*/
public String getMimeType();
/**
* Get rendition kind.
* @return
*/
public CMISRenditionKind getKind();
/**
* Get the height of image. Typically used for image renditions (expressed as pixels).
* @return
*/
public Integer getHeight();
/**
* Get the width of image. Typically used for image renditions (expressed as pixels).
* @return
*/
public Integer getWidth();
/**
* Get a human readable information about the rendition.
* @return
*/
public String getTitle();
/**
* Get the length of the rendition stream in bytes.
* @return
*/
public Integer getLength();
/**
* Get the rendition document id. If specified, then the rendition can also be accessed
* as a document object in the CMIS services.
* @return
*/
public String getRenditionDocumentId();
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis;
/**
* Enumeration of the CMIS Rendition Kinds
*
* @author Stas Sokolovsky
*/
public enum CMISRenditionKind implements EnumLabel
{
THUMBNAIL("cmis:thumbnail"),
WEB_PREVIEW("alf:webpreview"),
ICON16("alf:icon16"),
ICON32("alf:icon32");
private String label;
/**
* Construct
*
* @param label
*/
CMISRenditionKind(String label)
{
this.label = label;
}
/*
* (non-Javadoc)
*
* @see org.alfresco.cmis.EnumLabel#label()
*/
public String getLabel()
{
return label;
}
public static EnumFactory<CMISRenditionKind> FACTORY = new EnumFactory<CMISRenditionKind>(CMISRenditionKind.class);
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis;
import java.util.List;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Service to retrieving renditions of document
*
* @author Stas Sokolovsky
*/
public interface CMISRenditionService
{
/**
* Get renditions for a document.
*
* @param node node reference of document
* @param renditionFilter renditions filter
* @return list of renditions
*/
public List<CMISRendition> getRenditions(NodeRef node, String renditionFilter);
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis;
import org.alfresco.error.AlfrescoRuntimeException;
/**
* @author andyh
*
*/
public class CMISRuntimeException extends AlfrescoRuntimeException
{
/**
*
*/
private static final long serialVersionUID = 121670154804835474L;
/**
* @param msgId
*/
public CMISRuntimeException(String msgId)
{
super(msgId);
// TODO Auto-generated constructor stub
}
/**
* @param msgId
* @param msgParams
*/
public CMISRuntimeException(String msgId, Object[] msgParams)
{
super(msgId, msgParams);
// TODO Auto-generated constructor stub
}
/**
* @param msgId
* @param cause
*/
public CMISRuntimeException(String msgId, Throwable cause)
{
super(msgId, cause);
// TODO Auto-generated constructor stub
}
/**
* @param msgId
* @param msgParams
* @param cause
*/
public CMISRuntimeException(String msgId, Object[] msgParams, Throwable cause)
{
super(msgId, msgParams, cause);
// TODO Auto-generated constructor stub
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -18,7 +18,7 @@
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
@@ -83,6 +83,17 @@ public interface CMISServices
*/
public NodeRef getNode(String referenceType, String[] reference);
/**
* Gets a map of node attributes relating to renditions.
*
* @param nodeRef
* the node ref
* @param renditionFilter
* the rendition filter
* @return the attribute map
*/
public Map<String, Object> getRenditions(NodeRef nodeRef, String renditionFilter);
/**
* Query for node children
*

View File

@@ -139,7 +139,7 @@ public interface CMISTypeDefinition
*
* @return
*/
public boolean isIncludeInSuperTypeQuery();
public boolean isIncludedInSuperTypeQuery();
/**
* Is this type versionable? If true this implies all instances of the type are versionable.

View File

@@ -40,8 +40,8 @@ import java.util.TreeMap;
*/
public class EnumFactory<E extends Enum<E>>
{
private Enum<E> defaultEnum;
private Map<String, Enum<E>> labelMap = new HashMap<String, Enum<E>>(10);
private E defaultEnum;
private Map<String, E> labelMap = new HashMap<String, E>(10);
/**
* @param enumClass
@@ -55,7 +55,7 @@ public class EnumFactory<E extends Enum<E>>
* @param enumClass
* @param defaultEnum
*/
public EnumFactory(Class<E> enumClass, Enum<E> defaultEnum)
public EnumFactory(Class<E> enumClass, E defaultEnum)
{
this(enumClass, defaultEnum, false);
}
@@ -65,17 +65,17 @@ public class EnumFactory<E extends Enum<E>>
* @param defaultEnum
* @param caseSensitive case-sensitive lookup for Enum label
*/
public EnumFactory(Class<E> enumClass, Enum<E> defaultEnum, boolean caseSensitive)
public EnumFactory(Class<E> enumClass, E defaultEnum, boolean caseSensitive)
{
this.defaultEnum = defaultEnum;
// setup label map
labelMap = caseSensitive ? new HashMap<String, Enum<E>>(10) : new TreeMap<String, Enum<E>>(String.CASE_INSENSITIVE_ORDER);
labelMap = caseSensitive ? new HashMap<String, E>(10) : new TreeMap<String, E>(String.CASE_INSENSITIVE_ORDER);
EnumSet<E> enumSet = EnumSet.allOf(enumClass);
Iterator<E> iter = enumSet.iterator();
while(iter.hasNext())
{
Enum<E> e = iter.next();
E e = iter.next();
if (e instanceof EnumLabel)
{
labelMap.put(((EnumLabel)e).getLabel(), e);
@@ -109,7 +109,7 @@ public class EnumFactory<E extends Enum<E>>
* @param e enum
* @return label (or null, if no label specified)
*/
public String label(Enum<E> e)
public String label(E e)
{
if (e instanceof EnumLabel)
{
@@ -135,7 +135,7 @@ public class EnumFactory<E extends Enum<E>>
* @param label
* @return enum (or null, if no enum has specified label)
*/
public Enum<E> fromLabel(String label)
public E fromLabel(String label)
{
return labelMap.get(label);
}
@@ -148,9 +148,9 @@ public class EnumFactory<E extends Enum<E>>
* @param label
* @return enum (or default enum, if label is invalid)
*/
public Enum<E> toEnum(String label)
public E toEnum(String label)
{
Enum<E> e = (label == null) ? null : fromLabel(label);
E e = (label == null) ? null : fromLabel(label);
if (e == null)
{
e = defaultEnum;

View File

@@ -0,0 +1,80 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis;
import org.alfresco.error.AlfrescoRuntimeException;
/**
* Thrown when an unsupported CMISAclPropagationModeEnum is specified.
* @author andyh
*
*/
public class UnsupportedCMISAclPropagationMode extends AlfrescoRuntimeException
{
/**
*
*/
private static final long serialVersionUID = 3844368557738616296L;
/**
* @param msgId
* @param msgParams
* @param cause
*/
public UnsupportedCMISAclPropagationMode(String msgId, Object[] msgParams, Throwable cause)
{
super(msgId, msgParams, cause);
}
/**
* @param msgId
* @param msgParams
*/
public UnsupportedCMISAclPropagationMode(String msgId, Object[] msgParams)
{
super(msgId, msgParams);
}
/**
* @param msgId
* @param cause
*/
public UnsupportedCMISAclPropagationMode(String msgId, Throwable cause)
{
super(msgId, cause);
}
/**
* @param msgId
*/
public UnsupportedCMISAclPropagationMode(String msgId)
{
super(msgId);
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis.acl;
import java.util.ArrayList;
import java.util.List;
import org.alfresco.cmis.CMISAccessControlEntriesGroupedByPrincipalId;
import org.alfresco.cmis.CMISAccessControlEntry;
import org.alfresco.cmis.CMISConstraintException;
/**
* @author andyh
*
*/
public class CMISAccessControlEntriesGroupedByPrincipalIdImpl implements CMISAccessControlEntriesGroupedByPrincipalId
{
private String principalId;
private List<String> directPermissions = new ArrayList<String>();
private List<String> indirectPermissions = new ArrayList<String>();
/*package */ CMISAccessControlEntriesGroupedByPrincipalIdImpl(String principalId)
{
this.principalId = principalId;
}
/* (non-Javadoc)
* @see org.alfresco.cmis.CMISAccessControlEntriesGroupedByPrincipalId#getDirectPermissions()
*/
public List<String> getDirectPermissions()
{
return directPermissions;
}
/* (non-Javadoc)
* @see org.alfresco.cmis.CMISAccessControlEntriesGroupedByPrincipalId#getInirectPermissions()
*/
public List<String> getIndirectPermissions()
{
return indirectPermissions;
}
/* (non-Javadoc)
* @see org.alfresco.cmis.CMISAccessControlEntriesGroupedByPrincipalId#getPrincipalId()
*/
public String getPrincipalId()
{
return principalId;
}
/* package */ void addEntry(CMISAccessControlEntry entry)
{
if(!principalId.equals(entry.getPrincipalId()))
{
throw new CMISConstraintException("Grouping error in principal id");
}
if(entry.getDirect())
{
directPermissions.add(entry.getPermission());
}
else
{
indirectPermissions.add(entry.getPermission());
}
}
}

View File

@@ -0,0 +1,156 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis.acl;
import org.alfresco.cmis.CMISAccessControlEntry;
/**
* @author andyh
*
*/
public class CMISAccessControlEntryImpl implements CMISAccessControlEntry
{
private String principalId;
private String permission;
private int position;
/*package*/ CMISAccessControlEntryImpl(String principalId, String permission, int position)
{
this.principalId = principalId;
this.permission = permission;
this.position = position;
}
/**
* Can be used for external calls to add or delete aces.
* (All must be at position 0 === directly set on the object)
*
* @param principalId
* @param permission
*/
public CMISAccessControlEntryImpl(String principalId, String permission)
{
this(principalId, permission, 0);
}
/* (non-Javadoc)
* @see org.alfresco.cmis.CMISAccessControlEntry#getDirect()
*/
public boolean getDirect()
{
return position == 0;
}
/* (non-Javadoc)
* @see org.alfresco.cmis.CMISAccessControlEntry#getPermission()
*/
public String getPermission()
{
return permission;
}
/* (non-Javadoc)
* @see org.alfresco.cmis.CMISAccessControlEntry#getPrincipalId()
*/
public String getPrincipalId()
{
return principalId;
}
/**
* @return the position
*/
public int getPosition()
{
return position;
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((permission == null) ? 0 : permission.hashCode());
result = prime * result + position;
result = prime * result + ((principalId == null) ? 0 : principalId.hashCode());
return result;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final CMISAccessControlEntryImpl other = (CMISAccessControlEntryImpl) obj;
if (permission == null)
{
if (other.permission != null)
return false;
}
else if (!permission.equals(other.permission))
return false;
if (position != other.position)
return false;
if (principalId == null)
{
if (other.principalId != null)
return false;
}
else if (!principalId.equals(other.principalId))
return false;
return true;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("[");
builder.append(getPrincipalId()).append(", ");
builder.append(getPermission()).append(", ");
builder.append(getPosition()).append(", ");
builder.append(getDirect()).append("]");
return builder.toString();
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis.acl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.alfresco.cmis.CMISAccessControlEntriesGroupedByPrincipalId;
import org.alfresco.cmis.CMISAccessControlEntry;
import org.alfresco.cmis.CMISAccessControlReport;
/**
* A simple CMIS access control report
*
* @author andyh
*
*/
public class CMISAccessControlReportImpl implements CMISAccessControlReport
{
private ArrayList<CMISAccessControlEntry> entries = new ArrayList<CMISAccessControlEntry>();
private boolean extract = false;
/* (non-Javadoc)
* @see org.alfresco.cmis.CMISAccessControlReport#getAccessControlEntries()
*/
public List<CMISAccessControlEntry> getAccessControlEntries()
{
return entries;
}
/* (non-Javadoc)
* @see org.alfresco.cmis.CMISAccessControlReport#isExtract()
*/
public boolean isExtract()
{
return extract;
}
/**
* Set extract.
* @param extract
*/
public void setExtract(boolean extract)
{
this.extract = extract;
}
/*package*/ void addEntry(CMISAccessControlEntry entry)
{
removeEntry(entry);
entries.add(entry);
}
/*package*/ void removeEntry(CMISAccessControlEntry entry)
{
for(int i = 0; i < entries.size(); i++)
{
CMISAccessControlEntry current = entries.get(i);
if(current.getPrincipalId().equals(entry.getPrincipalId()) && current.getPermission().equals(entry.getPermission()))
{
entries.remove(i);
i--;
}
}
}
/* (non-Javadoc)
* @see org.alfresco.cmis.CMISAccessControlReport#getAccessControlEntriesGroupedByPrincipalId()
*/
public List<? extends CMISAccessControlEntriesGroupedByPrincipalId> getAccessControlEntriesGroupedByPrincipalId()
{
HashMap<String, CMISAccessControlEntriesGroupedByPrincipalIdImpl> grouped = new HashMap<String, CMISAccessControlEntriesGroupedByPrincipalIdImpl>();
for(CMISAccessControlEntry entry : getAccessControlEntries())
{
CMISAccessControlEntriesGroupedByPrincipalIdImpl value = grouped.get(entry.getPrincipalId());
if(value == null)
{
value = new CMISAccessControlEntriesGroupedByPrincipalIdImpl(entry.getPrincipalId());
grouped.put(entry.getPrincipalId(), value);
}
value.addEntry(entry);
}
ArrayList<CMISAccessControlEntriesGroupedByPrincipalIdImpl> answer = new ArrayList<CMISAccessControlEntriesGroupedByPrincipalIdImpl>(grouped.values());
return answer;
}
}

View File

@@ -0,0 +1,492 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis.acl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.alfresco.cmis.CMISAccessControlEntry;
import org.alfresco.cmis.CMISAccessControlFormatEnum;
import org.alfresco.cmis.CMISAccessControlReport;
import org.alfresco.cmis.CMISAccessControlService;
import org.alfresco.cmis.CMISAclCapabilityEnum;
import org.alfresco.cmis.CMISAclPropagationEnum;
import org.alfresco.cmis.CMISAclSupportedPermissionEnum;
import org.alfresco.cmis.CMISAllowedActionEnum;
import org.alfresco.cmis.CMISConstraintException;
import org.alfresco.cmis.CMISDictionaryService;
import org.alfresco.cmis.CMISPermissionDefinition;
import org.alfresco.cmis.CMISPermissionMapping;
import org.alfresco.cmis.CMISTypeDefinition;
import org.alfresco.cmis.mapping.CMISMapping;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.permissions.PermissionReference;
import org.alfresco.repo.security.permissions.impl.AccessPermissionImpl;
import org.alfresco.repo.security.permissions.impl.ModelDAO;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.AccessPermission;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.QName;
/**
* @author andyh
*/
public class CMISAccessControlServiceImpl implements CMISAccessControlService
{
private CMISAclCapabilityEnum aclCapabilityEnum;
private CMISAclSupportedPermissionEnum aclSupportedPermissionEnum;
private CMISAclPropagationEnum aclPropagationEnum;
private ModelDAO permissionModelDao;
private PermissionService permissionService;
private CMISMapping cmisMapping;
private NodeService nodeService;
private CMISDictionaryService cmisDictionaryService;
/**
* @param aclCapabilityEnum
* the aclCapabilityEnum to set
*/
public void setAclCapabilityEnum(CMISAclCapabilityEnum aclCapabilityEnum)
{
this.aclCapabilityEnum = aclCapabilityEnum;
}
/**
* Sets the acl supported permission enum.
*
* @param aclSupportedPermissionEnum
* the aclSupportedPermissionEnum to set
*/
public void setAclSupportedPermissionEnum(CMISAclSupportedPermissionEnum aclSupportedPermissionEnum)
{
this.aclSupportedPermissionEnum = aclSupportedPermissionEnum;
}
/**
* @param aclPropagationEnum
* the aclPropagationEnum to set
*/
public void setAclPropagationEnum(CMISAclPropagationEnum aclPropagationEnum)
{
this.aclPropagationEnum = aclPropagationEnum;
}
/**
* @param permissionModelDao
* the permissionModelDao to set
*/
public void setPermissionModelDao(ModelDAO permissionModelDao)
{
this.permissionModelDao = permissionModelDao;
}
/**
* @param permissionService
* the permissionService to set
*/
public void setPermissionService(PermissionService permissionService)
{
this.permissionService = permissionService;
}
/**
* Sets the cmis mapping.
*
* @param cmisMapping
* the cmis mapping
*/
public void setCMISMapping(CMISMapping cmisMapping)
{
this.cmisMapping = cmisMapping;
}
/**
* @param nodeService
* the nodeService to set
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @param cmisDictionaryService
* the cmisDictionaryService to set
*/
public void setCMISDictionaryService(CMISDictionaryService cmisDictionaryService)
{
this.cmisDictionaryService = cmisDictionaryService;
}
/*
* (non-Javadoc)
* @see org.alfresco.cmis.CMISAccessControlService#applyAcl(org.alfresco.service.cmr.repository.NodeRef,
* java.util.List)
*/
public CMISAccessControlReport applyAcl(NodeRef nodeRef, List<CMISAccessControlEntry> acesToApply)
{
Set<CMISAccessControlEntry> acesToAdd = new LinkedHashSet<CMISAccessControlEntry>(acesToApply);
List<? extends CMISAccessControlEntry> acesExisting = getAcl(nodeRef,
CMISAccessControlFormatEnum.REPOSITORY_SPECIFIC_PERMISSIONS).getAccessControlEntries();
List<CMISAccessControlEntry> acesToRemove = new ArrayList<CMISAccessControlEntry>(acesExisting.size());
for (CMISAccessControlEntry accessControlEntry : acesExisting)
{
// Only pay attention to existing direct entries
if (accessControlEntry.getDirect() && !acesToAdd.remove(accessControlEntry))
{
acesToRemove.add(accessControlEntry);
}
}
return applyAcl(nodeRef, acesToRemove, new ArrayList<CMISAccessControlEntry>(acesToAdd),
CMISAclPropagationEnum.PROPAGATE,
CMISAccessControlFormatEnum.REPOSITORY_SPECIFIC_PERMISSIONS);
}
/*
* (non-Javadoc)
*
* @see org.alfresco.cmis.CMISAccessControlService#applyAcl(org.alfresco.service.cmr.repository.NodeRef,
* java.util.List, java.util.List, org.alfresco.cmis.CMISAclPropagationEnum)
*/
public CMISAccessControlReport applyAcl(NodeRef nodeRef, List<CMISAccessControlEntry> acesToRemove, List<CMISAccessControlEntry> acesToAdd, CMISAclPropagationEnum propagation,
CMISAccessControlFormatEnum format)
{
if (propagation != CMISAclPropagationEnum.PROPAGATE)
{
throw new CMISConstraintException("Unsupported ACL propagation mode: " + propagation);
}
// Check controllable ACL
QName type = nodeService.getType(nodeRef);
CMISTypeDefinition cmisType = cmisDictionaryService.findTypeForClass(type);
if (false == cmisType.isControllableACL())
{
throw new CMISConstraintException("ACLs are not supported for type: " + cmisType.getDisplayName());
}
// TODO: Check valid permissions. We do not check this internally. Ignore for now ...
if (acesToRemove != null)
{
Set<AccessPermission> permissions = permissionService.getAllSetPermissions(nodeRef);
for (CMISAccessControlEntry entry : acesToRemove)
{
String alfrescoPermission = cmisMapping.getSetPermission(compressPermission(entry.getPermission()));
AccessPermission toCheck = new AccessPermissionImpl(alfrescoPermission, AccessStatus.ALLOWED, entry.getPrincipalId(), 0);
if (false == permissions.contains(toCheck))
{
throw new CMISConstraintException("No matching ACE found to delete");
}
permissionService.deletePermission(nodeRef, entry.getPrincipalId(), alfrescoPermission);
}
}
if (acesToAdd != null)
{
for (CMISAccessControlEntry entry : acesToAdd)
{
String alfrescoPermission = cmisMapping.getSetPermission(compressPermission(entry.getPermission()));
permissionService.setPermission(nodeRef, entry.getPrincipalId(), alfrescoPermission, true);
}
}
return getAcl(nodeRef, format);
}
/*
* (non-Javadoc)
*
* @see org.alfresco.cmis.CMISAccessControlService#getAcl(org.alfresco.service.cmr.repository.NodeRef,
* org.alfresco.cmis.CMISAccessControlFormatEnum)
*/
public CMISAccessControlReport getAcl(NodeRef nodeRef, CMISAccessControlFormatEnum format)
{
CMISAccessControlReportImpl answer = new CMISAccessControlReportImpl();
// Need to compact deny to mask correctly
Set<AccessPermission> permissions = permissionService.getAllSetPermissions(nodeRef);
ArrayList<AccessPermission> ordered = new ArrayList<AccessPermission>();
AccessPermissionComparator comparator = new AccessPermissionComparator();
for (AccessPermission current : permissions)
{
int index = Collections.binarySearch(ordered, current, comparator);
if (index < 0)
{
ordered.add(-index - 1, current);
}
}
for (AccessPermission entry : ordered)
{
if (entry.getAccessStatus() == AccessStatus.ALLOWED)
{
answer.addEntry(new CMISAccessControlEntryImpl(entry.getAuthority(), expandPermission(cmisMapping.getReportedPermission(getPermission(entry.getPermission()),
format)), entry.getPosition()));
}
else if (entry.getAccessStatus() == AccessStatus.DENIED)
{
answer.removeEntry(new CMISAccessControlEntryImpl(entry.getAuthority(), expandPermission(cmisMapping.getReportedPermission(getPermission(entry.getPermission()),
format)), entry.getPosition()));
}
}
return answer;
}
private String getPermission(String string)
{
PermissionReference permissionReference = permissionModelDao.getPermissionReference(null, string);
if (permissionModelDao.isUnique(permissionReference))
{
return permissionReference.getName();
}
else
{
return permissionReference.toString();
}
}
private String expandPermission(String permission)
{
if (permission.equals(CMIS_ALL_PERMISSION))
{
return permission;
}
else if (permission.equals(CMIS_READ_PERMISSION))
{
return permission;
}
else if (permission.equals(CMIS_WRITE_PERMISSION))
{
return permission;
}
else if (permission.startsWith("{"))
{
return permission;
}
else
{
PermissionReference permissionReference = permissionModelDao.getPermissionReference(null, permission);
return permissionReference.toString();
}
}
private String compressPermission(String permission) {
int sepIndex;
if (permission.equals(CMIS_ALL_PERMISSION) || permission.equals(CMIS_READ_PERMISSION)
|| permission.equals(CMIS_WRITE_PERMISSION) || !permission.startsWith("{")
|| (sepIndex = permission.lastIndexOf('.')) == -1) {
return permission;
}
return permission.substring(sepIndex + 1);
}
/*
* (non-Javadoc)
*
* @see org.alfresco.cmis.CMISAccessControlService#getAclCapability()
*/
public CMISAclCapabilityEnum getAclCapability()
{
return aclCapabilityEnum;
}
/**
* Set the acl capability enum.
*
* @param aclCapabilityEnum
*/
public void setAclCapability(CMISAclCapabilityEnum aclCapabilityEnum)
{
this.aclCapabilityEnum = aclCapabilityEnum;
}
/*
* (non-Javadoc)
* @see org.alfresco.cmis.CMISAccessControlService#getSupportedPermissions()
*/
public CMISAclSupportedPermissionEnum getSupportedPermissions()
{
return this.aclSupportedPermissionEnum;
}
/**
* Sets the supported permissions.
*
* @param aclSupportedPermissionEnum
* the supported permissions
*/
public void setSupportedPermissions(CMISAclSupportedPermissionEnum aclSupportedPermissionEnum)
{
this.aclSupportedPermissionEnum = aclSupportedPermissionEnum;
}
/*
* (non-Javadoc)
*
* @see org.alfresco.cmis.CMISAccessControlService#getAclPropagation()
*/
public CMISAclPropagationEnum getAclPropagation()
{
return aclPropagationEnum;
}
/**
* Set the acl propagation enum.
*
* @param aclPropagationEnum
*/
public void setAclPropagation(CMISAclPropagationEnum aclPropagationEnum)
{
this.aclPropagationEnum = aclPropagationEnum;
}
/*
* (non-Javadoc)
*
* @see org.alfresco.cmis.CMISAccessControlService#getPermissionMappings()
*/
public List<? extends CMISPermissionMapping> getPermissionMappings()
{
ArrayList<CMISPermissionMappingImpl> mappings = new ArrayList<CMISPermissionMappingImpl>();
for(CMISAllowedActionEnum e : EnumSet.allOf(CMISAllowedActionEnum.class))
{
Map<String, List<String>> enumMappings = e.getPermissionMapping();
for(String key : enumMappings.keySet())
{
List<String> list = enumMappings.get(key);
CMISPermissionMappingImpl mapping = new CMISPermissionMappingImpl(key, list);
mappings.add(mapping);
}
}
return mappings;
}
/*
* (non-Javadoc)
*
* @see org.alfresco.cmis.CMISAccessControlService#getRepositoryPermissions()
*/
public List<CMISPermissionDefinition> getRepositoryPermissions()
{
ArrayList<CMISPermissionDefinition> answer = new ArrayList<CMISPermissionDefinition>();
PermissionReference allPermission = permissionModelDao.getPermissionReference(null, PermissionService.ALL_PERMISSIONS);
Set<PermissionReference> all = permissionModelDao.getAllExposedPermissions();
for (PermissionReference pr : all)
{
addPermissionDefinition(answer, pr);
}
// Add All
addPermissionDefinition(answer, allPermission);
// Add CMIS permissions
answer.add(new CMISPermissionDefinitionImpl(CMIS_ALL_PERMISSION));
answer.add(new CMISPermissionDefinitionImpl(CMIS_READ_PERMISSION));
answer.add(new CMISPermissionDefinitionImpl(CMIS_WRITE_PERMISSION));
return answer;
}
/*
* (non-Javadoc)
* @see org.alfresco.cmis.CMISAccessControlService#getPrincipalAnonymous()
*/
public String getPrincipalAnonymous()
{
return AuthenticationUtil.getGuestUserName();
}
/*
* (non-Javadoc)
* @see org.alfresco.cmis.CMISAccessControlService#getPrincipalAnyone()
*/
public String getPrincipalAnyone()
{
return PermissionService.ALL_AUTHORITIES;
}
private void addPermissionDefinition(ArrayList<CMISPermissionDefinition> list, PermissionReference pr)
{
CMISPermissionDefinitionImpl def = new CMISPermissionDefinitionImpl(getPermissionString(pr));
list.add(def);
}
private String getPermissionString(PermissionReference pr)
{
StringBuilder builder = new StringBuilder();
builder.append(pr.getQName().toString());
builder.append(".");
builder.append(pr.getName());
return builder.toString();
}
public static class AccessPermissionComparator implements Comparator<AccessPermission>
{
/*
* (non-Javadoc)
*
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
public int compare(AccessPermission left, AccessPermission right)
{
if (left.getPosition() != right.getPosition())
{
return right.getPosition() - left.getPosition();
}
else
{
if (left.getAccessStatus() != right.getAccessStatus())
{
return (left.getAccessStatus() == AccessStatus.DENIED) ? -1 : 1;
}
else
{
int compare = left.getAuthority().compareTo(right.getAuthority());
if (compare != 0)
{
return compare;
}
else
{
return (left.getPermission().compareTo(right.getPermission()));
}
}
}
}
}
}

View File

@@ -0,0 +1,581 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis.acl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.alfresco.cmis.CMISAccessControlEntriesGroupedByPrincipalId;
import org.alfresco.cmis.CMISAccessControlEntry;
import org.alfresco.cmis.CMISAccessControlFormatEnum;
import org.alfresco.cmis.CMISAccessControlReport;
import org.alfresco.cmis.CMISAccessControlService;
import org.alfresco.cmis.CMISAclCapabilityEnum;
import org.alfresco.cmis.CMISAclPropagationEnum;
import org.alfresco.cmis.CMISConstraintException;
import org.alfresco.cmis.CMISPermissionDefinition;
import org.alfresco.cmis.CMISPermissionMapping;
import org.alfresco.cmis.acl.CMISAccessControlServiceImpl.AccessPermissionComparator;
import org.alfresco.cmis.mapping.BaseCMISTest;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.permissions.PermissionReference;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.AccessPermission;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.QName;
/**
* @author andyh
*/
public class CMISAccessControlServiceTest extends BaseCMISTest
{
private NodeRef parent;
private NodeRef child;
private NodeRef grandParent;
public void testAclPropagationMode()
{
assertEquals(CMISAclPropagationEnum.PROPAGATE, cmisAccessControlService.getAclPropagation());
}
public void testAclCapability()
{
assertEquals(CMISAclCapabilityEnum.MANAGE, cmisAccessControlService.getAclCapability());
}
public void testPermissions()
{
List<CMISPermissionDefinition> allPermissions = cmisAccessControlService.getRepositoryPermissions();
assertEquals(69, allPermissions.size());
HashSet<CMISPermissionDefinition> permissionSet = new HashSet<CMISPermissionDefinition>();
permissionSet.addAll(allPermissions);
assertEquals(69, permissionSet.size());
}
public void testAclReportingCmisPermissionsOnly()
{
createTestAcls();
CMISAccessControlReport grandParentReport = cmisAccessControlService.getAcl(grandParent, CMISAccessControlFormatEnum.CMIS_BASIC_PERMISSIONS);
assertFalse(grandParentReport.isExtract());
assertEquals(7, grandParentReport.getAccessControlEntries().size());
assertTrue(checkCounts(grandParentReport, PermissionService.ADMINISTRATOR_AUTHORITY, 1, 0));
assertTrue(checkCounts(grandParentReport, PermissionService.ALL_AUTHORITIES, 1, 0));
assertTrue(checkCounts(grandParentReport, "ToMask", 1, 0));
assertTrue(checkCounts(grandParentReport, "Full", 1, 0));
assertTrue(checkCounts(grandParentReport, "Reader", 1, 0));
assertTrue(checkCounts(grandParentReport, "Writer", 1, 0));
assertTrue(checkCounts(grandParentReport, "Multi", 1, 0));
CMISAccessControlReport parentReport = cmisAccessControlService.getAcl(parent, CMISAccessControlFormatEnum.CMIS_BASIC_PERMISSIONS);
assertFalse(parentReport.isExtract());
assertEquals(9, parentReport.getAccessControlEntries().size());
assertTrue(checkCounts(parentReport, PermissionService.ADMINISTRATOR_AUTHORITY, 0, 1));
assertTrue(checkCounts(parentReport, PermissionService.ALL_AUTHORITIES, 0, 1));
assertTrue(checkAbsent(parentReport, "ToMask"));
assertTrue(checkCounts(parentReport, "Full", 0, 1));
assertTrue(checkCounts(parentReport, "Reader", 0, 1));
assertTrue(checkCounts(parentReport, "Writer", 0, 1));
assertTrue(checkCounts(parentReport, "SplitRead", 1, 0));
assertTrue(checkCounts(parentReport, "SplitWrite", 1, 0));
assertTrue(checkCounts(parentReport, "DuplicateRead", 1, 0));
assertTrue(checkCounts(parentReport, "Multi", 1, 0));
CMISAccessControlReport childReport = cmisAccessControlService.getAcl(child, CMISAccessControlFormatEnum.CMIS_BASIC_PERMISSIONS);
assertFalse(childReport.isExtract());
assertEquals(12, childReport.getAccessControlEntries().size());
assertTrue(checkCounts(childReport, PermissionService.ADMINISTRATOR_AUTHORITY, 0, 1));
assertTrue(checkCounts(childReport, PermissionService.ALL_AUTHORITIES, 0, 1));
assertTrue(checkAbsent(childReport, "ToMask"));
assertTrue(checkCounts(childReport, "Full", 0, 1));
assertTrue(checkCounts(childReport, "Reader", 0, 1));
assertTrue(checkCounts(childReport, "Writer", 0, 1));
assertTrue(checkCounts(childReport, "SplitRead", 1, 0));
assertTrue(checkCounts(childReport, "SplitWrite", 1, 0));
assertTrue(checkCounts(childReport, "DuplicateRead", 1, 0));
assertTrue(checkCounts(childReport, "Writer2", 1, 0));
assertTrue(checkCounts(childReport, "Multi", 3, 0));
}
private Set<String> getAllPermissions()
{
HashSet<String> answer = new HashSet<String>();
PermissionReference allPermission = permissionModelDao.getPermissionReference(null, PermissionService.ALL_PERMISSIONS);
Set<PermissionReference> all = permissionModelDao.getAllPermissions();
for (PermissionReference pr : all)
{
answer.add(pr.toString());
}
// Add All
answer.add(allPermission.toString());
// Add CMIS permissions
answer.add(CMISAccessControlService.CMIS_ALL_PERMISSION);
answer.add(CMISAccessControlService.CMIS_READ_PERMISSION);
answer.add(CMISAccessControlService.CMIS_WRITE_PERMISSION);
return answer;
}
private boolean checkCounts(CMISAccessControlReport report, String key, int direct, int indirect)
{
// check all permissions are valid
Set<String> permissionNames = getAllPermissions();
for (CMISAccessControlEntry entry : report.getAccessControlEntries())
{
if (!permissionNames.contains(entry.getPermission()))
{
return false;
}
}
// check counts
for (CMISAccessControlEntriesGroupedByPrincipalId group : report.getAccessControlEntriesGroupedByPrincipalId())
{
if (group.getPrincipalId().equals(key))
{
if (group.getDirectPermissions().size() != direct)
{
return false;
}
if (group.getIndirectPermissions().size() != indirect)
{
return false;
}
return true;
}
}
return false;
}
private boolean checkAbsent(CMISAccessControlReport report, String key)
{
for (CMISAccessControlEntriesGroupedByPrincipalId group : report.getAccessControlEntriesGroupedByPrincipalId())
{
if (group.getPrincipalId().equals(key))
{
return false;
}
}
return true;
}
public void testAclReportingAllPermissions()
{
createTestAcls();
CMISAccessControlReport grandParentReport = cmisAccessControlService.getAcl(grandParent, CMISAccessControlFormatEnum.REPOSITORY_SPECIFIC_PERMISSIONS);
assertFalse(grandParentReport.isExtract());
assertEquals(7, grandParentReport.getAccessControlEntries().size());
assertTrue(checkCounts(grandParentReport, PermissionService.ADMINISTRATOR_AUTHORITY, 1, 0));
assertTrue(checkCounts(grandParentReport, PermissionService.ALL_AUTHORITIES, 1, 0));
assertTrue(checkCounts(grandParentReport, "ToMask", 1, 0));
assertTrue(checkCounts(grandParentReport, "Full", 1, 0));
assertTrue(checkCounts(grandParentReport, "Reader", 1, 0));
assertTrue(checkCounts(grandParentReport, "Writer", 1, 0));
assertTrue(checkCounts(grandParentReport, "Multi", 1, 0));
CMISAccessControlReport parentReport = cmisAccessControlService.getAcl(parent, CMISAccessControlFormatEnum.REPOSITORY_SPECIFIC_PERMISSIONS);
assertFalse(parentReport.isExtract());
assertEquals(10, parentReport.getAccessControlEntries().size());
assertTrue(checkCounts(parentReport, PermissionService.ADMINISTRATOR_AUTHORITY, 0, 1));
assertTrue(checkCounts(parentReport, PermissionService.ALL_AUTHORITIES, 0, 1));
assertTrue(checkAbsent(parentReport, "ToMask"));
assertTrue(checkCounts(parentReport, "Full", 0, 1));
assertTrue(checkCounts(parentReport, "Reader", 0, 1));
assertTrue(checkCounts(parentReport, "Writer", 0, 1));
assertTrue(checkCounts(parentReport, "SplitRead", 1, 0));
assertTrue(checkCounts(parentReport, "SplitWrite", 1, 0));
assertTrue(checkCounts(parentReport, "DuplicateRead", 1, 0));
assertTrue(checkCounts(parentReport, "Multi", 1, 1));
CMISAccessControlReport childReport = cmisAccessControlService.getAcl(child, CMISAccessControlFormatEnum.REPOSITORY_SPECIFIC_PERMISSIONS);
assertFalse(childReport.isExtract());
assertEquals(16, childReport.getAccessControlEntries().size());
assertTrue(checkCounts(childReport, PermissionService.ADMINISTRATOR_AUTHORITY, 0, 1));
assertTrue(checkCounts(childReport, PermissionService.ALL_AUTHORITIES, 0, 1));
assertTrue(checkAbsent(childReport, "ToMask"));
assertTrue(checkCounts(childReport, "Full", 0, 1));
assertTrue(checkCounts(childReport, "Reader", 0, 1));
assertTrue(checkCounts(childReport, "Writer", 0, 1));
assertTrue(checkCounts(childReport, "SplitRead", 1, 1));
assertTrue(checkCounts(childReport, "SplitWrite", 1, 1));
assertTrue(checkCounts(childReport, "DuplicateRead", 1, 0));
assertTrue(checkCounts(childReport, "Multi", 3, 2));
assertTrue(checkCounts(childReport, "Writer2", 1, 0));
}
private void createTestAcls()
{
grandParent = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("cm", "Parent", namespaceService), ContentModel.TYPE_FOLDER).getChildRef();
nodeService.setProperty(grandParent, ContentModel.PROP_NAME, "GrandParent");
parent = nodeService.createNode(grandParent, ContentModel.ASSOC_CHILDREN, QName.createQName("cm", "Child", namespaceService), ContentModel.TYPE_FOLDER).getChildRef();
nodeService.setProperty(parent, ContentModel.PROP_NAME, "Parent");
child = nodeService.createNode(parent, ContentModel.ASSOC_CHILDREN, QName.createQName("cm", "Child", namespaceService), ContentModel.TYPE_FOLDER).getChildRef();
nodeService.setProperty(child, ContentModel.PROP_NAME, "Child");
permissionService.setPermission(grandParent, PermissionService.ADMINISTRATOR_AUTHORITY, PermissionService.ALL_PERMISSIONS, true);
permissionService.setPermission(grandParent, PermissionService.ALL_AUTHORITIES, PermissionService.READ, true);
permissionService.setPermission(grandParent, "ToMask", PermissionService.READ, true);
permissionService.setPermission(grandParent, "Full", PermissionService.FULL_CONTROL, true);
permissionService.setPermission(grandParent, "Writer", PermissionService.WRITE, true);
permissionService.setPermission(grandParent, "Reader", PermissionService.READ, true);
permissionService.setPermission(grandParent, "Multi", PermissionService.DELETE, true);
permissionService.setPermission(parent, "ToMask", PermissionService.READ, false);
permissionService.setPermission(parent, "SplitRead", PermissionService.READ_PROPERTIES, true);
permissionService.setPermission(parent, "SplitWrite", PermissionService.WRITE_CONTENT, true);
permissionService.setPermission(parent, "DuplicateRead", PermissionService.READ, true);
permissionService.setPermission(parent, "Multi", PermissionService.CREATE_CHILDREN, true);
permissionService.setPermission(child, "SplitRead", PermissionService.READ_CONTENT, true);
permissionService.setPermission(child, "Writer2", PermissionService.WRITE, true);
permissionService.setPermission(child, "SplitWrite", PermissionService.WRITE_PROPERTIES, true);
permissionService.setPermission(child, "DuplicateRead", PermissionService.READ, true);
permissionService.setPermission(child, "Multi", PermissionService.READ, true);
permissionService.setPermission(child, "Multi", PermissionService.WRITE, true);
permissionService.setPermission(child, "Multi", PermissionService.SET_OWNER, true);
}
public void testAccessEntryOrdering()
{
createTestAcls();
Set<CMISPermissionDefinition> permDefs = new HashSet<CMISPermissionDefinition>();
permDefs.addAll(cmisAccessControlService.getRepositoryPermissions());
Set<AccessPermission> permissions = permissionService.getAllSetPermissions(child);
ArrayList<AccessPermission> ordered = new ArrayList<AccessPermission>();
AccessPermissionComparator comparator = new AccessPermissionComparator();
for (AccessPermission current : permissions)
{
int index = Collections.binarySearch(ordered, current, comparator);
if (index < 0)
{
ordered.add(-index - 1, current);
}
}
int i = 0;
assertEquals(4, ordered.get(i).getPosition());
assertEquals(AccessStatus.ALLOWED, ordered.get(i).getAccessStatus());
assertEquals("Full", ordered.get(i).getAuthority());
i++;
assertEquals(4, ordered.get(i).getPosition());
assertEquals(AccessStatus.ALLOWED, ordered.get(i).getAccessStatus());
assertEquals(PermissionService.ALL_AUTHORITIES, ordered.get(i).getAuthority());
i++;
assertEquals(4, ordered.get(i).getPosition());
assertEquals(AccessStatus.ALLOWED, ordered.get(i).getAccessStatus());
assertEquals("Multi", ordered.get(i).getAuthority());
i++;
assertEquals(4, ordered.get(i).getPosition());
assertEquals(AccessStatus.ALLOWED, ordered.get(i).getAccessStatus());
assertEquals(PermissionService.ADMINISTRATOR_AUTHORITY, ordered.get(i).getAuthority());
i++;
assertEquals(4, ordered.get(i).getPosition());
assertEquals(AccessStatus.ALLOWED, ordered.get(i).getAccessStatus());
assertEquals("Reader", ordered.get(i).getAuthority());
i++;
assertEquals(4, ordered.get(i).getPosition());
assertEquals(AccessStatus.ALLOWED, ordered.get(i).getAccessStatus());
assertEquals("ToMask", ordered.get(i).getAuthority());
i++;
assertEquals(4, ordered.get(i).getPosition());
assertEquals(AccessStatus.ALLOWED, ordered.get(i).getAccessStatus());
assertEquals("Writer", ordered.get(i).getAuthority());
i++;
assertEquals(2, ordered.get(i).getPosition());
assertEquals(AccessStatus.DENIED, ordered.get(i).getAccessStatus());
assertEquals("ToMask", ordered.get(i).getAuthority());
i++;
assertEquals(2, ordered.get(i).getPosition());
assertEquals(AccessStatus.ALLOWED, ordered.get(i).getAccessStatus());
assertEquals("DuplicateRead", ordered.get(i).getAuthority());
i++;
assertEquals(2, ordered.get(i).getPosition());
assertEquals(AccessStatus.ALLOWED, ordered.get(i).getAccessStatus());
assertEquals("Multi", ordered.get(i).getAuthority());
i++;
assertEquals(2, ordered.get(i).getPosition());
assertEquals(AccessStatus.ALLOWED, ordered.get(i).getAccessStatus());
assertEquals("SplitRead", ordered.get(i).getAuthority());
i++;
assertEquals(2, ordered.get(i).getPosition());
assertEquals(AccessStatus.ALLOWED, ordered.get(i).getAccessStatus());
assertEquals("SplitWrite", ordered.get(i).getAuthority());
i++;
assertEquals(0, ordered.get(i).getPosition());
assertEquals(AccessStatus.ALLOWED, ordered.get(i).getAccessStatus());
assertEquals("DuplicateRead", ordered.get(i).getAuthority());
i++;
assertEquals(0, ordered.get(i).getPosition());
assertEquals(AccessStatus.ALLOWED, ordered.get(i).getAccessStatus());
assertEquals("Multi", ordered.get(i).getAuthority());
i++;
assertEquals(0, ordered.get(i).getPosition());
assertEquals(AccessStatus.ALLOWED, ordered.get(i).getAccessStatus());
assertEquals("Multi", ordered.get(i).getAuthority());
i++;
assertEquals(0, ordered.get(i).getPosition());
assertEquals(AccessStatus.ALLOWED, ordered.get(i).getAccessStatus());
assertEquals("Multi", ordered.get(i).getAuthority());
i++;
assertEquals(0, ordered.get(i).getPosition());
assertEquals(AccessStatus.ALLOWED, ordered.get(i).getAccessStatus());
assertEquals("SplitRead", ordered.get(i).getAuthority());
i++;
assertEquals(0, ordered.get(i).getPosition());
assertEquals(AccessStatus.ALLOWED, ordered.get(i).getAccessStatus());
assertEquals("SplitWrite", ordered.get(i).getAuthority());
i++;
assertEquals(0, ordered.get(i).getPosition());
assertEquals(AccessStatus.ALLOWED, ordered.get(i).getAccessStatus());
assertEquals("Writer2", ordered.get(i).getAuthority());
}
public void testApplyAcl()
{
grandParent = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("cm", "Parent", namespaceService), ContentModel.TYPE_FOLDER).getChildRef();
nodeService.setProperty(grandParent, ContentModel.PROP_NAME, "GrandParent");
parent = nodeService.createNode(grandParent, ContentModel.ASSOC_CHILDREN, QName.createQName("cm", "Child", namespaceService), ContentModel.TYPE_FOLDER).getChildRef();
nodeService.setProperty(parent, ContentModel.PROP_NAME, "Parent");
child = nodeService.createNode(parent, ContentModel.ASSOC_CHILDREN, QName.createQName("cm", "Child", namespaceService), ContentModel.TYPE_CONTENT).getChildRef();
nodeService.setProperty(child, ContentModel.PROP_NAME, "Child");
List<CMISAccessControlEntry> acesToAdd = new ArrayList<CMISAccessControlEntry>();
acesToAdd.add(new CMISAccessControlEntryImpl(PermissionService.ADMINISTRATOR_AUTHORITY, PermissionService.ALL_PERMISSIONS));
acesToAdd.add(new CMISAccessControlEntryImpl(PermissionService.ALL_AUTHORITIES, PermissionService.READ));
acesToAdd.add(new CMISAccessControlEntryImpl("ToMask", PermissionService.READ));
acesToAdd.add(new CMISAccessControlEntryImpl("Full", PermissionService.FULL_CONTROL));
acesToAdd.add(new CMISAccessControlEntryImpl("Writer", PermissionService.WRITE));
acesToAdd.add(new CMISAccessControlEntryImpl("Reader", PermissionService.READ));
CMISAccessControlReport grandParentReport = cmisAccessControlService.applyAcl(grandParent, null, acesToAdd, CMISAclPropagationEnum.PROPAGATE,
CMISAccessControlFormatEnum.REPOSITORY_SPECIFIC_PERMISSIONS);
assertFalse(grandParentReport.isExtract());
assertEquals(6, grandParentReport.getAccessControlEntries().size());
List<CMISAccessControlEntry> acesToRemove = new ArrayList<CMISAccessControlEntry>();
acesToRemove.add(new CMISAccessControlEntryImpl("ToMask", PermissionService.READ));
grandParentReport = cmisAccessControlService.applyAcl(grandParent, acesToRemove, null, CMISAclPropagationEnum.PROPAGATE,
CMISAccessControlFormatEnum.REPOSITORY_SPECIFIC_PERMISSIONS);
assertFalse(grandParentReport.isExtract());
assertEquals(5, grandParentReport.getAccessControlEntries().size());
try
{
grandParentReport = cmisAccessControlService.applyAcl(grandParent, acesToRemove, null, CMISAclPropagationEnum.PROPAGATE,
CMISAccessControlFormatEnum.REPOSITORY_SPECIFIC_PERMISSIONS);
fail("A non existent ACE should not be removable");
}
catch (CMISConstraintException e)
{
}
acesToAdd = new ArrayList<CMISAccessControlEntry>();
acesToAdd.add(new CMISAccessControlEntryImpl("SplitRead", permissionModelDao.getPermissionReference(null, PermissionService.READ_PROPERTIES).toString()));
acesToAdd.add(new CMISAccessControlEntryImpl("SplitWrite", permissionModelDao.getPermissionReference(null, PermissionService.WRITE_CONTENT).toString()));
acesToAdd.add(new CMISAccessControlEntryImpl("DuplicateRead", permissionModelDao.getPermissionReference(null, PermissionService.READ).toString()));
CMISAccessControlReport parentReport = cmisAccessControlService.applyAcl(parent, null, acesToAdd, CMISAclPropagationEnum.PROPAGATE,
CMISAccessControlFormatEnum.REPOSITORY_SPECIFIC_PERMISSIONS);
assertFalse(parentReport.isExtract());
assertEquals(8, parentReport.getAccessControlEntries().size());
acesToAdd = new ArrayList<CMISAccessControlEntry>();
acesToAdd.add(new CMISAccessControlEntryImpl("SplitRead", PermissionService.READ_CONTENT));
acesToAdd.add(new CMISAccessControlEntryImpl("Writer2", PermissionService.WRITE));
acesToAdd.add(new CMISAccessControlEntryImpl("SplitWrite", PermissionService.WRITE_PROPERTIES));
acesToAdd.add(new CMISAccessControlEntryImpl("DuplicateRead", PermissionService.READ));
CMISAccessControlReport childReport = cmisAccessControlService.applyAcl(child, null, acesToAdd, CMISAclPropagationEnum.PROPAGATE,
CMISAccessControlFormatEnum.REPOSITORY_SPECIFIC_PERMISSIONS);
assertFalse(childReport.isExtract());
assertEquals(11, childReport.getAccessControlEntries().size());
grandParentReport = cmisAccessControlService.getAcl(grandParent, CMISAccessControlFormatEnum.CMIS_BASIC_PERMISSIONS);
assertFalse(grandParentReport.isExtract());
assertEquals(5, grandParentReport.getAccessControlEntries().size());
parentReport = cmisAccessControlService.getAcl(parent, CMISAccessControlFormatEnum.CMIS_BASIC_PERMISSIONS);
assertFalse(parentReport.isExtract());
assertEquals(8, parentReport.getAccessControlEntries().size());
childReport = cmisAccessControlService.getAcl(child, CMISAccessControlFormatEnum.CMIS_BASIC_PERMISSIONS);
assertFalse(childReport.isExtract());
assertEquals(9, childReport.getAccessControlEntries().size());
grandParentReport = cmisAccessControlService.getAcl(grandParent, CMISAccessControlFormatEnum.REPOSITORY_SPECIFIC_PERMISSIONS);
assertFalse(grandParentReport.isExtract());
assertEquals(5, grandParentReport.getAccessControlEntries().size());
parentReport = cmisAccessControlService.getAcl(parent, CMISAccessControlFormatEnum.REPOSITORY_SPECIFIC_PERMISSIONS);
assertFalse(parentReport.isExtract());
assertEquals(8, parentReport.getAccessControlEntries().size());
childReport = cmisAccessControlService.getAcl(child, CMISAccessControlFormatEnum.REPOSITORY_SPECIFIC_PERMISSIONS);
assertFalse(childReport.isExtract());
assertEquals(11, childReport.getAccessControlEntries().size());
acesToAdd = new ArrayList<CMISAccessControlEntry>();
acesToAdd.add(new CMISAccessControlEntryImpl("CMISReader", CMISAccessControlService.CMIS_READ_PERMISSION));
acesToAdd.add(new CMISAccessControlEntryImpl("CMISWriter", CMISAccessControlService.CMIS_WRITE_PERMISSION));
acesToAdd.add(new CMISAccessControlEntryImpl("CMISAll", CMISAccessControlService.CMIS_ALL_PERMISSION));
childReport = cmisAccessControlService.applyAcl(child, null, acesToAdd, CMISAclPropagationEnum.PROPAGATE, CMISAccessControlFormatEnum.REPOSITORY_SPECIFIC_PERMISSIONS);
assertFalse(childReport.isExtract());
assertEquals(14, childReport.getAccessControlEntries().size());
childReport = cmisAccessControlService.applyAcl(child, acesToAdd, acesToAdd, CMISAclPropagationEnum.PROPAGATE, CMISAccessControlFormatEnum.REPOSITORY_SPECIFIC_PERMISSIONS);
assertFalse(childReport.isExtract());
assertEquals(14, childReport.getAccessControlEntries().size());
childReport = cmisAccessControlService.applyAcl(child, acesToAdd, null, CMISAclPropagationEnum.PROPAGATE, CMISAccessControlFormatEnum.REPOSITORY_SPECIFIC_PERMISSIONS);
assertFalse(childReport.isExtract());
assertEquals(11, childReport.getAccessControlEntries().size());
try
{
childReport = cmisAccessControlService.applyAcl(child, acesToAdd, null, CMISAclPropagationEnum.PROPAGATE, CMISAccessControlFormatEnum.REPOSITORY_SPECIFIC_PERMISSIONS);
fail("A non existent ACE should not be removable");
}
catch (CMISConstraintException e)
{
}
}
public void testAllowableActionsAndPermissionMapping()
{
List<? extends CMISPermissionMapping> mappings = cmisAccessControlService.getPermissionMappings();
assertEquals(29, mappings.size());
assertTrue(contains(mappings, "canGetDescendants.Folder", CMISAccessControlService.CMIS_READ_PERMISSION, "{http://www.alfresco.org/model/system/1.0}base.ReadChildren"));
assertTrue(contains(mappings, "canGetFolderTree.Folder", CMISAccessControlService.CMIS_READ_PERMISSION, "{http://www.alfresco.org/model/system/1.0}base.ReadChildren"));
assertTrue(contains(mappings, "canGetChildren.Folder", CMISAccessControlService.CMIS_READ_PERMISSION, "{http://www.alfresco.org/model/system/1.0}base.ReadChildren"));
assertTrue(contains(mappings, "canGetFolderParent.Folder", CMISAccessControlService.CMIS_READ_PERMISSION, "{http://www.alfresco.org/model/system/1.0}base.ReadProperties"));
assertTrue(contains(mappings, "canGetObjectParents.Object", CMISAccessControlService.CMIS_READ_PERMISSION, "{http://www.alfresco.org/model/system/1.0}base.ReadProperties"));
assertTrue(contains(mappings, "canCreateDocument.Folder", CMISAccessControlService.CMIS_ALL_PERMISSION, "{http://www.alfresco.org/model/system/1.0}base.CreateChildren"));
assertTrue(contains(mappings, "canCreateFolder.Folder", CMISAccessControlService.CMIS_ALL_PERMISSION, "{http://www.alfresco.org/model/system/1.0}base.CreateChildren"));
// "canCreateRelationship.Source"
// "canCreateRelationship.Target"
assertTrue(contains(mappings, "canGetProperties.Object", CMISAccessControlService.CMIS_READ_PERMISSION, "{http://www.alfresco.org/model/system/1.0}base.ReadProperties"));
assertTrue(contains(mappings, "canGetRenditions.Object", CMISAccessControlService.CMIS_READ_PERMISSION, "{http://www.alfresco.org/model/system/1.0}base.ReadProperties"));
assertTrue(contains(mappings, "canGetContentStream.Object", CMISAccessControlService.CMIS_READ_PERMISSION, "{http://www.alfresco.org/model/system/1.0}base.ReadContent"));
assertTrue(contains(mappings, "canUpdateProperties.Object", CMISAccessControlService.CMIS_WRITE_PERMISSION, "{http://www.alfresco.org/model/system/1.0}base.WriteProperties"));
assertTrue(contains(mappings, "canMoveObject.Object", CMISAccessControlService.CMIS_ALL_PERMISSION, "{http://www.alfresco.org/model/system/1.0}base.DeleteNode"));
assertTrue(contains(mappings, "canMoveObject.Target", CMISAccessControlService.CMIS_ALL_PERMISSION, "{http://www.alfresco.org/model/system/1.0}base.CreateChildren"));
// "canMoveObject.Source"
assertTrue(contains(mappings, "canDelete.Object", CMISAccessControlService.CMIS_ALL_PERMISSION, "{http://www.alfresco.org/model/system/1.0}base.DeleteNode"));
// "canDelete.Folder"
// === SPEC BUG - should really be those below ...
// "canDeleteObject.Object"
// "canDeleteObject.Folder"
assertTrue(contains(mappings, "canSetContentStream.Document", CMISAccessControlService.CMIS_WRITE_PERMISSION, "{http://www.alfresco.org/model/system/1.0}base.WriteContent"));
assertTrue(contains(mappings, "canDeleteContentStream.Document", CMISAccessControlService.CMIS_WRITE_PERMISSION, "{http://www.alfresco.org/model/system/1.0}base.WriteContent"));
assertTrue(contains(mappings, "canDeleteTree.Folder", CMISAccessControlService.CMIS_ALL_PERMISSION, "{http://www.alfresco.org/model/system/1.0}base.DeleteNode"));
assertTrue(contains(mappings, "canAddObjectToFolder.Object", CMISAccessControlService.CMIS_READ_PERMISSION, "{http://www.alfresco.org/model/system/1.0}base.ReadProperties"));
assertTrue(contains(mappings, "canAddObjectToFolder.Folder", CMISAccessControlService.CMIS_ALL_PERMISSION, "{http://www.alfresco.org/model/system/1.0}base.CreateChildren"));
assertTrue(contains(mappings, "canRemoveObjectFromFolder.Object", CMISAccessControlService.CMIS_ALL_PERMISSION, "{http://www.alfresco.org/model/system/1.0}base.DeleteNode"));
// "canRemoveObjectFromFolder.Folder"
assertTrue(contains(mappings, "canCheckOut.Document", CMISAccessControlService.CMIS_ALL_PERMISSION, "{http://www.alfresco.org/model/content/1.0}lockable.CheckOut"));
assertTrue(contains(mappings, "canCancelCheckOut.Document", CMISAccessControlService.CMIS_ALL_PERMISSION, "{http://www.alfresco.org/model/content/1.0}lockable.CancelCheckOut"));
assertTrue(contains(mappings, "canCheckIn.Document", CMISAccessControlService.CMIS_ALL_PERMISSION, "{http://www.alfresco.org/model/content/1.0}lockable.CheckIn"));
assertTrue(contains(mappings, "canGetAllVersions.Document", CMISAccessControlService.CMIS_READ_PERMISSION, "{http://www.alfresco.org/model/system/1.0}base.Read"));
// "canGetObjectRelationships.Object"
assertTrue(contains(mappings, "canApplyPolicy.Object", CMISAccessControlService.CMIS_WRITE_PERMISSION, "{http://www.alfresco.org/model/system/1.0}base.Write"));
assertTrue(contains(mappings, "canApplyPolicy.Policy", CMISAccessControlService.CMIS_READ_PERMISSION));
assertTrue(contains(mappings, "canRemovePolicy.Object", CMISAccessControlService.CMIS_WRITE_PERMISSION, "{http://www.alfresco.org/model/system/1.0}base.Write"));
assertTrue(contains(mappings, "canRemovePolicy.Policy", CMISAccessControlService.CMIS_READ_PERMISSION));
assertTrue(contains(mappings, "canGetAppliedPolicies.Object", CMISAccessControlService.CMIS_READ_PERMISSION, "{http://www.alfresco.org/model/system/1.0}base.ReadProperties"));
assertTrue(contains(mappings, "canGetACL.Object", CMISAccessControlService.CMIS_ALL_PERMISSION, "{http://www.alfresco.org/model/system/1.0}base.ReadPermissions"));
assertTrue(contains(mappings, "canApplyACL.Object", CMISAccessControlService.CMIS_ALL_PERMISSION, "{http://www.alfresco.org/model/system/1.0}base.ChangePermissions"));
}
private boolean contains(List<? extends CMISPermissionMapping> mappings, String key, String... entries)
{
for (CMISPermissionMapping mapping : mappings)
{
if (mapping.getKey().equals(key))
{
// check entries are all valid
Set<String> permissionNames = getAllPermissions();
for (String permission : mapping.getPermissions())
{
if (!permissionNames.contains(permission))
{
return false;
}
}
if (entries.length > 0)
{
if (mapping.getPermissions().size() == entries.length)
{
for (String entry : entries)
{
if (!mapping.getPermissions().contains(entry))
{
return false;
}
}
return true;
}
}
else
{
return true;
}
}
}
return false;
}
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis.acl;
import org.alfresco.cmis.EnumFactory;
import org.alfresco.cmis.EnumLabel;
/**
* Part two of the allowable action key for the permission mappings
* @author andyh
*
*/
public enum CMISAllowedActionKeyTypeEnum implements EnumLabel
{
/**
* Folder
*/
FOLDER("Folder"),
/**
* Object
*/
OBJECT("Object"),
/**
* Source
*/
SOURCE("Source"),
/**
* Target
*/
TARGET("Target"),
/**
* Document
*/
DOCUMENT("Document"),
/**
* Policy
*/
POLICY("Policy");
private String label;
/**
* Construct
*
* @param label
*/
CMISAllowedActionKeyTypeEnum(String label)
{
this.label = label;
}
/*
* (non-Javadoc)
*
* @see org.alfresco.cmis.EnumLabel#label()
*/
public String getLabel()
{
return label;
}
/**
* Factory for CMISAclCapabilityEnum
*/
public static EnumFactory<CMISAllowedActionKeyTypeEnum> FACTORY = new EnumFactory<CMISAllowedActionKeyTypeEnum>(CMISAllowedActionKeyTypeEnum.class, null, true);
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis.acl;
import org.alfresco.cmis.CMISPermissionDefinition;
/**
* Implementation class for a simple CMIS permission definition.
*
* @author andyh
*
*/
public class CMISPermissionDefinitionImpl implements CMISPermissionDefinition
{
private String permission;
private String description;
/*package*/ CMISPermissionDefinitionImpl(String permission)
{
this.permission = permission;
}
/*package*/ CMISPermissionDefinitionImpl(String permission, String description)
{
this(permission);
this.description = description;
}
/* (non-Javadoc)
* @see org.alfresco.cmis.CMISPermissionDefinition#getDescription()
*/
public String getDescription()
{
if(description != null)
{
return description;
}
else
{
return permission;
}
}
/* (non-Javadoc)
* @see org.alfresco.cmis.CMISPermissionDefinition#getPermission()
*/
public String getPermission()
{
return permission;
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((permission == null) ? 0 : permission.hashCode());
return result;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final CMISPermissionDefinitionImpl other = (CMISPermissionDefinitionImpl) obj;
if (permission == null)
{
if (other.permission != null)
return false;
}
else if (!permission.equals(other.permission))
return false;
return true;
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis.acl;
import java.util.List;
import org.alfresco.cmis.CMISPermissionMapping;
/**
* @author andyh
*
*/
public class CMISPermissionMappingImpl implements CMISPermissionMapping
{
String key;
List<String> permissions;
CMISPermissionMappingImpl(String key, List<String> permissions)
{
this.key = key;
this.permissions = permissions;
}
/* (non-Javadoc)
* @see org.alfresco.cmis.CMISPermissionMapping#getKey()
*/
public String getKey()
{
return key;
}
/* (non-Javadoc)
* @see org.alfresco.cmis.CMISPermissionMapping#getPermissions()
*/
public List<String> getPermissions()
{
return permissions;
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis.acl;
import org.alfresco.cmis.CMISAllowedActionEnum;
/**
* @author andyh
*
*/
public interface CMISPermissionMappingKey
{
/**
* Get the allowed action enum.
* @return the allowed action enum.
*/
public CMISAllowedActionEnum getAllowedActionEnum();
/**
* Get the allowed action key type enum.
* @return the allowed action key type enum.
*/
public CMISAllowedActionKeyTypeEnum getAllowedActionKeyTypeEnum();
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis.changelog;
import java.util.Date;
import org.alfresco.cmis.CMISChangeEvent;
import org.alfresco.cmis.CMISChangeType;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* CMISChangeEvent Implementation
*
* @author Dmitry Velichkevich
*/
public class CMISChangeEventImpl implements CMISChangeEvent
{
private NodeRef node;
private CMISChangeType changeType;
private Date changeTime;
/**
* Construct a CMISChangeEvent using fields
*
* @param changeType change type
* @param node node reference
* @param changeTime change time
*/
public CMISChangeEventImpl(CMISChangeType changeType, NodeRef node, Date changeTime)
{
this.changeType = changeType;
this.node = node;
this.changeTime = changeTime;
}
/**
* @see org.alfresco.cmis.CMISChangeEvent#getChangeType()
*/
public CMISChangeType getChangeType()
{
return changeType;
}
/**
* @see org.alfresco.cmis.CMISChangeEvent#getNode()
*/
public NodeRef getNode()
{
return node;
}
/**
* @see org.alfresco.cmis.CMISChangeEvent#getChangeTime()
*/
public Date getChangeTime()
{
return changeTime;
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj)
{
if (!(obj instanceof CMISChangeEvent))
{
return false;
}
CMISChangeEvent converted = (CMISChangeEvent) obj;
return same(node, converted.getNode()) && same(changeType, converted.getChangeType()) && same(changeTime, converted.getChangeTime());
}
private boolean same(Object left, Object right)
{
return (null == left) ? (null == right) : (left.equals(right));
}
/**
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode()
{
int result = (null != node) ? (node.hashCode()) : (31);
return result * 37 + (null != changeType ? changeType.hashCode() : 31) + (null != changeTime ? changeTime.hashCode() : 31);
}
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing
*/
package org.alfresco.cmis.changelog;
import java.io.Serializable;
import org.alfresco.cmis.CMISServices;
import org.alfresco.repo.audit.extractor.AbstractDataExtractor;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* An extractor that allows to filter data using the following rule:
* Audit records should only be created for items in the CMIS domain model.
*
* @author Stas Sokolovsky
*/
public class CMISChangeLogDataExtractor extends AbstractDataExtractor
{
private CMISServices cmisService;
private FileFolderService fileFolderService;
/**
* Extracts the NodeRef from auditing data
*
* @see org.alfresco.repo.audit.extractor.DataExtractor.extractData(java.io.Serializable)
*/
public Serializable extractData(Serializable value) throws Throwable
{
return getNodeRef(value);
}
/**
* @return Returns <tt>true</tt> if items in the CMIS domain model
* @see org.alfresco.repo.audit.extractor.DataExtractor.isSupported(java.io.Serializable)
*/
public boolean isSupported(Serializable data)
{
boolean result = false;
if (data != null)
{
NodeRef nodeRef = getNodeRef(data);
if (nodeRef != null)
{
if (!fileFolderService.exists(nodeRef))
{
result = true;
}
// Does the node represent a file or folder
else if (fileFolderService.getFileInfo(nodeRef) != null)
{
// Is the node located within CMIS defaultRootStore
if (cmisService.getDefaultRootStoreRef().equals(nodeRef.getStoreRef()))
{
result = true;
}
}
}
}
return result;
}
/**
* Gets the NodeRef from auditing data
*
* @param data audit data
* @return Node Reference
*/
private NodeRef getNodeRef(Serializable data)
{
NodeRef nodeRef = null;
if (data instanceof FileInfo)
{
nodeRef = ((FileInfo) data).getNodeRef();
}
else if (data instanceof NodeRef)
{
nodeRef = (NodeRef) data;
}
return nodeRef;
}
/**
* Set the CMIS service
*
* @param cmisService CMIS service
*/
public void setCmisService(CMISServices cmisService)
{
this.cmisService = cmisService;
}
/**
* Set the FileFolder service
*
* @param fileFolderService FileFolder service
*/
public void setFileFolderService(FileFolderService fileFolderService)
{
this.fileFolderService = fileFolderService;
}
}

View File

@@ -0,0 +1,106 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis.changelog;
import java.util.LinkedList;
import java.util.List;
import org.alfresco.cmis.CMISChangeEvent;
import org.alfresco.cmis.CMISChangeLog;
/**
* CMISChangeLog Implementation
*
* @author Dmitry Velichkevich
*/
public class CMISChangeLogImpl implements CMISChangeLog
{
private boolean hasMoreItems;
private List<CMISChangeEvent> changeEvents = new LinkedList<CMISChangeEvent>();
private String nextChangeToken;
/**
* @see org.alfresco.cmis.CMISChangeLog#getChangeEvents()
*/
public List<CMISChangeEvent> getChangeEvents()
{
return changeEvents;
}
/**
* Set the change events
*
* @param changeEvents list of change events
*/
protected void setChangeEvents(List<CMISChangeEvent> changeEvents)
{
this.changeEvents = changeEvents;
}
/**
* @see org.alfresco.cmis.CMISChangeLog#getEventCount()
*/
public Integer getEventCount()
{
return changeEvents.size();
}
/**
* @see org.alfresco.cmis.CMISChangeLog#getNextChangeToken()
*/
public String getNextChangeToken()
{
return nextChangeToken;
}
/**
* Set the next ChangeToken
*
* @param nextChangeToken the next ChangeToken
*/
public void setNextChangeToken(String nextChangeToken)
{
this.nextChangeToken = nextChangeToken;
}
/**
* @see org.alfresco.cmis.CMISChangeLog#hasMoreItems()
*/
public boolean hasMoreItems()
{
return hasMoreItems;
}
/**
* Set the hasMoreItems value
*
* @param hasMoreItems hasMoreItems value
*/
protected void setHasMoreItems(boolean hasMoreItems)
{
this.hasMoreItems = hasMoreItems;
}
}

View File

@@ -0,0 +1,338 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis.changelog;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.alfresco.cmis.CMISBaseObjectTypeIds;
import org.alfresco.cmis.CMISCapabilityChanges;
import org.alfresco.cmis.CMISChangeEvent;
import org.alfresco.cmis.CMISChangeLog;
import org.alfresco.cmis.CMISChangeLogService;
import org.alfresco.cmis.CMISChangeType;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.service.cmr.audit.AuditQueryParameters;
import org.alfresco.service.cmr.audit.AuditService;
import org.alfresco.service.cmr.audit.AuditService.AuditQueryCallback;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* ChangeLog Service Implementation
*
* @author Dmitry Velichkevich
*/
public class CMISChangeLogServiceImpl implements CMISChangeLogService
{
private AuditService auditService;
private String cmisAuditApplicationName;
private List<CMISBaseObjectTypeIds> changesOnTypeCapability;
private static final String PATH_DELIMITER = "/";
private static final int DEFAULT_RETURN_SIZE = 100;
/**
* @see org.alfresco.cmis.CMISChangeLogService#getCapability()
*/
public CMISCapabilityChanges getCapability()
{
return (auditService.isAuditEnabled(cmisAuditApplicationName, PATH_DELIMITER + cmisAuditApplicationName)) ? (CMISCapabilityChanges.OBJECTIDSONLY)
: (CMISCapabilityChanges.NONE);
}
/**
* @see org.alfresco.cmis.CMISChangeLogService#getChangeLogEvents(java.lang.String, java.lang.Integer)
*/
public CMISChangeLog getChangeLogEvents(String changeLogToken, Integer maxItems)
{
if (!auditService.isAuditEnabled(cmisAuditApplicationName, ("/" + cmisAuditApplicationName)))
{
throw new AlfrescoRuntimeException("Auditing for " + cmisAuditApplicationName + " is disabled!");
}
CMISChangeLogImpl result = new CMISChangeLogImpl();
final List<CMISChangeEvent> changeEvents = result.getChangeEvents();
EntryIdCallback changeLogCollectingCallback = new EntryIdCallback(true)
{
@Override
public boolean handleAuditEntry(Long entryId, String user, long time, Map<String, Serializable> values)
{
List<CMISChangeEvent> changeLogEvents = convertValuesMapToChangeLogEvents(values, time);
changeEvents.addAll(changeLogEvents);
return super.handleAuditEntry(entryId, user, time, values);
}
};
Long from = changeLogToken != null ? Long.parseLong(changeLogToken) : null;
AuditQueryParameters params = new AuditQueryParameters();
params.setApplicationName(cmisAuditApplicationName);
params.setForward(true);
params.setFromId(from);
// Query one past the last item, so that we know what the next ID is
int maxAmount = ((null == maxItems) || (0 == maxItems)) ? (0) : (maxItems + 1);
auditService.auditQuery(changeLogCollectingCallback, params, maxAmount);
if ((0 != maxAmount) && (changeEvents.size() > maxItems))
{
changeEvents.remove(changeEvents.size() - 1);
result.setNextChangeToken(changeLogCollectingCallback.getEntryId().toString());
result.setHasMoreItems(true);
}
return result;
}
/**
* @see org.alfresco.cmis.CMISChangeLogService#getChangesIncomplete()
*/
public boolean getChangesIncomplete()
{
return true;
}
/**
* @see org.alfresco.cmis.CMISChangeLogService#getLastChangeLogToken()
*/
public String getLastChangeLogToken()
{
EntryIdCallback auditQueryCallback = new EntryIdCallback(false);
AuditQueryParameters params = new AuditQueryParameters();
params.setApplicationName(cmisAuditApplicationName);
params.setForward(false);
auditService.auditQuery(auditQueryCallback, params, 1);
return auditQueryCallback.getEntryId();
}
/**
* @see org.alfresco.cmis.CMISChangeLogService#getPreviousPageChangeLogToken(java.lang.String, java.lang.Integer)
*/
public String getPreviousPageChangeLogToken(String currentPageToken, Integer maxItems)
{
if (currentPageToken == null)
{
return null;
}
maxItems = maxItems == null ? DEFAULT_RETURN_SIZE : maxItems;
EntryIdCallback auditQueryCallback = new EntryIdCallback(false);
AuditQueryParameters params = new AuditQueryParameters();
params.setApplicationName(cmisAuditApplicationName);
params.setForward(false);
params.setToId(Long.parseLong(currentPageToken));
auditService.auditQuery(auditQueryCallback, params, maxItems);
return auditQueryCallback.getEntryId();
}
/**
* @see org.alfresco.cmis.CMISChangeLogService#getLastPageChangeLogToken(java.lang.String, java.lang.Integer)
*/
public String getLastPageChangeLogToken(String currentPageToken, Integer maxItems)
{
maxItems = maxItems == null ? DEFAULT_RETURN_SIZE : maxItems;
EntryIdCallback auditQueryCallback = new PageStartEntryIdCallback(maxItems);
AuditQueryParameters params = new AuditQueryParameters();
params.setApplicationName(cmisAuditApplicationName);
if (currentPageToken != null)
{
params.setFromId(Long.parseLong(currentPageToken));
}
auditService.auditQuery(auditQueryCallback, params, -1);
return auditQueryCallback.getEntryId();
}
/**
* @see org.alfresco.cmis.CMISChangeLogService#getChangesOnTypeCapability()
*/
public List<CMISBaseObjectTypeIds> getChangesOnTypeCapability()
{
if (null == changesOnTypeCapability)
{
changesOnTypeCapability = new LinkedList<CMISBaseObjectTypeIds>();
}
return changesOnTypeCapability;
}
/**
* Set the AuditService.
*
* @param auditService AuditService
*/
public void setAuditService(AuditService auditService)
{
this.auditService = auditService;
}
/**
* Set the Audit Application Name.
*
* @param cmisAuditApplicationName Audit Application Name
*/
public void setCmisAuditApplicationName(String cmisAuditApplicationName)
{
this.cmisAuditApplicationName = cmisAuditApplicationName;
}
/**
* Set the ChangesOnTypeCapability.
*
* @param changesOnTypeCapability list of CMISBaseObjectTypeIds
*/
public void setChangesOnTypeCapability(List<CMISBaseObjectTypeIds> changesOnTypeCapability)
{
this.changesOnTypeCapability = changesOnTypeCapability;
}
/**
* Converts audit values map to list of CMISChangeEvents.
*
* @param values audit values map
* @param time audit event time
* @return list of CMISChangeEvent
*/
private List<CMISChangeEvent> convertValuesMapToChangeLogEvents(Map<String, Serializable> values, long time)
{
List<CMISChangeEvent> result = new ArrayList<CMISChangeEvent>();
if (values != null && values.size() > 0)
{
for (Entry<String, Serializable> entry : values.entrySet())
{
if (entry.getKey() != null && entry.getValue() != null)
{
String path = entry.getKey();
CMISChangeType changeType = getCMISChangeType(path);
NodeRef nodeRef = entry.getValue() instanceof NodeRef ? (NodeRef) entry.getValue() : null;
if (changeType != null && nodeRef != null)
{
result.add(new CMISChangeEventImpl(changeType, nodeRef, new Date(time)));
}
}
}
}
return result;
}
/**
* Gets CMISChangeType by audit path.
*
* @param auditPath audit path
* @return CMISChangeType
*/
private CMISChangeType getCMISChangeType(String auditPath)
{
CMISChangeType result = null;
if (auditPath != null)
{
if (auditPath.startsWith(PATH_DELIMITER))
{
auditPath = auditPath.substring(PATH_DELIMITER.length());
}
if (auditPath.startsWith(cmisAuditApplicationName))
{
auditPath = auditPath.substring(cmisAuditApplicationName.length());
}
if (auditPath.startsWith(PATH_DELIMITER))
{
auditPath = auditPath.substring(PATH_DELIMITER.length());
}
auditPath = auditPath.toLowerCase();
for (CMISChangeType changeType : CMISChangeType.values())
{
if (auditPath.startsWith(changeType.getLabel()))
{
result = changeType;
break;
}
}
}
return result;
}
private class EntryIdCallback implements AuditQueryCallback
{
private final boolean valuesRequired;
private Long entryId;
public EntryIdCallback(boolean valuesRequired)
{
this.valuesRequired = valuesRequired;
}
public String getEntryId()
{
return entryId == null ? null : entryId.toString();
}
public boolean valuesRequired()
{
return this.valuesRequired;
}
public final boolean handleAuditEntry(Long entryId, String applicationName, String user, long time, Map<String, Serializable> values)
{
if (applicationName.equals(CMISChangeLogServiceImpl.this.cmisAuditApplicationName))
{
return handleAuditEntry(entryId, user, time, values);
}
return true;
}
public boolean handleAuditEntry(Long entryId, String user, long time, Map<String, Serializable> values)
{
this.entryId = entryId;
return true;
}
public boolean handleAuditEntryError(Long entryId, String errorMsg, Throwable error)
{
throw new AlfrescoRuntimeException(errorMsg, error);
}
};
private class PageStartEntryIdCallback extends EntryIdCallback
{
private final int pageSize;
private int indexWithinPage;
public PageStartEntryIdCallback(int pageSize)
{
super(false);
this.pageSize = pageSize;
this.indexWithinPage = -1;
}
@Override
public boolean handleAuditEntry(Long entryId, String user, long time, Map<String, Serializable> values)
{
if (++this.indexWithinPage == this.pageSize)
{
this.indexWithinPage = 0;
return super.handleAuditEntry(entryId, user, time, values);
}
return true;
}
}
}

View File

@@ -0,0 +1,419 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis.changelog;
import java.io.Serializable;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.alfresco.cmis.CMISCapabilityChanges;
import org.alfresco.cmis.CMISChangeEvent;
import org.alfresco.cmis.CMISChangeLog;
import org.alfresco.cmis.CMISChangeLogService;
import org.alfresco.cmis.CMISChangeType;
import org.alfresco.cmis.mapping.BaseCMISTest;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.management.subsystems.ApplicationContextFactory;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.ApplicationContextHelper;
import org.springframework.context.ApplicationContext;
import org.springframework.extensions.surf.util.Pair;
/**
* Base tests for {@link CMISChangeLogServiceImpl}
*
* @author Dmitry Velichkevich
*/
public class CMISChangeLogServiceTest extends BaseCMISTest
{
private static final String CMIS_AUTHORITY = "cmis";
private static final String CHANGE_PREFIX = "Changed";
private static final String INVALID_CHANGE_TOKEN = "<Invalid Change Token>";
private static final String[] NAME_PARTS = new String[] { "TestDocument (", ").txt", "TestFolder (", ")" };
private static int TOTAL_AMOUNT = 31;
private static int CREATED_AMOUNT = 18;
private static final int THE_HALFT_OF_CREATED_AMOUNT = CREATED_AMOUNT / 2;
private static final Map<CMISChangeType, Integer> EXPECTED_AMOUNTS = new HashMap<CMISChangeType, Integer>();
static
{
EXPECTED_AMOUNTS.put(CMISChangeType.CREATED, 5);
EXPECTED_AMOUNTS.put(CMISChangeType.DELETED, 3);
EXPECTED_AMOUNTS.put(CMISChangeType.SECURITY, 4);
EXPECTED_AMOUNTS.put(CMISChangeType.UPDATED, 6);
}
private ApplicationContextFactory auditSubsystem;
private CMISChangeLogService changeLogService;
private int actualCount = 0;
private Map<CMISChangeType, Integer> actualAmounts = new HashMap<CMISChangeType, Integer>();
private List<NodeRef> created = null;
private List<NodeRef> deleted = null;
private void disableAudit()
{
auditSubsystem.stop();
auditSubsystem.setProperty("audit.enabled", "true");
auditSubsystem.setProperty("audit.cmis.enabled", "false");
}
private void enableAudit()
{
auditSubsystem.stop();
auditSubsystem.setProperty("audit.enabled", "true");
auditSubsystem.setProperty("audit.cmis.enabled", "true");
}
/**
* Tests {@link CMISChangeLogServiceImpl} with disabled Auditing feature
*
* @throws Exception
*/
public void testServiceWithDisabledAuditing() throws Exception
{
disableAudit();
String lastChangeLogToken = changeLogService.getLastChangeLogToken();
createTestData(EXPECTED_AMOUNTS, false);
assertEquals(CMISCapabilityChanges.NONE, changeLogService.getCapability());
try
{
changeLogService.getChangeLogEvents(lastChangeLogToken, null);
fail("Changes Logging was not enabled but no one Change Log Service method thrown exception");
}
catch (Exception e)
{
assertTrue("Invalid exception type from Change Log Service method call with desabled Changes Logging", e instanceof AlfrescoRuntimeException);
}
}
/**
* Tests {@link CMISChangeLogServiceImpl} with enabled Auditing feature
*
* @throws Exception
*/
public void testEnabledAuditing() throws Exception
{
enableAudit();
String logToken = changeLogService.getLastChangeLogToken();
createTestData(EXPECTED_AMOUNTS, false);
assertEquals(CMISCapabilityChanges.OBJECTIDSONLY, changeLogService.getCapability());
CMISChangeLog changeLog = changeLogService.getChangeLogEvents(logToken, null);
assertChangeLog(logToken, changeLog);
assertChangeEvents(logToken, changeLog, null, FoldersAppearing.NOT_EXPECTED);
}
/**
* Validates Change Log descriptor that was returned for some Change Log Token
*
* @param logToken {@link String} value that represents last Change Log Token
* @param changeLog {@link CMISChangeLog} instance that represents Change Log descriptor
*/
private void assertChangeLog(String logToken, CMISChangeLog changeLog)
{
assertNotNull(("'" + logToken + "' Change Log Token has no descriptor"), changeLog);
assertNotNull(("Event Etries for '" + logToken + "' Change Log Token are undefined"), changeLog.getChangeEvents());
assertFalse(("Descriptor for '" + logToken + "' Change Log Token has no any Event Entry"), changeLog.getChangeEvents().isEmpty());
}
/**
* Creates test data which will represent Change Events of all possible types
*
* @param requiredAmounts {@link Map}&lt;{@link CMISChangeType}, {@link Integer}&gt; container instance that determines amount of Change Event for each Change Type
* @return pair containing list of created node refs, and list of deleted node refs
* @see CMISChangeType
*/
private Pair<List<NodeRef>, List<NodeRef>> createTestData(Map<CMISChangeType, Integer> requiredAmounts, boolean withFolders)
{
changeLogService.getLastChangeLogToken();
created = new LinkedList<NodeRef>();
deleted = new LinkedList<NodeRef>();
Pair<List<NodeRef>, List<NodeRef>> result = new Pair<List<NodeRef>, List<NodeRef>>(created, deleted);
NodeRef parentNodeRef = nodeService.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE);
SecureRandom randomizer = new SecureRandom();
for (CMISChangeType key : requiredAmounts.keySet())
{
Integer amount = requiredAmounts.get(key);
for (int i = 0; i < amount; i++)
{
boolean folder = withFolders && (0 == ((Math.abs(randomizer.nextInt()) % amount) % 2));
QName objectType = (folder) ? (ContentModel.TYPE_FOLDER) : (ContentModel.TYPE_CONTENT);
FileInfo object = fileFolderService.create(parentNodeRef, generateName(randomizer, folder), objectType);
created.add(object.getNodeRef());
addOneToAmount(actualAmounts, CMISChangeType.CREATED);
switch (key)
{
case DELETED:
{
nodeService.deleteNode(object.getNodeRef());
deleted.add(object.getNodeRef());
addOneToAmount(actualAmounts, CMISChangeType.DELETED);
break;
}
case SECURITY:
{
permissionService.setPermission(object.getNodeRef(), CMIS_AUTHORITY, PermissionService.EXECUTE_CONTENT, true);
addOneToAmount(actualAmounts, CMISChangeType.SECURITY);
break;
}
case UPDATED:
{
StringBuilder nameBuilder = new StringBuilder(CHANGE_PREFIX);
nameBuilder.append(nodeService.getProperty(object.getNodeRef(), ContentModel.PROP_NAME));
nodeService.setProperty(object.getNodeRef(), ContentModel.PROP_NAME, nameBuilder.toString());
addOneToAmount(actualAmounts, CMISChangeType.UPDATED);
}
}
actualCount++;
}
}
return result;
}
/**
* Deletes each element of created test data if element exist and current user has appropriate rights
*
* @param testData {@link Map}&lt;{@link NodeRef}, {@link Map}&lt;{@link QName}, {@link Serializable}&gt;&gt; container instance that contains test data
*/
private void deleteTestData()
{
if (created != null)
{
for (NodeRef object : created)
{
if (nodeService.exists(object) && (AccessStatus.ALLOWED == permissionService.hasPermission(object, PermissionService.DELETE)))
{
nodeService.deleteNode(object);
}
}
}
}
/**
* @param folder {@link Boolean} value that determines which name should be generated (for Folder or Document Object)
* @return {@link String} value that represents generated uniquely name for Document Object
*/
private String generateName(SecureRandom randomizer, boolean folder)
{
StringBuilder nameBuilder = new StringBuilder();
int i = (folder) ? (2) : (0);
nameBuilder.append(NAME_PARTS[i++]).append(Math.abs(randomizer.nextInt())).append(NAME_PARTS[i++]);
return nameBuilder.toString();
}
/**
* This method validates Change Event entries according to created earlier Objects. According to <b>assertProperties</b> parameter this method may and may not check properties
* of Change Event entry according to appropriate expected Object against Change Type
*
* @param expectedObjects {@link Map}&lt;{@link NodeRef}, {@link Map}&lt;{@link QName}, {@link Serializable}&gt;&gt; container instance that contains Ids and properties of
* expected Objects
* @param logToken {@link String} value that represents last Change Log Token
* @param changeLog {@link CMISChangeLog} instance that represents Change Log descriptor for last Change Log Token
* @param maxItems {@link Integer} value that determines high bound of Change Events amount
* @see CMISChangeType
*/
private void assertChangeEvents(String logToken, CMISChangeLog changeLog, Integer maxItems, FoldersAppearing foldersAppearing)
{
Map<CMISChangeType, Integer> logAmounts = new HashMap<CMISChangeType, Integer>();
boolean folderWasFound = false;
for (CMISChangeEvent event : changeLog.getChangeEvents())
{
assertNotNull(("One of the Change Log Event Enries is undefined for '" + logToken + "' Change Log Token"), event);
assertNotNull(("Change Event Entry Id of one of the Change Entries is undefined for '" + logToken + "' Change Log Token"), event.getNode());
assertNotNull(("Change Event Change Type of one of the Change Entries is undefined for '" + logToken + "' Change Log Token"), event.getChangeType());
assertTrue("Unexpected Object Id='" + event.getNode().toString() + "' from Change Log Token Entries list for '" + logToken + "' Change Log Token", created
.contains(event.getNode()));
if (!deleted.contains(event.getNode()))
{
folderWasFound = folderWasFound || fileFolderService.getFileInfo(event.getNode()).isFolder();
assertTrue(
("Object from Change Event Entries list is marked as '" + event.getChangeType().toString() + "' but does not exist for '" + logToken + "' Change Log Token"),
nodeService.exists(event.getNode()));
}
else
{
assertTrue("Object has been deleted", deleted.contains(event.getNode()));
assertFalse(("Object from Change Event Entries list is marked as 'DELETED' but it still exist for '" + logToken + "' Change Log Token"), nodeService.exists(event
.getNode()));
}
addOneToAmount(logAmounts, event.getChangeType());
}
if (FoldersAppearing.MUST_APPEAR == foldersAppearing)
{
assertTrue("No one Folder Object was returned", folderWasFound);
}
else
{
if (FoldersAppearing.NOT_EXPECTED == foldersAppearing)
{
assertFalse("Some Folder Object was found", folderWasFound);
}
}
if ((null == maxItems) || (maxItems >= TOTAL_AMOUNT))
{
for (CMISChangeType key : actualAmounts.keySet())
{
Integer actualAmount = actualAmounts.get(key);
Integer logAmount = logAmounts.get(key);
assertTrue(("Invalid Entries amount for '" + key.toString() + "' Change Type. Actual amount: " + actualAmount + ", but log amount: " + logAmount), actualAmount
.equals(logAmount));
}
}
}
private enum FoldersAppearing
{
NOT_EXPECTED, MAY_APPEAR, MUST_APPEAR
}
/**
* Determines which kind of Change was handled and increments appropriate amount to 1
*
* @param mappedAmounts {@link Map}&gt;{@link CMISChangeType}, {@link Integer}&lt; container instance that contains all accumulated amounts for each kind of Change
* @param changeType {@link CMISChangeType} enum value that determines kind of Change
*/
private void addOneToAmount(Map<CMISChangeType, Integer> mappedAmounts, CMISChangeType changeType)
{
Integer amount = mappedAmounts.get(changeType);
amount = (null == amount) ? (Integer.valueOf(1)) : (Integer.valueOf(amount.intValue() + 1));
mappedAmounts.put(changeType, amount);
}
/**
* Test {@link CMISChangeLogServiceImpl} with enabled Auditing feature for Max Items parameter
*
* @throws Exception
*/
public void testEnabledAuditingForMaxItems() throws Exception
{
enableAudit();
String logToken = changeLogService.getLastChangeLogToken();
createTestData(EXPECTED_AMOUNTS, false);
assertEquals(CMISCapabilityChanges.OBJECTIDSONLY, changeLogService.getCapability());
CMISChangeLog changeLog = changeLogService.getChangeLogEvents(logToken, THE_HALFT_OF_CREATED_AMOUNT);
assertChangeLog(logToken, changeLog);
assertChangeEvents(logToken, changeLog, THE_HALFT_OF_CREATED_AMOUNT, FoldersAppearing.NOT_EXPECTED);
assertEquals(THE_HALFT_OF_CREATED_AMOUNT, changeLog.getChangeEvents().size());
assertTrue("Not all Change Log Entries were requested but result set is indicating that no one more Entry is avilable", changeLog.hasMoreItems());
changeLog = changeLogService.getChangeLogEvents(logToken, TOTAL_AMOUNT);
assertChangeEvents(logToken, changeLog, TOTAL_AMOUNT, FoldersAppearing.NOT_EXPECTED);
assertFalse("All Change Log Entries were requested but result set is indicating that some more Entry(s) are available", changeLog.hasMoreItems());
}
/**
* This method tests {@link CMISChangeLogServiceImpl} on receiving Change Event Entries for Invalid Change Log Token with enable and disabled Changes Logging
*
* @throws Exception
*/
public void testReceivingChangeEventsForInvalidChangeToken() throws Exception
{
enableAudit();
try
{
changeLogService.getChangeLogEvents(INVALID_CHANGE_TOKEN, null);
fail("Change Events were received normally for Invalid Change Log Token");
}
catch (Exception e)
{
assertTrue("Invalid exception type from Change Log Service method call with enabled Changes Logging", e instanceof java.lang.NumberFormatException);
}
disableAudit();
try
{
changeLogService.getChangeLogEvents(INVALID_CHANGE_TOKEN, null);
fail("Changes Logging was not enabled but not one Change Log Service method thrown exception");
}
catch (Exception e)
{
assertTrue("Invalid exception type from Change Log Service method call with desabled Changes Logging", e instanceof AlfrescoRuntimeException);
}
}
/**
* This method tests {@link CMISChangeLogServiceImpl} on working with Change Event entries which could contain Folder Objects
*
* @throws Exception
*/
public void testReceivingOfChangeEventsExpectingFolders() throws Exception
{
enableAudit();
String changeToken = changeLogService.getLastChangeLogToken();
createTestData(EXPECTED_AMOUNTS, true);
CMISChangeLog changeLogEvents = changeLogService.getChangeLogEvents(changeToken, null);
assertChangeLog(changeToken, changeLogEvents);
assertChangeEvents(changeToken, changeLogEvents, null, FoldersAppearing.MUST_APPEAR);
}
/**
* This method tests {@link CMISChangeLogServiceImpl} on working with Change Event entries which could contain Folder Objects. Also this method tests behavior of Max Items
* parameter for Folder Objects
*
* @throws Exception
*/
public void testReceivingOfChangeEventsExpectingFoldersAndBoundedByMaxItems() throws Exception
{
enableAudit();
String changeToken = changeLogService.getLastChangeLogToken();
createTestData(EXPECTED_AMOUNTS, true);
CMISChangeLog changeLogEvents = changeLogService.getChangeLogEvents(changeToken, 15);
assertTrue("Not all Change Event Entries were requested but result set indicates that no more Entry(s) available", changeLogEvents.hasMoreItems());
assertChangeLog(changeToken, changeLogEvents);
assertChangeEvents(changeToken, changeLogEvents, 15, FoldersAppearing.MAY_APPEAR);
changeLogEvents = changeLogService.getChangeLogEvents(changeToken, TOTAL_AMOUNT);
assertChangeLog(changeToken, changeLogEvents);
assertChangeEvents(changeToken, changeLogEvents, TOTAL_AMOUNT, FoldersAppearing.MUST_APPEAR);
assertFalse("All Change Event Entries were requested but results indicating that some more Entry(s) available", changeLogEvents.hasMoreItems());
}
@Override
public void setUp() throws Exception
{
super.setUp();
ApplicationContext applicationContext = ApplicationContextHelper.getApplicationContext();
changeLogService = (CMISChangeLogService) applicationContext.getBean("CMISChangeLogService");
nodeService = (NodeService) applicationContext.getBean("NodeService");
permissionService = (PermissionService) applicationContext.getBean("PermissionService");
fileFolderService = (FileFolderService) applicationContext.getBean("FileFolderService");
auditSubsystem = (ApplicationContextFactory) applicationContext.getBean("Audit");
}
@Override
protected void tearDown() throws Exception
{
deleteTestData();
super.tearDown();
}
}

View File

@@ -78,7 +78,7 @@ public abstract class CMISAbstractTypeDefinition implements CMISTypeDefinition,
protected Boolean fullTextIndexed = null;
protected Boolean controllablePolicy = null;
protected Boolean controllableACL = null;
protected Boolean includeInSuperTypeQuery = null;
protected Boolean includedInSuperTypeQuery = null;
protected Collection<CMISTypeId> subTypeIds = null;
protected Collection<CMISTypeDefinition> subTypes = null;
protected Map<String, CMISPropertyDefinition> properties = null;
@@ -103,7 +103,7 @@ public abstract class CMISAbstractTypeDefinition implements CMISTypeDefinition,
if (fullTextIndexed == null) throw new IllegalStateException("fullTextIndexed not specified; objectTypeId=" + objectTypeId);
if (controllablePolicy == null) throw new IllegalStateException("controllablePolicy not specified; objectTypeId=" + objectTypeId);
if (controllableACL == null) throw new IllegalStateException("controllablePolicy not specified; objectTypeId=" + objectTypeId);
if (includeInSuperTypeQuery == null) throw new IllegalStateException("includeInSuperTypeQuery not specified; objectTypeId=" + objectTypeId);
if (includedInSuperTypeQuery == null) throw new IllegalStateException("includedInSuperTypeQuery not specified; objectTypeId=" + objectTypeId);
if (subTypeIds == null) throw new IllegalStateException("subTypeIds not specified; objectTypeId=" + objectTypeId);
if (subTypes == null) throw new IllegalStateException("subTypes not specified; objectTypeId=" + objectTypeId);
if (properties == null) throw new IllegalStateException("properties not specified; objectTypeId=" + objectTypeId);
@@ -413,11 +413,11 @@ public abstract class CMISAbstractTypeDefinition implements CMISTypeDefinition,
/*
* (non-Javadoc)
* @see org.alfresco.cmis.dictionary.CMISTypeDefinition#isIncludeInSuperTypeQuery()
* @see org.alfresco.cmis.dictionary.CMISTypeDefinition#isIncludedInSuperTypeQuery()
*/
public boolean isIncludeInSuperTypeQuery()
public boolean isIncludedInSuperTypeQuery()
{
return includeInSuperTypeQuery;
return includedInSuperTypeQuery;
}
/*

View File

@@ -42,6 +42,7 @@ import org.alfresco.cmis.mapping.CMISMapping;
import org.alfresco.repo.dictionary.IndexTokenisationMode;
import org.alfresco.repo.dictionary.constraint.ListOfValuesConstraint;
import org.alfresco.repo.dictionary.constraint.StringLengthConstraint;
import org.alfresco.repo.search.impl.lucene.analysis.DateAnalyser;
import org.alfresco.repo.search.impl.lucene.analysis.DateTimeAnalyser;
import org.alfresco.repo.search.impl.lucene.analysis.DoubleAnalyser;
import org.alfresco.repo.search.impl.lucene.analysis.FloatAnalyser;
@@ -51,6 +52,7 @@ import org.alfresco.repo.search.impl.lucene.analysis.PathAnalyser;
import org.alfresco.repo.search.impl.lucene.analysis.VerbatimAnalyser;
import org.alfresco.service.cmr.dictionary.Constraint;
import org.alfresco.service.cmr.dictionary.ConstraintDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
/**
@@ -64,22 +66,36 @@ public class CMISBasePropertyDefinition implements CMISPropertyDefinition, Seria
// Properties of Property
private CMISTypeDefinition typeDef;
private CMISPropertyId propertyId;
private String queryName;
private String displayName;
private String description;
private CMISDataTypeEnum propertyType;
private CMISCardinalityEnum cardinality;
private int maximumLength = -1;
private Collection<CMISChoice> choices = new HashSet<CMISChoice>();
private boolean isOpenChoice = false;
private boolean required;
private String defaultValue;
private CMISUpdatabilityEnum updatability;
private boolean queryable;
private boolean orderable;
private AbstractProperty propertyAccessor;
private CMISPropertyId propertyId;
private String queryName;
private String displayName;
private String description;
private CMISDataTypeEnum propertyType;
private CMISCardinalityEnum cardinality;
private int maximumLength = -1;
private Collection<CMISChoice> choices = new HashSet<CMISChoice>();
private boolean isOpenChoice = false;
private boolean required;
private String defaultValue;
private CMISUpdatabilityEnum updatability;
private boolean queryable;
private boolean orderable;
private AbstractProperty propertyAccessor;
/**
* Construct
@@ -122,6 +138,8 @@ public class CMISBasePropertyDefinition implements CMISPropertyDefinition, Seria
updatability = propDef.isProtected() ? CMISUpdatabilityEnum.READ_ONLY : CMISUpdatabilityEnum.READ_AND_WRITE;
queryable = propDef.isIndexed();
if (queryable)
{
if (cardinality == CMISCardinalityEnum.SINGLE_VALUED)
{
IndexTokenisationMode indexTokenisationMode = IndexTokenisationMode.TRUE;
if (propDef.getIndexTokenisationMode() != null)
@@ -137,7 +155,12 @@ public class CMISBasePropertyDefinition implements CMISPropertyDefinition, Seria
case TRUE:
default:
String analyserClassName = propDef.getDataType().getAnalyserClassName();
if (analyserClassName.equals(DateTimeAnalyser.class.getCanonicalName())
if(propDef.getDataType().getName().equals(DataTypeDefinition.BOOLEAN))
{
orderable = true;
}
else if (analyserClassName.equals(DateTimeAnalyser.class.getCanonicalName())
|| analyserClassName.equals(DateAnalyser.class.getCanonicalName())
|| analyserClassName.equals(DoubleAnalyser.class.getCanonicalName()) || analyserClassName.equals(FloatAnalyser.class.getCanonicalName())
|| analyserClassName.equals(IntegerAnalyser.class.getCanonicalName()) || analyserClassName.equals(LongAnalyser.class.getCanonicalName())
|| analyserClassName.equals(PathAnalyser.class.getCanonicalName()) || analyserClassName.equals(VerbatimAnalyser.class.getCanonicalName()))
@@ -154,6 +177,11 @@ public class CMISBasePropertyDefinition implements CMISPropertyDefinition, Seria
{
orderable = false;
}
}
else
{
orderable = false;
}
propertyAccessor = cmisMapping.getPropertyAccessor(propertyId);
}
@@ -327,7 +355,6 @@ public class CMISBasePropertyDefinition implements CMISPropertyDefinition, Seria
return propertyAccessor;
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()

View File

@@ -91,8 +91,8 @@ public class CMISDocumentTypeDefinition extends CMISAbstractTypeDefinition
queryable = true;
fullTextIndexed = true;
controllablePolicy = false;
controllableACL = false;
includeInSuperTypeQuery = true;
controllableACL = true;
includedInSuperTypeQuery = cmisClassDef.getIncludedInSuperTypeQuery();
actionEvaluators = cmisMapping.getActionEvaluators(objectTypeId.getScope());
@@ -160,7 +160,7 @@ public class CMISDocumentTypeDefinition extends CMISAbstractTypeDefinition
builder.append("Creatable=").append(isCreatable()).append(", ");
builder.append("Queryable=").append(isQueryable()).append(", ");
builder.append("FullTextIndexed=").append(isFullTextIndexed()).append(", ");
builder.append("IncludeInSuperTypeQuery=").append(isIncludeInSuperTypeQuery()).append(", ");
builder.append("IncludedInSuperTypeQuery=").append(isIncludedInSuperTypeQuery()).append(", ");
builder.append("ControllablePolicy=").append(isControllablePolicy()).append(", ");
builder.append("ControllableACL=").append(isControllableACL()).append(", ");
builder.append("Fileable=").append(isFileable()).append(", ");

View File

@@ -81,8 +81,8 @@ public class CMISFolderTypeDefinition extends CMISAbstractTypeDefinition
queryable = true;
fullTextIndexed = true;
controllablePolicy = false;
controllableACL = false;
includeInSuperTypeQuery = true;
controllableACL = true;
includedInSuperTypeQuery = cmisClassDef.getIncludedInSuperTypeQuery();
}
/*
@@ -104,7 +104,7 @@ public class CMISFolderTypeDefinition extends CMISAbstractTypeDefinition
builder.append("Creatable=").append(isCreatable()).append(", ");
builder.append("Queryable=").append(isQueryable()).append(", ");
builder.append("FullTextIndexed=").append(isFullTextIndexed()).append(", ");
builder.append("IncludeInSuperTypeQuery=").append(isIncludeInSuperTypeQuery()).append(", ");
builder.append("IncludedInSuperTypeQuery=").append(isIncludedInSuperTypeQuery()).append(", ");
builder.append("ControllablePolicy=").append(isControllablePolicy()).append(", ");
builder.append("ControllableACL=").append(isControllableACL()).append(", ");
builder.append("SubTypes=").append(getSubTypes(false).size()).append(", ");

View File

@@ -77,7 +77,7 @@ public class CMISObjectTypeDefinition extends CMISAbstractTypeDefinition
creatable = false;
queryable = false;
fullTextIndexed = false;
includeInSuperTypeQuery = false;
includedInSuperTypeQuery = cmisClassDef.getIncludedInSuperTypeQuery();
controllablePolicy = false;
controllableACL = false;
}
@@ -120,7 +120,7 @@ public class CMISObjectTypeDefinition extends CMISAbstractTypeDefinition
builder.append("Creatable=").append(isCreatable()).append(", ");
builder.append("Queryable=").append(isQueryable()).append(", ");
builder.append("Controllable=").append(isControllablePolicy()).append(", ");
builder.append("IncludeInSuperTypeQuery=").append(isIncludeInSuperTypeQuery()).append(", ");
builder.append("IncludedInSuperTypeQuery=").append(isIncludedInSuperTypeQuery()).append(", ");
builder.append("SubTypes=").append(getSubTypes(false).size()).append(", ");
builder.append("Properties=").append(getPropertyDefinitions().size());
builder.append("]");

View File

@@ -83,7 +83,7 @@ public class CMISPolicyTypeDefinition extends CMISAbstractTypeDefinition
creatable = false;
queryable = true;
includeInSuperTypeQuery = true;
includedInSuperTypeQuery = cmisClassDef.getIncludedInSuperTypeQuery();
fullTextIndexed = true;
controllablePolicy = false;
controllableACL = false;
@@ -147,7 +147,7 @@ public class CMISPolicyTypeDefinition extends CMISAbstractTypeDefinition
builder.append("Creatable=").append(isCreatable()).append(", ");
builder.append("Queryable=").append(isQueryable()).append(", ");
builder.append("Controllable=").append(isControllablePolicy()).append(", ");
builder.append("IncludeInSuperTypeQuery=").append(isIncludeInSuperTypeQuery()).append(", ");
builder.append("IncludedInSuperTypeQuery=").append(isIncludedInSuperTypeQuery()).append(", ");
builder.append("SubTypes=").append(getSubTypes(false).size()).append(", ");
builder.append("Properties=").append(getPropertyDefinitions().size());
builder.append("]");

View File

@@ -80,7 +80,7 @@ public class CMISRelationshipTypeDefinition extends CMISAbstractTypeDefinition
queryable = false;
fullTextIndexed = false;
includeInSuperTypeQuery = true;
includedInSuperTypeQuery = true;
controllablePolicy = false;
controllableACL = false;
@@ -247,7 +247,7 @@ public class CMISRelationshipTypeDefinition extends CMISAbstractTypeDefinition
builder.append("Creatable=").append(isCreatable()).append(", ");
builder.append("Queryable=").append(isQueryable()).append(", ");
builder.append("Controllable=").append(isControllablePolicy()).append(", ");
builder.append("IncludeInSuperTypeQuery=").append(isIncludeInSuperTypeQuery()).append(", ");
builder.append("IncludedInSuperTypeQuery=").append(isIncludedInSuperTypeQuery()).append(", ");
builder.append("AllowedSourceTypes=[");
for (CMISTypeDefinition type : getAllowedSourceTypes())
{

View File

@@ -139,7 +139,7 @@ public abstract class AbstractProperty implements CMISPropertyAccessor, CMISProp
throw new UnsupportedOperationException();
}
public String getLuceneSortField()
public String getLuceneSortField(LuceneQueryParser lqp)
{
throw new UnsupportedOperationException();
}

View File

@@ -239,7 +239,7 @@ public abstract class AbstractSimpleProperty extends AbstractProperty
* (non-Javadoc)
* @see org.alfresco.cmis.property.PropertyLuceneBuilder#getLuceneSortField()
*/
public String getLuceneSortField()
public String getLuceneSortField(LuceneQueryParser lqp)
{
return getLuceneFieldName();
}

View File

@@ -31,12 +31,17 @@ import javax.transaction.UserTransaction;
import junit.framework.TestCase;
import org.alfresco.cmis.CMISAccessControlService;
import org.alfresco.cmis.CMISDictionaryService;
import org.alfresco.cmis.CMISQueryService;
import org.alfresco.cmis.CMISRenditionService;
import org.alfresco.cmis.CMISServices;
import org.alfresco.repo.dictionary.DictionaryDAO;
import org.alfresco.repo.dictionary.NamespaceDAOImpl;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.MutableAuthenticationDao;
import org.alfresco.repo.security.permissions.impl.ModelDAO;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.model.FileFolderService;
@@ -47,6 +52,7 @@ import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.MutableAuthenticationService;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.thumbnail.ThumbnailService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ApplicationContextHelper;
@@ -69,6 +75,10 @@ public abstract class BaseCMISTest extends TestCase
protected CMISDictionaryService cmisDictionaryService;
protected CMISRenditionService cmisRenditionService;
protected CMISAccessControlService cmisAccessControlService;
protected DictionaryService dictionaryService;
protected TransactionService transactionService;
@@ -99,6 +109,14 @@ public abstract class BaseCMISTest extends TestCase
protected PermissionService permissionService;
protected ThumbnailService thumbnailService;
protected ModelDAO permissionModelDao;
protected DictionaryDAO dictionaryDAO;
protected NamespaceDAOImpl namespaceDao;
public void setUp() throws Exception
{
serviceRegistry = (ServiceRegistry) ctx.getBean("ServiceRegistry");
@@ -107,6 +125,8 @@ public abstract class BaseCMISTest extends TestCase
cmisMapping = (CMISMapping) ctx.getBean("CMISMapping");
cmisQueryService = (CMISQueryService) ctx.getBean("CMISQueryService");
cmisService = (CMISServices) ctx.getBean("CMISService");
cmisRenditionService = (CMISRenditionService) ctx.getBean("CMISRenditionService");
cmisAccessControlService = (CMISAccessControlService) ctx.getBean("CMISAccessControlService");
dictionaryService = (DictionaryService) ctx.getBean("dictionaryService");
nodeService = (NodeService) ctx.getBean("nodeService");
fileFolderService = (FileFolderService) ctx.getBean("fileFolderService");
@@ -124,6 +144,13 @@ public abstract class BaseCMISTest extends TestCase
authenticationService = (MutableAuthenticationService) ctx.getBean("authenticationService");
authenticationDAO = (MutableAuthenticationDao) ctx.getBean("authenticationDao");
thumbnailService = (ThumbnailService) ctx.getBean("thumbnailService");
permissionModelDao = (ModelDAO) ctx.getBean("permissionsModelDAO");
dictionaryDAO = (DictionaryDAO) ctx.getBean("dictionaryDAO");
namespaceDao = (NamespaceDAOImpl) ctx.getBean("namespaceDAO");
testTX = transactionService.getUserTransaction();
testTX.begin();
this.authenticationComponent.setSystemUserAsCurrentUser();

View File

@@ -26,9 +26,13 @@ package org.alfresco.cmis.mapping;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import org.alfresco.cmis.CMISAccessControlFormatEnum;
import org.alfresco.cmis.CMISAccessControlService;
import org.alfresco.cmis.CMISActionEvaluator;
import org.alfresco.cmis.CMISAllowedActionEnum;
import org.alfresco.cmis.CMISDataTypeEnum;
@@ -65,7 +69,7 @@ public class CMISMapping implements InitializingBean
* The Alfresco CMIS Namespace
*/
public static String CMIS_MODEL_NS = "cmis";
public static String CMIS_MODEL_URI = "http://www.alfresco.org/model/cmis/1.0/cd04";
public static String CMIS_MODEL_URI = "http://www.alfresco.org/model/cmis/1.0/cd07";
/**
* The Alfresco CMIS Model name.
@@ -108,6 +112,10 @@ public class CMISMapping implements InitializingBean
private Map<CMISScope, Map<CMISAllowedActionEnum, CMISActionEvaluator>> actionEvaluators = new HashMap<CMISScope, Map<CMISAllowedActionEnum, CMISActionEvaluator>>();
private Set<String> cmisRead = new HashSet<String>();
private Set<String> cmisWrite = new HashSet<String>();
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
@@ -197,6 +205,9 @@ public class CMISMapping implements InitializingBean
// Action Evaluator Mappings
//
// NOTE: The order of evaluators is important - they must be in the order as specified in CMIS-Core.xsd
// so that schema validation passes
registerEvaluator(CMISScope.DOCUMENT, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_DELETE_OBJECT, PermissionService.DELETE_NODE));
registerEvaluator(CMISScope.DOCUMENT, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_UPDATE_PROPERTIES, PermissionService.WRITE_PROPERTIES));
registerEvaluator(CMISScope.DOCUMENT, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_PROPERTIES, PermissionService.READ_PROPERTIES));
@@ -219,8 +230,8 @@ public class CMISMapping implements InitializingBean
registerEvaluator(CMISScope.DOCUMENT, new FixedValueActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_REMOVE_POLICY, false));
registerEvaluator(CMISScope.DOCUMENT, new FixedValueActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_CREATE_RELATIONSHIP, true));
registerEvaluator(CMISScope.DOCUMENT, new FixedValueActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_RENDITIONS, true));
registerEvaluator(CMISScope.DOCUMENT, new FixedValueActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_ACL, false));
registerEvaluator(CMISScope.DOCUMENT, new FixedValueActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_APPLY_ACL, false));
registerEvaluator(CMISScope.DOCUMENT, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_ACL, PermissionService.READ_PERMISSIONS));
registerEvaluator(CMISScope.DOCUMENT, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_APPLY_ACL, PermissionService.CHANGE_PERMISSIONS));
registerEvaluator(CMISScope.FOLDER, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_DELETE_OBJECT, PermissionService.DELETE_NODE));
registerEvaluator(CMISScope.FOLDER, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_UPDATE_PROPERTIES, PermissionService.WRITE_PROPERTIES));
@@ -239,9 +250,9 @@ public class CMISMapping implements InitializingBean
registerEvaluator(CMISScope.FOLDER, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_CREATE_DOCUMENT, PermissionService.CREATE_CHILDREN));
registerEvaluator(CMISScope.FOLDER, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_CREATE_FOLDER, PermissionService.CREATE_CHILDREN));
registerEvaluator(CMISScope.FOLDER, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_CREATE_RELATIONSHIP, PermissionService.CREATE_ASSOCIATIONS));
registerEvaluator(CMISScope.FOLDER, new FixedValueActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_CREATE_POLICY, false));
registerEvaluator(CMISScope.FOLDER, new FixedValueActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_ACL, false));
registerEvaluator(CMISScope.FOLDER, new FixedValueActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_APPLY_ACL, false));
registerEvaluator(CMISScope.FOLDER, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_DELETE_TREE, PermissionService.DELETE_NODE));
registerEvaluator(CMISScope.FOLDER, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_ACL, PermissionService.READ_PERMISSIONS));
registerEvaluator(CMISScope.FOLDER, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_APPLY_ACL, PermissionService.CHANGE_PERMISSIONS));
registerEvaluator(CMISScope.RELATIONSHIP, new FixedValueActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_DELETE_OBJECT, true));
registerEvaluator(CMISScope.RELATIONSHIP, new FixedValueActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_UPDATE_PROPERTIES, false));
@@ -259,6 +270,15 @@ public class CMISMapping implements InitializingBean
registerEvaluator(CMISScope.POLICY, new FixedValueActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_OBJECT_RELATIONSHIPS, false));
registerEvaluator(CMISScope.POLICY, new FixedValueActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_ACL, false));
registerEvaluator(CMISScope.POLICY, new FixedValueActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_APPLY_ACL, false));
cmisRead.add(PermissionService.READ_PROPERTIES);
cmisRead.add(PermissionService.READ_CONTENT);
cmisRead.add(PermissionService.READ);
cmisWrite.add(PermissionService.WRITE_PROPERTIES);
cmisWrite.add(PermissionService.WRITE_CONTENT);
cmisWrite.add(PermissionService.WRITE);
}
@@ -770,4 +790,54 @@ public class CMISMapping implements InitializingBean
logger.debug("Registered Action Evaluator: scope=" + scope + ", evaluator=" + evaluator);
}
/**
* @param permission
* @param format
* @return
*/
public String getReportedPermission(String permission, CMISAccessControlFormatEnum format)
{
if(format == CMISAccessControlFormatEnum.REPOSITORY_SPECIFIC_PERMISSIONS)
{
return permission;
}
else
{
if(cmisRead.contains(permission))
{
return CMISAccessControlService.CMIS_READ_PERMISSION;
}
else if(cmisWrite.contains(permission))
{
return CMISAccessControlService.CMIS_WRITE_PERMISSION;
}
else
{
return CMISAccessControlService.CMIS_ALL_PERMISSION;
}
}
}
public String getSetPermission(String permission)
{
if(permission.equals(CMISAccessControlService.CMIS_READ_PERMISSION))
{
return PermissionService.READ;
}
else if(permission.equals(CMISAccessControlService.CMIS_WRITE_PERMISSION))
{
return PermissionService.WRITE;
}
else if(permission.equals(CMISAccessControlService.CMIS_ALL_PERMISSION))
{
return PermissionService.ALL_PERMISSIONS;
}
else
{
return permission;
}
}
}

View File

@@ -105,7 +105,6 @@ public class CMISPropertyServiceTest extends BaseCMISTest
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_LENGTH), 0L);
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_MIME_TYPE), "application/octet-stream");
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_FILENAME), "BaseContent");
assertNotNull(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_PARENT_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_ALLOWED_CHILD_OBJECT_TYPE_IDS));
}
@@ -139,7 +138,6 @@ public class CMISPropertyServiceTest extends BaseCMISTest
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_LENGTH), 0L);
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_MIME_TYPE), "application/octet-stream");
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_FILENAME), "BaseContent");
assertNotNull(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_PARENT_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_ALLOWED_CHILD_OBJECT_TYPE_IDS));
@@ -191,7 +189,6 @@ public class CMISPropertyServiceTest extends BaseCMISTest
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_LENGTH), 0L);
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_MIME_TYPE), "application/octet-stream");
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_FILENAME), "BaseContent");
assertNotNull(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_PARENT_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_ALLOWED_CHILD_OBJECT_TYPE_IDS));
@@ -237,7 +234,6 @@ public class CMISPropertyServiceTest extends BaseCMISTest
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_LENGTH), 0L);
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_MIME_TYPE), "application/octet-stream");
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_FILENAME), "BaseContent");
assertNotNull(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_PARENT_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_ALLOWED_CHILD_OBJECT_TYPE_IDS));
@@ -272,7 +268,6 @@ public class CMISPropertyServiceTest extends BaseCMISTest
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_LENGTH), 0L);
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_MIME_TYPE), "application/octet-stream");
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_FILENAME), "BaseContent");
assertNotNull(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_PARENT_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_ALLOWED_CHILD_OBJECT_TYPE_IDS));
@@ -317,7 +312,6 @@ public class CMISPropertyServiceTest extends BaseCMISTest
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_LENGTH), 0L);
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_MIME_TYPE), "application/octet-stream");
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_FILENAME), "BaseContent (Working Copy)");
assertNotNull(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_PARENT_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_ALLOWED_CHILD_OBJECT_TYPE_IDS));
@@ -349,7 +343,6 @@ public class CMISPropertyServiceTest extends BaseCMISTest
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_LENGTH), 0L);
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_MIME_TYPE), "application/octet-stream");
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_FILENAME), "BaseContent");
assertNotNull(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_PARENT_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_ALLOWED_CHILD_OBJECT_TYPE_IDS));
@@ -394,7 +387,6 @@ public class CMISPropertyServiceTest extends BaseCMISTest
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_LENGTH), 0L);
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_MIME_TYPE), "application/octet-stream");
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_FILENAME), "BaseContent (Working Copy)");
assertNotNull(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_PARENT_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_ALLOWED_CHILD_OBJECT_TYPE_IDS));
@@ -426,7 +418,6 @@ public class CMISPropertyServiceTest extends BaseCMISTest
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_LENGTH), 0L);
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_MIME_TYPE), "application/octet-stream");
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_FILENAME), "BaseContent");
assertNotNull(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_PARENT_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_ALLOWED_CHILD_OBJECT_TYPE_IDS));
@@ -461,7 +452,6 @@ public class CMISPropertyServiceTest extends BaseCMISTest
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_LENGTH), 0L);
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_MIME_TYPE), "application/octet-stream");
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_FILENAME), "BaseContent");
assertNotNull(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_PARENT_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_ALLOWED_CHILD_OBJECT_TYPE_IDS));
@@ -493,7 +483,6 @@ public class CMISPropertyServiceTest extends BaseCMISTest
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_LENGTH), 0L);
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_MIME_TYPE), "application/octet-stream");
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_FILENAME), "BaseContent");
assertNotNull(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_PARENT_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_ALLOWED_CHILD_OBJECT_TYPE_IDS));
@@ -538,7 +527,6 @@ public class CMISPropertyServiceTest extends BaseCMISTest
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_LENGTH), 0L);
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_MIME_TYPE), "application/octet-stream");
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_FILENAME), "BaseContent (Working Copy)");
assertNotNull(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_PARENT_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_ALLOWED_CHILD_OBJECT_TYPE_IDS));
@@ -570,7 +558,6 @@ public class CMISPropertyServiceTest extends BaseCMISTest
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_LENGTH), 0L);
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_MIME_TYPE), "application/octet-stream");
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_FILENAME), "BaseContent");
assertNotNull(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_PARENT_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_ALLOWED_CHILD_OBJECT_TYPE_IDS));
@@ -615,7 +602,6 @@ public class CMISPropertyServiceTest extends BaseCMISTest
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_LENGTH), 0L);
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_MIME_TYPE), "application/octet-stream");
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_FILENAME), "BaseContent (Working Copy)");
assertNotNull(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_PARENT_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_ALLOWED_CHILD_OBJECT_TYPE_IDS));
@@ -650,7 +636,6 @@ public class CMISPropertyServiceTest extends BaseCMISTest
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_LENGTH), 0L);
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_MIME_TYPE), "application/octet-stream");
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_FILENAME), "BaseContent");
assertNotNull(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_PARENT_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_ALLOWED_CHILD_OBJECT_TYPE_IDS));
@@ -695,7 +680,6 @@ public class CMISPropertyServiceTest extends BaseCMISTest
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_LENGTH), 0L);
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_MIME_TYPE), "application/octet-stream");
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_FILENAME), "BaseContent (Working Copy)");
assertNotNull(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_PARENT_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_ALLOWED_CHILD_OBJECT_TYPE_IDS));
@@ -730,7 +714,6 @@ public class CMISPropertyServiceTest extends BaseCMISTest
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_LENGTH), 0L);
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_MIME_TYPE), "application/octet-stream");
assertEquals(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_FILENAME), "BaseContent");
assertNotNull(properties.get(CMISDictionaryModel.PROP_CONTENT_STREAM_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_PARENT_ID));
assertNull(properties.get(CMISDictionaryModel.PROP_ALLOWED_CHILD_OBJECT_TYPE_IDS));

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -18,7 +18,7 @@
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
@@ -27,14 +27,18 @@ package org.alfresco.cmis.mapping;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.alfresco.cmis.CMISDictionaryModel;
import org.alfresco.cmis.CMISDictionaryService;
import org.alfresco.cmis.CMISPropertyDefinition;
import org.alfresco.cmis.CMISRelationshipDirectionEnum;
import org.alfresco.cmis.CMISRendition;
import org.alfresco.cmis.CMISRenditionService;
import org.alfresco.cmis.CMISScope;
import org.alfresco.cmis.CMISServices;
import org.alfresco.cmis.CMISTypeDefinition;
@@ -111,6 +115,7 @@ public class CMISServicesImpl implements CMISServices, ApplicationContextAware,
private NodeService nodeService;
private TenantAdminService tenantAdminService;
private ProcessorLifecycle lifecycle = new ProcessorLifecycle();
private CMISRenditionService cmisRenditionService;
// CMIS supported version
private String cmisVersion = "[undefined]";
@@ -222,6 +227,17 @@ public class CMISServicesImpl implements CMISServices, ApplicationContextAware,
this.repository = repository;
}
/**
* Sets the cmis rendition service.
*
* @param cmisRenditionService
* the cmis rendition service
*/
public void setCMISRenditionService(CMISRenditionService cmisRenditionService)
{
this.cmisRenditionService = cmisRenditionService;
}
/* (non-Javadoc)
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
*/
@@ -377,6 +393,30 @@ public class CMISServicesImpl implements CMISServices, ApplicationContextAware,
return nodeRef;
}
/*
* (non-Javadoc)
* @see org.alfresco.cmis.CMISServices#getRenditions(org.alfresco.service.cmr.repository.NodeRef, java.lang.String)
*/
public Map<String, Object> getRenditions(NodeRef nodeRef, String renditionFilter)
{
Map<String, Object> result = new TreeMap<String, Object>();
List<CMISRendition> renditions = cmisRenditionService.getRenditions(nodeRef, renditionFilter);
if (renditions == null)
{
renditions = Collections.emptyList();
}
result.put("node", nodeRef);
result.put("renditionFilter", renditionFilter); // Record rendition filter to aid recursion on node maps
result.put("renditions", renditions);
List<NodeRef> renditionNodes = new ArrayList<NodeRef>(renditions.size());
for (CMISRendition rendition : renditions)
{
renditionNodes.add(rendition.getNodeRef());
}
result.put("renditionNodes", renditionNodes);
return result;
}
/*
* (non-Javadoc)
* @see org.alfresco.cmis.CMISServices#getChildren(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.cmis.CMISTypesFilterEnum)

View File

@@ -27,11 +27,14 @@ package org.alfresco.cmis.mapping;
import java.io.Serializable;
import org.alfresco.cmis.CMISDictionaryModel;
import org.alfresco.model.ContentModel;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
/**
* Accessor for CMIS content stream length property
* Accessor for CMIS content stream property id
*
* @author andyh
*/
@@ -53,7 +56,13 @@ public class ContentStreamIdProperty extends AbstractProperty
*/
public Serializable getValue(NodeRef nodeRef)
{
return "cm:content";
Serializable sValue = getServiceRegistry().getNodeService().getProperty(nodeRef, ContentModel.PROP_CONTENT);
if (sValue != null)
{
ContentData contentData = DefaultTypeConverter.INSTANCE.convert(ContentData.class, sValue);
return contentData.getContentUrl();
}
return null;
}
}

View File

@@ -25,7 +25,15 @@
package org.alfresco.cmis.mapping;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import org.alfresco.repo.search.MLAnalysisMode;
import org.alfresco.repo.search.SearcherException;
import org.alfresco.repo.search.impl.lucene.LuceneQueryParser;
import org.alfresco.repo.search.impl.lucene.analysis.DateTimeAnalyser;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
@@ -33,6 +41,8 @@ import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.namespace.QName;
import org.apache.lucene.index.IndexReader.FieldOption;
import org.springframework.extensions.surf.util.I18NUtil;
/**
* A simple 1-1 property mapping from a CMIS property name to an alfresco property
@@ -41,6 +51,96 @@ import org.alfresco.service.namespace.QName;
*/
public class DirectProperty extends AbstractSimpleProperty
{
/*
* (non-Javadoc)
* @see org.alfresco.cmis.mapping.AbstractSimpleProperty#getLuceneSortField()
*/
@Override
public String getLuceneSortField(LuceneQueryParser lqp)
{
String field = getLuceneFieldName();
// need to find the real field to use
Locale sortLocale = null;
PropertyDefinition propertyDef = getServiceRegistry().getDictionaryService().getProperty(QName.createQName(field.substring(1)));
if (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))
{
throw new SearcherException("Order on content properties is not curently supported");
}
else if ((propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT)) || (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT)))
{
List<Locale> locales = lqp.getSearchParameters().getLocales();
if (((locales == null) || (locales.size() == 0)))
{
locales = Collections.singletonList(I18NUtil.getLocale());
}
if (locales.size() > 1)
{
throw new SearcherException("Order on text/mltext properties with more than one locale is not curently supported");
}
sortLocale = locales.get(0);
// find best field match
HashSet<String> allowableLocales = new HashSet<String>();
MLAnalysisMode analysisMode = lqp.getConfig().getDefaultMLSearchAnalysisMode();
for (Locale l : MLAnalysisMode.getLocales(analysisMode, sortLocale, false))
{
allowableLocales.add(l.toString());
}
String sortField = field;
for (Object current : lqp.getIndexReader().getFieldNames(FieldOption.INDEXED))
{
String currentString = (String) current;
if (currentString.startsWith(field) && currentString.endsWith(".sort"))
{
String fieldLocale = currentString.substring(field.length() + 1, currentString.length() - 5);
if (allowableLocales.contains(fieldLocale))
{
if (fieldLocale.equals(sortLocale.toString()))
{
sortField = currentString;
break;
}
else if (sortLocale.toString().startsWith(fieldLocale))
{
if (sortField.equals(field) || (currentString.length() < sortField.length()))
{
sortField = currentString;
}
}
else if (fieldLocale.startsWith(sortLocale.toString()))
{
if (sortField.equals(field) || (currentString.length() < sortField.length()))
{
sortField = currentString;
}
}
}
}
}
field = sortField;
}
else if (propertyDef.getDataType().getName().equals(DataTypeDefinition.DATETIME))
{
DataTypeDefinition dataType = propertyDef.getDataType();
String analyserClassName = dataType.getAnalyserClassName();
if (analyserClassName.equals(DateTimeAnalyser.class.getCanonicalName()))
{
field = field + ".sort";
}
}
return field;
}
private QName alfrescoName;
/**
@@ -58,7 +158,6 @@ public class DirectProperty extends AbstractSimpleProperty
/*
* (non-Javadoc)
*
* @see org.alfresco.cmis.property.AbstractPropertyAccessor#getMappedProperty()
*/
public QName getMappedProperty()
@@ -68,7 +167,6 @@ public class DirectProperty extends AbstractSimpleProperty
/*
* (non-Javadoc)
*
* @see org.alfresco.cmis.property.PropertyAccessor#getValue(org.alfresco.service.cmr.repository.NodeRef)
*/
public Serializable getValue(NodeRef nodeRef)
@@ -112,6 +210,4 @@ public class DirectProperty extends AbstractSimpleProperty
return pd.getDataType();
}
}

View File

@@ -313,7 +313,7 @@ public class FixedValueProperty extends AbstractProperty
* (non-Javadoc)
* @see org.alfresco.cmis.property.PropertyLuceneBuilder#getLuceneSortField()
*/
public String getLuceneSortField()
public String getLuceneSortField(LuceneQueryParser lqp)
{
throw new UnsupportedOperationException();
}

View File

@@ -69,6 +69,8 @@ public class ObjectIdProperty extends AbstractProperty
* @see org.alfresco.cmis.mapping.AbstractProperty#getValue(org.alfresco.service.cmr.repository.NodeRef)
*/
public Serializable getValue(NodeRef nodeRef)
{
if (!getServiceRegistry().getNodeService().hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY))
{
if (getServiceRegistry().getNodeService().hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE))
{
@@ -83,6 +85,7 @@ public class ObjectIdProperty extends AbstractProperty
return builder.toString();
}
}
}
return nodeRef.toString();
}
@@ -270,7 +273,7 @@ public class ObjectIdProperty extends AbstractProperty
/* (non-Javadoc)
* @see org.alfresco.cmis.property.NamedPropertyAccessor#getLuceneSortField(java.lang.String)
*/
public String getLuceneSortField()
public String getLuceneSortField(LuceneQueryParser lqp)
{
return getLuceneFieldName();
}

View File

@@ -266,7 +266,7 @@ public class ObjectTypeIdProperty extends AbstractProperty
* (non-Javadoc)
* @see org.alfresco.cmis.property.PropertyLuceneBuilder#getLuceneSortField()
*/
public String getLuceneSortField()
public String getLuceneSortField(LuceneQueryParser lqp)
{
return getLuceneFieldName();
}

View File

@@ -222,7 +222,7 @@ public class ParentProperty extends AbstractProperty
* (non-Javadoc)
* @see org.alfresco.cmis.property.PropertyLuceneBuilder#getLuceneSortField()
*/
public String getLuceneSortField()
public String getLuceneSortField(LuceneQueryParser lqp)
{
return getLuceneFieldName();
}

View File

@@ -0,0 +1,273 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis.renditions;
import org.alfresco.cmis.CMISRendition;
import org.alfresco.cmis.CMISRenditionKind;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* CMIS Rendition Implementation
*
* @author Stas Sokolovsky
*/
public class CMISRenditionImpl implements CMISRendition
{
private NodeRef nodeRef;
private String streamId;
private String mimeType;
private CMISRenditionKind kind;
private Integer height;
private Integer width;
private String title;
private Integer length;
private String renditionDocumentId;
/**
* Construct a CmisRendition using fields
*
* @param nodeRef the rendition node reference
* @param streamId rendition stream id
* @param mimeType the MIME type of the rendition stream
* @param kind a categorization String associated with the rendition
* @param height the height of image
* @param width the width of image
* @param title rendition title
* @param length the length of the rendition stream in bytes
* @param renditionDocumentId the rendition document id
*/
public CMISRenditionImpl(NodeRef nodeRef, String streamId, String mimeType, CMISRenditionKind kind, Integer height, Integer width, String title, Integer length, String renditionDocumentId)
{
super();
this.nodeRef = nodeRef;
this.streamId = streamId;
this.mimeType = mimeType;
this.kind = kind;
this.height = height;
this.width = width;
this.title = title;
this.length = length;
this.renditionDocumentId = renditionDocumentId;
}
/**
* Default constructor
*/
public CMISRenditionImpl()
{
super();
}
/**
* @see org.alfresco.cmis.CMISRendition#getStreamId()
*/
public String getStreamId()
{
return streamId;
}
/**
* Set the rendition stream id
*
* @param streamId rendition stream id
*/
public void setStreamId(String streamId)
{
this.streamId = streamId;
}
/**
* @see org.alfresco.cmis.CMISRendition#getMimeType()
*/
public String getMimeType()
{
return mimeType;
}
/**
* Set the MIME type of the rendition stream
*
* @param mimeType the MIME type of the rendition stream
*/
public void setMimeType(String mimeType)
{
this.mimeType = mimeType;
}
/**
* @see org.alfresco.cmis.CMISRendition#getKind()
*/
public CMISRenditionKind getKind()
{
return kind;
}
/**
* Set rendition kind
*
* @param kind rendition kind
*/
public void setKind(CMISRenditionKind kind)
{
this.kind = kind;
}
/**
* @see org.alfresco.cmis.CMISRendition#getHeight()
*/
public Integer getHeight()
{
return height;
}
/**
* Set the height of image
*
* @param height the height of image
*/
public void setHeight(Integer height)
{
this.height = height;
}
/**
* @see org.alfresco.cmis.CMISRendition#getWidth()
*/
public Integer getWidth()
{
return width;
}
/**
* Set the width of image
*
* @param width the width of image
*/
public void setWidth(Integer width)
{
this.width = width;
}
/**
* @see org.alfresco.cmis.CMISRendition#getTitle()
*/
public String getTitle()
{
return title;
}
/**
* Set the title of rendition
*
* @param title the title
*/
public void setTitle(String title)
{
this.title = title;
}
/**
* @see org.alfresco.cmis.CMISRendition#getLength()
*/
public Integer getLength()
{
return length;
}
/**
* Set the length of the rendition stream in bytes
*
* @param length length of the rendition stream in bytes
*/
public void setLength(Integer length)
{
this.length = length;
}
/**
* @see org.alfresco.cmis.CMISRendition#getRenditionDocumentId()
*/
public String getRenditionDocumentId()
{
return renditionDocumentId;
}
/**
* Set the rendition document id
*
* @param renditionDocumentId the rendition document id
*/
public void setRenditionDocumentId(String renditionDocumentId)
{
this.renditionDocumentId = renditionDocumentId;
}
/*
* (non-Javadoc)
* @see org.alfresco.cmis.CMISRendition#getNodeRef()
*/
public NodeRef getNodeRef()
{
return this.nodeRef;
}
/**
* @param nodeRef
* the nodeRef to set
*/
public void setNodeRef(NodeRef nodeRef)
{
this.nodeRef = nodeRef;
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj instanceof CMISRendition)
{
CMISRendition that = (CMISRendition) obj;
return (this.getStreamId() != null && that.getStreamId() != null && this.getStreamId().equals(that.getStreamId()));
}
else
{
return false;
}
}
/**
* @see java.lang.Object#hashCode()
*/
public int hashCode()
{
return this.getStreamId() != null ? this.getStreamId().hashCode() : 0x01;
}
}

View File

@@ -0,0 +1,517 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis.renditions;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import org.alfresco.cmis.CMISRendition;
import org.alfresco.cmis.CMISRenditionKind;
import org.alfresco.cmis.CMISRenditionService;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.transform.magick.ImageResizeOptions;
import org.alfresco.repo.content.transform.magick.ImageTransformationOptions;
import org.alfresco.repo.thumbnail.ThumbnailDefinition;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.thumbnail.ThumbnailService;
/**
* Rendition Service Implementation
*
* @author Stas Sokolovsky
*/
public class CMISRenditionServiceImpl implements CMISRenditionService
{
/** Rendition filter constants */
private static final String FILTER_WILDCARD = "*";
private static final String FILTER_NONE = "cmis:none";
private static final String FILTER_DELIMITER = ",";
private static final String SUBTYPES_POSTFIX = "/*";
private static final String SUBTYPES_DELIMITER = "/";
/** Service dependencies */
private ThumbnailService thumbnailService;
private NodeService nodeService;
/** Kind to thumbnail mapping */
private Map<String, List<String>> kindToThumbnailNames = new HashMap<String, List<String>>();
private Map<String, CMISRenditionKind> thumbnailNamesToKind = new HashMap<String, CMISRenditionKind>();
/** Custom renditions */
private CustomRenditionsCache customRenditionsCache;
/**
* @see org.alfresco.cmis.CMISRenditionService#getRenditions(org.alfresco.service.cmr.repository.NodeRef, java.lang.String)
*/
public List<CMISRendition> getRenditions(NodeRef node, String renditionFilter)
{
Collection<CMISRendition> result = null;
ThumbnailFilter thumbnailFilter = getThumbnailFilter(renditionFilter);
if (thumbnailFilter != null)
{
result = getRenditions(node, thumbnailFilter);
}
return result != null ? new ArrayList<CMISRendition>(result) : null;
}
/**
* Get renditions for a document.
*
* @param node node reference of document
* @param thumbnailFilter thumbnail filter
* @return set of renditions
*/
private Set<CMISRendition> getRenditions(NodeRef node, ThumbnailFilter thumbnailFilter)
{
Set<CMISRendition> result = new HashSet<CMISRendition>();
if (thumbnailFilter.isAny())
{
result.addAll(getAllRenditions(node));
}
else
{
for (String thumbnailName : thumbnailFilter.getThumbnailNames())
{
CMISRendition rendition = getRenditionByThumbnailName(node, thumbnailName);
if (rendition != null)
{
result.add(rendition);
}
}
for (String mimetype : thumbnailFilter.getMimetypes())
{
result.addAll(getRenditionByMimetype(node, mimetype));
}
}
result.addAll(getCustomRenditions(thumbnailFilter));
return result;
}
/**
* Get rendition by thumbnail name.
*
* @param node node reference of document
* @param thumbnailName thumbnail name
* @return rendition
*/
private CMISRendition getRenditionByThumbnailName(NodeRef node, String thumbnailName)
{
CMISRendition result = null;
NodeRef thumbnailNode = thumbnailService.getThumbnailByName(node, ContentModel.PROP_CONTENT, thumbnailName);
if (thumbnailNode != null)
{
result = getRendition(thumbnailNode, node);
}
return result;
}
/**
* Get rendition by mimetype.
*
* @param node node reference of document
* @param mimetype rendition mimetype
* @return list of renditions
*/
private List<CMISRendition> getRenditionByMimetype(NodeRef node, String mimetype)
{
List<CMISRendition> result = new ArrayList<CMISRendition>();
List<NodeRef> thumbnails = thumbnailService.getThumbnails(node, ContentModel.PROP_CONTENT, mimetype, null);
if (thumbnails != null)
{
for (NodeRef thumbnailNode : thumbnails)
{
CMISRendition rendition = getRendition(thumbnailNode, node);
if (rendition != null)
{
result.add(rendition);
}
}
}
return result;
}
/**
* Convert the rendition filter to thumbnail filter.
*
* @param renditionFilter rendition filter
* @return thumbnail filter
*/
private ThumbnailFilter getThumbnailFilter(String renditionFilter)
{
ThumbnailFilter result = null;
if (renditionFilter != null && !renditionFilter.equals(FILTER_NONE))
{
result = new ThumbnailFilter();
if (renditionFilter.equals(FILTER_WILDCARD))
{
result.setAny(true);
}
else
{
String[] filterElements = renditionFilter.split(FILTER_DELIMITER);
if (filterElements == null || filterElements.length < 1)
{
throw new AlfrescoRuntimeException("Invalid rendition filter");
}
for (String filterElement : filterElements)
{
filterElement = filterElement.trim();
if (isRenditionKind(filterElement))
{
CMISRenditionKind kind = null;
for (CMISRenditionKind renditionKind : CMISRenditionKind.values())
{
if (renditionKind.getLabel().equals(filterElement))
{
kind = renditionKind;
}
}
result.getKinds().add(kind);
List<String> thumbnails = kindToThumbnailNames.get(filterElement);
if (thumbnails != null)
{
result.getThumbnailNames().addAll(thumbnails);
}
}
else
{
result.getMimetypes().add(filterElement);
}
}
}
}
return result;
}
/**
* Get the thumbnail definition.
*
* @param thumbnailName thumbnail name
* @return thumbnail definition
*/
private ThumbnailDefinition getThumbnailDefinition(String thumbnailName)
{
return thumbnailService.getThumbnailRegistry().getThumbnailDefinition(thumbnailName);
}
/**
* Get the image attributes of thumbnail.
*
* @param thumbnailName thumbnail name
* @return image attributes
*/
private ImageResizeOptions getImageAttributes(String thumbnailName)
{
ThumbnailDefinition thumbnailDefinition = getThumbnailDefinition(thumbnailName);
if (thumbnailDefinition != null && thumbnailDefinition.getTransformationOptions() != null
&& thumbnailDefinition.getTransformationOptions() instanceof ImageTransformationOptions)
{
return ((ImageTransformationOptions) thumbnailDefinition.getTransformationOptions()).getResizeOptions();
}
return null;
}
/**
* @param filterElement filter element
* @return true if filter element is rendition kind
*/
private boolean isRenditionKind(String filterElement)
{
boolean result = false;
for (CMISRenditionKind renditionKind : CMISRenditionKind.values())
{
if (renditionKind.getLabel().equals(filterElement))
{
result = true;
}
}
return result;
}
/**
* Create CMISRendition by thumbnailNode and documentNode.
*
* @param thumbnailNode thumbnail node reference
* @param documentNode document node reference
* @return CMISRendition
*/
private CMISRendition getRendition(NodeRef thumbnailNode, NodeRef documentNode)
{
CMISRenditionImpl rendition = null;
String thumbnailName = (String) nodeService.getProperty(thumbnailNode, ContentModel.PROP_THUMBNAIL_NAME);
CMISRenditionKind kind = thumbnailNamesToKind.get(thumbnailName);
if (thumbnailName != null && kind != null)
{
rendition = new CMISRenditionImpl();
ContentData contentData = (ContentData) nodeService.getProperty(thumbnailNode, ContentModel.PROP_CONTENT);
rendition.setNodeRef(thumbnailNode);
rendition.setStreamId(thumbnailNode.toString());
rendition.setRenditionDocumentId(documentNode.toString());
rendition.setTitle(thumbnailName);
rendition.setKind(kind);
rendition.setMimeType(contentData.getMimetype());
rendition.setLength((int) contentData.getSize());
ImageResizeOptions imageAttributes = getImageAttributes(thumbnailName);
if (imageAttributes != null)
{
rendition.setWidth(imageAttributes.getWidth());
rendition.setHeight(imageAttributes.getHeight());
}
}
return rendition;
}
/**
* Get custom renditions.
*
* @param thumbnailFilter thumbnail filter
* @return list of renditions
*/
private List<CMISRendition> getCustomRenditions(ThumbnailFilter filter)
{
List<CMISRendition> result = new ArrayList<CMISRendition>();
if (customRenditionsCache != null)
{
if (filter.isAny())
{
result.addAll(customRenditionsCache.getAllRenditions());
}
else
{
for (CMISRenditionKind kind : filter.getKinds())
{
List<CMISRendition> renditions = customRenditionsCache.getRenditionsByKind(kind);
if (renditions != null)
{
result.addAll(renditions);
}
}
for (String mimetype : filter.getMimetypes())
{
List<CMISRendition> renditions = customRenditionsCache.getRenditionsByMimeType(mimetype);
if (renditions != null)
{
result.addAll(renditions);
}
}
}
}
return result;
}
/**
* Get all renditions for a document.
*
* @param node document node ref
* @return list of renditions
*/
private List<CMISRendition> getAllRenditions(NodeRef node)
{
return getRenditionByMimetype(node, null);
}
/**
* Set rendition kind mapping.
*
* @param RenditionKind to Thumbnail Definition mapping
*/
public void setRenditionKindMapping(Map<String, List<String>> renditionKinds)
{
this.kindToThumbnailNames = renditionKinds;
for (Entry<String, List<String>> entry : renditionKinds.entrySet())
{
CMISRenditionKind kind = null;
for (CMISRenditionKind renditionKind : CMISRenditionKind.values())
{
if (renditionKind.getLabel().equals(entry.getKey()))
{
kind = renditionKind;
}
}
for (String thumbnailName : entry.getValue())
{
thumbnailNamesToKind.put(thumbnailName, kind);
}
}
}
/**
* Set custom renditions.
*
* @param renditions list of renditions
*/
public void setCustomRenditions(List<CMISRendition> renditions)
{
this.customRenditionsCache = new CustomRenditionsCache(renditions);
}
/**
* Set the NodeService.
*
* @param nodeService NodeService
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* Set the ThumbnailService.
*
* @param thumbnailService thumbnailService
*/
public void setThumbnailService(ThumbnailService thumbnailService)
{
this.thumbnailService = thumbnailService;
}
/**
* Cache that aggregates renditions informaition to allow fast searching by kind and mimetype
*/
private class CustomRenditionsCache
{
private Map<CMISRenditionKind, List<CMISRendition>> renditionsByKind;
private Map<String, List<CMISRendition>> renditionsByMimeType;
private Map<String, List<CMISRendition>> renditionsByBaseMimeType;
private List<CMISRendition> allRenditions;
public CustomRenditionsCache(List<CMISRendition> renditions)
{
allRenditions = renditions;
renditionsByKind = new HashMap<CMISRenditionKind, List<CMISRendition>>(renditions.size());
renditionsByMimeType = new HashMap<String, List<CMISRendition>>(renditions.size());
renditionsByBaseMimeType = new HashMap<String, List<CMISRendition>>(renditions.size());
for (CMISRendition rendition : renditions)
{
String baseType = getBaseType(rendition.getMimeType());
if (!renditionsByKind.containsKey(rendition.getKind()))
{
renditionsByKind.put(rendition.getKind(), new ArrayList<CMISRendition>(1));
}
if (!renditionsByMimeType.containsKey(rendition.getMimeType()))
{
renditionsByMimeType.put(rendition.getMimeType(), new ArrayList<CMISRendition>(1));
}
if (!renditionsByBaseMimeType.containsKey(baseType))
{
renditionsByBaseMimeType.put(baseType, new ArrayList<CMISRendition>(1));
}
renditionsByKind.get(rendition.getKind()).add(rendition);
renditionsByMimeType.get(rendition.getMimeType()).add(rendition);
renditionsByBaseMimeType.get(baseType).add(rendition);
}
}
public List<CMISRendition> getRenditionsByKind(CMISRenditionKind kind)
{
return renditionsByKind.get(kind);
}
public List<CMISRendition> getRenditionsByMimeType(String mimetype)
{
if (mimetype.endsWith(SUBTYPES_POSTFIX))
{
String baseMimetype = mimetype.substring(0, mimetype.length() - SUBTYPES_POSTFIX.length());
return renditionsByBaseMimeType.get(baseMimetype);
}
else
{
return renditionsByMimeType.get(mimetype);
}
}
public Collection<CMISRendition> getAllRenditions()
{
return allRenditions;
}
private String getBaseType(String mimetype)
{
String baseMymetype = mimetype;
int subTypeIndex = mimetype.indexOf(SUBTYPES_DELIMITER);
if (subTypeIndex > 0 || subTypeIndex < mimetype.length())
{
baseMymetype = mimetype.substring(0, subTypeIndex);
}
return baseMymetype;
}
}
/**
* Parsed RenditionFilter
*/
private class ThumbnailFilter
{
private List<CMISRenditionKind> kinds = new ArrayList<CMISRenditionKind>();
private List<String> thumbnailNames = new ArrayList<String>();
private List<String> mimetypes = new ArrayList<String>();
private boolean any = false;
public List<String> getThumbnailNames()
{
return thumbnailNames;
}
public List<String> getMimetypes()
{
return mimetypes;
}
public List<CMISRenditionKind> getKinds()
{
return kinds;
}
public boolean isAny()
{
return any;
}
public void setAny(boolean any)
{
this.any = any;
}
}
}

View File

@@ -0,0 +1,370 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.cmis.renditions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import org.alfresco.cmis.CMISRendition;
import org.alfresco.cmis.CMISRenditionKind;
import org.alfresco.cmis.mapping.BaseCMISTest;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.content.transform.magick.ImageTransformationOptions;
import org.alfresco.repo.thumbnail.ThumbnailDefinition;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* @author Stas Sokolovsky
*/
public class CMISRenditionServiceTest extends BaseCMISTest
{
private static final String[] THUMBNAIL_NAMES = new String[] { "doclib", "webpreview", "imgpreview" };
private static final CMISRenditionKind[] THUMBNAIL_KINDS = new CMISRenditionKind[] { CMISRenditionKind.THUMBNAIL, CMISRenditionKind.WEB_PREVIEW, CMISRenditionKind.WEB_PREVIEW };
private NodeRef document;
private List<CMISRendition> documentRenditions = new ArrayList<CMISRendition>();
private CMISRendition icon16Rendition = new CMISRenditionImpl(null, "alf:icon16", "image/gif", CMISRenditionKind.ICON16, 16, 16, null, null, null);
private CMISRendition icon32Rendition = new CMISRenditionImpl(null, "alf:icon32", "image/gif", CMISRenditionKind.ICON32, 32, 32, null, null, null);
public void setUp() throws Exception
{
super.setUp();
String documentName = "TestDocument" + System.currentTimeMillis();
document = createDocument(documentName, "Test Content", MimetypeMap.MIMETYPE_PDF);
documentRenditions = new ArrayList<CMISRendition>();
for (int i = 0; i < THUMBNAIL_NAMES.length; ++i)
{
documentRenditions.add(createRendition(document, THUMBNAIL_NAMES[i], THUMBNAIL_KINDS[i]));
}
}
public void testGetAllRenditions()
{
List<CMISRendition> receivedRenditions = cmisRenditionService.getRenditions(document, "*");
List<CMISRendition> expectedRenditions = new ArrayList<CMISRendition>();
expectedRenditions.addAll(documentRenditions);
expectedRenditions.add(icon16Rendition);
expectedRenditions.add(icon32Rendition);
assertRendiions(receivedRenditions, expectedRenditions);
}
public void testGetRenditionsByKind()
{
testGetRenditionsByKind(CMISRenditionKind.THUMBNAIL);
testGetRenditionsByKind(CMISRenditionKind.WEB_PREVIEW);
testGetRenditionsByKind(CMISRenditionKind.ICON16);
testGetRenditionsByKind(CMISRenditionKind.ICON32);
testGetRenditionsByKind(CMISRenditionKind.WEB_PREVIEW, CMISRenditionKind.ICON32);
testGetRenditionsByKind(CMISRenditionKind.THUMBNAIL, CMISRenditionKind.WEB_PREVIEW, CMISRenditionKind.ICON16, CMISRenditionKind.ICON32);
}
public void testGetRenditionsByMimetype()
{
for (CMISRendition rendition : documentRenditions)
{
testGetRenditionsByMimetype(rendition.getMimeType());
}
testGetRenditionsByMimetype(icon16Rendition.getMimeType());
testGetRenditionsByMimetype(icon32Rendition.getMimeType());
String[] mimetypes = getDifferentMimetypes();
testGetRenditionsByMimetype(mimetypes);
}
public void testEmptyFilter()
{
List<CMISRendition> receivedRenditions = cmisRenditionService.getRenditions(document, null);
assertTrue(receivedRenditions == null || receivedRenditions.size() == 0);
receivedRenditions = cmisRenditionService.getRenditions(document, "cmis:none");
assertTrue(receivedRenditions == null || receivedRenditions.size() == 0);
}
public void testRenditionsByBaseMimetype()
{
String[] mimetypes = getDifferentMimetypes();
List<String> baseMimetypeFilters = new ArrayList<String>();
for (String mimetype : mimetypes)
{
String baseMimetype = getBaseType(mimetype);
String filter = baseMimetype + "/*";
baseMimetypeFilters.add(filter);
testGetRenditionsByMimetype(filter);
}
testGetRenditionsByMimetype(baseMimetypeFilters.toArray(new String[0]));
}
public void testMixedFilter()
{
String[] mimetypes = getDifferentMimetypes();
testGetRenditions(THUMBNAIL_KINDS, mimetypes);
}
private void testGetRenditionsByMimetype(String... mimetypes)
{
testGetRenditions(null, mimetypes);
}
private void testGetRenditionsByKind(CMISRenditionKind... kinds)
{
testGetRenditions(kinds, null);
}
private void testGetRenditions(CMISRenditionKind[] kinds, String[] mimetypes)
{
String filter = createFilter(kinds, mimetypes);
List<CMISRendition> receivedRenditions = cmisRenditionService.getRenditions(document, filter);
List<CMISRendition> expectedRenditions = new ArrayList<CMISRendition>();
if (kinds != null)
{
for (CMISRenditionKind kind : kinds)
{
expectedRenditions.addAll(getRenditionsByKind(kind));
}
}
if (mimetypes != null)
{
for (String mimetype : mimetypes)
{
expectedRenditions.addAll(getRenditionsByMimetype(mimetype));
}
}
assertRendiions(receivedRenditions, expectedRenditions);
}
protected void tearDown() throws Exception
{
fileFolderService.delete(document);
super.tearDown();
}
private CMISRendition createRendition(NodeRef nodeRef, String thumbnailName, CMISRenditionKind kind)
{
ThumbnailDefinition details = thumbnailService.getThumbnailRegistry().getThumbnailDefinition(thumbnailName);
NodeRef thumbnailNodeRef = thumbnailService.createThumbnail(nodeRef, ContentModel.PROP_CONTENT, details.getMimetype(), details.getTransformationOptions(), details
.getName());
CMISRenditionImpl rendition = new CMISRenditionImpl();
rendition.setStreamId(thumbnailNodeRef.toString());
rendition.setKind(kind);
rendition.setMimeType(details.getMimetype());
if (details.getTransformationOptions() instanceof ImageTransformationOptions)
{
ImageTransformationOptions imageOptions = (ImageTransformationOptions) details.getTransformationOptions();
rendition.setWidth(imageOptions.getResizeOptions().getWidth());
rendition.setHeight(imageOptions.getResizeOptions().getHeight());
}
return rendition;
}
private NodeRef createDocument(String documentName, String documentContent, String mimetype)
{
NodeRef textDocument = fileFolderService.create(rootNodeRef, "TEXT" + documentName, ContentModel.TYPE_CONTENT).getNodeRef();
ContentWriter contentWriter = fileFolderService.getWriter(textDocument);
contentWriter.setEncoding("UTF-8");
contentWriter.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN);
contentWriter.setLocale(Locale.ENGLISH);
contentWriter.putContent(documentContent);
ContentReader contentReader = fileFolderService.getReader(textDocument);
NodeRef document = fileFolderService.create(rootNodeRef, documentName, ContentModel.TYPE_CONTENT).getNodeRef();
contentWriter = fileFolderService.getWriter(document);
contentWriter.setEncoding("UTF-8");
contentWriter.setMimetype(mimetype);
contentWriter.setLocale(Locale.ENGLISH);
if (contentService.isTransformable(contentReader, contentWriter))
{
contentService.transform(contentReader, contentWriter);
}
fileFolderService.delete(textDocument);
return document;
}
private void assertRendiions(List<CMISRendition> receivedRenditions, List<CMISRendition> expectedRenditions)
{
assertNotNull(receivedRenditions);
expectedRenditions = removeReplication(expectedRenditions);
assertTrue(receivedRenditions.size() == expectedRenditions.size());
for (CMISRendition rendition : receivedRenditions)
{
assertNotNull(rendition);
assertNotNull(rendition.getStreamId());
}
Collections.sort(receivedRenditions, renditionsComparator);
Collections.sort(expectedRenditions, renditionsComparator);
for (int i = 0; i < expectedRenditions.size(); ++i)
{
assertRendition(receivedRenditions.get(i), expectedRenditions.get(i));
}
}
private void assertRendition(CMISRendition receivedRendition, CMISRendition expectedRendition)
{
assertEquals(expectedRendition.getStreamId(), receivedRendition.getStreamId());
assertEquals(expectedRendition.getKind(), receivedRendition.getKind());
assertEquals(expectedRendition.getMimeType(), receivedRendition.getMimeType());
assertEquals(expectedRendition.getWidth(), receivedRendition.getWidth());
assertEquals(expectedRendition.getHeight(), receivedRendition.getHeight());
}
private List<CMISRendition> removeReplication(List<CMISRendition> renditions)
{
return new ArrayList<CMISRendition>(new HashSet<CMISRendition>(renditions));
}
private Comparator<CMISRendition> renditionsComparator = new Comparator<CMISRendition>()
{
public int compare(CMISRendition rendition1, CMISRendition rendition2)
{
return rendition1.getStreamId().compareTo(rendition2.getStreamId());
}
};
private List<CMISRendition> getRenditionsByKind(CMISRenditionKind kind)
{
return getRenditions(kind, null);
}
private List<CMISRendition> getRenditionsByMimetype(String mimetype)
{
return getRenditions(null, mimetype);
}
private List<CMISRendition> getRenditions(CMISRenditionKind kind, String mimetype)
{
List<CMISRendition> result = new ArrayList<CMISRendition>();
List<CMISRendition> allRenditions = new ArrayList<CMISRendition>(documentRenditions);
allRenditions.add(icon16Rendition);
allRenditions.add(icon32Rendition);
for (CMISRendition rendition : allRenditions)
{
if (isRenditionSatisfyConditions(rendition, kind, mimetype))
{
result.add(rendition);
}
}
return result;
}
private boolean isRenditionSatisfyConditions(CMISRendition rendition, CMISRenditionKind kind, String mimetype)
{
if (kind != null)
{
if (!kind.equals(rendition.getKind()))
{
return false;
}
}
if (mimetype != null)
{
if (mimetype.endsWith("/*"))
{
String baseMimetype = getBaseType(mimetype);
if (!rendition.getMimeType().startsWith(baseMimetype))
{
return false;
}
}
else if (!mimetype.equals(rendition.getMimeType()))
{
return false;
}
}
return true;
}
private String[] getDifferentMimetypes()
{
List<CMISRendition> allRenditions = new ArrayList<CMISRendition>(documentRenditions);
allRenditions.add(icon16Rendition);
allRenditions.add(icon32Rendition);
Set<String> mimetypes = new HashSet<String>();
for (CMISRendition rendition : allRenditions)
{
mimetypes.add(rendition.getMimeType());
}
return mimetypes.toArray(new String[0]);
}
private String getBaseType(String mimetype)
{
String baseMymetype = mimetype;
int subTypeIndex = mimetype.indexOf("/");
if (subTypeIndex > 0 || subTypeIndex < mimetype.length())
{
baseMymetype = mimetype.substring(0, subTypeIndex);
}
return baseMymetype;
}
private String createFilter(CMISRenditionKind[] kinds, String[] mimetypes)
{
StringBuilder filter = new StringBuilder();
if (kinds != null)
{
for (CMISRenditionKind kind : kinds)
{
filter.append(kind.getLabel());
filter.append(",");
}
}
if (mimetypes != null)
{
for (String mimetype : mimetypes)
{
filter.append(mimetype);
filter.append(",");
}
}
filter.delete(filter.length() - 1, filter.length());
return filter.toString();
}
}

View File

@@ -0,0 +1,297 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- xsi:schemaLocation="http://www.alfresco.org/model/dictionary/1.0 ../../../../../../config/alfresco/model/modelSchema.xsd" -->
<model name="test:cmis-query-test" xmlns="http://www.alfresco.org/model/dictionary/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<description>Test model the cmis query tests</description>
<author>Alfresco</author>
<published>2010-01-22</published>
<version>0.1</version>
<imports>
<import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d"/>
<import uri="http://www.alfresco.org/model/system/1.0" prefix="sys"/>
<import uri="http://www.alfresco.org/model/content/1.0" prefix="cm"/>
</imports>
<namespaces>
<namespace uri="http://www.alfresco.org/test/cmis-query-test" prefix="test"/>
</namespaces>
<types>
<type name="test:extendedContent">
<title>Extended Content</title>
<parent>cm:content</parent>
<includedInSuperTypeQuery>true</includedInSuperTypeQuery>
<properties>
<!-- d:text -> String -->
<property name="test:singleTextBoth">
<type>d:text</type>
<mandatory>false</mandatory>
<multiple>false</multiple>
<index enabled="true" >
<atomic>true</atomic>
<tokenised>both</tokenised>
</index>
</property>
<property name="test:singleTextUntokenised">
<type>d:text</type>
<mandatory>false</mandatory>
<multiple>false</multiple>
<index enabled="true" >
<atomic>true</atomic>
<tokenised>false</tokenised>
</index>
</property>
<property name="test:singleTextTokenised">
<type>d:text</type>
<mandatory>false</mandatory>
<multiple>false</multiple>
<index enabled="true" >
<atomic>true</atomic>
<tokenised>true</tokenised>
</index>
</property>
<property name="test:multipleTextBoth">
<type>d:text</type>
<mandatory>false</mandatory>
<multiple>true</multiple>
<index enabled="true" >
<atomic>true</atomic>
<tokenised>both</tokenised>
</index>
</property>
<property name="test:multipleTextUntokenised">
<type>d:text</type>
<mandatory>false</mandatory>
<multiple>true</multiple>
<index enabled="true" >
<atomic>true</atomic>
<tokenised>false</tokenised>
</index>
</property>
<property name="test:multipleTextTokenised">
<type>d:text</type>
<mandatory>false</mandatory>
<multiple>true</multiple>
<index enabled="true" >
<atomic>true</atomic>
<tokenised>true</tokenised>
</index>
</property>
<!-- d:mltext -> String -->
<property name="test:singleMLTextBoth">
<type>d:mltext</type>
<mandatory>false</mandatory>
<multiple>false</multiple>
<index enabled="true" >
<atomic>true</atomic>
<tokenised>both</tokenised>
</index>
</property>
<property name="test:singleMLTextUntokenised">
<type>d:mltext</type>
<mandatory>false</mandatory>
<multiple>false</multiple>
<index enabled="true" >
<atomic>true</atomic>
<tokenised>false</tokenised>
</index>
</property>
<property name="test:singleMLTextTokenised">
<type>d:mltext</type>
<mandatory>false</mandatory>
<multiple>false</multiple>
<index enabled="true" >
<atomic>true</atomic>
<tokenised>true</tokenised>
</index>
</property>
<property name="test:multipleMLTextBoth">
<type>d:mltext</type>
<mandatory>false</mandatory>
<multiple>true</multiple>
<index enabled="true" >
<atomic>true</atomic>
<tokenised>both</tokenised>
</index>
</property>
<property name="test:multipleMLTextUntokenised">
<type>d:mltext</type>
<mandatory>false</mandatory>
<multiple>true</multiple>
<index enabled="true" >
<atomic>true</atomic>
<tokenised>false</tokenised>
</index>
</property>
<property name="test:multipleMLTextTokenised">
<type>d:mltext</type>
<mandatory>false</mandatory>
<multiple>true</multiple>
<index enabled="true" >
<atomic>true</atomic>
<tokenised>true</tokenised>
</index>
</property>
<!-- d:float -> Decimal -->
<property name="test:singleFloat">
<type>d:float</type>
<mandatory>false</mandatory>
<multiple>false</multiple>
<index enabled="true" >
<atomic>true</atomic>
<tokenised>true</tokenised>
</index>
</property>
<property name="test:multipleFloat">
<type>d:float</type>
<mandatory>false</mandatory>
<multiple>true</multiple>
<index enabled="true" >
<atomic>true</atomic>
<tokenised>true</tokenised>
</index>
</property>
<!-- d:double -> Decimal -->
<property name="test:singleDouble">
<type>d:double</type>
<mandatory>false</mandatory>
<multiple>false</multiple>
<index enabled="true">
<atomic>true</atomic>
<tokenised>true</tokenised>
</index>
</property>
<property name="test:multipleDouble">
<type>d:double</type>
<mandatory>false</mandatory>
<multiple>true</multiple>
<index enabled="true">
<atomic>true</atomic>
<tokenised>true</tokenised>
</index>
</property>
<!-- d:int -> Integer -->
<property name="test:singleInteger">
<type>d:int</type>
<mandatory>false</mandatory>
<multiple>false</multiple>
<index enabled="true">
<atomic>true</atomic>
<tokenised>true</tokenised>
</index>
</property>
<property name="test:multipleInteger">
<type>d:int</type>
<mandatory>false</mandatory>
<multiple>true</multiple>
<index enabled="true">
<atomic>true</atomic>
<tokenised>true</tokenised>
</index>
</property>
<!-- d:long -> Integer -->
<property name="test:singleLong">
<type>d:long</type>
<mandatory>false</mandatory>
<multiple>false</multiple>
<index enabled="true">
<atomic>true</atomic>
<tokenised>true</tokenised>
</index>
</property>
<property name="test:multipleLong">
<type>d:long</type>
<mandatory>false</mandatory>
<multiple>true</multiple>
<index enabled="true">
<atomic>true</atomic>
<tokenised>true</tokenised>
</index>
</property>
<!-- d:boolean -> Boolean -->
<property name="test:singleBoolean">
<type>d:boolean</type>
<mandatory>false</mandatory>
<multiple>false</multiple>
<index enabled="true">
<atomic>true</atomic>
<tokenised>true</tokenised>
</index>
</property>
<property name="test:multipleBoolean">
<type>d:boolean</type>
<mandatory>false</mandatory>
<multiple>true</multiple>
<index enabled="true">
<atomic>true</atomic>
<tokenised>true</tokenised>
</index>
</property>
<!-- d:date -> DateTime -->
<property name="test:singleDate">
<type>d:date</type>
<mandatory>false</mandatory>
<multiple>false</multiple>
<index enabled="true">
<atomic>true</atomic>
<tokenised>true</tokenised>
</index>
</property>
<property name="test:multipleDate">
<type>d:date</type>
<mandatory>false</mandatory>
<multiple>true</multiple>
<index enabled="true">
<atomic>true</atomic>
<tokenised>true</tokenised>
</index>
</property>
<!-- d:datetime -> DateTime -->
<property name="test:singleDatetime">
<type>d:datetime</type>
<mandatory>false</mandatory>
<multiple>false</multiple>
<index enabled="true">
<atomic>true</atomic>
<tokenised>true</tokenised>
</index>
</property>
<property name="test:multipleDatetime">
<type>d:datetime</type>
<mandatory>false</mandatory>
<multiple>true</multiple>
<index enabled="true">
<atomic>true</atomic>
<tokenised>true</tokenised>
</index>
</property>
</properties>
</type>
<type name="test:extendedFolder">
<title>Extended Folder</title>
<parent>cm:folder</parent>
</type>
</types>
</model>

View File

@@ -29,13 +29,13 @@ import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.alfresco.cmis.CMISCardinalityEnum;
import org.alfresco.cmis.CMISDataTypeEnum;
import org.alfresco.cmis.CMISDictionaryService;
import org.alfresco.cmis.CMISJoinEnum;
import org.alfresco.cmis.CMISPropertyDefinition;
@@ -46,6 +46,8 @@ import org.alfresco.cmis.CMISTypeDefinition;
import org.alfresco.cmis.CMISQueryOptions.CMISQueryMode;
import org.alfresco.repo.search.impl.parsers.CMISLexer;
import org.alfresco.repo.search.impl.parsers.CMISParser;
import org.alfresco.repo.search.impl.parsers.FTSParser;
import org.alfresco.repo.search.impl.parsers.FTSQueryException;
import org.alfresco.repo.search.impl.parsers.FTSQueryParser;
import org.alfresco.repo.search.impl.querymodel.Argument;
import org.alfresco.repo.search.impl.querymodel.ArgumentDefinition;
@@ -60,8 +62,10 @@ import org.alfresco.repo.search.impl.querymodel.LiteralArgument;
import org.alfresco.repo.search.impl.querymodel.Order;
import org.alfresco.repo.search.impl.querymodel.Ordering;
import org.alfresco.repo.search.impl.querymodel.ParameterArgument;
import org.alfresco.repo.search.impl.querymodel.PredicateMode;
import org.alfresco.repo.search.impl.querymodel.PropertyArgument;
import org.alfresco.repo.search.impl.querymodel.Query;
import org.alfresco.repo.search.impl.querymodel.QueryModelException;
import org.alfresco.repo.search.impl.querymodel.QueryModelFactory;
import org.alfresco.repo.search.impl.querymodel.Selector;
import org.alfresco.repo.search.impl.querymodel.SelectorArgument;
@@ -97,6 +101,11 @@ import org.antlr.runtime.tree.CommonTree;
*/
public class CMISQueryParser
{
private enum EscapeMode
{
LITERAL, LIKE, CONTAINS;
}
private CMISQueryOptions options;
private CMISDictionaryService cmisDictionaryService;
@@ -105,20 +114,21 @@ public class CMISQueryParser
private CMISScope[] validScopes;
private static CMISScope[] STRICT_SCOPES = new CMISScope[] { CMISScope.DOCUMENT, CMISScope.FOLDER };
private boolean hasScore = false;
private static CMISScope[] ALFRESCO_SCOPES = new CMISScope[] { CMISScope.DOCUMENT, CMISScope.FOLDER, CMISScope.POLICY };
private boolean hasContains = false;
public CMISQueryParser(CMISQueryOptions options, CMISDictionaryService cmisDictionaryService, CMISJoinEnum joinSupport)
{
this.options = options;
this.cmisDictionaryService = cmisDictionaryService;
this.joinSupport = joinSupport;
this.validScopes = (options.getQueryMode() == CMISQueryMode.CMS_STRICT) ? STRICT_SCOPES : ALFRESCO_SCOPES;
this.validScopes = (options.getQueryMode() == CMISQueryMode.CMS_STRICT) ? CmisFunctionEvaluationContext.STRICT_SCOPES : CmisFunctionEvaluationContext.ALFRESCO_SCOPES;
}
public Query parse(QueryModelFactory factory, FunctionEvaluationContext functionEvaluationContext)
{
CMISParser parser = null;
try
{
@@ -134,13 +144,17 @@ public class CMISQueryParser
Map<String, Selector> selectors = source.getSelectors();
ArrayList<Column> columns = buildColumns(queryNode, factory, selectors, options.getQuery());
HashSet<String> columnNames = new HashSet<String>();
HashMap<String, Column> columnMap = new HashMap<String, Column>();
for (Column column : columns)
{
if (!columnNames.add(column.getAlias()))
if (columnMap.containsKey(column.getAlias()))
{
throw new CMISQueryException("Duplicate column alias for " + column.getAlias());
}
else
{
columnMap.put(column.getAlias(), column);
}
}
ArrayList<Ordering> orderings = buildOrderings(queryNode, factory, selectors, columns);
@@ -149,7 +163,7 @@ public class CMISQueryParser
CommonTree orNode = (CommonTree) queryNode.getFirstChildWithType(CMISParser.DISJUNCTION);
if (orNode != null)
{
constraint = buildDisjunction(orNode, factory, functionEvaluationContext, selectors, columns);
constraint = buildDisjunction(orNode, factory, functionEvaluationContext, selectors, columnMap);
}
Query query = factory.createQuery(columns, source, constraint, orderings);
@@ -157,6 +171,14 @@ public class CMISQueryParser
// TODO: validate query and use of ID, function arguments matching
// up etc
if (options.getQueryMode() == CMISQueryMode.CMS_STRICT)
{
if (hasScore && !hasContains)
{
throw new CMISQueryException("Function SCORE() used without matching CONTAINS() function");
}
}
return query;
}
catch (RecognitionException e)
@@ -180,13 +202,13 @@ public class CMISQueryParser
* @return
*/
private Constraint buildDisjunction(CommonTree orNode, QueryModelFactory factory, FunctionEvaluationContext functionEvaluationContext, Map<String, Selector> selectors,
ArrayList<Column> columns)
HashMap<String, Column> columnMap)
{
List<Constraint> constraints = new ArrayList<Constraint>(orNode.getChildCount());
for (int i = 0; i < orNode.getChildCount(); i++)
{
CommonTree andNode = (CommonTree) orNode.getChild(i);
Constraint constraint = buildConjunction(andNode, factory, functionEvaluationContext, selectors, columns);
Constraint constraint = buildConjunction(andNode, factory, functionEvaluationContext, selectors, columnMap);
constraints.add(constraint);
}
if (constraints.size() == 1)
@@ -207,13 +229,13 @@ public class CMISQueryParser
* @return
*/
private Constraint buildConjunction(CommonTree andNode, QueryModelFactory factory, FunctionEvaluationContext functionEvaluationContext, Map<String, Selector> selectors,
ArrayList<Column> columns)
HashMap<String, Column> columnMap)
{
List<Constraint> constraints = new ArrayList<Constraint>(andNode.getChildCount());
for (int i = 0; i < andNode.getChildCount(); i++)
{
CommonTree notNode = (CommonTree) andNode.getChild(i);
Constraint constraint = buildNegation(notNode, factory, functionEvaluationContext, selectors, columns);
Constraint constraint = buildNegation(notNode, factory, functionEvaluationContext, selectors, columnMap);
constraints.add(constraint);
}
if (constraints.size() == 1)
@@ -234,17 +256,17 @@ public class CMISQueryParser
* @return
*/
private Constraint buildNegation(CommonTree notNode, QueryModelFactory factory, FunctionEvaluationContext functionEvaluationContext, Map<String, Selector> selectors,
ArrayList<Column> columns)
HashMap<String, Column> columnMap)
{
if (notNode.getType() == CMISParser.NEGATION)
{
Constraint constraint = buildTest((CommonTree) notNode.getChild(0), factory, functionEvaluationContext, selectors, columns);
Constraint constraint = buildTest((CommonTree) notNode.getChild(0), factory, functionEvaluationContext, selectors, columnMap);
constraint.setOccur(Occur.EXCLUDE);
return constraint;
}
else
{
return buildTest(notNode, factory, functionEvaluationContext, selectors, columns);
return buildTest(notNode, factory, functionEvaluationContext, selectors, columnMap);
}
}
@@ -256,15 +278,15 @@ public class CMISQueryParser
* @return
*/
private Constraint buildTest(CommonTree testNode, QueryModelFactory factory, FunctionEvaluationContext functionEvaluationContext, Map<String, Selector> selectors,
ArrayList<Column> columns)
HashMap<String, Column> columnMap)
{
if (testNode.getType() == CMISParser.DISJUNCTION)
{
return buildDisjunction(testNode, factory, functionEvaluationContext, selectors, columns);
return buildDisjunction(testNode, factory, functionEvaluationContext, selectors, columnMap);
}
else
{
return buildPredicate(testNode, factory, functionEvaluationContext, selectors, columns);
return buildPredicate(testNode, factory, functionEvaluationContext, selectors, columnMap);
}
}
@@ -276,7 +298,7 @@ public class CMISQueryParser
* @return
*/
private Constraint buildPredicate(CommonTree predicateNode, QueryModelFactory factory, FunctionEvaluationContext functionEvaluationContext, Map<String, Selector> selectors,
ArrayList<Column> columns)
Map<String, Column> columnMap)
{
String functionName;
Function function;
@@ -290,11 +312,12 @@ public class CMISQueryParser
function = factory.getFunction(functionName);
functionArguments = new LinkedHashMap<String, Argument>();
argNode = (CommonTree) predicateNode.getChild(0);
arg = getFunctionArgument(argNode, function.getArgumentDefinition(Child.ARG_PARENT), factory, selectors);
arg = getFunctionArgument(argNode, function.getArgumentDefinition(Child.ARG_PARENT), factory, selectors, columnMap, false);
functionArguments.put(arg.getName(), arg);
if (predicateNode.getChildCount() > 1)
{
arg = getFunctionArgument(argNode, function.getArgumentDefinition(Child.ARG_SELECTOR), factory, selectors);
argNode = (CommonTree) predicateNode.getChild(1);
arg = getFunctionArgument(argNode, function.getArgumentDefinition(Child.ARG_SELECTOR), factory, selectors, columnMap, false);
if (!arg.isQueryable())
{
throw new CMISQueryException("The property is not queryable: " + argNode.getText());
@@ -303,6 +326,7 @@ public class CMISQueryParser
}
return factory.createFunctionalConstraint(function, functionArguments);
case CMISParser.PRED_COMPARISON:
switch (predicateNode.getChild(2).getType())
{
case CMISParser.EQUALS:
@@ -333,23 +357,28 @@ public class CMISQueryParser
throw new CMISQueryException("Unknown comparison function " + predicateNode.getChild(2).getText());
}
functionArguments = new LinkedHashMap<String, Argument>();
argNode = (CommonTree) predicateNode.getChild(0);
arg = getFunctionArgument(argNode, function.getArgumentDefinition(BaseComparison.ARG_MODE), factory, selectors, columnMap, false);
functionArguments.put(arg.getName(), arg);
argNode = (CommonTree) predicateNode.getChild(1);
arg = getFunctionArgument(argNode, function.getArgumentDefinition(BaseComparison.ARG_LHS), factory, selectors);
arg = getFunctionArgument(argNode, function.getArgumentDefinition(BaseComparison.ARG_LHS), factory, selectors, columnMap, false);
functionArguments.put(arg.getName(), arg);
argNode = (CommonTree) predicateNode.getChild(3);
arg = getFunctionArgument(argNode, function.getArgumentDefinition(BaseComparison.ARG_RHS), factory, selectors);
arg = getFunctionArgument(argNode, function.getArgumentDefinition(BaseComparison.ARG_RHS), factory, selectors, columnMap, false);
functionArguments.put(arg.getName(), arg);
checkPredicateConditionsForComparisons(function, functionArguments, functionEvaluationContext, columnMap);
return factory.createFunctionalConstraint(function, functionArguments);
case CMISParser.PRED_DESCENDANT:
functionName = Descendant.NAME;
function = factory.getFunction(functionName);
argNode = (CommonTree) predicateNode.getChild(0);
functionArguments = new LinkedHashMap<String, Argument>();
arg = getFunctionArgument(argNode, function.getArgumentDefinition(Descendant.ARG_ANCESTOR), factory, selectors);
arg = getFunctionArgument(argNode, function.getArgumentDefinition(Descendant.ARG_ANCESTOR), factory, selectors, columnMap, false);
functionArguments.put(arg.getName(), arg);
if (predicateNode.getChildCount() > 1)
{
arg = getFunctionArgument(argNode, function.getArgumentDefinition(Descendant.ARG_SELECTOR), factory, selectors);
argNode = (CommonTree) predicateNode.getChild(1);
arg = getFunctionArgument(argNode, function.getArgumentDefinition(Descendant.ARG_SELECTOR), factory, selectors, columnMap, false);
functionArguments.put(arg.getName(), arg);
}
return factory.createFunctionalConstraint(function, functionArguments);
@@ -358,14 +387,23 @@ public class CMISQueryParser
function = factory.getFunction(functionName);
argNode = (CommonTree) predicateNode.getChild(0);
functionArguments = new LinkedHashMap<String, Argument>();
arg = getFunctionArgument(argNode, function.getArgumentDefinition(Exists.ARG_PROPERTY), factory, selectors);
arg = getFunctionArgument(argNode, function.getArgumentDefinition(Exists.ARG_PROPERTY), factory, selectors, columnMap, false);
functionArguments.put(arg.getName(), arg);
arg = factory.createLiteralArgument(Exists.ARG_NOT, DataTypeDefinition.BOOLEAN, (predicateNode.getChildCount() > 1));
functionArguments.put(arg.getName(), arg);
// Applies to both single valued and multi-valued properties - no checks required
return factory.createFunctionalConstraint(function, functionArguments);
case CMISParser.PRED_FTS:
if (options.getQueryMode() == CMISQueryMode.CMS_STRICT)
{
if (hasContains)
{
throw new CMISQueryException("Only one CONTAINS() function can be included in a single query statement.");
}
}
String ftsExpression = predicateNode.getChild(0).getText();
FTSQueryParser ftsQueryParser = new FTSQueryParser();
ftsExpression = ftsExpression.substring(1, ftsExpression.length() - 1);
ftsExpression = unescape(ftsExpression, EscapeMode.CONTAINS);
Selector selector;
if (predicateNode.getChildCount() > 1)
{
@@ -399,42 +437,253 @@ public class CMISQueryParser
defaultConnective = options.getDefaultFTSConnective();
defaultFieldConnective = options.getDefaultFTSFieldConnective();
}
return ftsQueryParser.buildFTS(ftsExpression.substring(1, ftsExpression.length() - 1), factory, functionEvaluationContext, selector, columns, defaultConnective,
defaultFieldConnective, null, options.getDefaultFieldName());
FTSParser.Mode mode;
if (options.getQueryMode() == CMISQueryMode.CMS_STRICT)
{
mode = FTSParser.Mode.CMIS;
}
else
{
if (defaultConnective == Connective.AND)
{
mode = FTSParser.Mode.DEFAULT_CONJUNCTION;
}
else
{
mode = FTSParser.Mode.DEFAULT_DISJUNCTION;
}
}
Constraint ftsConstraint = FTSQueryParser.buildFTS(ftsExpression, factory, functionEvaluationContext, selector, columnMap, mode, defaultFieldConnective, null, options.getDefaultFieldName());
ftsConstraint.setBoost(1000.0f);
hasContains = true;
return ftsConstraint;
case CMISParser.PRED_IN:
functionName = In.NAME;
function = factory.getFunction(functionName);
functionArguments = new LinkedHashMap<String, Argument>();
argNode = (CommonTree) predicateNode.getChild(0);
arg = getFunctionArgument(argNode, function.getArgumentDefinition(In.ARG_MODE), factory, selectors);
arg = getFunctionArgument(argNode, function.getArgumentDefinition(In.ARG_MODE), factory, selectors, columnMap, false);
functionArguments.put(arg.getName(), arg);
argNode = (CommonTree) predicateNode.getChild(1);
arg = getFunctionArgument(argNode, function.getArgumentDefinition(In.ARG_PROPERTY), factory, selectors);
arg = getFunctionArgument(argNode, function.getArgumentDefinition(In.ARG_PROPERTY), factory, selectors, columnMap, false);
functionArguments.put(arg.getName(), arg);
argNode = (CommonTree) predicateNode.getChild(2);
arg = getFunctionArgument(argNode, function.getArgumentDefinition(In.ARG_LIST), factory, selectors);
arg = getFunctionArgument(argNode, function.getArgumentDefinition(In.ARG_LIST), factory, selectors, columnMap, false);
functionArguments.put(arg.getName(), arg);
arg = factory.createLiteralArgument(In.ARG_NOT, DataTypeDefinition.BOOLEAN, (predicateNode.getChildCount() > 3));
functionArguments.put(arg.getName(), arg);
checkPredicateConditionsForIn(functionArguments, functionEvaluationContext, columnMap);
return factory.createFunctionalConstraint(function, functionArguments);
case CMISParser.PRED_LIKE:
functionName = Like.NAME;
function = factory.getFunction(functionName);
functionArguments = new LinkedHashMap<String, Argument>();
argNode = (CommonTree) predicateNode.getChild(0);
arg = getFunctionArgument(argNode, function.getArgumentDefinition(Like.ARG_PROPERTY), factory, selectors);
arg = getFunctionArgument(argNode, function.getArgumentDefinition(Like.ARG_PROPERTY), factory, selectors, columnMap, false);
functionArguments.put(arg.getName(), arg);
argNode = (CommonTree) predicateNode.getChild(1);
arg = getFunctionArgument(argNode, function.getArgumentDefinition(Like.ARG_EXP), factory, selectors);
arg = getFunctionArgument(argNode, function.getArgumentDefinition(Like.ARG_EXP), factory, selectors, columnMap, true);
functionArguments.put(arg.getName(), arg);
arg = factory.createLiteralArgument(Like.ARG_NOT, DataTypeDefinition.BOOLEAN, (predicateNode.getChildCount() > 2));
functionArguments.put(arg.getName(), arg);
checkPredicateConditionsForLike(functionArguments, functionEvaluationContext, columnMap);
return factory.createFunctionalConstraint(function, functionArguments);
default:
return null;
}
}
private void checkPredicateConditionsForIn(Map<String, Argument> functionArguments, FunctionEvaluationContext functionEvaluationContext, Map<String, Column> columnMap)
{
if (options.getQueryMode() == CMISQueryMode.CMS_STRICT)
{
PropertyArgument propertyArgument = (PropertyArgument) functionArguments.get(In.ARG_PROPERTY);
LiteralArgument modeArgument = (LiteralArgument) functionArguments.get(In.ARG_MODE);
String modeString = DefaultTypeConverter.INSTANCE.convert(String.class, modeArgument.getValue(functionEvaluationContext));
PredicateMode mode = PredicateMode.valueOf(modeString);
String propertyName = propertyArgument.getPropertyName();
Column column = columnMap.get(propertyName);
if (column != null)
{
// check for function type
if (column.getFunction().getName().equals(PropertyAccessor.NAME))
{
PropertyArgument arg = (PropertyArgument) column.getFunctionArguments().get(PropertyAccessor.ARG_PROPERTY);
propertyName = arg.getPropertyName();
}
else
{
throw new CMISQueryException("Complex column reference not supoprted in LIKE " + propertyName);
}
}
boolean isMultiValued = functionEvaluationContext.isMultiValued(propertyName);
switch (mode)
{
case ANY:
if (isMultiValued)
{
break;
}
else
{
throw new QueryModelException("Predicate mode " + PredicateMode.ANY + " is not supported for IN and single valued properties");
}
case SINGLE_VALUED_PROPERTY:
if (isMultiValued)
{
throw new QueryModelException("Predicate mode " + PredicateMode.SINGLE_VALUED_PROPERTY + " is not supported for IN and multi-valued properties");
}
else
{
break;
}
default:
throw new QueryModelException("Unsupported predicate mode " + mode);
}
CMISPropertyDefinition propDef = cmisDictionaryService.findPropertyByQueryName(propertyName);
if (propDef.getDataType() == CMISDataTypeEnum.BOOLEAN)
{
throw new QueryModelException("In is not supported for properties of type Boolean");
}
}
}
private void checkPredicateConditionsForComparisons(Function function, Map<String, Argument> functionArguments, FunctionEvaluationContext functionEvaluationContext,
Map<String, Column> columnMap)
{
if (options.getQueryMode() == CMISQueryMode.CMS_STRICT)
{
((BaseComparison) function).setPropertyAndStaticArguments(functionArguments);
String propertyName = ((BaseComparison) function).getPropertyName();
LiteralArgument modeArgument = (LiteralArgument) functionArguments.get(BaseComparison.ARG_MODE);
String modeString = DefaultTypeConverter.INSTANCE.convert(String.class, modeArgument.getValue(functionEvaluationContext));
PredicateMode mode = PredicateMode.valueOf(modeString);
Column column = columnMap.get(propertyName);
if (column != null)
{
// check for function type
if (column.getFunction().getName().equals(PropertyAccessor.NAME))
{
PropertyArgument arg = (PropertyArgument) column.getFunctionArguments().get(PropertyAccessor.ARG_PROPERTY);
propertyName = arg.getPropertyName();
}
else
{
throw new CMISQueryException("Complex column reference not supoprted in LIKE " + propertyName);
}
}
boolean isMultiValued = functionEvaluationContext.isMultiValued(propertyName);
switch (mode)
{
case ANY:
if (isMultiValued)
{
if (function.getName().equals(Equals.NAME))
{
break;
}
else
{
throw new QueryModelException("Predicate mode " + PredicateMode.ANY + " is only supported for " + Equals.NAME + " (and multi-valued properties).");
}
}
else
{
throw new QueryModelException("Predicate mode " + PredicateMode.ANY + " is not supported for " + function.getName() + " and single valued properties");
}
case SINGLE_VALUED_PROPERTY:
if (isMultiValued)
{
throw new QueryModelException("Predicate mode "
+ PredicateMode.SINGLE_VALUED_PROPERTY + " is not supported for " + function.getName() + " and multi-valued properties");
}
else
{
break;
}
default:
throw new QueryModelException("Unsupported predicate mode " + mode);
}
// limit support for ID and Boolean
CMISPropertyDefinition propDef = cmisDictionaryService.findPropertyByQueryName(propertyName);
if (propDef.getDataType() == CMISDataTypeEnum.ID)
{
if (function.getName().equals(Equals.NAME) || function.getName().equals(NotEquals.NAME))
{
return;
}
else
{
throw new QueryModelException("Comparison " + function.getName() + " is not supported for properties of type ID");
}
}
else if (propDef.getDataType() == CMISDataTypeEnum.BOOLEAN)
{
if (function.getName().equals(Equals.NAME))
{
return;
}
else
{
throw new QueryModelException("Comparison " + function.getName() + " is not supported for properties of type Boolean");
}
}
}
}
private void checkPredicateConditionsForLike(Map<String, Argument> functionArguments, FunctionEvaluationContext functionEvaluationContext, Map<String, Column> columnMap)
{
if (options.getQueryMode() == CMISQueryMode.CMS_STRICT)
{
PropertyArgument propertyArgument = (PropertyArgument) functionArguments.get(Like.ARG_PROPERTY);
boolean isMultiValued = functionEvaluationContext.isMultiValued(propertyArgument.getPropertyName());
if (isMultiValued)
{
throw new QueryModelException("Like is not supported for multi-valued properties");
}
String cmisPropertyName = propertyArgument.getPropertyName();
Column column = columnMap.get(cmisPropertyName);
if (column != null)
{
// check for function type
if (column.getFunction().getName().equals(PropertyAccessor.NAME))
{
PropertyArgument arg = (PropertyArgument) column.getFunctionArguments().get(PropertyAccessor.ARG_PROPERTY);
cmisPropertyName = arg.getPropertyName();
}
else
{
throw new CMISQueryException("Complex column reference not supoprted in LIKE " + cmisPropertyName);
}
}
CMISPropertyDefinition propDef = cmisDictionaryService.findPropertyByQueryName(cmisPropertyName);
if (propDef.getDataType() != CMISDataTypeEnum.STRING)
{
throw new CMISQueryException("LIKE is only supported against String types" + cmisPropertyName);
}
}
}
/**
* @param queryNode
* @param factory
@@ -480,6 +729,16 @@ public class CMISQueryParser
match = column;
break;
}
if (column.getFunction().getName().equals(PropertyAccessor.NAME))
{
PropertyArgument arg = (PropertyArgument) column.getFunctionArguments().get(PropertyAccessor.ARG_PROPERTY);
String propertyName = arg.getPropertyName();
if (propertyName.equals(columnName))
{
match = column;
break;
}
}
}
// in strict mode the ordered column must be selected
if ((options.getQueryMode() == CMISQueryMode.CMS_STRICT) && (match == null))
@@ -513,6 +772,13 @@ public class CMISQueryParser
throw new CMISQueryException("Invalid column for " + typeDef.getQueryName() + "." + columnName);
}
// Check column/property applies to selector/type
if (!typeDef.getPropertyDefinitions().containsKey(propDef.getPropertyId().getId()))
{
throw new CMISQueryException("Invalid column for " + typeDef.getQueryName() + "." + columnName);
}
// check there is a matching selector
if (options.getQueryMode() == CMISQueryMode.CMS_STRICT)
@@ -639,8 +905,6 @@ public class CMISQueryParser
}
Map<String, CMISPropertyDefinition> propDefs = typeDef.getPropertyDefinitions();
for (CMISPropertyDefinition definition : propDefs.values())
{
if (definition.getCardinality() == CMISCardinalityEnum.SINGLE_VALUED)
{
Function function = factory.getFunction(PropertyAccessor.NAME);
Argument arg = factory.createPropertyArgument(PropertyAccessor.ARG_PROPERTY, definition.isQueryable(), definition.isOrderable(), selector.getAlias(),
@@ -656,7 +920,6 @@ public class CMISQueryParser
}
}
}
}
CommonTree columnsNode = (CommonTree) queryNode.getFirstChildWithType(CMISParser.COLUMNS);
if (columnsNode != null)
@@ -686,8 +949,6 @@ public class CMISQueryParser
}
Map<String, CMISPropertyDefinition> propDefs = typeDef.getPropertyDefinitions();
for (CMISPropertyDefinition definition : propDefs.values())
{
if (definition.getCardinality() == CMISCardinalityEnum.SINGLE_VALUED)
{
Function function = factory.getFunction(PropertyAccessor.NAME);
Argument arg = factory.createPropertyArgument(PropertyAccessor.ARG_PROPERTY, definition.isQueryable(), definition.isOrderable(), selector.getAlias(),
@@ -702,7 +963,6 @@ public class CMISQueryParser
}
}
}
}
if (columnNode.getType() == CMISParser.COLUMN)
{
@@ -735,6 +995,13 @@ public class CMISQueryParser
}
CMISPropertyDefinition propDef = cmisDictionaryService.findPropertyByQueryName(columnName);
if (propDef == null)
{
throw new CMISQueryException("Invalid column for " + typeDef.getQueryName() + " " + columnName);
}
// Check column/property applies to selector/type
if (!typeDef.getPropertyDefinitions().containsKey(propDef.getPropertyId().getId()))
{
throw new CMISQueryException("Invalid column for " + typeDef.getQueryName() + "." + columnName);
}
@@ -780,7 +1047,7 @@ public class CMISQueryParser
if (functionNode.getChildCount() > childIndex + 1)
{
CommonTree argNode = (CommonTree) functionNode.getChild(childIndex++);
Argument arg = getFunctionArgument(argNode, definition, factory, selectors);
Argument arg = getFunctionArgument(argNode, definition, factory, selectors, null, function.getName().equals(Like.NAME));
functionArguments.put(arg.getName(), arg);
}
else
@@ -806,6 +1073,11 @@ public class CMISQueryParser
int start = getStringPosition(query, functionNode.getLine(), functionNode.getCharPositionInLine());
int end = getStringPosition(query, rparenNode.getLine(), rparenNode.getCharPositionInLine());
if (function.getName().equals(Score.NAME))
{
hasScore = true;
}
String alias;
if (function.getName().equals(Score.NAME))
{
@@ -860,11 +1132,12 @@ public class CMISQueryParser
return position + charPositionInLine;
}
private Argument getFunctionArgument(CommonTree argNode, ArgumentDefinition definition, QueryModelFactory factory, Map<String, Selector> selectors)
private Argument getFunctionArgument(CommonTree argNode, ArgumentDefinition definition, QueryModelFactory factory, Map<String, Selector> selectors,
Map<String, Column> columnMap, boolean inLike)
{
if (argNode.getType() == CMISParser.COLUMN_REF)
{
PropertyArgument arg = buildColumnReference(definition.getName(), argNode, factory);
PropertyArgument arg = buildColumnReference(definition.getName(), argNode, factory, selectors, columnMap);
if (!arg.isQueryable())
{
throw new CMISQueryException("Column refers to unqueryable property " + arg.getPropertyName());
@@ -943,6 +1216,7 @@ public class CMISQueryParser
{
String text = argNode.getChild(0).getText();
text = text.substring(1, text.length() - 1);
text = unescape(text, inLike ? EscapeMode.LIKE : EscapeMode.LITERAL);
LiteralArgument arg = factory.createLiteralArgument(definition.getName(), DataTypeDefinition.TEXT, text);
return arg;
}
@@ -1003,7 +1277,7 @@ public class CMISQueryParser
for (int i = 0; i < argNode.getChildCount(); i++)
{
CommonTree arg = (CommonTree) argNode.getChild(i);
arguments.add(getFunctionArgument(arg, definition, factory, selectors));
arguments.add(getFunctionArgument(arg, definition, factory, selectors, columnMap, inLike));
}
ListArgument arg = factory.createListArgument(definition.getName(), arguments);
if (!arg.isQueryable())
@@ -1017,6 +1291,11 @@ public class CMISQueryParser
LiteralArgument arg = factory.createLiteralArgument(definition.getName(), DataTypeDefinition.TEXT, argNode.getText());
return arg;
}
else if (argNode.getType() == CMISParser.SINGLE_VALUED_PROPERTY)
{
LiteralArgument arg = factory.createLiteralArgument(definition.getName(), DataTypeDefinition.TEXT, argNode.getText());
return arg;
}
else if (argNode.getType() == CMISParser.NOT)
{
LiteralArgument arg = factory.createLiteralArgument(definition.getName(), DataTypeDefinition.TEXT, argNode.getText());
@@ -1039,7 +1318,7 @@ public class CMISQueryParser
if (argNode.getChildCount() > childIndex + 1)
{
CommonTree currentArgNode = (CommonTree) argNode.getChild(childIndex++);
Argument arg = getFunctionArgument(currentArgNode, currentDefinition, factory, selectors);
Argument arg = getFunctionArgument(currentArgNode, currentDefinition, factory, selectors, columnMap, inLike);
functionArguments.put(arg.getName(), arg);
}
else
@@ -1110,14 +1389,6 @@ public class CMISQueryParser
throw new CMISQueryException("Type is not queryable " + tableName + " -> " + typeDef.getTypeId());
}
}
// check sub types all include in super type query
for(CMISTypeDefinition subType : typeDef.getSubTypes(true))
{
if(!subType.isIncludeInSuperTypeQuery())
{
throw new CMISQueryException("includeInSuperTypeQuery=falss is not support for "+tableName+ " descendant type "+subType.getQueryName());
}
}
return factory.createSelector(typeDef.getTypeId().getQName(), alias);
}
else
@@ -1149,14 +1420,7 @@ public class CMISQueryParser
throw new CMISQueryException("Type is not queryable " + tableName + " -> " + typeDef.getTypeId());
}
}
// check sub types all include in super type query
for(CMISTypeDefinition subType : typeDef.getSubTypes(true))
{
if(!subType.isIncludeInSuperTypeQuery())
{
throw new CMISQueryException("includeInSuperTypeQuery=falss is not support for "+tableName+ " descendant type "+subType.getQueryName());
}
}
Source lhs = factory.createSelector(typeDef.getTypeId().getQName(), alias);
List<CommonTree> list = (List<CommonTree>) (source.getChildren());
@@ -1183,7 +1447,7 @@ public class CMISQueryParser
CommonTree joinConditionNode = (CommonTree) joinNode.getFirstChildWithType(CMISParser.ON);
if (joinConditionNode != null)
{
PropertyArgument arg1 = buildColumnReference(Equals.ARG_LHS, (CommonTree) joinConditionNode.getChild(0), factory);
PropertyArgument arg1 = buildColumnReference(Equals.ARG_LHS, (CommonTree) joinConditionNode.getChild(0), factory, null, null);
if (!lhs.getSelectors().containsKey(arg1.getSelector()) && !rhs.getSelectors().containsKey(arg1.getSelector()))
{
throw new CMISQueryException("No table with alias " + arg1.getSelector());
@@ -1198,7 +1462,7 @@ public class CMISQueryParser
{
throw new CMISQueryException("Unknown function: " + functionNameNode.getText());
}
PropertyArgument arg2 = buildColumnReference(Equals.ARG_RHS, (CommonTree) joinConditionNode.getChild(2), factory);
PropertyArgument arg2 = buildColumnReference(Equals.ARG_RHS, (CommonTree) joinConditionNode.getChild(2), factory, null, null);
if (!lhs.getSelectors().containsKey(arg2.getSelector()) && !rhs.getSelectors().containsKey(arg2.getSelector()))
{
throw new CMISQueryException("No table with alias " + arg2.getSelector());
@@ -1219,27 +1483,193 @@ public class CMISQueryParser
}
}
public PropertyArgument buildColumnReference(String argumentName, CommonTree columnReferenceNode, QueryModelFactory factory)
public PropertyArgument buildColumnReference(String argumentName, CommonTree columnReferenceNode, QueryModelFactory factory, Map<String, Selector> selectors,
Map<String, Column> columnMap)
{
String cmisPropertyName = columnReferenceNode.getChild(0).getText();
String qualifer = "";
String qualifier = "";
if (columnReferenceNode.getChildCount() > 1)
{
qualifer = columnReferenceNode.getChild(1).getText();
qualifier = columnReferenceNode.getChild(1).getText();
}
if ((qualifier == "") && (columnMap != null))
{
Column column = columnMap.get(cmisPropertyName);
if (column != null)
{
// check for function type
if (column.getFunction().getName().equals(PropertyAccessor.NAME))
{
PropertyArgument arg = (PropertyArgument) column.getFunctionArguments().get(PropertyAccessor.ARG_PROPERTY);
cmisPropertyName = arg.getPropertyName();
qualifier = arg.getSelector();
}
else
{
// TODO: should be able to return non property arguments
// The implementation should throw out what it can not support at build time.
throw new CMISQueryException("Complex column reference unsupported (only direct column references are currently supported) " + cmisPropertyName);
}
}
}
CMISPropertyDefinition propDef = cmisDictionaryService.findPropertyByQueryName(cmisPropertyName);
if (propDef == null)
{
throw new CMISQueryException("Unknown column/property " + cmisPropertyName);
}
if (selectors != null)
{
Selector selector = selectors.get(qualifier);
if (selector == null)
{
if ((qualifier.equals("")) && (selectors.size() == 1))
{
selector = selectors.get(selectors.keySet().iterator().next());
}
else
{
throw new CMISQueryException("No selector for " + qualifier);
}
}
CMISTypeDefinition typeDef = cmisDictionaryService.findTypeForClass(selector.getType(), validScopes);
if (typeDef == null)
{
throw new CMISQueryException("Type unsupported in CMIS queries: " + selector.getAlias());
}
// Check column/property applies to selector/type
if (!typeDef.getPropertyDefinitions().containsKey(propDef.getPropertyId().getId()))
{
throw new CMISQueryException("Invalid column for " + typeDef.getQueryName() + "." + cmisPropertyName);
}
}
if (options.getQueryMode() == CMISQueryMode.CMS_STRICT)
{
if (!propDef.isQueryable())
{
throw new CMISQueryException("Column is not queryable " + qualifer + "." + cmisPropertyName);
throw new CMISQueryException("Column is not queryable " + qualifier + "." + cmisPropertyName);
}
}
return factory.createPropertyArgument(argumentName, propDef.isQueryable(), propDef.isOrderable(), qualifer, propDef.getPropertyId().getId());
return factory.createPropertyArgument(argumentName, propDef.isQueryable(), propDef.isOrderable(), qualifier, propDef.getPropertyId().getId());
}
private String unescape(String string, EscapeMode mode)
{
StringBuilder builder = new StringBuilder(string.length());
boolean lastWasEscape = false;
for (int i = 0; i < string.length(); i++)
{
char c = string.charAt(i);
if (lastWasEscape)
{
// Need to keep escaping for like as we have the same escaping
if (mode == EscapeMode.LIKE)
{
// Like does its own escaping - so pass through \ % and _
if (c == '\'')
{
builder.append(c);
}
else if (c == '%')
{
builder.append('\\');
builder.append(c);
}
else if (c == '_')
{
builder.append('\\');
builder.append(c);
}
else if (c == '\\')
{
builder.append('\\');
builder.append(c);
}
else
{
throw new UnsupportedOperationException("Unsupported escape pattern in <" + string + "> at position " + i);
}
}
else if (mode == EscapeMode.CONTAINS)
{
if (options.getQueryMode() == CMISQueryMode.CMS_STRICT)
{
if (c == '\'')
{
builder.append(c);
}
else if (c == '\\')
{
builder.append('\\');
builder.append(c);
}
else
{
throw new UnsupportedOperationException("Unsupported escape pattern in <" + string + "> at position " + i);
}
}
else
{
if (c == '\'')
{
builder.append(c);
}
else
{
builder.append(c);
}
}
}
else if (mode == EscapeMode.LITERAL)
{
if (c == '\'')
{
builder.append(c);
}
else if (c == '\\')
{
builder.append(c);
}
else
{
throw new UnsupportedOperationException("Unsupported escape pattern in <" + string + "> at position " + i);
}
}
else
{
throw new UnsupportedOperationException("Unsupported escape pattern in <" + string + "> at position " + i);
}
lastWasEscape = false;
}
else
{
if (c == '\\')
{
lastWasEscape = true;
}
else
{
builder.append(c);
}
}
}
if (lastWasEscape)
{
throw new FTSQueryException("Escape character at end of string " + string);
}
return builder.toString();
}
}

View File

@@ -34,7 +34,9 @@ import org.alfresco.cmis.CMISQueryEnum;
import org.alfresco.cmis.CMISQueryOptions;
import org.alfresco.cmis.CMISQueryService;
import org.alfresco.cmis.CMISResultSet;
import org.alfresco.cmis.CMISScope;
import org.alfresco.cmis.CMISServices;
import org.alfresco.cmis.CMISQueryOptions.CMISQueryMode;
import org.alfresco.repo.search.impl.querymodel.Query;
import org.alfresco.repo.search.impl.querymodel.QueryEngine;
import org.alfresco.repo.search.impl.querymodel.QueryEngineResults;
@@ -117,9 +119,13 @@ public class CMISQueryServiceImpl implements CMISQueryService
joinSupport = CMISJoinEnum.INNER_JOIN_SUPPORT;
}
// TODO: Refactor to avoid duplication of valid scopes here and in CMISQueryParser
CMISScope[] validScopes = (options.getQueryMode() == CMISQueryMode.CMS_STRICT) ? CmisFunctionEvaluationContext.STRICT_SCOPES : CmisFunctionEvaluationContext.ALFRESCO_SCOPES;
CmisFunctionEvaluationContext functionContext = new CmisFunctionEvaluationContext();
functionContext.setCmisDictionaryService(cmisDictionaryService);
functionContext.setNodeService(nodeService);
functionContext.setValidScopes(validScopes);
CMISQueryParser parser = new CMISQueryParser(options, cmisDictionaryService, joinSupport);
Query query = parser.parse(queryEngine.getQueryModelFactory(), functionContext);

View File

@@ -25,7 +25,6 @@
package org.alfresco.cmis.search;
import org.alfresco.cmis.CMISDataTypeEnum;
import org.alfresco.cmis.CMISDictionaryService;
import org.alfresco.cmis.CMISPropertyDefinition;
import org.alfresco.cmis.CMISResultSetColumn;
import org.alfresco.service.namespace.QName;

View File

@@ -43,7 +43,6 @@ import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.search.LimitBy;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.ResultSetRow;
@@ -79,7 +78,6 @@ public class CMISResultSetImpl implements CMISResultSet, Serializable
/*
* (non-Javadoc)
*
* @see org.alfresco.cmis.search.CMISResultSet#close()
*/
public void close()
@@ -98,7 +96,6 @@ public class CMISResultSetImpl implements CMISResultSet, Serializable
/*
* (non-Javadoc)
*
* @see org.alfresco.cmis.search.CMISResultSet#getMetaData()
*/
public CMISResultSetMetaData getMetaData()
@@ -108,7 +105,6 @@ public class CMISResultSetImpl implements CMISResultSet, Serializable
/*
* (non-Javadoc)
*
* @see org.alfresco.cmis.search.CMISResultSet#getRow(int)
*/
public CMISResultSetRow getRow(int i)
@@ -118,7 +114,6 @@ public class CMISResultSetImpl implements CMISResultSet, Serializable
/*
* (non-Javadoc)
*
* @see org.alfresco.cmis.search.CMISResultSet#hasMore()
*/
public boolean hasMore()
@@ -135,7 +130,6 @@ public class CMISResultSetImpl implements CMISResultSet, Serializable
/*
* (non-Javadoc)
*
* @see org.alfresco.cmis.search.CMISResultSet#length()
*/
public int getLength()
@@ -149,7 +143,6 @@ public class CMISResultSetImpl implements CMISResultSet, Serializable
/*
* (non-Javadoc)
*
* @see org.alfresco.cmis.search.CMISResultSet#start()
*/
public int getStart()
@@ -159,7 +152,6 @@ public class CMISResultSetImpl implements CMISResultSet, Serializable
/*
* (non-Javadoc)
*
* @see java.lang.Iterable#iterator()
*/
public Iterator<CMISResultSetRow> iterator()
@@ -212,12 +204,35 @@ public class CMISResultSetImpl implements CMISResultSet, Serializable
{
return refs.values().iterator().next();
}
else
else if(allNodeRefsEqual(refs))
{
return refs.values().iterator().next();
}
else {
throw new IllegalStateException("Ambiguous selector");
}
}
private boolean allNodeRefsEqual(Map<String, NodeRef> selected)
{
NodeRef last = null;
for (NodeRef current : selected.values())
{
if (last == null)
{
last = current;
}
else
{
if (!last.equals(current))
{
return false;
}
}
}
return true;
}
public List<NodeRef> getNodeRefs()
{
ArrayList<NodeRef> nodeRefs = new ArrayList<NodeRef>(length());
@@ -240,12 +255,36 @@ public class CMISResultSetImpl implements CMISResultSet, Serializable
{
return scores.values().iterator().next();
}
else if(allScoresEqual(scores))
{
return scores.values().iterator().next();
}
else
{
throw new IllegalStateException("Ambiguous selector");
}
}
private boolean allScoresEqual(Map<String, Float> scores)
{
Float last = null;
for (Float current : scores.values())
{
if (last == null)
{
last = current;
}
else
{
if (!last.equals(current))
{
return false;
}
}
}
return true;
}
public int length()
{
return getLength();

View File

@@ -33,7 +33,9 @@ import org.alfresco.cmis.CMISResultSet;
import org.alfresco.cmis.CMISResultSetMetaData;
import org.alfresco.cmis.CMISResultSetRow;
import org.alfresco.repo.search.impl.querymodel.Column;
import org.alfresco.repo.search.impl.querymodel.PropertyArgument;
import org.alfresco.repo.search.impl.querymodel.Query;
import org.alfresco.repo.search.impl.querymodel.impl.functions.PropertyAccessor;
import org.alfresco.repo.search.results.ResultSetSPIWrapper;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
@@ -66,7 +68,8 @@ public class CMISResultSetRowImpl implements CMISResultSetRow
private CMISDictionaryService cmisDictionaryService;
public CMISResultSetRowImpl(CMISResultSet resultSet, int index, Map<String, Float> scores, NodeService nodeService, Map<String, NodeRef> nodeRefs, Query query, CMISDictionaryService cmisDictionaryService)
public CMISResultSetRowImpl(CMISResultSet resultSet, int index, Map<String, Float> scores, NodeService nodeService, Map<String, NodeRef> nodeRefs, Query query,
CMISDictionaryService cmisDictionaryService)
{
this.resultSet = resultSet;
this.index = index;
@@ -79,7 +82,6 @@ public class CMISResultSetRowImpl implements CMISResultSetRow
/*
* (non-Javadoc)
*
* @see org.alfresco.cmis.search.CMISResultSetRow#getIndex()
*/
public int getIndex()
@@ -89,7 +91,6 @@ public class CMISResultSetRowImpl implements CMISResultSetRow
/*
* (non-Javadoc)
*
* @see org.alfresco.cmis.search.CMISResultSetRow#getResultSet()
*/
public ResultSet getResultSet()
@@ -99,7 +100,6 @@ public class CMISResultSetRowImpl implements CMISResultSetRow
/*
* (non-Javadoc)
*
* @see org.alfresco.cmis.search.CMISResultSetRow#getScore()
*/
public float getScore()
@@ -115,7 +115,6 @@ public class CMISResultSetRowImpl implements CMISResultSetRow
/*
* (non-Javadoc)
*
* @see org.alfresco.cmis.search.CMISResultSetRow#getScore(java.lang.String)
*/
public float getScore(String selectorName)
@@ -125,7 +124,6 @@ public class CMISResultSetRowImpl implements CMISResultSetRow
/*
* (non-Javadoc)
*
* @see org.alfresco.cmis.search.CMISResultSetRow#getScores()
*/
public Map<String, Float> getScores()
@@ -135,7 +133,6 @@ public class CMISResultSetRowImpl implements CMISResultSetRow
/*
* (non-Javadoc)
*
* @see org.alfresco.cmis.search.CMISResultSetRow#getScore(java.lang.String)
*/
public NodeRef getNodeRef(String selectorName)
@@ -145,7 +142,6 @@ public class CMISResultSetRowImpl implements CMISResultSetRow
/*
* (non-Javadoc)
*
* @see org.alfresco.cmis.search.CMISResultSetRow#getScores()
*/
public Map<String, NodeRef> getNodeRefs()
@@ -155,14 +151,9 @@ public class CMISResultSetRowImpl implements CMISResultSetRow
/*
* (non-Javadoc)
*
* @see org.alfresco.cmis.search.CMISResultSetRow#getValue(java.lang.String)
*/
public Serializable getValue(String columnName)
{
for (Column column : query.getColumns())
{
if (column.getAlias().equals(columnName))
{
CmisFunctionEvaluationContext context = new CmisFunctionEvaluationContext();
context.setCmisDictionaryService(cmisDictionaryService);
@@ -170,15 +161,55 @@ public class CMISResultSetRowImpl implements CMISResultSetRow
context.setNodeService(nodeService);
context.setScores(scores);
context.setScore(getScore());
for (Column column : query.getColumns())
{
if (column.getAlias().equals(columnName))
{
return column.getFunction().getValue(column.getFunctionArguments(), context);
}
// Special case for one selector - ignore any table aliases
// also allows look up direct and not by alias
// Perhaps we should add the duplicates instead
// TODO: check SQL 92 for single alias table behaviour for selectors
if (nodeRefs.size() == 1)
{
if (column.getFunction().getName().equals(PropertyAccessor.NAME))
{
PropertyArgument arg = (PropertyArgument) column.getFunctionArguments().get(PropertyAccessor.ARG_PROPERTY);
String propertyName = arg.getPropertyName();
if (propertyName.equals(columnName))
{
return column.getFunction().getValue(column.getFunctionArguments(), context);
}
StringBuilder builder = new StringBuilder();
builder.append(arg.getSelector()).append(".").append(propertyName);
propertyName = builder.toString();
if (propertyName.equals(columnName))
{
return column.getFunction().getValue(column.getFunctionArguments(), context);
}
}
}
else
{
if (column.getFunction().getName().equals(PropertyAccessor.NAME))
{
PropertyArgument arg = (PropertyArgument) column.getFunctionArguments().get(PropertyAccessor.ARG_PROPERTY);
StringBuilder builder = new StringBuilder();
builder.append(arg.getSelector()).append(".").append(arg.getPropertyName());
String propertyName = builder.toString();
if (propertyName.equals(columnName))
{
return column.getFunction().getValue(column.getFunctionArguments(), context);
}
}
}
}
return null;
}
/*
* (non-Javadoc)
*
* @see org.alfresco.cmis.search.CMISResultSetRow#getValues()
*/
public Map<String, Serializable> getValues()
@@ -208,9 +239,33 @@ public class CMISResultSetRowImpl implements CMISResultSetRow
{
return nodeRefs.values().iterator().next();
}
else if(allNodeRefsEqual(nodeRefs))
{
return nodeRefs.values().iterator().next();
}
throw new UnsupportedOperationException("Ambiguous selector");
}
private boolean allNodeRefsEqual(Map<String, NodeRef> selected)
{
NodeRef last = null;
for(NodeRef current : selected.values())
{
if(last == null)
{
last = current;
}
else
{
if(!last.equals(current))
{
return false;
}
}
}
return true;
}
public QName getQName()
{
return getChildAssocRef().getQName();

View File

@@ -28,15 +28,20 @@ import java.io.Serializable;
import java.util.Collection;
import java.util.Map;
import org.alfresco.cmis.CMISCardinalityEnum;
import org.alfresco.cmis.CMISDictionaryModel;
import org.alfresco.cmis.CMISDictionaryService;
import org.alfresco.cmis.CMISPropertyDefinition;
import org.alfresco.cmis.CMISQueryException;
import org.alfresco.cmis.CMISScope;
import org.alfresco.cmis.CMISTypeDefinition;
import org.alfresco.repo.search.impl.lucene.LuceneFunction;
import org.alfresco.repo.search.impl.lucene.LuceneQueryParser;
import org.alfresco.repo.search.impl.querymodel.FunctionArgument;
import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext;
import org.alfresco.repo.search.impl.querymodel.PredicateMode;
import org.alfresco.repo.search.impl.querymodel.QueryModelException;
import org.alfresco.repo.search.impl.querymodel.Selector;
import org.alfresco.repo.search.impl.querymodel.impl.functions.Lower;
import org.alfresco.repo.search.impl.querymodel.impl.functions.Upper;
import org.alfresco.service.cmr.repository.NodeRef;
@@ -49,6 +54,10 @@ import org.apache.lucene.search.Query;
*/
public class CmisFunctionEvaluationContext implements FunctionEvaluationContext
{
public static CMISScope[] STRICT_SCOPES = new CMISScope[] { CMISScope.DOCUMENT, CMISScope.FOLDER };
public static CMISScope[] ALFRESCO_SCOPES = new CMISScope[] { CMISScope.DOCUMENT, CMISScope.FOLDER, CMISScope.POLICY };
private Map<String, NodeRef> nodeRefs;
private Map<String, Float> scores;
@@ -57,6 +66,8 @@ public class CmisFunctionEvaluationContext implements FunctionEvaluationContext
private CMISDictionaryService cmisDictionaryService;
private CMISScope[] validScopes;
private Float score;
/**
@@ -95,6 +106,15 @@ public class CmisFunctionEvaluationContext implements FunctionEvaluationContext
this.cmisDictionaryService = cmisDictionaryService;
}
/**
* @param validScopes
* the valid scopes to set
*/
public void setValidScopes(CMISScope[] validScopes)
{
this.validScopes = validScopes;
}
/*
* (non-Javadoc)
*
@@ -268,7 +288,7 @@ public class CmisFunctionEvaluationContext implements FunctionEvaluationContext
public String getLuceneSortField(LuceneQueryParser lqp, String propertyName)
{
CMISPropertyDefinition propertyDef = cmisDictionaryService.findProperty(propertyName, null);
return propertyDef.getPropertyLuceneBuilder().getLuceneSortField();
return propertyDef.getPropertyLuceneBuilder().getLuceneSortField(lqp);
}
@@ -341,5 +361,44 @@ public class CmisFunctionEvaluationContext implements FunctionEvaluationContext
}
}
/* (non-Javadoc)
* @see org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext#checkFieldApplies(org.alfresco.service.namespace.QName, java.lang.String)
*/
public void checkFieldApplies(Selector selector, String propertyName)
{
CMISPropertyDefinition propDef = cmisDictionaryService.findPropertyByQueryName(propertyName);
if (propDef == null)
{
throw new CMISQueryException("Unknown column/property " + propertyName);
}
CMISTypeDefinition typeDef = cmisDictionaryService.findTypeForClass(selector.getType(), validScopes);
if (typeDef == null)
{
throw new CMISQueryException("Type unsupported in CMIS queries: " + selector.getAlias());
}
// Check column/property applies to selector/type
if (!typeDef.getPropertyDefinitions().containsKey(propDef.getPropertyId().getId()))
{
throw new CMISQueryException("Invalid column for " + typeDef.getQueryName() + "." + propertyName);
}
}
/* (non-Javadoc)
* @see org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext#isMultiValued(java.lang.String)
*/
public boolean isMultiValued(String propertyName)
{
CMISPropertyDefinition propDef = cmisDictionaryService.findPropertyByQueryName(propertyName);
if (propDef == null)
{
throw new CMISQueryException("Unknown column/property " + propertyName);
}
return propDef.getCardinality() == CMISCardinalityEnum.MULTI_VALUED;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -18,9 +18,9 @@
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.audit;
@@ -40,6 +40,7 @@ public class AuditBootstrap extends AbstractLifecycleBean
{
private TransactionService transactionService;
private AuditModelRegistry auditModelRegistry;
private boolean isActive = true;
public void setTransactionService(TransactionService transactionService)
{
@@ -51,6 +52,11 @@ public class AuditBootstrap extends AbstractLifecycleBean
this.auditModelRegistry = registry;
}
public void setActive(boolean isActive)
{
this.isActive = isActive;
}
/**
* @see AuditModelRegistry#loadAuditModels()
*/
@@ -63,6 +69,12 @@ public class AuditBootstrap extends AbstractLifecycleBean
return;
}
// Don't start if we've been configured out
if (!this.isActive)
{
return;
}
RetryingTransactionCallback<Void> callback = new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable

View File

@@ -33,7 +33,8 @@ import org.alfresco.repo.audit.extractor.DataExtractor;
import org.alfresco.repo.audit.generator.DataGenerator;
import org.alfresco.repo.audit.model.AuditApplication;
import org.alfresco.repo.audit.model.AuditModelException;
import org.alfresco.repo.audit.model.AuditModelRegistry;
import org.alfresco.repo.audit.model.AuditModelRegistryImpl;
import org.alfresco.repo.management.subsystems.ApplicationContextFactory;
import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.PathMapper;
import org.apache.commons.logging.Log;
@@ -57,12 +58,15 @@ public class AuditBootstrapTest extends TestCase
private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
private static final Log logger = LogFactory.getLog(AuditBootstrapTest.class);
private AuditModelRegistry auditModelRegistry;
private AuditModelRegistryImpl auditModelRegistry;
@Override
public void setUp() throws Exception
{
auditModelRegistry = (AuditModelRegistry) ctx.getBean("auditModel.modelRegistry");
// We have to look inside the subsystem for this test
ApplicationContextFactory subsystem = (ApplicationContextFactory) ctx.getBean("Audit");
ApplicationContext subCtx = subsystem.getApplicationContext();
auditModelRegistry = (AuditModelRegistryImpl) subCtx.getBean("auditModel.modelRegistry");
// Register a new model
URL testModelUrl = ResourceUtils.getURL("classpath:alfresco/audit/alfresco-audit-test.xml");

View File

@@ -94,6 +94,15 @@ public interface AuditComponent
* V3.2 from here on. Put all fixes to the older audit code before this point, please.
*/
/**
* Determines whether the given source path is mapped to any audit applications. Allows optimizations to be made in
* calling components.
*
* @return <code>true</code> if the given source path is mapped to one or more audit applications
* @since 3.3
*/
public boolean isSourcePathMapped(String sourcePath);
/**
* Delete audit entries for the given application and time range
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -18,9 +18,9 @@
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.audit;
@@ -954,6 +954,15 @@ public class AuditComponentImpl implements AuditComponent
}
}
/**
* {@inheritDoc}
* @since 3.2
*/
public boolean isSourcePathMapped(String sourcePath)
{
return !auditModelRegistry.getAuditPathMapper().isEmpty();
}
/**
* {@inheritDoc}
* @since 3.2
@@ -971,7 +980,7 @@ public class AuditComponentImpl implements AuditComponent
{
logger.debug("No audit application named '" + applicationName + "' has been registered.");
}
return true;
return false;
}
// Check the path against the application
application.checkPath(path);
@@ -1162,10 +1171,11 @@ public class AuditComponentImpl implements AuditComponent
ParameterCheck.mandatory("rootPath", rootPath);
AuditApplication.checkPathFormat(rootPath);
if (values == null || values.isEmpty())
if (values == null || values.isEmpty() || !isSourcePathMapped(rootPath))
{
return Collections.emptyMap();
}
// Build the key paths using the session root path
Map<String, Serializable> pathedValues = new HashMap<String, Serializable>(values.size() * 2);
for (Map.Entry<String, Serializable> entry : values.entrySet())

View File

@@ -38,7 +38,8 @@ import junit.framework.TestCase;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.audit.model.AuditApplication;
import org.alfresco.repo.audit.model.AuditModelException;
import org.alfresco.repo.audit.model.AuditModelRegistry;
import org.alfresco.repo.audit.model.AuditModelRegistryImpl;
import org.alfresco.repo.management.subsystems.ApplicationContextFactory;
import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
@@ -79,9 +80,9 @@ public class AuditComponentTest extends TestCase
private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
private AuditModelRegistry auditModelRegistry;
private ApplicationContextFactory subsystem;
private AuditModelRegistryImpl auditModelRegistry;
private AuditComponent auditComponent;
private AuditMethodInterceptor auditMethodInterceptor;
private AuditService auditService;
private ServiceRegistry serviceRegistry;
private TransactionService transactionService;
@@ -93,9 +94,11 @@ public class AuditComponentTest extends TestCase
@Override
public void setUp() throws Exception
{
auditModelRegistry = (AuditModelRegistry) ctx.getBean("auditModel.modelRegistry");
// We have to look inside the subsystem for this test
subsystem = (ApplicationContextFactory) ctx.getBean("Audit");
ApplicationContext subCtx = subsystem.getApplicationContext();
auditModelRegistry = (AuditModelRegistryImpl) subCtx.getBean("auditModel.modelRegistry");
auditComponent = (AuditComponent) ctx.getBean("auditComponent");
auditMethodInterceptor = (AuditMethodInterceptor) ctx.getBean("AuditMethodInterceptor");
serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY);
auditService = serviceRegistry.getAuditService();
transactionService = serviceRegistry.getTransactionService();
@@ -129,17 +132,14 @@ public class AuditComponentTest extends TestCase
}
};
transactionService.getRetryingTransactionHelper().doInTransaction(resetDisabledPathsCallback);
auditMethodInterceptor.setEnabled(true);
auditMethodInterceptor.setUseNewConfig(true);
}
@Override
public void tearDown() throws Exception
{
AuthenticationUtil.clearCurrentSecurityContext();
auditMethodInterceptor.setEnabled(false);
auditMethodInterceptor.setUseNewConfig(false);
// Throw away the reconfigured registry in the subsystem
subsystem.stop();
}
public void testSetUp()
@@ -315,6 +315,11 @@ public class AuditComponentTest extends TestCase
AuditQueryCallback callback = new AuditQueryCallback()
{
public boolean valuesRequired()
{
return true;
}
public boolean handleAuditEntry(
Long entryId, String applicationName, String user, long time, Map<String, Serializable> values)
{
@@ -482,6 +487,11 @@ public class AuditComponentTest extends TestCase
final StringBuilder sb = new StringBuilder();
AuditQueryCallback auditQueryCallback = new AuditQueryCallback()
{
public boolean valuesRequired()
{
return true;
}
public boolean handleAuditEntry(
Long entryId,
String applicationName,
@@ -576,6 +586,11 @@ public class AuditComponentTest extends TestCase
{
AuditQueryCallback auditQueryCallback = new AuditQueryCallback()
{
public boolean valuesRequired()
{
return true;
}
public boolean handleAuditEntry(
Long entryId,
String applicationName,
@@ -604,4 +619,5 @@ public class AuditComponentTest extends TestCase
params.setToId(Long.MAX_VALUE);
auditService.auditQuery(auditQueryCallback, params, 1);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -18,7 +18,7 @@
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
@@ -84,8 +84,9 @@ import org.apache.commons.logging.LogFactory;
*/
public class AuditMethodInterceptor implements MethodInterceptor
{
public static final String AUDIT_PATH_API_PRE = "/alfresco-api/pre";
public static final String AUDIT_PATH_API_POST = "/alfresco-api/post";
public static final String AUDIT_PATH_API_ROOT = "/alfresco-api";
public static final String AUDIT_PATH_API_PRE = AUDIT_PATH_API_ROOT + "/pre";
public static final String AUDIT_PATH_API_POST = AUDIT_PATH_API_ROOT + "/post";
public static final String AUDIT_SNIPPET_ARGS = "/args";
public static final String AUDIT_SNIPPET_RESULT = "/result";
public static final String AUDIT_SNIPPET_ERROR = "/error";
@@ -97,24 +98,17 @@ public class AuditMethodInterceptor implements MethodInterceptor
private AuditComponent auditComponent;
private TransactionService transactionService;
private boolean enabled = false;
// SysAdmin cache - used to cluster certain configuration parameters
private boolean useNewConfig = false;
private final ThreadLocal<Boolean> inAudit = new ThreadLocal<Boolean>();
private final ThreadLocal<Boolean> auditEnabled = new ThreadLocal<Boolean>();
public AuditMethodInterceptor()
{
super();
}
/**
* Enable or disable auditing at a high level (default: <b>false</b>)
*/
public void setEnabled(boolean enabled)
{
this.enabled = enabled;
}
/**
* Use the new audit configuration (default: <b>false</b>)
*
@@ -142,6 +136,23 @@ public class AuditMethodInterceptor implements MethodInterceptor
public Object invoke(MethodInvocation mi) throws Throwable
{
// Cache the enabled flag at the top of the stack
Boolean wasEnabled = auditEnabled.get();
try
{
boolean enabled;
if (wasEnabled == null)
{
// There hasn't been an invocation in this thread yet, so check whether we are currently enabled in the
// audit subsystem
enabled = this.auditComponent.isSourcePathMapped(AUDIT_PATH_API_ROOT);
auditEnabled.set(enabled);
}
else
{
enabled = wasEnabled;
}
if(!enabled)
{
// No auditing
@@ -157,6 +168,11 @@ public class AuditMethodInterceptor implements MethodInterceptor
// Use previous configuration
return auditComponent.audit(mi);
}
}
finally
{
auditEnabled.set(wasEnabled);
}
}

View File

@@ -39,7 +39,8 @@ import org.springframework.util.ResourceUtils;
public class AuditModelReader implements InitializingBean
{
private URL auditModelUrl;
private AuditModelRegistry auditModelRegistry;
private AuditModelRegistryImpl auditModelRegistry;
private boolean isActive = true;
/**
* Set the XML location using <b>file:</b>, <b>classpath:</b> or any of the
@@ -56,16 +57,32 @@ public class AuditModelReader implements InitializingBean
*
* @param auditModelRegistry the registry that combines all loaded models
*/
public void setAuditModelRegistry(AuditModelRegistry auditModelRegistry)
public void setAuditModelRegistry(AuditModelRegistryImpl auditModelRegistry)
{
this.auditModelRegistry = auditModelRegistry;
}
/**
* Controls whether or not this bean registers its model with the registry on initialization.
*
* @param isActive
* <code>true</code> if this bean should register its model with the registry on initialization.
*/
public void setActive(boolean isActive)
{
this.isActive = isActive;
}
/**
* Pulls in the configuration and registers it
*/
public void afterPropertiesSet() throws Exception
{
if (!this.isActive)
{
return;
}
PropertyCheck.mandatory(this, "configUrl", auditModelUrl);
PropertyCheck.mandatory(this, "auditModelRegistry", auditModelRegistry);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -18,209 +18,27 @@
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.audit.model;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.bind.ValidationEventLocator;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.error.ExceptionStackUtil;
import org.alfresco.repo.audit.extractor.DataExtractor;
import org.alfresco.repo.audit.generator.DataGenerator;
import org.alfresco.repo.audit.model._3.Application;
import org.alfresco.repo.audit.model._3.Audit;
import org.alfresco.repo.audit.model._3.DataExtractors;
import org.alfresco.repo.audit.model._3.DataGenerators;
import org.alfresco.repo.audit.model._3.ObjectFactory;
import org.alfresco.repo.audit.model._3.PathMap;
import org.alfresco.repo.audit.model._3.PathMappings;
import org.alfresco.repo.domain.audit.AuditDAO;
import org.alfresco.repo.domain.audit.AuditDAO.AuditApplicationInfo;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.PathMapper;
import org.springframework.extensions.surf.util.PropertyCheck;
import org.alfresco.util.registry.NamedObjectRegistry;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.ResourceUtils;
import org.xml.sax.SAXParseException;
/**
* Component used to store audit model definitions. It ensures that duplicate application and converter
* definitions are detected and provides a single lookup for code using the Audit model.
* Interface for component used to store audit model definitions.
*
* @author Derek Hulley
* @since 3.2
* @since 3.3
*/
public class AuditModelRegistry
public interface AuditModelRegistry
{
public static final String AUDIT_SCHEMA_LOCATION = "classpath:alfresco/audit/alfresco-audit-3.2.xsd";
public static final String AUDIT_RESERVED_KEY_USERNAME = "username";
public static final String AUDIT_RESERVED_KEY_SYSTEMTIME = "systemTime";
private static final Log logger = LogFactory.getLog(AuditModelRegistry.class);
private TransactionService transactionService;
private AuditDAO auditDAO;
private NamedObjectRegistry<DataExtractor> dataExtractors;
private NamedObjectRegistry<DataGenerator> dataGenerators;
private final ReentrantReadWriteLock.ReadLock readLock;
private final ReentrantReadWriteLock.WriteLock writeLock;
private final ObjectFactory objectFactory;
private final Set<URL> auditModelUrls;
private final List<Audit> auditModels;
/**
* Used to lookup path translations
*/
private PathMapper auditPathMapper;
/**
* Used to lookup the audit application java hierarchy
*/
private final Map<String, AuditApplication> auditApplicationsByKey;
/**
* Used to lookup the audit application java hierarchy
*/
private final Map<String, AuditApplication> auditApplicationsByName;
/**
* Default constructor
*/
public AuditModelRegistry()
{
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
readLock = lock.readLock();
writeLock = lock.writeLock();
objectFactory = new ObjectFactory();
auditModelUrls = new HashSet<URL>(7);
auditModels = new ArrayList<Audit>(7);
auditPathMapper = new PathMapper();
auditApplicationsByKey = new HashMap<String, AuditApplication>(7);
auditApplicationsByName = new HashMap<String, AuditApplication>(7);
}
/**
* Service to ensure DAO calls are transactionally wrapped
*/
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
}
/**
* Set the DAO used to persisted the registered audit models
*/
public void setAuditDAO(AuditDAO auditDAO)
{
this.auditDAO = auditDAO;
}
/**
* Set the registry of {@link DataExtractor data extractors}
*/
public void setDataExtractors(NamedObjectRegistry<DataExtractor> dataExtractors)
{
this.dataExtractors = dataExtractors;
}
/**
* Set the registry of {@link DataGenerator data generators}
*/
public void setDataGenerators(NamedObjectRegistry<DataGenerator> dataGenerators)
{
this.dataGenerators = dataGenerators;
}
/**
* Ensures that all properties have been set for use.
*/
private void checkProperties()
{
PropertyCheck.mandatory(this, "transactionService", transactionService);
PropertyCheck.mandatory(this, "auditDAO", auditDAO);
PropertyCheck.mandatory(this, "dataExtractors", dataExtractors);
PropertyCheck.mandatory(this, "dataGenerators", dataGenerators);
}
/**
* Register an audit model at a given URL.
*
* @param auditModelUrl the source of the model
*/
public void registerModel(URL auditModelUrl)
{
checkProperties();
writeLock.lock();
try
{
if (auditModelUrls.contains(auditModelUrl))
{
logger.warn("An audit model has already been registered at URL " + auditModelUrl);
}
auditModelUrls.add(auditModelUrl);
}
finally
{
writeLock.unlock();
}
}
/**
* Register an audit model at a given node reference.
*
* @param auditModelNodeRef the source of the audit model
*/
public void registerModel(NodeRef auditModelNodeRef)
{
checkProperties();
writeLock.lock();
try
{
throw new UnsupportedOperationException();
}
finally
{
writeLock.unlock();
}
}
/**
* Cleans out all derived data
*/
private void clearCaches()
{
auditModels.clear();
auditApplicationsByKey.clear();
auditApplicationsByName.clear();
}
/**
* Method to load audit models into memory. This method is also responsible for persisting
* the audit models for later retrieval. Models are loaded from the locations given by the
@@ -229,59 +47,7 @@ public class AuditModelRegistry
* Note, the models are loaded in a new transaction, so this method can be called by any code
* at any time.
*/
public void loadAuditModels()
{
checkProperties();
RetryingTransactionCallback<Void> loadModelsCallback = new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
// Load models from the URLs
Set<URL> auditModelUrlsInner = new HashSet<URL>(auditModelUrls);
for (URL auditModelUrl : auditModelUrlsInner)
{
try
{
Audit audit = AuditModelRegistry.unmarshallModel(auditModelUrl);
// That worked, so now get an input stream and write the model
Long auditModelId = auditDAO.getOrCreateAuditModel(auditModelUrl).getFirst();
// Now cache it (eagerly)
cacheAuditElements(auditModelId, audit);
}
catch (Throwable e)
{
// Mainly for test purposes, we clear out the failed URL
auditModelUrls.remove(auditModelUrl);
clearCaches();
throw new AuditModelException(
"Failed to load audit model: " + auditModelUrl,
e);
}
}
// NOTE: If we support other types of loading, then that will have to go here, too
// Done
return null;
}
};
writeLock.lock();
// Drop all cached data
clearCaches();
try
{
auditPathMapper = new PathMapper();
transactionService.getRetryingTransactionHelper().doInTransaction(loadModelsCallback,
transactionService.isReadOnly(), true);
auditPathMapper.lock();
}
finally
{
writeLock.unlock();
}
}
public void loadAuditModels();
/**
* Get the application model for the given root key (as defined on the application)
@@ -289,18 +55,7 @@ public class AuditModelRegistry
* @param key the key defined on the application
* @return the java model (<tt>null</tt> if not found)
*/
public AuditApplication getAuditApplicationByKey(String key)
{
readLock.lock();
try
{
return auditApplicationsByKey.get(key);
}
finally
{
readLock.unlock();
}
}
public AuditApplication getAuditApplicationByKey(String key);
/**
* Get the application model for the given application name
@@ -308,285 +63,11 @@ public class AuditModelRegistry
* @param applicationName the name of the audited application
* @return the java model (<tt>null</tt> if not found)
*/
public AuditApplication getAuditApplicationByName(String applicationName)
{
readLock.lock();
try
{
return auditApplicationsByName.get(applicationName);
}
finally
{
readLock.unlock();
}
}
public AuditApplication getAuditApplicationByName(String applicationName);
/**
* Get the
* @return
* Get the path mapper.
* @return the path mapper
*/
public PathMapper getAuditPathMapper()
{
return auditPathMapper;
}
/**
* Unmarshalls the Audit model from the URL.
*
* @throws AlfrescoRuntimeException if an IOException occurs
*/
public static Audit unmarshallModel(URL configUrl)
{
try
{
// Load it
InputStream is = new BufferedInputStream(configUrl.openStream());
return unmarshallModel(is, configUrl.toString());
}
catch (IOException e)
{
throw new AlfrescoRuntimeException("The Audit model XML failed to load: " + configUrl, e);
}
}
/**
* Unmarshalls the Audit model from a stream
*/
private static Audit unmarshallModel(InputStream is, final String source)
{
final Schema schema;
final JAXBContext jaxbCtx;
final Unmarshaller jaxbUnmarshaller;
try
{
SchemaFactory sf = SchemaFactory.newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI);
schema = sf.newSchema(ResourceUtils.getURL(AUDIT_SCHEMA_LOCATION));
jaxbCtx = JAXBContext.newInstance("org.alfresco.repo.audit.model._3");
jaxbUnmarshaller = jaxbCtx.createUnmarshaller();
jaxbUnmarshaller.setSchema(schema);
jaxbUnmarshaller.setEventHandler(new ValidationEventHandler()
{
public boolean handleEvent(ValidationEvent ve)
{
if (ve.getSeverity() == ValidationEvent.FATAL_ERROR || ve.getSeverity() == ValidationEvent.ERROR)
{
ValidationEventLocator locator = ve.getLocator();
logger.error("Invalid Audit XML: \n" +
" Source: " + source + "\n" +
" Location: Line " + locator.getLineNumber() + " column " + locator.getColumnNumber() + "\n" +
" Error: " + ve.getMessage());
}
return false;
}
});
}
catch (Throwable e)
{
throw new AlfrescoRuntimeException(
"Failed to load Alfresco Audit Schema from " + AUDIT_SCHEMA_LOCATION, e);
}
try
{
// Unmarshall with validation
@SuppressWarnings("unchecked")
JAXBElement<Audit> auditElement = (JAXBElement<Audit>) jaxbUnmarshaller.unmarshal(is);
Audit audit = auditElement.getValue();
// Done
return audit;
}
catch (Throwable e)
{
// Dig out a SAXParseException, if there is one
Throwable saxError = ExceptionStackUtil.getCause(e, SAXParseException.class);
if (saxError != null)
{
e = saxError;
}
throw new AuditModelException(
"Failed to read Audit model XML: \n" +
" Source: " + source + "\n" +
" Error: " + e.getMessage());
}
finally
{
try { is.close(); } catch (IOException e) {}
}
}
private void cacheAuditElements(Long auditModelId, Audit audit)
{
Map<String, DataExtractor> dataExtractorsByName = new HashMap<String, DataExtractor>(13);
Map<String, DataGenerator> dataGeneratorsByName = new HashMap<String, DataGenerator>(13);
// Get the data extractors and check for duplicates
DataExtractors extractorsElement = audit.getDataExtractors();
if (extractorsElement == null)
{
extractorsElement = objectFactory.createDataExtractors();
}
List<org.alfresco.repo.audit.model._3.DataExtractor> extractorElements = extractorsElement.getDataExtractor();
for (org.alfresco.repo.audit.model._3.DataExtractor extractorElement : extractorElements)
{
String name = extractorElement.getName();
// If the name is taken, make sure that they are equal
if (dataExtractorsByName.containsKey(name))
{
throw new AuditModelException(
"Audit data extractor '" + name + "' has already been defined.");
}
// Construct the converter
final DataExtractor dataExtractor;
if (extractorElement.getClazz() != null)
{
try
{
Class<?> dataExtractorClazz = Class.forName(extractorElement.getClazz());
dataExtractor = (DataExtractor) dataExtractorClazz.newInstance();
}
catch (ClassNotFoundException e)
{
throw new AuditModelException(
"Audit data extractor '" + name + "' class not found: " + extractorElement.getClazz());
}
catch (Exception e)
{
throw new AuditModelException(
"Audit data extractor '" + name + "' could not be constructed: " + extractorElement.getClazz());
}
}
else if (extractorElement.getRegisteredName() != null)
{
String registeredName = extractorElement.getRegisteredName();
dataExtractor = dataExtractors.getNamedObject(registeredName);
if (dataExtractor == null)
{
throw new AuditModelException(
"No registered audit data extractor exists for '" + registeredName + "'.");
}
}
else
{
throw new AuditModelException(
"Audit data extractor has no class or registered name: " + name);
}
// Store
dataExtractorsByName.put(name, dataExtractor);
}
// Get the data generators and check for duplicates
DataGenerators generatorsElement = audit.getDataGenerators();
if (generatorsElement == null)
{
generatorsElement = objectFactory.createDataGenerators();
}
List<org.alfresco.repo.audit.model._3.DataGenerator> generatorElements = generatorsElement.getDataGenerator();
for (org.alfresco.repo.audit.model._3.DataGenerator generatorElement : generatorElements)
{
String name = generatorElement.getName();
// If the name is taken, make sure that they are equal
if (dataGeneratorsByName.containsKey(name))
{
throw new AuditModelException(
"Audit data generator '" + name + "' has already been defined.");
}
// Construct the generator
final DataGenerator dataGenerator;
if (generatorElement.getClazz() != null)
{
try
{
Class<?> dataGeneratorClazz = Class.forName(generatorElement.getClazz());
dataGenerator = (DataGenerator) dataGeneratorClazz.newInstance();
}
catch (ClassNotFoundException e)
{
throw new AuditModelException(
"Audit data generator '" + name + "' class not found: " + generatorElement.getClazz());
}
catch (Exception e)
{
throw new AuditModelException(
"Audit data generator '" + name + "' could not be constructed: " + generatorElement.getClazz());
}
}
else if (generatorElement.getRegisteredName() != null)
{
String registeredName = generatorElement.getRegisteredName();
dataGenerator = dataGenerators.getNamedObject(registeredName);
if (dataGenerator == null)
{
throw new AuditModelException(
"No registered audit data generator exists for '" + registeredName + "'.");
}
}
else
{
throw new AuditModelException(
"Audit data generator has no class or registered name: " + name);
}
// Store
dataGeneratorsByName.put(name, dataGenerator);
}
// Get the application and check for duplicates
List<Application> applications = audit.getApplication();
for (Application application : applications)
{
String key = application.getKey();
if (auditApplicationsByKey.containsKey(key))
{
throw new AuditModelException(
"Audit application key '" + key + "' is used by: " + auditApplicationsByKey.get(key));
}
String name = application.getName();
if (auditApplicationsByName.containsKey(name))
{
throw new AuditModelException(
"Audit application '" + name + "' is used by: " + auditApplicationsByName.get(name));
}
// Get the ID of the application
AuditApplicationInfo appInfo = auditDAO.getAuditApplication(name);
if (appInfo == null)
{
appInfo = auditDAO.createAuditApplication(name, auditModelId);
}
else
{
// Update it with the new model ID
auditDAO.updateAuditApplicationModel(appInfo.getId(), auditModelId);
}
AuditApplication wrapperApp = new AuditApplication(
dataExtractorsByName,
dataGeneratorsByName,
application,
appInfo.getId(),
appInfo.getDisabledPathsId());
auditApplicationsByName.put(name, wrapperApp);
auditApplicationsByKey.put(key, wrapperApp);
}
// Pull out all the audit path maps
buildAuditPathMap(audit);
// Store the model itself
auditModels.add(audit);
}
/**
* Construct the reverse lookup maps for quick conversion of data to target maps
*/
private void buildAuditPathMap(Audit audit)
{
PathMappings pathMappings = audit.getPathMappings();
if (pathMappings == null)
{
pathMappings = objectFactory.createPathMappings();
}
for (PathMap pathMap : pathMappings.getPathMap())
{
String sourcePath = pathMap.getSource();
String targetPath = pathMap.getTarget();
auditPathMapper.addPathMap(sourcePath, targetPath);
}
}
public PathMapper getAuditPathMapper();
}

View File

@@ -0,0 +1,578 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.audit.model;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.bind.ValidationEventLocator;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.error.ExceptionStackUtil;
import org.alfresco.repo.audit.extractor.DataExtractor;
import org.alfresco.repo.audit.generator.DataGenerator;
import org.alfresco.repo.audit.model._3.Application;
import org.alfresco.repo.audit.model._3.Audit;
import org.alfresco.repo.audit.model._3.DataExtractors;
import org.alfresco.repo.audit.model._3.DataGenerators;
import org.alfresco.repo.audit.model._3.ObjectFactory;
import org.alfresco.repo.audit.model._3.PathMap;
import org.alfresco.repo.audit.model._3.PathMappings;
import org.alfresco.repo.domain.audit.AuditDAO;
import org.alfresco.repo.domain.audit.AuditDAO.AuditApplicationInfo;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.PathMapper;
import org.alfresco.util.registry.NamedObjectRegistry;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.extensions.surf.util.PropertyCheck;
import org.springframework.util.ResourceUtils;
import org.xml.sax.SAXParseException;
/**
* Component used to store audit model definitions. It ensures that duplicate application and converter
* definitions are detected and provides a single lookup for code using the Audit model.
*
* @author Derek Hulley
* @since 3.2
*/
public class AuditModelRegistryImpl implements AuditModelRegistry
{
private static final String AUDIT_SCHEMA_LOCATION = "classpath:alfresco/audit/alfresco-audit-3.2.xsd";
private static final Log logger = LogFactory.getLog(AuditModelRegistryImpl.class);
private TransactionService transactionService;
private AuditDAO auditDAO;
private NamedObjectRegistry<DataExtractor> dataExtractors;
private NamedObjectRegistry<DataGenerator> dataGenerators;
private final ReentrantReadWriteLock.ReadLock readLock;
private final ReentrantReadWriteLock.WriteLock writeLock;
private final ObjectFactory objectFactory;
private final Set<URL> auditModelUrls;
private final List<Audit> auditModels;
/**
* Used to lookup path translations
*/
private PathMapper auditPathMapper;
/**
* Used to lookup the audit application java hierarchy
*/
private final Map<String, AuditApplication> auditApplicationsByKey;
/**
* Used to lookup the audit application java hierarchy
*/
private final Map<String, AuditApplication> auditApplicationsByName;
/**
* Default constructor
*/
public AuditModelRegistryImpl()
{
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
readLock = lock.readLock();
writeLock = lock.writeLock();
objectFactory = new ObjectFactory();
auditModelUrls = new HashSet<URL>(7);
auditModels = new ArrayList<Audit>(7);
auditPathMapper = new PathMapper();
auditApplicationsByKey = new HashMap<String, AuditApplication>(7);
auditApplicationsByName = new HashMap<String, AuditApplication>(7);
}
/**
* Service to ensure DAO calls are transactionally wrapped
*/
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
}
/**
* Set the DAO used to persisted the registered audit models
*/
public void setAuditDAO(AuditDAO auditDAO)
{
this.auditDAO = auditDAO;
}
/**
* Set the registry of {@link DataExtractor data extractors}
*/
public void setDataExtractors(NamedObjectRegistry<DataExtractor> dataExtractors)
{
this.dataExtractors = dataExtractors;
}
/**
* Set the registry of {@link DataGenerator data generators}
*/
public void setDataGenerators(NamedObjectRegistry<DataGenerator> dataGenerators)
{
this.dataGenerators = dataGenerators;
}
/**
* Ensures that all properties have been set for use.
*/
private void checkProperties()
{
PropertyCheck.mandatory(this, "transactionService", transactionService);
PropertyCheck.mandatory(this, "auditDAO", auditDAO);
PropertyCheck.mandatory(this, "dataExtractors", dataExtractors);
PropertyCheck.mandatory(this, "dataGenerators", dataGenerators);
}
/**
* Register an audit model at a given URL.
*
* @param auditModelUrl the source of the model
*/
public void registerModel(URL auditModelUrl)
{
checkProperties();
writeLock.lock();
try
{
if (auditModelUrls.contains(auditModelUrl))
{
logger.warn("An audit model has already been registered at URL " + auditModelUrl);
}
auditModelUrls.add(auditModelUrl);
}
finally
{
writeLock.unlock();
}
}
/**
* Register an audit model at a given node reference.
*
* @param auditModelNodeRef the source of the audit model
*/
public void registerModel(NodeRef auditModelNodeRef)
{
checkProperties();
writeLock.lock();
try
{
throw new UnsupportedOperationException();
}
finally
{
writeLock.unlock();
}
}
/**
* Cleans out all derived data
*/
private void clearCaches()
{
auditModels.clear();
auditApplicationsByKey.clear();
auditApplicationsByName.clear();
}
/**
* @see org.alfresco.repo.audit.model.AuditModelRegistry#loadAuditModels()
*/
public void loadAuditModels()
{
checkProperties();
RetryingTransactionCallback<Void> loadModelsCallback = new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
// Load models from the URLs
Set<URL> auditModelUrlsInner = new HashSet<URL>(auditModelUrls);
for (URL auditModelUrl : auditModelUrlsInner)
{
try
{
Audit audit = AuditModelRegistryImpl.unmarshallModel(auditModelUrl);
// That worked, so now get an input stream and write the model
Long auditModelId = auditDAO.getOrCreateAuditModel(auditModelUrl).getFirst();
// Now cache it (eagerly)
cacheAuditElements(auditModelId, audit);
}
catch (Throwable e)
{
// Mainly for test purposes, we clear out the failed URL
auditModelUrls.remove(auditModelUrl);
clearCaches();
throw new AuditModelException(
"Failed to load audit model: " + auditModelUrl,
e);
}
}
// NOTE: If we support other types of loading, then that will have to go here, too
// Done
return null;
}
};
writeLock.lock();
// Drop all cached data
clearCaches();
try
{
auditPathMapper = new PathMapper();
transactionService.getRetryingTransactionHelper().doInTransaction(loadModelsCallback,
transactionService.isReadOnly(), true);
auditPathMapper.lock();
}
finally
{
writeLock.unlock();
}
}
/**
* @see org.alfresco.repo.audit.model.AuditModelRegistry#getAuditApplicationByKey(java.lang.String)
*/
public AuditApplication getAuditApplicationByKey(String key)
{
readLock.lock();
try
{
return auditApplicationsByKey.get(key);
}
finally
{
readLock.unlock();
}
}
/**
* @see org.alfresco.repo.audit.model.AuditModelRegistry#getAuditApplicationByName(java.lang.String)
*/
public AuditApplication getAuditApplicationByName(String applicationName)
{
readLock.lock();
try
{
return auditApplicationsByName.get(applicationName);
}
finally
{
readLock.unlock();
}
}
/**
* @see org.alfresco.repo.audit.model.AuditModelRegistry#getAuditPathMapper()
*/
public PathMapper getAuditPathMapper()
{
return auditPathMapper;
}
/**
* Unmarshalls the Audit model from the URL.
*
* @throws AlfrescoRuntimeException if an IOException occurs
*/
public static Audit unmarshallModel(URL configUrl)
{
try
{
// Load it
InputStream is = new BufferedInputStream(configUrl.openStream());
return unmarshallModel(is, configUrl.toString());
}
catch (IOException e)
{
throw new AlfrescoRuntimeException("The Audit model XML failed to load: " + configUrl, e);
}
}
/**
* Unmarshalls the Audit model from a stream
*/
private static Audit unmarshallModel(InputStream is, final String source)
{
final Schema schema;
final JAXBContext jaxbCtx;
final Unmarshaller jaxbUnmarshaller;
try
{
SchemaFactory sf = SchemaFactory.newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI);
schema = sf.newSchema(ResourceUtils.getURL(AUDIT_SCHEMA_LOCATION));
jaxbCtx = JAXBContext.newInstance("org.alfresco.repo.audit.model._3");
jaxbUnmarshaller = jaxbCtx.createUnmarshaller();
jaxbUnmarshaller.setSchema(schema);
jaxbUnmarshaller.setEventHandler(new ValidationEventHandler()
{
public boolean handleEvent(ValidationEvent ve)
{
if (ve.getSeverity() == ValidationEvent.FATAL_ERROR || ve.getSeverity() == ValidationEvent.ERROR)
{
ValidationEventLocator locator = ve.getLocator();
logger.error("Invalid Audit XML: \n" +
" Source: " + source + "\n" +
" Location: Line " + locator.getLineNumber() + " column " + locator.getColumnNumber() + "\n" +
" Error: " + ve.getMessage());
}
return false;
}
});
}
catch (Throwable e)
{
throw new AlfrescoRuntimeException(
"Failed to load Alfresco Audit Schema from " + AUDIT_SCHEMA_LOCATION, e);
}
try
{
// Unmarshall with validation
@SuppressWarnings("unchecked")
JAXBElement<Audit> auditElement = (JAXBElement<Audit>) jaxbUnmarshaller.unmarshal(is);
Audit audit = auditElement.getValue();
// Done
return audit;
}
catch (Throwable e)
{
// Dig out a SAXParseException, if there is one
Throwable saxError = ExceptionStackUtil.getCause(e, SAXParseException.class);
if (saxError != null)
{
e = saxError;
}
throw new AuditModelException(
"Failed to read Audit model XML: \n" +
" Source: " + source + "\n" +
" Error: " + e.getMessage());
}
finally
{
try { is.close(); } catch (IOException e) {}
}
}
private void cacheAuditElements(Long auditModelId, Audit audit)
{
Map<String, DataExtractor> dataExtractorsByName = new HashMap<String, DataExtractor>(13);
Map<String, DataGenerator> dataGeneratorsByName = new HashMap<String, DataGenerator>(13);
// Get the data extractors and check for duplicates
DataExtractors extractorsElement = audit.getDataExtractors();
if (extractorsElement == null)
{
extractorsElement = objectFactory.createDataExtractors();
}
List<org.alfresco.repo.audit.model._3.DataExtractor> extractorElements = extractorsElement.getDataExtractor();
for (org.alfresco.repo.audit.model._3.DataExtractor extractorElement : extractorElements)
{
String name = extractorElement.getName();
// If the name is taken, make sure that they are equal
if (dataExtractorsByName.containsKey(name))
{
throw new AuditModelException(
"Audit data extractor '" + name + "' has already been defined.");
}
// Construct the converter
final DataExtractor dataExtractor;
if (extractorElement.getClazz() != null)
{
try
{
Class<?> dataExtractorClazz = Class.forName(extractorElement.getClazz());
dataExtractor = (DataExtractor) dataExtractorClazz.newInstance();
}
catch (ClassNotFoundException e)
{
throw new AuditModelException(
"Audit data extractor '" + name + "' class not found: " + extractorElement.getClazz());
}
catch (Exception e)
{
throw new AuditModelException(
"Audit data extractor '" + name + "' could not be constructed: " + extractorElement.getClazz());
}
}
else if (extractorElement.getRegisteredName() != null)
{
String registeredName = extractorElement.getRegisteredName();
dataExtractor = dataExtractors.getNamedObject(registeredName);
if (dataExtractor == null)
{
throw new AuditModelException(
"No registered audit data extractor exists for '" + registeredName + "'.");
}
}
else
{
throw new AuditModelException(
"Audit data extractor has no class or registered name: " + name);
}
// Store
dataExtractorsByName.put(name, dataExtractor);
}
// Get the data generators and check for duplicates
DataGenerators generatorsElement = audit.getDataGenerators();
if (generatorsElement == null)
{
generatorsElement = objectFactory.createDataGenerators();
}
List<org.alfresco.repo.audit.model._3.DataGenerator> generatorElements = generatorsElement.getDataGenerator();
for (org.alfresco.repo.audit.model._3.DataGenerator generatorElement : generatorElements)
{
String name = generatorElement.getName();
// If the name is taken, make sure that they are equal
if (dataGeneratorsByName.containsKey(name))
{
throw new AuditModelException(
"Audit data generator '" + name + "' has already been defined.");
}
// Construct the generator
final DataGenerator dataGenerator;
if (generatorElement.getClazz() != null)
{
try
{
Class<?> dataGeneratorClazz = Class.forName(generatorElement.getClazz());
dataGenerator = (DataGenerator) dataGeneratorClazz.newInstance();
}
catch (ClassNotFoundException e)
{
throw new AuditModelException(
"Audit data generator '" + name + "' class not found: " + generatorElement.getClazz());
}
catch (Exception e)
{
throw new AuditModelException(
"Audit data generator '" + name + "' could not be constructed: " + generatorElement.getClazz());
}
}
else if (generatorElement.getRegisteredName() != null)
{
String registeredName = generatorElement.getRegisteredName();
dataGenerator = dataGenerators.getNamedObject(registeredName);
if (dataGenerator == null)
{
throw new AuditModelException(
"No registered audit data generator exists for '" + registeredName + "'.");
}
}
else
{
throw new AuditModelException(
"Audit data generator has no class or registered name: " + name);
}
// Store
dataGeneratorsByName.put(name, dataGenerator);
}
// Get the application and check for duplicates
List<Application> applications = audit.getApplication();
for (Application application : applications)
{
String key = application.getKey();
if (auditApplicationsByKey.containsKey(key))
{
throw new AuditModelException(
"Audit application key '" + key + "' is used by: " + auditApplicationsByKey.get(key));
}
String name = application.getName();
if (auditApplicationsByName.containsKey(name))
{
throw new AuditModelException(
"Audit application '" + name + "' is used by: " + auditApplicationsByName.get(name));
}
// Get the ID of the application
AuditApplicationInfo appInfo = auditDAO.getAuditApplication(name);
if (appInfo == null)
{
appInfo = auditDAO.createAuditApplication(name, auditModelId);
}
else
{
// Update it with the new model ID
auditDAO.updateAuditApplicationModel(appInfo.getId(), auditModelId);
}
AuditApplication wrapperApp = new AuditApplication(
dataExtractorsByName,
dataGeneratorsByName,
application,
appInfo.getId(),
appInfo.getDisabledPathsId());
auditApplicationsByName.put(name, wrapperApp);
auditApplicationsByKey.put(key, wrapperApp);
}
// Pull out all the audit path maps
buildAuditPathMap(audit);
// Store the model itself
auditModels.add(audit);
}
/**
* Construct the reverse lookup maps for quick conversion of data to target maps
*/
private void buildAuditPathMap(Audit audit)
{
PathMappings pathMappings = audit.getPathMappings();
if (pathMappings == null)
{
pathMappings = objectFactory.createPathMappings();
}
for (PathMap pathMap : pathMappings.getPathMap())
{
String sourcePath = pathMap.getSource();
String targetPath = pathMap.getTarget();
auditPathMapper.addPathMap(sourcePath, targetPath);
}
}
}

View File

@@ -159,6 +159,11 @@ import org.alfresco.service.namespace.QName;
return type.getArchive();
}
public Boolean getIncludedInSuperTypeQuery()
{
return type.getIncludedInSuperTypeQuery();
}
/* (non-Javadoc)
* @see org.alfresco.repo.dictionary.ClassDefinition#getProperties()
*/

View File

@@ -41,6 +41,7 @@ public abstract class M2Class
private String description = null;
private String parentName = null;
private Boolean archive = null;
private Boolean includedInSuperTypeQuery = null;
private List<M2Property> properties = new ArrayList<M2Property>();
private List<M2PropertyOverride> propertyOverrides = new ArrayList<M2PropertyOverride>();
@@ -110,6 +111,16 @@ public abstract class M2Class
this.archive = Boolean.valueOf(archive);
}
public Boolean getIncludedInSuperTypeQuery()
{
return includedInSuperTypeQuery;
}
public void setIncludedInSuperTypeQuery(boolean includedInSuperTypeQuery)
{
this.includedInSuperTypeQuery = Boolean.valueOf(includedInSuperTypeQuery);
}
public M2Property createProperty(String name)
{
M2Property property = new M2Property();

View File

@@ -71,6 +71,8 @@ import org.alfresco.util.EqualsHelper;
private Set<QName> inheritedDefaultAspectNames = new HashSet<QName>();
private Boolean archive = null;
private Boolean inheritedArchive = null;
private Boolean includedInSuperTypeQuery = null;
private Boolean inheritedIncludedInSuperTypeQuery = null;
/**
* Construct
@@ -88,6 +90,7 @@ import org.alfresco.util.EqualsHelper;
// Resolve Names
this.name = QName.createQName(m2Class.getName(), resolver);
this.archive = m2Class.getArchive();
this.includedInSuperTypeQuery = m2Class.getIncludedInSuperTypeQuery();
if (m2Class.getParentName() != null && m2Class.getParentName().length() > 0)
{
this.parentName = QName.createQName(m2Class.getParentName(), resolver);
@@ -315,6 +318,13 @@ import org.alfresco.util.EqualsHelper;
// archive not explicitly set on this class and there is a parent class
inheritedArchive = ((M2ClassDefinition)parentClass).getArchive();
}
// resolve includedInSuperTypeQuery inheritance
if (parentClass != null && includedInSuperTypeQuery == null)
{
// archive not explicitly set on this class and there is a parent class
inheritedIncludedInSuperTypeQuery = ((M2ClassDefinition)parentClass).getIncludedInSuperTypeQuery();
}
}
/* (non-Javadoc)
@@ -383,6 +393,22 @@ import org.alfresco.util.EqualsHelper;
return archive == null ? inheritedArchive : archive;
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.dictionary.ClassDefinition#includedInSuperTypeQuery()
*/
public Boolean getIncludedInSuperTypeQuery()
{
if(includedInSuperTypeQuery != null)
{
return includedInSuperTypeQuery;
}
if(inheritedIncludedInSuperTypeQuery != null)
{
return inheritedIncludedInSuperTypeQuery;
}
return Boolean.TRUE;
}
/* (non-Javadoc)
* @see org.alfresco.repo.dictionary.ClassDefinition#getProperties()
*/
@@ -618,6 +644,23 @@ import org.alfresco.util.EqualsHelper;
}
}
// check includedInSuperTypeQuery/inheritedIncludedInSuperTypeQuery
if (includedInSuperTypeQuery == null)
{
if (classDef.getIncludedInSuperTypeQuery() != null)
{
isUpdatedIncrementally = true;
}
}
else
{
Boolean classIncludedInSuperTypeQuery = classDef.getIncludedInSuperTypeQuery();
if (classIncludedInSuperTypeQuery == null || classIncludedInSuperTypeQuery.booleanValue() != includedInSuperTypeQuery.booleanValue())
{
isUpdatedIncrementally = true;
}
}
String modelDiffType;
if (isAspect())
{

View File

@@ -61,6 +61,7 @@
<value name="description" field="description" usage="optional"/>
<value name="parent" field="parentName" usage="optional"/>
<value name="archive" field="archive" usage="optional"/>
<value name="includedInSuperTypeQuery" field="includedInSuperTypeQuery" usage="optional"/>
<structure name="properties" usage="optional">
<collection field="properties" item-type="org.alfresco.repo.dictionary.M2Property" factory="org.alfresco.repo.dictionary.M2Model.createList"/>
</structure>

View File

@@ -362,6 +362,12 @@ public abstract class AbstractAuditDAOImpl implements AuditDAO
this.callback = callback;
this.more = true;
}
public boolean valuesRequired()
{
return callback.valuesRequired();
}
@SuppressWarnings("unchecked")
public void processResult(AuditQueryResult row)
{
@@ -371,7 +377,10 @@ public abstract class AbstractAuditDAOImpl implements AuditDAO
return;
}
// See if the value is available or if it has to be built up
Map<String, Serializable> auditValues = row.getAuditValue();
Map<String, Serializable> auditValues = null;
if (valuesRequired())
{
auditValues = row.getAuditValue();
if (auditValues == null)
{
List<PropertyIdSearchRow> propMapRows = row.getAuditValueRows();
@@ -383,10 +392,8 @@ public abstract class AbstractAuditDAOImpl implements AuditDAO
if (auditValuesPair == null)
{
// Ignore
more = callback.handleAuditEntryError(
row.getAuditEntryId(),
"Audit entry not joined to audit properties: " + row,
null);
more = callback.handleAuditEntryError(row.getAuditEntryId(),
"Audit entry not joined to audit properties: " + row, null);
return;
}
auditValues = (Map<String, Serializable>) auditValuesPair.getSecond();
@@ -396,34 +403,30 @@ public abstract class AbstractAuditDAOImpl implements AuditDAO
// Resolve the map
try
{
auditValues = (Map<String, Serializable>) propertyValueDAO.convertPropertyIdSearchRows(propMapRows);
auditValues = (Map<String, Serializable>) propertyValueDAO
.convertPropertyIdSearchRows(propMapRows);
}
catch (ClassCastException e)
{
more = callback.handleAuditEntryError(
row.getAuditEntryId(),
"Audit entry not linked to a Map<String, Serializable> value: " + row,
e);
more = callback.handleAuditEntryError(row.getAuditEntryId(),
"Audit entry not linked to a Map<String, Serializable> value: " + row, e);
return;
}
catch (Throwable e)
{
more = callback.handleAuditEntryError(
row.getAuditEntryId(),
"Audit entry unable to extract audited values: " + row,
e);
more = callback.handleAuditEntryError(row.getAuditEntryId(),
"Audit entry unable to extract audited values: " + row, e);
return;
}
if (auditValues == null)
{
more = callback.handleAuditEntryError(
row.getAuditEntryId(),
"Audit entry incompletely joined to audit properties: " + row,
null);
more = callback.handleAuditEntryError(row.getAuditEntryId(),
"Audit entry incompletely joined to audit properties: " + row, null);
return;
}
}
}
}
// Resolve the application and username
String auditAppName = (String) propertyValueDAO.getPropertyValueById(row.getAuditAppNameId()).getSecond();
Long auditUserId = row.getAuditUserId();

Some files were not shown because too many files have changed in this diff Show More