diff --git a/core/pom.xml b/core/pom.xml
index 7df1af98..2f47f0b7 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -5,7 +5,7 @@
jp.aegif.nemaki
core
war
- 2.3.12
+ 2.3.13
core
NemakiWare server
https://github.com/NemakiWare/NemakiWare
@@ -34,11 +34,8 @@
junit
- junit
- 4.10
- jar
- test
- false
+ junit
+ 4.12
@@ -246,7 +243,7 @@
net.sf.ehcache
ehcache
- 2.7.2
+ 2.10.2
@@ -274,6 +271,18 @@
cloning
1.9.1
+
+
+ com.google.guava
+ guava
+ 19.0
+
+
+
+ joda-time
+ joda-time
+ 2.9.3
+
jp.aegif.nemakiware
@@ -301,9 +310,9 @@
2.19.1
true
-
- **/*TestGroup.java
-
+
+ **/*TestGroup.java
+
diff --git a/core/src/main/java/jp/aegif/nemaki/businesslogic/ContentService.java b/core/src/main/java/jp/aegif/nemaki/businesslogic/ContentService.java
index 50b0a950..3343c412 100644
--- a/core/src/main/java/jp/aegif/nemaki/businesslogic/ContentService.java
+++ b/core/src/main/java/jp/aegif/nemaki/businesslogic/ContentService.java
@@ -390,7 +390,7 @@ Item createItem(CallContext callContext, String repositoryId,
* @return
*/
Content update(String repositoryId, Content content);
-
+
/**
* Update properties of a content
*
diff --git a/core/src/main/java/jp/aegif/nemaki/businesslogic/impl/ContentServiceImpl.java b/core/src/main/java/jp/aegif/nemaki/businesslogic/impl/ContentServiceImpl.java
index 5bc73350..96ce96b8 100644
--- a/core/src/main/java/jp/aegif/nemaki/businesslogic/impl/ContentServiceImpl.java
+++ b/core/src/main/java/jp/aegif/nemaki/businesslogic/impl/ContentServiceImpl.java
@@ -27,15 +27,11 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
import jp.aegif.nemaki.businesslogic.ContentService;
import jp.aegif.nemaki.businesslogic.rendition.RenditionManager;
import jp.aegif.nemaki.cmis.aspect.query.solr.SolrUtil;
@@ -43,7 +39,6 @@
import jp.aegif.nemaki.cmis.factory.info.RepositoryInfo;
import jp.aegif.nemaki.cmis.factory.info.RepositoryInfoMap;
import jp.aegif.nemaki.dao.ContentDaoService;
-import jp.aegif.nemaki.dao.impl.cached.ContentDaoServiceImpl;
import jp.aegif.nemaki.model.Ace;
import jp.aegif.nemaki.model.Acl;
import jp.aegif.nemaki.model.Archive;
@@ -212,10 +207,11 @@ public Folder getParent(String repositoryId, String objectId) {
public List getChildren(String repositoryId, String folderId) {
List children = new ArrayList();
- List indices = contentDaoService.getLatestChildrenIndex(repositoryId, folderId);
+ List indices = contentDaoService.getChildren(repositoryId, folderId);
if (CollectionUtils.isEmpty(indices))
return null;
-
+
+ //TODO getを重複して行う必要なし
for (Content c : indices) {
if (c.isDocument()) {
Document d = contentDaoService.getDocument(repositoryId, c.getId());
@@ -613,14 +609,14 @@ public Document checkOut(CallContext callContext, String repositoryId, String ob
public void cancelCheckOut(CallContext callContext, String repositoryId, String objectId,
ExtensionsData extension) {
Document pwc = getDocument(repositoryId, objectId);
- VersionSeries vs = getVersionSeries(repositoryId, pwc);
-
+
writeChangeEvent(callContext, repositoryId, pwc, ChangeType.DELETED);
// Delete attachment & document itself(without archiving)
contentDaoService.delete(repositoryId, pwc.getAttachmentNodeId());
contentDaoService.delete(repositoryId, pwc.getId());
+ VersionSeries vs = getVersionSeries(repositoryId, pwc);
// Reverse the effect of checkout
setModifiedSignature(callContext, vs);
vs.setVersionSeriesCheckedOut(false);
@@ -628,6 +624,14 @@ public void cancelCheckOut(CallContext callContext, String repositoryId, String
vs.setVersionSeriesCheckedOutId("");
contentDaoService.update(repositoryId, vs);
+ List versions = getAllVersions(callContext, repositoryId, vs.getId());
+ if(CollectionUtils.isNotEmpty(versions)){
+ //Collections.sort(versions, new VersionComparator());
+ for(Document version : versions){
+ contentDaoService.refreshCmisObjectData(repositoryId, version.getId());
+ }
+ }
+
// Call Solr indexing(optional)
solrUtil.callSolrIndexing(repositoryId);
}
@@ -637,10 +641,11 @@ public Document checkIn(CallContext callContext, String repositoryId, Holder policies,
org.apache.chemistry.opencmis.commons.data.Acl addAces,
org.apache.chemistry.opencmis.commons.data.Acl removeAces, ExtensionsData extension) {
+
String id = objectId.getValue();
+
Document pwc = getDocument(repositoryId, id);
Document checkedIn = buildCopyDocumentWithBasicProperties(callContext, pwc);
-
Document latest = getDocumentOfLatestVersion(repositoryId, pwc.getVersionSeriesId());
// When PWCUpdatable is true
@@ -657,6 +662,9 @@ public Document checkIn(CallContext callContext, String repositoryId, Holder injectPropertyValue(Collection> pro
}
@Override
- public synchronized Content updateProperties(CallContext callContext, String repositoryId, Properties properties,
+ public Content updateProperties(CallContext callContext, String repositoryId, Properties properties,
Content content) {
Content modified = modifyProperties(callContext, repositoryId, properties, content);
@@ -1130,9 +1135,9 @@ public synchronized Content updateProperties(CallContext callContext, String rep
return result;
}
-
+
@Override
- public synchronized Content update(String repositoryId, Content content) {
+ public Content update(String repositoryId, Content content) {
Content result = null;
if (content instanceof Document) {
@@ -1174,18 +1179,31 @@ private void setUpdatePropertyValue(String repositoryId, Content content, Proper
}
@Override
- public synchronized void move(String repositoryId, Content content, Folder target) {
+ public void move(String repositoryId, Content content, Folder target) {
+ String sourceId = content.getParentId();
+
content.setParentId(target.getId());
String uniqueName = buildUniqueName(repositoryId, content.getName(), target.getId(), null);
content.setName(uniqueName);
- update(repositoryId, content);
+
+ move(repositoryId, content, sourceId);
// Call Solr indexing(optional)
solrUtil.callSolrIndexing(repositoryId);
}
+
+ private Content move(String repositoryId, Content content, String sourceId){
+ Content result = null;
+ if(content instanceof Document){
+ result = contentDaoService.move(repositoryId, (Document)content, sourceId);
+ }else if(content instanceof Folder){
+ result = contentDaoService.move(repositoryId, (Folder)content, sourceId);
+ }
+ return result;
+ }
@Override
- public synchronized void applyPolicy(CallContext callContext, String repositoryId, String policyId, String objectId,
+ public void applyPolicy(CallContext callContext, String repositoryId, String policyId, String objectId,
ExtensionsData extension) {
Policy policy = getPolicy(repositoryId, policyId);
List ids = policy.getAppliedIds();
@@ -1199,7 +1217,7 @@ public synchronized void applyPolicy(CallContext callContext, String repositoryI
}
@Override
- public synchronized void removePolicy(CallContext callContext, String repositoryId, String policyId,
+ public void removePolicy(CallContext callContext, String repositoryId, String policyId,
String objectId, ExtensionsData extension) {
Policy policy = getPolicy(repositoryId, policyId);
List ids = policy.getAppliedIds();
@@ -1218,6 +1236,12 @@ public synchronized void removePolicy(CallContext callContext, String repository
@Override
public void delete(CallContext callContext, String repositoryId, String objectId, Boolean deletedWithParent) {
Content content = getContent(repositoryId, objectId);
+
+ //TODO workaround
+ if(content == null){
+ //If content is already deleted, do nothing;
+ return;
+ }
// Record the change event(Before the content is deleted!)
writeChangeEvent(callContext, repositoryId, content, ChangeType.DELETED);
@@ -1390,7 +1414,7 @@ private boolean isPreviewEnabled() {
}
@Override
- public synchronized void appendAttachment(CallContext callContext, String repositoryId, Holder objectId,
+ public void appendAttachment(CallContext callContext, String repositoryId, Holder objectId,
Holder changeToken, ContentStream contentStream, boolean isLastChunk, ExtensionsData extension) {
Document document = contentDaoService.getDocument(repositoryId, objectId.getValue());
AttachmentNode attachment = getAttachment(repositoryId, document.getAttachmentNodeId());
diff --git a/core/src/main/java/jp/aegif/nemaki/cmis/aspect/CompileService.java b/core/src/main/java/jp/aegif/nemaki/cmis/aspect/CompileService.java
index 56bb9cb4..28f75801 100644
--- a/core/src/main/java/jp/aegif/nemaki/cmis/aspect/CompileService.java
+++ b/core/src/main/java/jp/aegif/nemaki/cmis/aspect/CompileService.java
@@ -42,9 +42,9 @@ public ObjectData compileObjectData(CallContext context,
String repositoryId, Content content, String filter,
Boolean includeAllowableActions, IncludeRelationships includeRelationships, String renditionFilter, Boolean includeAcl);
- public ObjectList compileObjectDataList(CallContext callContext,
+ public ObjectList compileObjectDataList(CallContext callContext,
String repositoryId, List contents, String filter,
- Boolean includeAllowableActions, IncludeRelationships includeRelationships, String renditionFilter, Boolean includeAcl, BigInteger maxItems, BigInteger skipCount, boolean folderOnly);
+ Boolean includeAllowableActions, IncludeRelationships includeRelationships, String renditionFilter, Boolean includeAcl, BigInteger maxItems, BigInteger skipCount, boolean folderOnly, String orderBy);
public ObjectList compileChangeDataList(CallContext context, String repositoryId,
List changes, Holder changeLogToken, Boolean includeProperties,
diff --git a/core/src/main/java/jp/aegif/nemaki/cmis/aspect/impl/CompileServiceImpl.java b/core/src/main/java/jp/aegif/nemaki/cmis/aspect/impl/CompileServiceImpl.java
index f2188b43..07f157fb 100644
--- a/core/src/main/java/jp/aegif/nemaki/cmis/aspect/impl/CompileServiceImpl.java
+++ b/core/src/main/java/jp/aegif/nemaki/cmis/aspect/impl/CompileServiceImpl.java
@@ -84,6 +84,7 @@
import jp.aegif.nemaki.businesslogic.ContentService;
import jp.aegif.nemaki.cmis.aspect.CompileService;
import jp.aegif.nemaki.cmis.aspect.PermissionService;
+import jp.aegif.nemaki.cmis.aspect.SortUtil;
import jp.aegif.nemaki.cmis.aspect.type.TypeManager;
import jp.aegif.nemaki.cmis.factory.info.AclCapabilities;
import jp.aegif.nemaki.cmis.factory.info.RepositoryInfoMap;
@@ -103,7 +104,6 @@
import jp.aegif.nemaki.model.Rendition;
import jp.aegif.nemaki.model.VersionSeries;
import jp.aegif.nemaki.util.DataUtil;
-import jp.aegif.nemaki.util.cache.NemakiCache;
import jp.aegif.nemaki.util.cache.NemakiCachePool;
import jp.aegif.nemaki.util.constant.CmisExtensionToken;
import net.sf.ehcache.Element;
@@ -120,6 +120,7 @@ public class CompileServiceImpl implements CompileService {
private TypeManager typeManager;
private AclCapabilities aclCapabilities;
private NemakiCachePool nemakiCachePool;
+ private SortUtil sortUtil;
private boolean includeRelationshipsEnabled = true;
@@ -276,90 +277,65 @@ private List filterRelationships(String objectId, List b
}
@Override
- public ObjectList compileObjectDataList(CallContext callContext,
+ public ObjectList compileObjectDataList(CallContext callContext,
String repositoryId, List contents, String filter,
Boolean includeAllowableActions, IncludeRelationships includeRelationships,
String renditionFilter, Boolean includeAcl, BigInteger maxItems,
- BigInteger skipCount, boolean folderOnly) {
- ObjectListImpl list = new ObjectListImpl();
- list.setObjects(new ArrayList());
-
- //Return empty result when no children exist
+ BigInteger skipCount, boolean folderOnly, String orderBy) {
if (CollectionUtils.isEmpty(contents)) {
+ //Empty list
+ ObjectListImpl list = new ObjectListImpl();
+ list.setObjects(new ArrayList());
list.setNumItems(BigInteger.ZERO);
list.setHasMoreItems(false);
return list;
- }
-
- // Convert skip and max to integer
- int skip = (skipCount == null ? 0 : skipCount.intValue());
- if (skip < 0) {
- skip = 0;
- }
- int max = (maxItems == null ? Integer.MAX_VALUE : maxItems.intValue());
- if (max < 0) {
- max = Integer.MAX_VALUE;
- }
-
- int count = 0;
- Listods = new ArrayList();
- for(T t : contents){
- //Skip items
- if (count < skip) {
- count++;
- continue;
- }
- if (ods.size() >= max) {
- break;
- }
+ }else{
+ Listods = new ArrayList();
+ for(T c : contents){
+ //Filter by folderOnly
+ if(folderOnly && !c.isFolder()){
+ continue;
+ }
- Content _c = (Content) t;
- //Filter by folderOnly
- if(folderOnly && !_c.isFolder()){
- continue;
- }
+ //Get each ObjectData
+ ObjectData _od;
+ Element v = nemakiCachePool.get(repositoryId).getObjectDataCache().get(c.getId());
+ if(v == null){
+ _od = compileObjectDataWithFullAttributes(callContext, repositoryId, c);
+ }else{
+ _od = (ObjectDataImpl)v.getObjectValue();
+ }
- //Convert content class
- Content c = null;
- if(_c.isFolder()){
- c = (Folder)t;
- }else if(_c.isDocument()){
- c = (Document)t;
- }else if(_c.isPolicy()){
- c = (Policy)t;
- }else if(_c.isRelationship()){
- c = (Relationship)t;
- }else if(_c.isItem()){
- c = (Item)t;
+ ObjectData od = filterObjectDataInList(callContext, repositoryId, _od, filter, includeAllowableActions, includeRelationships, renditionFilter, includeAcl);
+ if(od != null){
+ ods.add(od);
+ }
}
-
- //Get each ObjectData
- ObjectData _od;
- Element v = nemakiCachePool.get(repositoryId).getObjectDataCache().get(c.getId());
- if(v == null){
- _od = compileObjectDataWithFullAttributes(callContext, repositoryId, c);
+
+ //Sort
+ sortUtil.sort(repositoryId, ods, orderBy);
+
+ //Set metadata
+ ObjectListImpl list = new ObjectListImpl();
+ Integer _skipCount = skipCount.intValue();
+ Integer _maxItems = maxItems.intValue();
+
+ if(_skipCount >= ods.size()){
+ list.setHasMoreItems(false);
+ list.setObjects(new ArrayList());
}else{
- _od = (ObjectDataImpl)v.getObjectValue();
+ //hasMoreItems
+ Boolean hasMoreItems = _skipCount + _maxItems < ods.size();
+ list.setHasMoreItems(hasMoreItems);
+ //paged list
+ Integer toIndex = Math.min(_skipCount + _maxItems, ods.size());
+ list.setObjects(new ArrayList<>(ods.subList(_skipCount, toIndex)));
}
+ //totalNumItem
+ list.setNumItems(BigInteger.valueOf(ods.size()));
- ObjectData od = filterObjectDataInList(callContext, repositoryId, _od, filter, includeAllowableActions, includeRelationships, renditionFilter, includeAcl);
- if(od != null){
- ods.add(od);
- }
- }
- list.setObjects(ods);
-
- //Set list meta data
- if(CollectionUtils.isEmpty(list.getObjects())){
return list;
- }else{
- list.setNumItems(BigInteger.valueOf(contents.size()));
- if(max + skip < contents.size()){
- list.setHasMoreItems(true);
- }
}
-
- return list;
}
@Override
@@ -1462,4 +1438,8 @@ public void setNemakiCachePool(NemakiCachePool nemakiCachePool) {
public void setIncludeRelationshipsEnabled(boolean includeRelationshipsEnabled) {
this.includeRelationshipsEnabled = includeRelationshipsEnabled;
}
+
+ public void setSortUtil(SortUtil sortUtil) {
+ this.sortUtil = sortUtil;
+ }
}
\ No newline at end of file
diff --git a/core/src/main/java/jp/aegif/nemaki/cmis/aspect/impl/ExceptionServiceImpl.java b/core/src/main/java/jp/aegif/nemaki/cmis/aspect/impl/ExceptionServiceImpl.java
index d35a89c4..5adc34e6 100644
--- a/core/src/main/java/jp/aegif/nemaki/cmis/aspect/impl/ExceptionServiceImpl.java
+++ b/core/src/main/java/jp/aegif/nemaki/cmis/aspect/impl/ExceptionServiceImpl.java
@@ -23,10 +23,8 @@
import java.math.BigDecimal;
import java.math.BigInteger;
-import java.security.acl.Permission;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -34,7 +32,6 @@
import org.apache.chemistry.opencmis.commons.PropertyIds;
import org.apache.chemistry.opencmis.commons.data.ContentStream;
-import org.apache.chemistry.opencmis.commons.data.PermissionMapping;
import org.apache.chemistry.opencmis.commons.data.Properties;
import org.apache.chemistry.opencmis.commons.data.PropertyData;
import org.apache.chemistry.opencmis.commons.data.PropertyDecimal;
@@ -91,7 +88,6 @@
import jp.aegif.nemaki.model.VersionSeries;
import jp.aegif.nemaki.util.DataUtil;
import jp.aegif.nemaki.util.constant.DomainType;
-import jp.aegif.nemaki.util.constant.PropertyKey;
public class ExceptionServiceImpl implements ExceptionService,
ApplicationContextAware {
@@ -342,6 +338,11 @@ public void objectNotFoundParentFolder(String repositoryId, String id, Content c
@Override
public void permissionDenied(CallContext context, String repositoryId,
String key, Content content) {
+ if(content == null){
+ System.out.println();
+ }
+
+
String baseTypeId = content.getType();
Acl acl = contentService.calculateAcl(repositoryId, content);
permissionDeniedInternal(context, repositoryId, key, acl, baseTypeId, content);
diff --git a/core/src/main/java/jp/aegif/nemaki/cmis/aspect/query/solr/SolrQueryProcessor.java b/core/src/main/java/jp/aegif/nemaki/cmis/aspect/query/solr/SolrQueryProcessor.java
index 51245d43..4de0027b 100644
--- a/core/src/main/java/jp/aegif/nemaki/cmis/aspect/query/solr/SolrQueryProcessor.java
+++ b/core/src/main/java/jp/aegif/nemaki/cmis/aspect/query/solr/SolrQueryProcessor.java
@@ -27,15 +27,16 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.locks.Lock;
import jp.aegif.nemaki.businesslogic.ContentService;
import jp.aegif.nemaki.cmis.aspect.CompileService;
import jp.aegif.nemaki.cmis.aspect.ExceptionService;
import jp.aegif.nemaki.cmis.aspect.PermissionService;
-import jp.aegif.nemaki.cmis.aspect.SortUtil;
import jp.aegif.nemaki.cmis.aspect.query.QueryProcessor;
import jp.aegif.nemaki.cmis.aspect.type.TypeManager;
import jp.aegif.nemaki.model.Content;
+import jp.aegif.nemaki.util.lock.ThreadLockService;
import org.antlr.runtime.tree.Tree;
import org.apache.chemistry.opencmis.commons.PropertyIds;
@@ -69,8 +70,8 @@ public class SolrQueryProcessor implements QueryProcessor {
private PermissionService permissionService;
private CompileService compileService;
private ExceptionService exceptionService;
+ private ThreadLockService threadLockService;
private SolrUtil solrUtil;
- private SortUtil sortUtil;
private static final Log logger = LogFactory
.getLog(SolrQueryProcessor.class);
@@ -228,42 +229,37 @@ public ObjectList query(CallContext callContext, String repositoryId,
}
}
-
- // Filter out by permissions
- List permitted = permissionService.getFiltered(
- callContext, repositoryId, contents);
-
- // Filter return value with SELECT clause
- Map requestedWithAliasKey = queryObject
- .getRequestedPropertiesByAlias();
- String filter = null;
- if (!requestedWithAliasKey.keySet().contains("*")) {
- // Create filter(queryNames) from query aliases
- filter = StringUtils.join(requestedWithAliasKey.values(), ",");
- }
-
- // Build ObjectList
- ObjectList result = compileService.compileObjectDataList(
- callContext, repositoryId, permitted, filter,
- includeAllowableActions, includeRelationships, renditionFilter, false,
- maxItems, skipCount, false);
-
- // Sort
- List sortSpecs = queryObject.getOrderBys();
- List _orderBy = new ArrayList();
- for (SortSpec sortSpec : sortSpecs) {
- List _sortSpec = new ArrayList();
- _sortSpec.add(sortSpec.getSelector().getName());
- if (!sortSpec.isAscending()) {
- _sortSpec.add("DESC");
+
+
+ List locks = threadLockService.readLocks(repositoryId, contents);
+ try{
+ threadLockService.bulkLock(locks);
+
+ // Filter out by permissions
+ List permitted = permissionService.getFiltered(
+ callContext, repositoryId, contents);
+
+ // Filter return value with SELECT clause
+ Map requestedWithAliasKey = queryObject
+ .getRequestedPropertiesByAlias();
+ String filter = null;
+ if (!requestedWithAliasKey.keySet().contains("*")) {
+ // Create filter(queryNames) from query aliases
+ filter = StringUtils.join(requestedWithAliasKey.values(), ",");
}
- _orderBy.add(StringUtils.join(_sortSpec, " "));
- }
- String orderBy = StringUtils.join(_orderBy, ",");
- sortUtil.sort(repositoryId, result.getObjects(), orderBy);
+ // Build ObjectList
+ String orderBy = orderBy(queryObject);
+ ObjectList result = compileService.compileObjectDataList(
+ callContext, repositoryId, permitted, filter,
+ includeAllowableActions, includeRelationships, renditionFilter, false,
+ maxItems, skipCount, false, orderBy);
- return result;
+ return result;
+
+ }finally{
+ threadLockService.bulkUnlock(locks);
+ }
} else {
ObjectListImpl nullList = new ObjectListImpl();
nullList.setHasMoreItems(false);
@@ -271,6 +267,22 @@ public ObjectList query(CallContext callContext, String repositoryId,
return nullList;
}
}
+
+ private String orderBy(QueryObject queryObject){
+ List sortSpecs = queryObject.getOrderBys();
+ List _orderBy = new ArrayList();
+ for (SortSpec sortSpec : sortSpecs) {
+ List _sortSpec = new ArrayList();
+ _sortSpec.add(sortSpec.getSelector().getName());
+ if (!sortSpec.isAscending()) {
+ _sortSpec.add("DESC");
+ }
+
+ _orderBy.add(StringUtils.join(_sortSpec, " "));
+ }
+ String orderBy = StringUtils.join(_orderBy, ",");
+ return orderBy;
+ }
private Tree extractWhereTree(Tree tree){
for (int i = 0; i < tree.getChildCount(); i++) {
@@ -313,7 +325,7 @@ public void setSolrUtil(SolrUtil solrUtil) {
this.solrUtil = solrUtil;
}
- public void setSortUtil(SortUtil sortUtil) {
- this.sortUtil = sortUtil;
+ public void setThreadLockService(ThreadLockService threadLockService) {
+ this.threadLockService = threadLockService;
}
}
\ No newline at end of file
diff --git a/core/src/main/java/jp/aegif/nemaki/cmis/service/impl/AclServiceImpl.java b/core/src/main/java/jp/aegif/nemaki/cmis/service/impl/AclServiceImpl.java
index 7081b16c..23aa86c7 100644
--- a/core/src/main/java/jp/aegif/nemaki/cmis/service/impl/AclServiceImpl.java
+++ b/core/src/main/java/jp/aegif/nemaki/cmis/service/impl/AclServiceImpl.java
@@ -21,6 +21,7 @@
package jp.aegif.nemaki.cmis.service.impl;
import java.util.List;
+import java.util.concurrent.locks.Lock;
import jp.aegif.nemaki.businesslogic.ContentService;
import jp.aegif.nemaki.cmis.aspect.CompileService;
@@ -30,12 +31,10 @@
import jp.aegif.nemaki.cmis.factory.info.RepositoryInfoMap;
import jp.aegif.nemaki.cmis.service.AclService;
import jp.aegif.nemaki.model.Content;
-import jp.aegif.nemaki.util.PropertyManager;
-import jp.aegif.nemaki.util.cache.NemakiCache;
import jp.aegif.nemaki.util.cache.NemakiCachePool;
import jp.aegif.nemaki.util.constant.DomainType;
import jp.aegif.nemaki.util.constant.PrincipalId;
-import jp.aegif.nemaki.util.constant.PropertyKey;
+import jp.aegif.nemaki.util.lock.ThreadLockService;
import org.apache.chemistry.opencmis.commons.data.Ace;
import org.apache.chemistry.opencmis.commons.data.Acl;
@@ -56,79 +55,102 @@ public class AclServiceImpl implements AclService {
private CompileService compileService;
private ExceptionService exceptionService;
private TypeManager typeManager;
- private PropertyManager propertyManager;
+ private ThreadLockService threadLockService;
private NemakiCachePool nemakiCachePool;
private RepositoryInfoMap repositoryInfoMap;
@Override
public Acl getAcl(CallContext callContext, String repositoryId,
String objectId, Boolean onlyBasicPermissions) {
- // //////////////////
- // General Exception
- // //////////////////
+
exceptionService.invalidArgumentRequired("objectId", objectId);
- Content content = contentService.getContent(repositoryId, objectId);
- exceptionService.objectNotFound(DomainType.OBJECT, content, objectId);
- exceptionService.permissionDenied(callContext,repositoryId, PermissionMapping.CAN_GET_ACL_OBJECT, content);
-
- // //////////////////
- // Body of the method
- // //////////////////
- jp.aegif.nemaki.model.Acl acl = contentService.calculateAcl(repositoryId, content);
- return compileService.compileAcl(acl, content.isAclInherited(), onlyBasicPermissions);
+
+ Lock lock = threadLockService.getReadLock(repositoryId, objectId);
+
+ try{
+ lock.lock();
+
+ // //////////////////
+ // General Exception
+ // //////////////////
+
+ Content content = contentService.getContent(repositoryId, objectId);
+ exceptionService.objectNotFound(DomainType.OBJECT, content, objectId);
+ exceptionService.permissionDenied(callContext,repositoryId, PermissionMapping.CAN_GET_ACL_OBJECT, content);
+
+ // //////////////////
+ // Body of the method
+ // //////////////////
+ jp.aegif.nemaki.model.Acl acl = contentService.calculateAcl(repositoryId, content);
+ return compileService.compileAcl(acl, content.isAclInherited(), onlyBasicPermissions);
+ }finally{
+ lock.unlock();
+ }
}
@Override
public Acl applyAcl(CallContext callContext, String repositoryId, String objectId,
Acl acl, AclPropagation aclPropagation) {
- // //////////////////
- // General Exception
- // //////////////////
exceptionService.invalidArgumentRequired("objectId", objectId);
- Content content = contentService.getContent(repositoryId, objectId);
- exceptionService.objectNotFound(DomainType.OBJECT, content, objectId);
- exceptionService.permissionDenied(callContext,repositoryId, PermissionMapping.CAN_APPLY_ACL_OBJECT, content);
-
- // //////////////////
- // Specific Exception
- // //////////////////
- TypeDefinition td = typeManager.getTypeDefinition(repositoryId, content);
- if(!td.isControllableAcl()) exceptionService.constraint(objectId, "applyAcl cannot be performed on the object whose controllableAcl = false");
- exceptionService.constraintAclPropagationDoesNotMatch(aclPropagation);
- exceptionService.constraintPermissionDefined(repositoryId, acl, objectId);
-
- // //////////////////
- // Body of the method
- // //////////////////
- //Check ACL inheritance
- boolean inherited = true; //Inheritance defaults to true if nothing input
- List exts = acl.getExtensions();
- if(!CollectionUtils.isEmpty(exts)){
- for(CmisExtensionElement ext : exts){
- if(ext.getName().equals("inherited")){
- inherited = Boolean.valueOf(ext.getValue());
+
+ Lock lock = threadLockService.getReadLock(repositoryId, objectId);
+
+ try{
+ lock.lock();
+
+ // //////////////////
+ // General Exception
+ // //////////////////
+
+ Content content = contentService.getContent(repositoryId, objectId);
+ exceptionService.objectNotFound(DomainType.OBJECT, content, objectId);
+ exceptionService.permissionDenied(callContext,repositoryId, PermissionMapping.CAN_APPLY_ACL_OBJECT, content);
+
+ // //////////////////
+ // Specific Exception
+ // //////////////////
+ TypeDefinition td = typeManager.getTypeDefinition(repositoryId, content);
+ if(!td.isControllableAcl()) exceptionService.constraint(objectId, "applyAcl cannot be performed on the object whose controllableAcl = false");
+ exceptionService.constraintAclPropagationDoesNotMatch(aclPropagation);
+ exceptionService.constraintPermissionDefined(repositoryId, acl, objectId);
+
+ // //////////////////
+ // Body of the method
+ // //////////////////
+ //Check ACL inheritance
+ boolean inherited = true; //Inheritance defaults to true if nothing input
+ List exts = acl.getExtensions();
+ if(!CollectionUtils.isEmpty(exts)){
+ for(CmisExtensionElement ext : exts){
+ if(ext.getName().equals("inherited")){
+ inherited = Boolean.valueOf(ext.getValue());
+ }
}
+ if(!content.isAclInherited().equals(inherited)) content.setAclInherited(inherited);
}
- if(!content.isAclInherited().equals(inherited)) content.setAclInherited(inherited);
- }
- jp.aegif.nemaki.model.Acl nemakiAcl = new jp.aegif.nemaki.model.Acl();
- //REPOSUTORYDETERMINED or PROPAGATE is considered as PROPAGATE
- boolean objectOnly = (aclPropagation == AclPropagation.OBJECTONLY)? true : false;
- for(Ace ace : acl.getAces()){
- if(ace.isDirect()){
- jp.aegif.nemaki.model.Ace nemakiAce = new jp.aegif.nemaki.model.Ace(ace.getPrincipalId(), ace.getPermissions(), objectOnly);
- nemakiAcl.getLocalAces().add(nemakiAce);
+ jp.aegif.nemaki.model.Acl nemakiAcl = new jp.aegif.nemaki.model.Acl();
+ //REPOSUTORYDETERMINED or PROPAGATE is considered as PROPAGATE
+ boolean objectOnly = (aclPropagation == AclPropagation.OBJECTONLY)? true : false;
+ for(Ace ace : acl.getAces()){
+ if(ace.isDirect()){
+ jp.aegif.nemaki.model.Ace nemakiAce = new jp.aegif.nemaki.model.Ace(ace.getPrincipalId(), ace.getPermissions(), objectOnly);
+ nemakiAcl.getLocalAces().add(nemakiAce);
+ }
}
- }
- convertSystemPrinciaplId(repositoryId, nemakiAcl);
- content.setAcl(nemakiAcl);
- contentService.update(repositoryId, content);
-
- nemakiCachePool.get(repositoryId).removeCmisCache(objectId);
+ convertSystemPrinciaplId(repositoryId, nemakiAcl);
+ content.setAcl(nemakiAcl);
+ contentService.update(repositoryId, content);
+
+ nemakiCachePool.get(repositoryId).removeCmisCache(objectId);
+
+ return getAcl(callContext, repositoryId, objectId, false);
+
+ }finally{
+ lock.unlock();
+ }
- return getAcl(callContext, repositoryId, objectId, false);
}
private void convertSystemPrinciaplId(String repositoryId, jp.aegif.nemaki.model.Acl acl){
@@ -163,8 +185,8 @@ public void setTypeManager(TypeManager typeManager) {
this.typeManager = typeManager;
}
- public void setPropertyManager(PropertyManager propertyManager) {
- this.propertyManager = propertyManager;
+ public void setThreadLockService(ThreadLockService threadLockService) {
+ this.threadLockService = threadLockService;
}
public void setNemakiCachePool(NemakiCachePool nemakiCachePool) {
diff --git a/core/src/main/java/jp/aegif/nemaki/cmis/service/impl/NavigationServiceImpl.java b/core/src/main/java/jp/aegif/nemaki/cmis/service/impl/NavigationServiceImpl.java
index 4aba946e..77e434fa 100644
--- a/core/src/main/java/jp/aegif/nemaki/cmis/service/impl/NavigationServiceImpl.java
+++ b/core/src/main/java/jp/aegif/nemaki/cmis/service/impl/NavigationServiceImpl.java
@@ -25,18 +25,19 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.locks.Lock;
import jp.aegif.nemaki.businesslogic.ContentService;
import jp.aegif.nemaki.cmis.aspect.CompileService;
import jp.aegif.nemaki.cmis.aspect.ExceptionService;
import jp.aegif.nemaki.cmis.aspect.PermissionService;
-import jp.aegif.nemaki.cmis.aspect.SortUtil;
import jp.aegif.nemaki.cmis.service.NavigationService;
import jp.aegif.nemaki.model.Content;
import jp.aegif.nemaki.model.Document;
import jp.aegif.nemaki.model.Folder;
import jp.aegif.nemaki.util.DataUtil;
import jp.aegif.nemaki.util.constant.DomainType;
+import jp.aegif.nemaki.util.lock.ThreadLockService;
import org.apache.chemistry.opencmis.commons.PropertyIds;
import org.apache.chemistry.opencmis.commons.data.ExtensionsData;
@@ -64,7 +65,7 @@ public class NavigationServiceImpl implements NavigationService {
private ExceptionService exceptionService;
private CompileService compileService;
private PermissionService permissionService;
- private SortUtil sortUtil;
+ private ThreadLockService threadLockService;
@Override
public ObjectInFolderList getChildren(CallContext callContext,
@@ -74,31 +75,37 @@ public ObjectInFolderList getChildren(CallContext callContext,
String renditionFilter, Boolean includePathSegments,
BigInteger maxItems, BigInteger skipCount,
ExtensionsData extension, Holder parentObjectData) {
- // //////////////////
- // General Exception
- // //////////////////
- exceptionService.invalidArgumentRequiredString("folderId", folderId);
- Folder folder = contentService.getFolder(repositoryId, folderId);
- exceptionService.permissionDenied(callContext,
- repositoryId, PermissionMapping.CAN_GET_CHILDREN_FOLDER, folder);
- // //////////////////
- // Specific Exception
- // //////////////////
- exceptionService.invalidArgumentFolderId(folder, folderId);
-
- // //////////////////
- // Body of the method
- // //////////////////
- // Set ObjectData of parent folder for ObjectInfo
- ObjectData _parent = compileService.compileObjectData(
- callContext, repositoryId, folder, filter,
- includeAllowableActions, includeRelationships, renditionFilter, false);
- parentObjectData.setValue(_parent);
-
- return getChildrenInternal(callContext, repositoryId, folderId, filter,
- orderBy, includeAllowableActions, includeRelationships,
- renditionFilter, includePathSegments, maxItems, skipCount, false);
+ exceptionService.invalidArgumentRequiredString("folderId", folderId);
+
+ Lock parentLock = threadLockService.getReadLock(repositoryId, folderId);
+
+ try{
+ parentLock.lock();
+
+ // //////////////////
+ // General Exception
+ // //////////////////
+ Folder folder = contentService.getFolder(repositoryId, folderId);
+ exceptionService.invalidArgumentFolderId(folder, folderId);
+ exceptionService.permissionDenied(callContext,
+ repositoryId, PermissionMapping.CAN_GET_CHILDREN_FOLDER, folder);
+
+ // //////////////////
+ // Body of the method
+ // //////////////////
+ // Set ObjectData of parent folder for ObjectInfo
+ ObjectData _parent = compileService.compileObjectData(
+ callContext, repositoryId, folder, filter,
+ includeAllowableActions, includeRelationships, renditionFilter, false);
+ parentObjectData.setValue(_parent);
+
+ return getChildrenInternal(callContext, repositoryId, folderId, filter,
+ orderBy, includeAllowableActions, includeRelationships,
+ renditionFilter, includePathSegments, maxItems, skipCount, false);
+ }finally{
+ parentLock.unlock();
+ }
}
private ObjectInFolderList getChildrenInternal(CallContext callContext,
@@ -115,34 +122,41 @@ private ObjectInFolderList getChildrenInternal(CallContext callContext,
// Build ObjectList
List contents = contentService.getChildren(repositoryId, folderId);
-
- contents = permissionService.getFiltered(callContext, repositoryId, contents);
-
- ObjectList ol = compileService.compileObjectDataList(callContext,
- repositoryId, contents, filter,
- includeAllowableActions, includeRelationships, renditionFilter, false,
- maxItems, skipCount, folderOnly);
-
- // Sort
- sortUtil.sort(repositoryId, ol.getObjects(), orderBy);
-
- // Build ObjectInFolderList
- for (ObjectData od : ol.getObjects()) {
- ObjectInFolderDataImpl objectInFolder = new ObjectInFolderDataImpl();
- objectInFolder.setObject(od);
- if (includePathSegments) {
- String name = DataUtil.getStringProperty(od.getProperties(),
- PropertyIds.NAME);
- objectInFolder.setPathSegment(name);
+
+
+ List locks = threadLockService.readLocks(repositoryId, contents);
+
+ try{
+ threadLockService.bulkLock(locks);
+
+ contents = permissionService.getFiltered(callContext, repositoryId, contents);
+
+ ObjectList ol = compileService.compileObjectDataList(callContext,
+ repositoryId, contents, filter,
+ includeAllowableActions, includeRelationships, renditionFilter, false,
+ maxItems, skipCount, folderOnly, orderBy);
+
+
+ // Build ObjectInFolderList
+ for (ObjectData od : ol.getObjects()) {
+ ObjectInFolderDataImpl objectInFolder = new ObjectInFolderDataImpl();
+ objectInFolder.setObject(od);
+ if (includePathSegments) {
+ String name = DataUtil.getStringProperty(od.getProperties(),
+ PropertyIds.NAME);
+ objectInFolder.setPathSegment(name);
+ }
+ result.getObjects().add(objectInFolder);
}
- result.getObjects().add(objectInFolder);
- }
- result.setNumItems(ol.getNumItems());
- result.setHasMoreItems(ol.hasMoreItems());
+ result.setNumItems(ol.getNumItems());
+ result.setHasMoreItems(ol.hasMoreItems());
- return result;
+ return result;
+ }finally{
+ threadLockService.bulkUnlock(locks);
+ }
}
-
+
@Override
public List getDescendants(
CallContext callContext, String repositoryId, String folderId,
@@ -151,41 +165,51 @@ public List getDescendants(
String renditionFilter, Boolean includePathSegment,
boolean foldersOnly, ExtensionsData extension, Holder anscestorObjectData) {
- // //////////////////
- // General Exception
- // //////////////////
exceptionService.invalidArgumentRequiredString("folderId", folderId);
- Folder folder = contentService.getFolder(repositoryId, folderId);
- exceptionService.permissionDenied(callContext,
- repositoryId, PermissionMapping.CAN_GET_DESCENDENTS_FOLDER, folder);
-
- // //////////////////
- // Specific Exception
- // //////////////////
- exceptionService.invalidArgumentFolderId(folder, folderId);
- exceptionService.invalidArgumentDepth(depth);
-
- // //////////////////
- // Body of the method
- // //////////////////
- // check depth
- int d = (depth == null ? 2 : depth.intValue());
-
- // set defaults if values not set
- boolean iaa = (includeAllowableActions == null ? false
- : includeAllowableActions.booleanValue());
- boolean ips = (includePathSegment == null ? false : includePathSegment
- .booleanValue());
-
- // Set ObjectData of the starting folder for ObjectInfo
- ObjectData _folder = compileService.compileObjectData(
- callContext, repositoryId, folder, filter,
- includeAllowableActions, includeRelationships, renditionFilter, false);
- anscestorObjectData.setValue(_folder);
-
- // get the tree.
- return getDescendantsInternal(callContext, repositoryId, _folder, filter, iaa,
- false, includeRelationships, null, ips, 0, d, foldersOnly);
+
+ Lock parentLock = threadLockService.getReadLock(repositoryId, folderId);
+
+ try{
+ parentLock.lock();
+
+ // //////////////////
+ // General Exception
+ // //////////////////
+ Folder folder = contentService.getFolder(repositoryId, folderId);
+ exceptionService.permissionDenied(callContext,
+ repositoryId, PermissionMapping.CAN_GET_DESCENDENTS_FOLDER, folder);
+
+ // //////////////////
+ // Specific Exception
+ // //////////////////
+ exceptionService.invalidArgumentFolderId(folder, folderId);
+ exceptionService.invalidArgumentDepth(depth);
+
+ // //////////////////
+ // Body of the method
+ // //////////////////
+ // check depth
+ int d = (depth == null ? 2 : depth.intValue());
+
+ // set defaults if values not set
+ boolean iaa = (includeAllowableActions == null ? false
+ : includeAllowableActions.booleanValue());
+ boolean ips = (includePathSegment == null ? false : includePathSegment
+ .booleanValue());
+
+ // Set ObjectData of the starting folder for ObjectInfo
+ ObjectData _folder = compileService.compileObjectData(
+ callContext, repositoryId, folder, filter,
+ includeAllowableActions, includeRelationships, renditionFilter, false);
+ anscestorObjectData.setValue(_folder);
+
+ // get the tree.
+ return getDescendantsInternal(callContext, repositoryId, _folder, filter, iaa,
+ false, includeRelationships, null, ips, 0, d, foldersOnly);
+
+ }finally{
+ parentLock.unlock();
+ }
}
private List getDescendantsInternal(
@@ -234,27 +258,46 @@ private List getDescendantsInternal(
@Override
public ObjectData getFolderParent(CallContext callContext, String repositoryId,
String folderId, String filter) {
- // //////////////////
- // General Exception
- // //////////////////
+
exceptionService.invalidArgumentRequiredString("folderId", folderId);
- Folder folder = (Folder) contentService.getContent(repositoryId, folderId);
- exceptionService.objectNotFound(DomainType.OBJECT, folder, folderId);
- exceptionService.permissionDenied(callContext,
- repositoryId, PermissionMapping.CAN_GET_FOLDER_PARENT_OBJECT, folder);
-
- // //////////////////
- // Specific Exception
- // //////////////////
- Folder parent = contentService.getParent(repositoryId, folderId);
- exceptionService.objectNotFoundParentFolder(repositoryId, folderId, parent);
- exceptionService.invalidArgumentRootFolder(repositoryId, folder);
-
- // //////////////////
- // Body of the method
- // //////////////////
- return compileService.compileObjectData(callContext, repositoryId,
- parent, filter, true, IncludeRelationships.NONE, null, true);
+
+ Lock childLock = threadLockService.getReadLock(repositoryId, folderId);
+
+ try{
+ childLock.lock();
+
+ // //////////////////
+ // General Exception
+ // //////////////////
+ Folder folder = (Folder) contentService.getContent(repositoryId, folderId);
+ exceptionService.objectNotFound(DomainType.OBJECT, folder, folderId);
+ exceptionService.permissionDenied(callContext,
+ repositoryId, PermissionMapping.CAN_GET_FOLDER_PARENT_OBJECT, folder);
+
+ // //////////////////
+ // Specific Exception
+ // //////////////////
+ Folder parent = contentService.getParent(repositoryId, folderId);
+
+ Lock parentLock = threadLockService.getReadLock(repositoryId, parent.getId());
+ try{
+ parentLock.lock();
+
+ exceptionService.objectNotFoundParentFolder(repositoryId, folderId, parent);
+ exceptionService.invalidArgumentRootFolder(repositoryId, folder);
+
+ // //////////////////
+ // Body of the method
+ // //////////////////
+ return compileService.compileObjectData(callContext, repositoryId,
+ parent, filter, true, IncludeRelationships.NONE, null, true);
+
+ }finally{
+ parentLock.unlock();
+ }
+ }finally{
+ childLock.unlock();
+ }
}
@Override
@@ -262,37 +305,59 @@ public List getObjectParents(CallContext callContext,
String repositoryId, String objectId, String filter,
Boolean includeAllowableActions, IncludeRelationships includeRelationships,
String renditionFilter, Boolean includeRelativePathSegment, ExtensionsData extension) {
- // //////////////////
- // General Exception
- // //////////////////
- exceptionService.invalidArgumentRequired("objectId", objectId);
- Content content = contentService.getContent(repositoryId, objectId);
- exceptionService.objectNotFound(DomainType.OBJECT, content, objectId);
- exceptionService.permissionDenied(callContext,
- repositoryId, PermissionMapping.CAN_GET_PARENTS_FOLDER, content);
- // //////////////////
- // Specific Exception
- // //////////////////
- Folder parent = contentService.getParent(repositoryId, objectId);
- exceptionService.objectNotFoundParentFolder(repositoryId, objectId, parent);
- exceptionService.invalidArgumentRootFolder(repositoryId, content);
+ exceptionService.invalidArgumentRequired("objectId", objectId);
+
+ Lock childLock = threadLockService.getReadLock(repositoryId, objectId);
+
+ try{
+ childLock.lock();
+
+ // //////////////////
+ // General Exception
+ // //////////////////
+ Content content = contentService.getContent(repositoryId, objectId);
+ exceptionService.objectNotFound(DomainType.OBJECT, content, objectId);
+ exceptionService.permissionDenied(callContext,
+ repositoryId, PermissionMapping.CAN_GET_PARENTS_FOLDER, content);
+
+
+ //Get parent
+ Folder parent = contentService.getParent(repositoryId, objectId);
+ Lock parentLock = threadLockService.getReadLock(repositoryId, parent.getId());
+
+ try{
+ parentLock.lock();
+
+ // //////////////////
+ // Specific Exception
+ // //////////////////
+ exceptionService.objectNotFoundParentFolder(repositoryId, objectId, parent);
+ exceptionService.invalidArgumentRootFolder(repositoryId, content);
+
+ // //////////////////
+ // Body of the method
+ // //////////////////
+ ObjectParentDataImpl result = new ObjectParentDataImpl();
+ ObjectData o = compileService.compileObjectData(callContext,
+ repositoryId, parent, filter, includeAllowableActions,
+ includeRelationships, null, true);
+ result.setObject(o);
+ boolean irps = (includeRelativePathSegment == null ? false
+ : includeRelativePathSegment.booleanValue());
+ if (irps) {
+ result.setRelativePathSegment(content.getName());
+ }
- // //////////////////
- // Body of the method
- // //////////////////
- ObjectParentDataImpl result = new ObjectParentDataImpl();
- ObjectData o = compileService.compileObjectData(callContext,
- repositoryId, parent, filter, includeAllowableActions,
- includeRelationships, null, true);
- result.setObject(o);
- boolean irps = (includeRelativePathSegment == null ? false
- : includeRelativePathSegment.booleanValue());
- if (irps) {
- result.setRelativePathSegment(content.getName());
+ return Collections.singletonList((ObjectParentData) result);
+
+ }finally{
+ parentLock.unlock();
+ }
+
+ }finally{
+ childLock.unlock();
}
-
- return Collections.singletonList((ObjectParentData) result);
}
@Override
@@ -322,14 +387,22 @@ public ObjectList getCheckedOutDocs(CallContext callContext,
//Folder ID can be null, which means all PWCs are returned.
List checkedOuts = contentService.getCheckedOutDocs(repositoryId,
folderId, orderBy, extension);
-
- ObjectList list = compileService.compileObjectDataList(
- callContext, repositoryId, checkedOuts, filter,
- includeAllowableActions, includeRelationships, renditionFilter, false,
- maxItems, skipCount, false);
- sortUtil.sort(repositoryId, list.getObjects(), orderBy);
-
- return list;
+
+ List locks = threadLockService.readLocks(repositoryId, checkedOuts);
+
+ try{
+ threadLockService.bulkLock(locks);
+
+ ObjectList list = compileService.compileObjectDataList(
+ callContext, repositoryId, checkedOuts, filter,
+ includeAllowableActions, includeRelationships, renditionFilter, false,
+ maxItems, skipCount, false, orderBy);
+
+ return list;
+
+ }finally{
+ threadLockService.bulkUnlock(locks);
+ }
}
public void setContentService(ContentService contentService) {
@@ -348,9 +421,7 @@ public void setPermissionService(PermissionService permissionService) {
this.permissionService = permissionService;
}
-
- public void setSortUtil(SortUtil sortUtil) {
- this.sortUtil = sortUtil;
+ public void setThreadLockService(ThreadLockService threadLockService) {
+ this.threadLockService = threadLockService;
}
-
}
diff --git a/core/src/main/java/jp/aegif/nemaki/cmis/service/impl/ObjectServiceImpl.java b/core/src/main/java/jp/aegif/nemaki/cmis/service/impl/ObjectServiceImpl.java
index 2c21255a..d8fe7360 100644
--- a/core/src/main/java/jp/aegif/nemaki/cmis/service/impl/ObjectServiceImpl.java
+++ b/core/src/main/java/jp/aegif/nemaki/cmis/service/impl/ObjectServiceImpl.java
@@ -34,6 +34,8 @@
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
import org.apache.chemistry.opencmis.commons.PropertyIds;
import org.apache.chemistry.opencmis.commons.data.Acl;
@@ -82,9 +84,9 @@
import jp.aegif.nemaki.model.Rendition;
import jp.aegif.nemaki.model.VersionSeries;
import jp.aegif.nemaki.util.DataUtil;
-import jp.aegif.nemaki.util.PropertyManager;
import jp.aegif.nemaki.util.cache.NemakiCachePool;
import jp.aegif.nemaki.util.constant.DomainType;
+import jp.aegif.nemaki.util.lock.ThreadLockService;
public class ObjectServiceImpl implements ObjectService {
@@ -98,6 +100,7 @@ public class ObjectServiceImpl implements ObjectService {
private CompileService compileService;
private SolrUtil solrUtil;
private NemakiCachePool nemakiCachePool;
+ private ThreadLockService threadLockService;
private int threadMax;
@Override
@@ -112,17 +115,25 @@ public ObjectData getObjectByPath(CallContext callContext, String repositoryId,
exceptionService.invalidArgumentRequired("objectId", path);
// FIXME path is not preserved in db.
Content content = contentService.getContentByPath(repositoryId, path);
- // TODO create objectNotFoundByPath method
- exceptionService.objectNotFound(DomainType.OBJECT, content, path);
- exceptionService.permissionDenied(callContext,
- repositoryId, PermissionMapping.CAN_GET_PROPERTIES_OBJECT, content);
- // //////////////////
- // Body of the method
- // //////////////////
- return compileService.compileObjectData(callContext, repositoryId,
- content, filter, includeAllowableActions,
- includeRelationships, renditionFilter, includeAcl);
+ Lock lock = threadLockService.getReadLock(repositoryId, content.getId());
+ try{
+ lock.lock();
+
+ // TODO create objectNotFoundByPath method
+ exceptionService.objectNotFound(DomainType.OBJECT, content, path);
+ exceptionService.permissionDenied(callContext,
+ repositoryId, PermissionMapping.CAN_GET_PROPERTIES_OBJECT, content);
+
+ // //////////////////
+ // Body of the method
+ // //////////////////
+ return compileService.compileObjectData(callContext, repositoryId,
+ content, filter, includeAllowableActions,
+ includeRelationships, renditionFilter, includeAcl);
+ }finally{
+ lock.unlock();
+ }
}
@Override
@@ -131,54 +142,72 @@ public ObjectData getObject(CallContext callContext, String repositoryId,
Boolean includeAllowableActions, IncludeRelationships includeRelationships,
String renditionFilter, Boolean includePolicyIds,
Boolean includeAcl, ExtensionsData extension) {
- // //////////////////
- // General Exception
- // //////////////////
+
exceptionService.invalidArgumentRequired("objectId", objectId);
- Content content = contentService.getContent(repositoryId, objectId);
- // WORK AROUND: getObject(versionSeriesId) is interpreted as
- // getDocumentOflatestVersion
- if (content == null) {
- VersionSeries versionSeries = contentService
- .getVersionSeries(repositoryId, objectId);
- if (versionSeries != null) {
- content = contentService.getDocumentOfLatestVersion(repositoryId, objectId);
+
+ Lock lock = threadLockService.getReadLock(repositoryId, objectId);
+ try{
+ lock.lock();
+
+ // //////////////////
+ // General Exception
+ // //////////////////
+ Content content = contentService.getContent(repositoryId, objectId);
+ // WORK AROUND: getObject(versionSeriesId) is interpreted as
+ // getDocumentOflatestVersion
+ if (content == null) {
+ VersionSeries versionSeries = contentService
+ .getVersionSeries(repositoryId, objectId);
+ if (versionSeries != null) {
+ content = contentService.getDocumentOfLatestVersion(repositoryId, objectId);
+ }
}
+ exceptionService.objectNotFound(DomainType.OBJECT, content, objectId);
+ exceptionService.permissionDenied(callContext,
+ repositoryId, PermissionMapping.CAN_GET_PROPERTIES_OBJECT, content);
+
+ // //////////////////
+ // Body of the method
+ // //////////////////
+ ObjectData object = compileService.compileObjectData(callContext,
+ repositoryId, content, filter, includeAllowableActions,
+ includeRelationships, null, includeAcl);
+
+ return object;
+ }finally{
+ lock.unlock();
}
- exceptionService.objectNotFound(DomainType.OBJECT, content, objectId);
- exceptionService.permissionDenied(callContext,
- repositoryId, PermissionMapping.CAN_GET_PROPERTIES_OBJECT, content);
-
- // //////////////////
- // Body of the method
- // //////////////////
- ObjectData object = compileService.compileObjectData(callContext,
- repositoryId, content, filter, includeAllowableActions,
- includeRelationships, null, includeAcl);
-
- return object;
}
@Override
public ContentStream getContentStream(CallContext callContext,
String repositoryId, String objectId, String streamId,
BigInteger offset, BigInteger length) {
- // //////////////////
- // General Exception
- // //////////////////
- exceptionService.invalidArgumentRequired("objectId", objectId);
- Content content = contentService.getContent(repositoryId, objectId);
- exceptionService.objectNotFound(DomainType.OBJECT, content, objectId);
- exceptionService.permissionDenied(callContext,
- repositoryId, PermissionMapping.CAN_GET_PROPERTIES_OBJECT, content);
- // //////////////////
- // Body of the method
- // //////////////////
- if (streamId == null) {
- return getContentStreamInternal(repositoryId, content, offset, length);
- } else {
- return getRenditionStream(repositoryId, content, streamId);
+ exceptionService.invalidArgumentRequired("objectId", objectId);
+
+ Lock lock = threadLockService.getReadLock(repositoryId, objectId);
+ try{
+ lock.lock();
+
+ // //////////////////
+ // General Exception
+ // //////////////////
+ Content content = contentService.getContent(repositoryId, objectId);
+ exceptionService.objectNotFound(DomainType.OBJECT, content, objectId);
+ exceptionService.permissionDenied(callContext,
+ repositoryId, PermissionMapping.CAN_GET_PROPERTIES_OBJECT, content);
+
+ // //////////////////
+ // Body of the method
+ // //////////////////
+ if (streamId == null) {
+ return getContentStreamInternal(repositoryId, content, offset, length);
+ } else {
+ return getRenditionStream(repositoryId, content, streamId);
+ }
+ }finally{
+ lock.unlock();
}
}
@@ -231,37 +260,56 @@ private ContentStream getRenditionStream(String repositoryId, Content content, S
public List getRenditions(CallContext callContext,
String repositoryId, String objectId, String renditionFilter,
BigInteger maxItems, BigInteger skipCount, ExtensionsData extension) {
- List renditions = contentService.getRenditions(repositoryId, objectId);
-
- List results = new ArrayList();
- for (Rendition rnd : renditions) {
- RenditionDataImpl data = new RenditionDataImpl(rnd.getId(),
- rnd.getMimetype(), BigInteger.valueOf(rnd.getLength()),
- rnd.getKind(), rnd.getTitle(), BigInteger.valueOf(rnd
- .getWidth()), BigInteger.valueOf(rnd.getHeight()),
- rnd.getRenditionDocumentId());
- results.add(data);
+
+ Lock lock = threadLockService.getReadLock(repositoryId, objectId);
+ try{
+ lock.lock();
+
+ List renditions = contentService.getRenditions(repositoryId, objectId);
+
+ List results = new ArrayList();
+ for (Rendition rnd : renditions) {
+ RenditionDataImpl data = new RenditionDataImpl(rnd.getId(),
+ rnd.getMimetype(), BigInteger.valueOf(rnd.getLength()),
+ rnd.getKind(), rnd.getTitle(), BigInteger.valueOf(rnd
+ .getWidth()), BigInteger.valueOf(rnd.getHeight()),
+ rnd.getRenditionDocumentId());
+ results.add(data);
+ }
+ return results;
+ }finally{
+ lock.unlock();
}
- return results;
}
@Override
public AllowableActions getAllowableActions(CallContext callContext,
String repositoryId, String objectId) {
- // //////////////////
- // General Exception
- // //////////////////
- exceptionService.invalidArgumentRequired("objectId", objectId);
- Content content = contentService.getContent(repositoryId, objectId);
- exceptionService.objectNotFound(DomainType.OBJECT, content, objectId);
- // NOTE: The permission key doesn't exist according to CMIS
- // specification.
- // //////////////////
- // Body of the method
- // //////////////////
- return compileService.compileAllowableActions(callContext,
- repositoryId, content);
+ exceptionService.invalidArgumentRequired("objectId", objectId);
+
+ Lock lock = threadLockService.getReadLock(repositoryId, objectId);
+
+ try{
+ lock.lock();
+
+ // //////////////////
+ // General Exception
+ // //////////////////
+ Content content = contentService.getContent(repositoryId, objectId);
+ exceptionService.objectNotFound(DomainType.OBJECT, content, objectId);
+ // NOTE: The permission key doesn't exist according to CMIS
+ // specification.
+
+ // //////////////////
+ // Body of the method
+ // //////////////////
+ return compileService.compileAllowableActions(callContext,
+ repositoryId, content);
+
+ }finally{
+ lock.unlock();
+ }
}
@Override
@@ -448,71 +496,88 @@ public String createDocumentFromSource(CallContext callContext,
public void setContentStream(CallContext callContext,
String repositoryId, Holder objectId,
boolean overwriteFlag, ContentStream contentStream, Holder changeToken) {
- // //////////////////
- // General Exception
- // //////////////////
- String id = objectId.getValue();
-
- exceptionService.invalidArgumentRequiredString("objectId", id);
- exceptionService
- .invalidArgumentRequired("contentStream", contentStream);
- Document doc = (Document) contentService.getContent(repositoryId, id);
- exceptionService.objectNotFound(DomainType.OBJECT, doc, id);
- exceptionService.permissionDenied(callContext,
- repositoryId, PermissionMapping.CAN_SET_CONTENT_DOCUMENT, doc);
- DocumentTypeDefinition td = (DocumentTypeDefinition) typeManager
- .getTypeDefinition(repositoryId, doc.getObjectType());
- exceptionService.constraintImmutable(repositoryId, doc, td);
-
- // //////////////////
- // Specific Exception
- // //////////////////
- exceptionService.contentAlreadyExists(doc, overwriteFlag);
- exceptionService.streamNotSupported(td, contentStream);
- exceptionService.updateConflict(doc, changeToken);
- exceptionService.versioning(doc);
- Folder parent = contentService.getParent(repositoryId, id);
- exceptionService.objectNotFoundParentFolder(repositoryId, id, parent);
+
+ exceptionService.invalidArgumentRequiredHolderString("objectId", objectId);
+
+ Lock lock = threadLockService.getWriteLock(repositoryId, objectId.getValue());
+ try{
+ lock.lock();
+ // //////////////////
+ // General Exception
+ // //////////////////
+
+ exceptionService
+ .invalidArgumentRequired("contentStream", contentStream);
+ Document doc = (Document) contentService.getContent(repositoryId, objectId.getValue());
+ exceptionService.objectNotFound(DomainType.OBJECT, doc, objectId.getValue());
+ exceptionService.permissionDenied(callContext,
+ repositoryId, PermissionMapping.CAN_SET_CONTENT_DOCUMENT, doc);
+ DocumentTypeDefinition td = (DocumentTypeDefinition) typeManager
+ .getTypeDefinition(repositoryId, doc.getObjectType());
+ exceptionService.constraintImmutable(repositoryId, doc, td);
+
+ // //////////////////
+ // Specific Exception
+ // //////////////////
+ exceptionService.contentAlreadyExists(doc, overwriteFlag);
+ exceptionService.streamNotSupported(td, contentStream);
+ exceptionService.updateConflict(doc, changeToken);
+ exceptionService.versioning(doc);
+ Folder parent = contentService.getParent(repositoryId, objectId.getValue());
+ exceptionService.objectNotFoundParentFolder(repositoryId, objectId.getValue(), parent);
+
+ // //////////////////
+ // Body of the method
+ // //////////////////
+ String oldId = objectId.getValue();
+
+ // TODO Externalize versioningState
+ if(doc.isPrivateWorkingCopy()){
+ Document result = contentService.replacePwc(callContext, repositoryId,
+ doc, contentStream);
+ objectId.setValue(result.getId());
+ }else{
+ Document result = contentService.createDocumentWithNewStream(callContext, repositoryId,
+ doc, contentStream);
+ objectId.setValue(result.getId());
+ }
- // //////////////////
- // Body of the method
- // //////////////////
- String oldId = objectId.getValue();
-
- // TODO Externalize versioningState
- if(doc.isPrivateWorkingCopy()){
- Document result = contentService.replacePwc(callContext, repositoryId,
- doc, contentStream);
- objectId.setValue(result.getId());
- }else{
- Document result = contentService.createDocumentWithNewStream(callContext, repositoryId,
- doc, contentStream);
- objectId.setValue(result.getId());
+ nemakiCachePool.get(repositoryId).removeCmisCache(oldId);
+ }finally{
+ lock.unlock();
}
-
- nemakiCachePool.get(repositoryId).removeCmisCache(oldId);
}
@Override
public void deleteContentStream(CallContext callContext,
String repositoryId, Holder objectId,
Holder changeToken, ExtensionsData extension) {
- // //////////////////
- // Exception
- // //////////////////
+
exceptionService.invalidArgumentRequiredHolderString("objectId",
objectId);
- Document document = contentService.getDocument(repositoryId, objectId.getValue());
- exceptionService.objectNotFound(DomainType.OBJECT, document,
- document.getId());
- exceptionService.constraintContentStreamRequired(repositoryId, document);
-
- // //////////////////
- // Body of the method
- // //////////////////
- contentService.deleteContentStream(callContext, repositoryId, objectId);
-
- nemakiCachePool.get(repositoryId).removeCmisCache(objectId.getValue());
+
+ Lock lock = threadLockService.getWriteLock(repositoryId, objectId.getValue());
+ try{
+ lock.lock();
+
+ // //////////////////
+ // Exception
+ // //////////////////
+ Document document = contentService.getDocument(repositoryId, objectId.getValue());
+ exceptionService.objectNotFound(DomainType.OBJECT, document,
+ document.getId());
+ exceptionService.constraintContentStreamRequired(repositoryId, document);
+
+ // //////////////////
+ // Body of the method
+ // //////////////////
+ contentService.deleteContentStream(callContext, repositoryId, objectId);
+
+ nemakiCachePool.get(repositoryId).removeCmisCache(objectId.getValue());
+
+ }finally{
+ lock.unlock();
+ }
}
@Override
@@ -520,36 +585,44 @@ public void appendContentStream(CallContext callContext,
String repositoryId, Holder objectId,
Holder changeToken, ContentStream contentStream,
boolean isLastChunk, ExtensionsData extension) {
- // //////////////////
- // General Exception
- // //////////////////
- String id = objectId.getValue();
-
- exceptionService.invalidArgumentRequiredString("objectId", id);
- exceptionService
- .invalidArgumentRequired("contentStream", contentStream);
- Document doc = (Document) contentService.getContent(repositoryId, id);
- exceptionService.objectNotFound(DomainType.OBJECT, doc, id);
- exceptionService.permissionDenied(callContext,
- repositoryId, PermissionMapping.CAN_SET_CONTENT_DOCUMENT, doc);
- DocumentTypeDefinition td = (DocumentTypeDefinition) typeManager
- .getTypeDefinition(repositoryId, doc.getObjectType());
- exceptionService.constraintImmutable(repositoryId, doc, td);
-
- // //////////////////
- // Specific Exception
- // //////////////////
- exceptionService.streamNotSupported(td, contentStream);
- exceptionService.updateConflict(doc, changeToken);
- exceptionService.versioning(doc);
-
- // //////////////////
- // Body of the method
- // //////////////////
- contentService.appendAttachment(callContext, repositoryId, objectId,
- changeToken, contentStream, isLastChunk, extension);
-
- nemakiCachePool.get(repositoryId).removeCmisCache(objectId.getValue());
+
+ exceptionService.invalidArgumentRequiredHolderString("objectId", objectId);
+
+ Lock lock = threadLockService.getWriteLock(repositoryId, objectId.getValue());
+ try{
+ lock.lock();
+
+ // //////////////////
+ // General Exception
+ // //////////////////
+
+ exceptionService
+ .invalidArgumentRequired("contentStream", contentStream);
+ Document doc = (Document) contentService.getContent(repositoryId, objectId.getValue());
+ exceptionService.objectNotFound(DomainType.OBJECT, doc, objectId.getValue());
+ exceptionService.permissionDenied(callContext,
+ repositoryId, PermissionMapping.CAN_SET_CONTENT_DOCUMENT, doc);
+ DocumentTypeDefinition td = (DocumentTypeDefinition) typeManager
+ .getTypeDefinition(repositoryId, doc.getObjectType());
+ exceptionService.constraintImmutable(repositoryId, doc, td);
+
+ // //////////////////
+ // Specific Exception
+ // //////////////////
+ exceptionService.streamNotSupported(td, contentStream);
+ exceptionService.updateConflict(doc, changeToken);
+ exceptionService.versioning(doc);
+
+ // //////////////////
+ // Body of the method
+ // //////////////////
+ contentService.appendAttachment(callContext, repositoryId, objectId,
+ changeToken, contentStream, isLastChunk, extension);
+
+ nemakiCachePool.get(repositoryId).removeCmisCache(objectId.getValue());
+ }finally{
+ lock.unlock();
+ }
}
@Override
@@ -683,21 +756,29 @@ public String createItem(CallContext callContext, String repositoryId,
public void updateProperties(CallContext callContext,
String repositoryId, Holder objectId,
Properties properties, Holder changeToken) {
-
- // //////////////////
- // Exception
- // //////////////////
- Content content = checkExceptionBeforeUpdateProperties(callContext,
- repositoryId, objectId, properties, changeToken);
-
- // //////////////////
- // Body of the method
- // //////////////////
- String id = objectId.getValue();
-
- contentService.updateProperties(callContext, repositoryId, properties, content);
-
- nemakiCachePool.get(repositoryId).removeCmisCache(id);
+
+ exceptionService.invalidArgumentRequiredHolderString("objectId",
+ objectId);
+
+ Lock lock = threadLockService.getWriteLock(repositoryId, objectId.getValue());
+ try{
+ lock.lock();
+
+ // //////////////////
+ // Exception
+ // //////////////////
+ Content content = checkExceptionBeforeUpdateProperties(callContext,
+ repositoryId, objectId, properties, changeToken);
+
+ // //////////////////
+ // Body of the method
+ // //////////////////
+ contentService.updateProperties(callContext, repositoryId, properties, content);
+
+ nemakiCachePool.get(repositoryId).removeCmisCache(objectId.getValue());
+ }finally{
+ lock.unlock();
+ }
}
private Content checkExceptionBeforeUpdateProperties(
@@ -706,8 +787,6 @@ private Content checkExceptionBeforeUpdateProperties(
// //////////////////
// General Exception
// //////////////////
- exceptionService.invalidArgumentRequiredHolderString("objectId",
- objectId);
exceptionService.invalidArgumentRequiredCollection("properties",
properties.getPropertyList());
Content content = contentService.getContent(repositoryId, objectId.getValue());
@@ -737,14 +816,14 @@ private Content checkExceptionBeforeUpdateProperties(
public List bulkUpdateProperties(
CallContext callContext,
String repositoryId,
- List objectIdAndChangeToken, Properties properties,
+ List objectIdAndChangeTokenList, Properties properties,
List addSecondaryTypeIds, List removeSecondaryTypeIds, ExtensionsData extension) {
// //////////////////
// General Exception
// //////////////////
// Each permission is checked at each execution
exceptionService.invalidArgumentRequiredCollection(
- "objectIdAndChangeToken", objectIdAndChangeToken);
+ "objectIdAndChangeToken", objectIdAndChangeTokenList);
exceptionService.invalidArgumentSecondaryTypeIds(repositoryId, properties);
// //////////////////
@@ -752,67 +831,132 @@ public List bulkUpdateProperties(
// //////////////////
List results = new ArrayList();
- for (BulkUpdateObjectIdAndChangeToken idAndToken : objectIdAndChangeToken) {
+ ExecutorService executor = Executors.newCachedThreadPool();
+ List tasks = new ArrayList<>();
+ for (BulkUpdateObjectIdAndChangeToken objectIdAndChangeToken : objectIdAndChangeTokenList) {
+ tasks.add(new BulkUpdateTask(callContext, repositoryId, objectIdAndChangeToken, properties, addSecondaryTypeIds, removeSecondaryTypeIds, extension));
+ }
+
+ try {
+ List> _results = executor.invokeAll(tasks);
+ for(Future _result : _results){
+ try{
+ BulkUpdateObjectIdAndChangeToken result = _result.get();
+ results.add(result);
+ }catch(Exception e){
+ //TODO log
+ //do nothing
+ }
+ }
+ } catch (InterruptedException e1) {
+ //TODO log
+ e1.printStackTrace();
+ }
+
+ return results;
+ }
+
+ private class BulkUpdateTask implements Callable{
+
+ private CallContext callContext;
+ private String repositoryId;
+ private BulkUpdateObjectIdAndChangeToken objectIdAndChangeToken;
+ private Properties properties;
+ private List addSecondaryTypeIds;
+ private List removeSecondaryTypeIds;
+ private ExtensionsData extension;
+
+ public BulkUpdateTask(CallContext callContext, String repositoryId, BulkUpdateObjectIdAndChangeToken objectIdAndChangeToken,
+ Properties properties, List addSecondaryTypeIds, List removeSecondaryTypeIds,
+ ExtensionsData extension) {
+ super();
+ this.callContext = callContext;
+ this.repositoryId = repositoryId;
+ this.objectIdAndChangeToken = objectIdAndChangeToken;
+ this.properties = properties;
+ this.addSecondaryTypeIds = addSecondaryTypeIds;
+ this.removeSecondaryTypeIds = removeSecondaryTypeIds;
+ this.extension = extension;
+ }
+
+ @Override
+ public BulkUpdateObjectIdAndChangeToken call() throws Exception {
+ exceptionService.invalidArgumentRequiredString("objectId",
+ objectIdAndChangeToken.getId());
+
+ Lock lock = threadLockService.getWriteLock(repositoryId, objectIdAndChangeToken.getId());
try {
+ lock.lock();
+
Content content = checkExceptionBeforeUpdateProperties(
callContext, repositoryId,
- new Holder(idAndToken.getId()),
- properties, new Holder(idAndToken.getChangeToken()));
+ new Holder(objectIdAndChangeToken.getId()),
+ properties, new Holder(objectIdAndChangeToken.getChangeToken()));
contentService.updateProperties(callContext, repositoryId,
properties, content);
nemakiCachePool.get(repositoryId).removeCmisCache(content.getId());
BulkUpdateObjectIdAndChangeToken result = new BulkUpdateObjectIdAndChangeTokenImpl(
- idAndToken.getId(), content.getId(),
+ objectIdAndChangeToken.getId(), content.getId(),
String.valueOf(content.getChangeToken()));
- results.add(result);
+ return result;
} catch (Exception e) {
// Don't throw an error
// Don't return any BulkUpdateObjectIdAndChangetoken
+ }finally{
+ lock.unlock();
}
+
+ // TODO Auto-generated method stub
+ return null;
}
-
- return results;
+
}
-
+
@Override
public void moveObject(CallContext callContext, String repositoryId,
Holder objectId, String sourceFolderId, String targetFolderId) {
- // //////////////////
- // General Exception
- // //////////////////
+
exceptionService.invalidArgumentRequiredHolderString("objectId",
objectId);
- exceptionService.invalidArgumentRequiredString("sourceFolderId",
- sourceFolderId);
- exceptionService.invalidArgumentRequiredString("targetFolderId",
- targetFolderId);
- Content content = contentService.getContent(repositoryId, objectId.getValue());
- exceptionService.objectNotFound(DomainType.OBJECT, content,
- objectId.getValue());
- Folder source = contentService.getFolder(repositoryId, sourceFolderId);
- exceptionService.objectNotFound(DomainType.OBJECT, source,
- sourceFolderId);
- Folder target = contentService.getFolder(repositoryId, targetFolderId);
- exceptionService.objectNotFound(DomainType.OBJECT, target,
- targetFolderId);
- exceptionService.permissionDenied(callContext,
- repositoryId, PermissionMapping.CAN_MOVE_OBJECT, content);
- exceptionService.permissionDenied(callContext,
- repositoryId, PermissionMapping.CAN_MOVE_SOURCE, source);
- exceptionService.permissionDenied(callContext,
- repositoryId, PermissionMapping.CAN_MOVE_TARGET, target);
+
+ Lock lock = threadLockService.getWriteLock(repositoryId, objectId.getValue());
+ try{
+ lock.lock();
+ // //////////////////
+ // General Exception
+ // //////////////////
+ exceptionService.invalidArgumentRequiredString("sourceFolderId",
+ sourceFolderId);
+ exceptionService.invalidArgumentRequiredString("targetFolderId",
+ targetFolderId);
+ Content content = contentService.getContent(repositoryId, objectId.getValue());
+ exceptionService.objectNotFound(DomainType.OBJECT, content,
+ objectId.getValue());
+ Folder source = contentService.getFolder(repositoryId, sourceFolderId);
+ exceptionService.objectNotFound(DomainType.OBJECT, source,
+ sourceFolderId);
+ Folder target = contentService.getFolder(repositoryId, targetFolderId);
+ exceptionService.objectNotFound(DomainType.OBJECT, target,
+ targetFolderId);
+ exceptionService.permissionDenied(callContext,
+ repositoryId, PermissionMapping.CAN_MOVE_OBJECT, content);
+ exceptionService.permissionDenied(callContext,
+ repositoryId, PermissionMapping.CAN_MOVE_SOURCE, source);
+ exceptionService.permissionDenied(callContext,
+ repositoryId, PermissionMapping.CAN_MOVE_TARGET, target);
- // //////////////////
- // Body of the method
- // //////////////////
- contentService.move(repositoryId, content, target);
+ // //////////////////
+ // Body of the method
+ // //////////////////
+ contentService.move(repositoryId, content, target);
- nemakiCachePool.get(repositoryId).removeCmisCache(content.getId());
+ nemakiCachePool.get(repositoryId).removeCmisCache(content.getId());
+ }finally{
+ lock.unlock();
+ }
}
-
-
@Override
public void deleteObject(CallContext callContext, String repositoryId,
String objectId, Boolean allVersions) {
@@ -1002,6 +1146,10 @@ public void setNemakiCachePool(NemakiCachePool nemakiCachePool) {
this.nemakiCachePool = nemakiCachePool;
}
+ public void setThreadLockService(ThreadLockService threadLockService) {
+ this.threadLockService = threadLockService;
+ }
+
public void setThreadMax(int threadMax) {
this.threadMax = threadMax;
}
diff --git a/core/src/main/java/jp/aegif/nemaki/cmis/service/impl/ObjectServiceInternalImpl.java b/core/src/main/java/jp/aegif/nemaki/cmis/service/impl/ObjectServiceInternalImpl.java
index b316b973..155b0c67 100644
--- a/core/src/main/java/jp/aegif/nemaki/cmis/service/impl/ObjectServiceInternalImpl.java
+++ b/core/src/main/java/jp/aegif/nemaki/cmis/service/impl/ObjectServiceInternalImpl.java
@@ -1,6 +1,7 @@
package jp.aegif.nemaki.cmis.service.impl;
import java.util.List;
+import java.util.concurrent.locks.Lock;
import org.apache.chemistry.opencmis.commons.data.PermissionMapping;
import org.apache.chemistry.opencmis.commons.server.CallContext;
@@ -13,6 +14,7 @@
import jp.aegif.nemaki.model.Content;
import jp.aegif.nemaki.util.cache.NemakiCachePool;
import jp.aegif.nemaki.util.constant.DomainType;
+import jp.aegif.nemaki.util.lock.ThreadLockService;
public class ObjectServiceInternalImpl implements jp.aegif.nemaki.cmis.service.ObjectServiceInternal{
private static final Log log = LogFactory
@@ -20,6 +22,7 @@ public class ObjectServiceInternalImpl implements jp.aegif.nemaki.cmis.service.O
private ContentService contentService;
private ExceptionService exceptionService;
+ private ThreadLockService threadLockService;
private NemakiCachePool nemakiCachePool;
@Override
@@ -33,47 +36,59 @@ public void deleteObjectInternal(CallContext callContext, String repositoryId,
@Override
public void deleteObjectInternal(CallContext callContext, String repositoryId,
Content content, Boolean allVersions, Boolean deleteWithParent) {
- // //////////////////
- // General Exception
- // //////////////////
- String objectId = content.getId();
- exceptionService.objectNotFound(DomainType.OBJECT, content, objectId);
- exceptionService.permissionDenied(callContext,
- repositoryId, PermissionMapping.CAN_DELETE_OBJECT, content);
- exceptionService.constraintDeleteRootFolder(repositoryId, objectId);
+
+ Lock lock = threadLockService.getWriteLock(repositoryId, content.getId());
+
+ try{
+ lock.lock();
+
+ // //////////////////
+ // General Exception
+ // //////////////////
+ String objectId = content.getId();
+ exceptionService.permissionDenied(callContext,
+ repositoryId, PermissionMapping.CAN_DELETE_OBJECT, content);
+ exceptionService.constraintDeleteRootFolder(repositoryId, objectId);
- // //////////////////
- // Body of the method
- // //////////////////
- if (content.isDocument()) {
- contentService.deleteDocument(callContext, repositoryId,
- content.getId(), allVersions, deleteWithParent);
- } else if (content.isFolder()) {
- List children = contentService.getChildren(repositoryId, objectId);
- if (!CollectionUtils.isEmpty(children)) {
- exceptionService
- .constraint(objectId,
- "deleteObject method is invoked on a folder containing objects.");
+ exceptionService.objectNotFound(DomainType.OBJECT, content, objectId);
+
+ // //////////////////
+ // Body of the method
+ // //////////////////
+ if (content.isDocument()) {
+ contentService.deleteDocument(callContext, repositoryId,
+ content.getId(), allVersions, deleteWithParent);
+ } else if (content.isFolder()) {
+ List children = contentService.getChildren(repositoryId, objectId);
+ if (!CollectionUtils.isEmpty(children)) {
+ exceptionService
+ .constraint(objectId,
+ "deleteObject method is invoked on a folder containing objects.");
+ }
+ contentService.delete(callContext, repositoryId, objectId, deleteWithParent);
+
+ } else {
+ contentService.delete(callContext, repositoryId, objectId, deleteWithParent);
}
- contentService.delete(callContext, repositoryId, objectId, deleteWithParent);
- } else {
- contentService.delete(callContext, repositoryId, objectId, deleteWithParent);
+ nemakiCachePool.get(repositoryId).removeCmisCache(content.getId());
+ }finally{
+ lock.unlock();
}
-
- nemakiCachePool.get(repositoryId).removeCmisCache(content.getId());
}
-
public void setContentService(ContentService contentService) {
this.contentService = contentService;
}
-
public void setExceptionService(ExceptionService exceptionService) {
this.exceptionService = exceptionService;
}
+ public void setThreadLockService(ThreadLockService threadLockService) {
+ this.threadLockService = threadLockService;
+ }
+
public void setNemakiCachePool(NemakiCachePool nemakiCachePool) {
this.nemakiCachePool = nemakiCachePool;
}
diff --git a/core/src/main/java/jp/aegif/nemaki/cmis/service/impl/PolicyServiceImpl.java b/core/src/main/java/jp/aegif/nemaki/cmis/service/impl/PolicyServiceImpl.java
index 4ac35e8d..7269ce05 100644
--- a/core/src/main/java/jp/aegif/nemaki/cmis/service/impl/PolicyServiceImpl.java
+++ b/core/src/main/java/jp/aegif/nemaki/cmis/service/impl/PolicyServiceImpl.java
@@ -23,6 +23,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.locks.Lock;
import jp.aegif.nemaki.businesslogic.ContentService;
import jp.aegif.nemaki.cmis.aspect.CompileService;
@@ -31,9 +32,9 @@
import jp.aegif.nemaki.cmis.service.PolicyService;
import jp.aegif.nemaki.model.Content;
import jp.aegif.nemaki.model.Policy;
-import jp.aegif.nemaki.util.cache.NemakiCache;
import jp.aegif.nemaki.util.cache.NemakiCachePool;
import jp.aegif.nemaki.util.constant.DomainType;
+import jp.aegif.nemaki.util.lock.ThreadLockService;
import org.apache.chemistry.opencmis.commons.data.ExtensionsData;
import org.apache.chemistry.opencmis.commons.data.ObjectData;
@@ -49,65 +50,91 @@ public class PolicyServiceImpl implements PolicyService {
private CompileService compileService;
private ExceptionService exceptionService;
private TypeManager typeManager;
+ private ThreadLockService threadLockService;
private NemakiCachePool nemakiCachePool;
@Override
public void applyPolicy(CallContext callContext, String repositoryId,
String policyId, String objectId, ExtensionsData extension) {
- // //////////////////
- // General Exception
- // //////////////////
exceptionService.invalidArgumentRequiredString("objectId", objectId);
exceptionService.invalidArgumentRequiredString("policyId", policyId);
- Content content = contentService.getContent(repositoryId, objectId);
- exceptionService.objectNotFound(DomainType.OBJECT, content, objectId);
- exceptionService.permissionDenied(callContext,
- repositoryId, PermissionMapping.CAN_ADD_POLICY_OBJECT, content);
- Policy policy = contentService.getPolicy(repositoryId, policyId);
- exceptionService.objectNotFound(DomainType.OBJECT, policy, policyId);
- exceptionService.permissionDenied(callContext,
- repositoryId, PermissionMapping.CAN_ADD_POLICY_POLICY, policy);
-
- // //////////////////
- // Specific Exception
- // //////////////////
- TypeDefinition td = typeManager.getTypeDefinition(repositoryId, content);
- if (!td.isControllablePolicy())
- exceptionService
- .constraint(objectId,
- "appyPolicy cannot be performed on the object whose controllablePolicy = false");
-
- // //////////////////
- // Body of the method
- // //////////////////
- contentService.applyPolicy(callContext, repositoryId, policyId, objectId, extension);
- nemakiCachePool.get(repositoryId).removeCmisCache(objectId);
+ Lock objectLock = threadLockService.getWriteLock(repositoryId, objectId);
+ Lock policyLock = threadLockService.getReadLock(repositoryId, policyId);
+ try{
+ objectLock.lock();
+ policyLock.lock();
+
+ // //////////////////
+ // General Exception
+ // //////////////////
+ Content content = contentService.getContent(repositoryId, objectId);
+ exceptionService.objectNotFound(DomainType.OBJECT, content, objectId);
+ exceptionService.permissionDenied(callContext,
+ repositoryId, PermissionMapping.CAN_ADD_POLICY_OBJECT, content);
+ Policy policy = contentService.getPolicy(repositoryId, policyId);
+ exceptionService.objectNotFound(DomainType.OBJECT, policy, policyId);
+ exceptionService.permissionDenied(callContext,
+ repositoryId, PermissionMapping.CAN_ADD_POLICY_POLICY, policy);
+
+ // //////////////////
+ // Specific Exception
+ // //////////////////
+ TypeDefinition td = typeManager.getTypeDefinition(repositoryId, content);
+ if (!td.isControllablePolicy())
+ exceptionService
+ .constraint(objectId,
+ "appyPolicy cannot be performed on the object whose controllablePolicy = false");
+
+ // //////////////////
+ // Body of the method
+ // //////////////////
+ contentService.applyPolicy(callContext, repositoryId, policyId, objectId, extension);
+
+ nemakiCachePool.get(repositoryId).removeCmisCache(objectId);
+
+ }finally{
+ objectLock.unlock();
+ policyLock.unlock();
+ }
}
@Override
public void removePolicy(CallContext callContext, String repositoryId,
String policyId, String objectId, ExtensionsData extension) {
- // //////////////////
- // General Exception
- // //////////////////
+
exceptionService.invalidArgumentRequiredString("objectId", objectId);
exceptionService.invalidArgumentRequiredString("policyId", policyId);
- Content content = contentService.getContent(repositoryId, objectId);
- exceptionService.objectNotFound(DomainType.OBJECT, content, objectId);
- exceptionService.permissionDenied(callContext,
- repositoryId, PermissionMapping.CAN_REMOVE_POLICY_OBJECT, content);
- Policy policy = contentService.getPolicy(repositoryId, policyId);
- exceptionService.objectNotFound(DomainType.OBJECT, policy, policyId);
- exceptionService.permissionDenied(callContext,
- repositoryId, PermissionMapping.CAN_REMOVE_POLICY_POLICY, policy);
- // //////////////////
- // Body of the method
- // //////////////////
- contentService.removePolicy(callContext, repositoryId, policyId, objectId, extension);
-
- nemakiCachePool.get(repositoryId).removeCmisCache(objectId);
+ Lock objectLock = threadLockService.getWriteLock(repositoryId, objectId);
+ Lock policyLock = threadLockService.getReadLock(repositoryId, policyId);
+ try{
+ objectLock.lock();
+ policyLock.lock();
+
+ // //////////////////
+ // General Exception
+ // //////////////////
+ Content content = contentService.getContent(repositoryId, objectId);
+ exceptionService.objectNotFound(DomainType.OBJECT, content, objectId);
+ exceptionService.permissionDenied(callContext,
+ repositoryId, PermissionMapping.CAN_REMOVE_POLICY_OBJECT, content);
+ Policy policy = contentService.getPolicy(repositoryId, policyId);
+ exceptionService.objectNotFound(DomainType.OBJECT, policy, policyId);
+ exceptionService.permissionDenied(callContext,
+ repositoryId, PermissionMapping.CAN_REMOVE_POLICY_POLICY, policy);
+
+ // //////////////////
+ // Body of the method
+ // //////////////////
+ contentService.removePolicy(callContext, repositoryId, policyId, objectId, extension);
+
+ nemakiCachePool.get(repositoryId).removeCmisCache(objectId);
+
+ }finally{
+ objectLock.unlock();
+ policyLock.unlock();
+ }
}
@Override
@@ -127,15 +154,24 @@ public List getAppliedPolicies(CallContext callContext,
// //////////////////
List policies = contentService.getAppliedPolicies(repositoryId,
objectId, extension);
- List objects = new ArrayList();
- if (!CollectionUtils.isEmpty(policies)) {
- for (Policy policy : policies) {
- objects.add(compileService.compileObjectData(callContext,
- repositoryId, policy, filter, true, IncludeRelationships.NONE,
- null, true));
+
+ List locks = threadLockService.readLocks(repositoryId, policies);
+ try{
+ threadLockService.bulkLock(locks);
+
+ List objects = new ArrayList();
+ if (!CollectionUtils.isEmpty(policies)) {
+ for (Policy policy : policies) {
+ objects.add(compileService.compileObjectData(callContext,
+ repositoryId, policy, filter, true, IncludeRelationships.NONE,
+ null, true));
+ }
}
+ return objects;
+
+ }finally{
+ threadLockService.bulkUnlock(locks);
}
- return objects;
}
public void setContentService(ContentService contentService) {
@@ -158,6 +194,10 @@ public void setTypeManager(TypeManager typeManager) {
this.typeManager = typeManager;
}
+ public void setThreadLockService(ThreadLockService threadLockService) {
+ this.threadLockService = threadLockService;
+ }
+
public void setNemakiCachePool(NemakiCachePool nemakiCachePool) {
this.nemakiCachePool = nemakiCachePool;
}
diff --git a/core/src/main/java/jp/aegif/nemaki/cmis/service/impl/RelationshipServiceImpl.java b/core/src/main/java/jp/aegif/nemaki/cmis/service/impl/RelationshipServiceImpl.java
index 4e55b398..368a24a8 100644
--- a/core/src/main/java/jp/aegif/nemaki/cmis/service/impl/RelationshipServiceImpl.java
+++ b/core/src/main/java/jp/aegif/nemaki/cmis/service/impl/RelationshipServiceImpl.java
@@ -26,6 +26,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.locks.Lock;
import jp.aegif.nemaki.businesslogic.ContentService;
import jp.aegif.nemaki.cmis.aspect.CompileService;
@@ -35,6 +36,7 @@
import jp.aegif.nemaki.model.Content;
import jp.aegif.nemaki.model.Relationship;
import jp.aegif.nemaki.util.constant.DomainType;
+import jp.aegif.nemaki.util.lock.ThreadLockService;
import org.apache.chemistry.opencmis.commons.data.ExtensionsData;
import org.apache.chemistry.opencmis.commons.data.ObjectList;
@@ -49,6 +51,7 @@ public class RelationshipServiceImpl implements RelationshipService {
private ContentService contentService;
private CompileService compileService;
private ExceptionService exceptionService;
+ private ThreadLockService threadLockService;
@Override
public ObjectList getObjectRelationships(CallContext callContext,
@@ -56,53 +59,63 @@ public ObjectList getObjectRelationships(CallContext callContext,
Boolean includeSubRelationshipTypes, RelationshipDirection relationshipDirection,
String typeId, String filter,
Boolean includeAllowableActions, BigInteger maxItems, BigInteger skipCount, ExtensionsData extension) {
- // //////////////////
- // General Exception
- // //////////////////
+
exceptionService.invalidArgumentRequiredString("objectId", objectId);
- Content content = contentService.getContent(repositoryId, objectId);
- exceptionService.objectNotFound(DomainType.OBJECT, content, objectId);
- exceptionService.permissionDenied(callContext,
- repositoryId, PermissionMapping.CAN_GET_OBJECT_RELATIONSHIPS_OBJECT, content);
+
+ Lock lock = threadLockService.getReadLock(repositoryId, objectId);
+ try{
+ lock.lock();
+
+ // //////////////////
+ // General Exception
+ // //////////////////
+
+ Content content = contentService.getContent(repositoryId, objectId);
+ exceptionService.objectNotFound(DomainType.OBJECT, content, objectId);
+ exceptionService.permissionDenied(callContext,
+ repositoryId, PermissionMapping.CAN_GET_OBJECT_RELATIONSHIPS_OBJECT, content);
- // //////////////////
- // Body of the method
- // //////////////////
- // Set default
- relationshipDirection = (relationshipDirection == null) ? RelationshipDirection.SOURCE
- : relationshipDirection;
+ // //////////////////
+ // Body of the method
+ // //////////////////
+ // Set default
+ relationshipDirection = (relationshipDirection == null) ? RelationshipDirection.SOURCE
+ : relationshipDirection;
- List rels = contentService.getRelationsipsOfObject(
- repositoryId, objectId, relationshipDirection);
+ List rels = contentService.getRelationsipsOfObject(
+ repositoryId, objectId, relationshipDirection);
- // Filtering results
- List extracted = new ArrayList();
- if (typeId != null) {
- Set typeIds = new HashSet();
- typeIds.add(typeId);
+ // Filtering results
+ List extracted = new ArrayList();
+ if (typeId != null) {
+ Set typeIds = new HashSet();
+ typeIds.add(typeId);
- if (includeSubRelationshipTypes) {
- List descendants = typeManager
- .getTypesDescendants(repositoryId, typeId,
- BigInteger.valueOf(-1), false);
- for (TypeDefinitionContainer tdc : descendants) {
- typeIds.add(tdc.getTypeDefinition().getId());
+ if (includeSubRelationshipTypes) {
+ List descendants = typeManager
+ .getTypesDescendants(repositoryId, typeId,
+ BigInteger.valueOf(-1), false);
+ for (TypeDefinitionContainer tdc : descendants) {
+ typeIds.add(tdc.getTypeDefinition().getId());
+ }
}
- }
- for (Relationship rel : rels) {
- if (typeIds.contains(rel.getId())) {
- extracted.add(rel);
+ for (Relationship rel : rels) {
+ if (typeIds.contains(rel.getId())) {
+ extracted.add(rel);
+ }
}
+ } else {
+ extracted = rels;
}
- } else {
- extracted = rels;
- }
- // Compile to ObjectData
- return compileService.compileObjectDataList(callContext,
- repositoryId, extracted, filter,
- includeAllowableActions, IncludeRelationships.NONE, null, false, maxItems, skipCount, false);
+ // Compile to ObjectData
+ return compileService.compileObjectDataList(callContext,
+ repositoryId, extracted, filter,
+ includeAllowableActions, IncludeRelationships.NONE, null, false, maxItems, skipCount, false, null);
+ }finally{
+ lock.unlock();
+ }
}
public void setTypeManager(TypeManager typeManager) {
@@ -120,4 +133,8 @@ public void setCompileService(CompileService compileService) {
public void setExceptionService(ExceptionService exceptionService) {
this.exceptionService = exceptionService;
}
+
+ public void setThreadLockService(ThreadLockService threadLockService) {
+ this.threadLockService = threadLockService;
+ }
}
diff --git a/core/src/main/java/jp/aegif/nemaki/cmis/service/impl/VersioningServiceImpl.java b/core/src/main/java/jp/aegif/nemaki/cmis/service/impl/VersioningServiceImpl.java
index dacfa62c..9f88ebfd 100644
--- a/core/src/main/java/jp/aegif/nemaki/cmis/service/impl/VersioningServiceImpl.java
+++ b/core/src/main/java/jp/aegif/nemaki/cmis/service/impl/VersioningServiceImpl.java
@@ -26,6 +26,7 @@
import java.util.Comparator;
import java.util.GregorianCalendar;
import java.util.List;
+import java.util.concurrent.locks.Lock;
import jp.aegif.nemaki.businesslogic.ContentService;
import jp.aegif.nemaki.cmis.aspect.CompileService;
@@ -36,6 +37,7 @@
import jp.aegif.nemaki.model.VersionSeries;
import jp.aegif.nemaki.util.cache.NemakiCachePool;
import jp.aegif.nemaki.util.constant.DomainType;
+import jp.aegif.nemaki.util.lock.ThreadLockService;
import org.apache.chemistry.opencmis.commons.data.Acl;
import org.apache.chemistry.opencmis.commons.data.ContentStream;
@@ -53,6 +55,7 @@ public class VersioningServiceImpl implements VersioningService {
private CompileService compileService;
private ExceptionService exceptionService;
private NemakiCachePool nemakiCachePool;
+ private ThreadLockService threadLockService;
@Override
/**
@@ -60,65 +63,86 @@ public class VersioningServiceImpl implements VersioningService {
*/
public void checkOut(CallContext callContext, String repositoryId,
Holder objectId, ExtensionsData extension, Holder contentCopied) {
- // //////////////////
- // General Exception
- // //////////////////
- String id = objectId.getValue();
- exceptionService.invalidArgumentRequiredString("objectId", id);
- Document document = contentService.getDocument(repositoryId, id);
- exceptionService.objectNotFound(DomainType.OBJECT, document, id);
- exceptionService.permissionDenied(callContext,
- repositoryId, PermissionMapping.CAN_CHECKOUT_DOCUMENT, document);
- // //////////////////
- // Specific Exception
- // //////////////////
- // CMIS doesn't define the error type when checkOut is performed
- // repeatedly
- exceptionService.constraintAlreadyCheckedOut(repositoryId, document);
- exceptionService.constraintVersionable(repositoryId, document.getObjectType());
- exceptionService.versioning(document);
-
- // //////////////////
- // Body of the method
- // //////////////////
- Document pwc = contentService.checkOut(callContext, repositoryId, id, extension);
- objectId.setValue(pwc.getId());
- Holder copied = new Holder(true);
- contentCopied = copied;
+ exceptionService.invalidArgumentRequiredHolderString("objectId", objectId);
+ String originalId = objectId.getValue();
+
+ Lock lock = threadLockService.getWriteLock(repositoryId, objectId.getValue());
- nemakiCachePool.get(repositoryId).removeCmisCache(id);
+ try{
+ lock.lock();
+ // //////////////////
+ // General Exception
+ // //////////////////
+ Document document = contentService.getDocument(repositoryId, objectId.getValue());
+ exceptionService.objectNotFound(DomainType.OBJECT, document, objectId.getValue());
+ exceptionService.permissionDenied(callContext,
+ repositoryId, PermissionMapping.CAN_CHECKOUT_DOCUMENT, document);
+
+ // //////////////////
+ // Specific Exception
+ // //////////////////
+ // CMIS doesn't define the error type when checkOut is performed
+ // repeatedly
+ exceptionService.constraintAlreadyCheckedOut(repositoryId, document);
+ exceptionService.constraintVersionable(repositoryId, document.getObjectType());
+ exceptionService.versioning(document);
+
+ // //////////////////
+ // Body of the method
+ // //////////////////
+ Document pwc = contentService.checkOut(callContext, repositoryId, objectId.getValue(), extension);
+ objectId.setValue(pwc.getId());
+ Holder copied = new Holder(true);
+ contentCopied = copied;
+
+ nemakiCachePool.get(repositoryId).removeCmisCache(originalId);
+ }finally{
+ lock.unlock();
+ }
}
@Override
public void cancelCheckOut(CallContext callContext, String repositoryId,
String objectId, ExtensionsData extension) {
- // //////////////////
- // General Exception
- // //////////////////
+
exceptionService.invalidArgumentRequiredString("objectId", objectId);
- Document document = contentService.getDocument(repositoryId, objectId);
- exceptionService.objectNotFound(DomainType.OBJECT, document, objectId);
- exceptionService.permissionDenied(callContext,
- repositoryId, PermissionMapping.CAN_CHECKIN_DOCUMENT, document);
+
+ Lock lock = threadLockService.getWriteLock(repositoryId, objectId);
+
+ try{
+ lock.lock();
+
+ // //////////////////
+ // General Exception
+ // //////////////////
+ Document document = contentService.getDocument(repositoryId, objectId);
+ exceptionService.objectNotFound(DomainType.OBJECT, document, objectId);
+ exceptionService.permissionDenied(callContext,
+ repositoryId, PermissionMapping.CAN_CHECKIN_DOCUMENT, document);
- // //////////////////
- // Specific Exception
- // //////////////////
- exceptionService.constraintVersionable(repositoryId, document.getObjectType());
+ // //////////////////
+ // Specific Exception
+ // //////////////////
+ exceptionService.constraintVersionable(repositoryId, document.getObjectType());
- // //////////////////
- // Body of the method
- // //////////////////
- contentService.cancelCheckOut(callContext, repositoryId, objectId, extension);
+ // //////////////////
+ // Body of the method
+ // //////////////////
+ contentService.cancelCheckOut(callContext, repositoryId, objectId, extension);
- //remove cache
- nemakiCachePool.get(repositoryId).removeCmisCache(objectId);
- Document latest = contentService.getDocumentOfLatestVersion(repositoryId, document.getVersionSeriesId());
- //Latest document does not exit when pwc is created as the first version
- if(latest != null){
- nemakiCachePool.get(repositoryId).removeCmisCache(latest.getId());
+ //remove cache
+ nemakiCachePool.get(repositoryId).removeCmisCache(objectId);
+ Document latest = contentService.getDocumentOfLatestVersion(repositoryId, document.getVersionSeriesId());
+ //Latest document does not exit when pwc is created as the first version
+ if(latest != null){
+ nemakiCachePool.get(repositoryId).removeCmisCache(latest.getId());
+ }
+ }finally{
+ lock.unlock();
}
+
+
}
@Override
@@ -126,33 +150,50 @@ public void checkIn(CallContext callContext, String repositoryId,
Holder objectId, Boolean major, Properties properties,
ContentStream contentStream, String checkinComment, List policies,
Acl addAces, Acl removeAces, ExtensionsData extension) {
- // //////////////////
- // General Exception
- // //////////////////
- String id = objectId.getValue();
- exceptionService.invalidArgumentRequiredString("objectId", id);
- Document document = contentService.getDocument(repositoryId, id);
- exceptionService.objectNotFound(DomainType.OBJECT, document, id);
- exceptionService.permissionDenied(callContext,
- repositoryId, PermissionMapping.CAN_CANCEL_CHECKOUT_DOCUMENT, document);
- // //////////////////
- // Specific Exception
- // //////////////////
- exceptionService.constraintVersionable(repositoryId, document.getObjectType());
- // TODO implement
- // exceptionService.streamNotSupported(documentTypeDefinition,
- // contentStream);
-
- // //////////////////
- // Body of the method
- // //////////////////
- Document checkedIn = contentService.checkIn(callContext, repositoryId,
- objectId, major, properties, contentStream, checkinComment,
- policies, addAces, removeAces, extension);
- objectId.setValue(checkedIn.getId());
+ exceptionService.invalidArgumentRequiredHolderString("objectId", objectId);
+ final String pwcId = objectId.getValue();
+
+ Lock lock = threadLockService.getWriteLock(repositoryId, objectId.getValue());
- nemakiCachePool.get(repositoryId).removeCmisCache(id);
+ try{
+ lock.lock();
+
+ // //////////////////
+ // General Exception
+ // //////////////////
+
+ Document pwc = contentService.getDocument(repositoryId, objectId.getValue());
+ exceptionService.objectNotFound(DomainType.OBJECT, pwc, objectId.getValue());
+ exceptionService.permissionDenied(callContext,
+ repositoryId, PermissionMapping.CAN_CANCEL_CHECKOUT_DOCUMENT, pwc);
+
+ // //////////////////
+ // Specific Exception
+ // //////////////////
+ exceptionService.constraintVersionable(repositoryId, pwc.getObjectType());
+ // TODO implement
+ // exceptionService.streamNotSupported(documentTypeDefinition,
+ // contentStream);
+
+ // //////////////////
+ // Body of the method
+ // //////////////////
+ Document latest = contentService
+ .getDocumentOfLatestVersion(repositoryId, pwc.getVersionSeriesId());
+
+ Document checkedIn = contentService.checkIn(callContext, repositoryId,
+ objectId, major, properties, contentStream, checkinComment,
+ policies, addAces, removeAces, extension);
+ objectId.setValue(checkedIn.getId());
+
+ nemakiCachePool.get(repositoryId).removeCmisCache(pwc.getId());
+ if(latest != null){
+ nemakiCachePool.get(repositoryId).removeCmisCache(latest.getId());
+ }
+ }finally{
+ lock.unlock();
+ }
}
@Override
@@ -183,18 +224,28 @@ public ObjectData getObjectOfLatestVersion(CallContext context,
document = contentService
.getDocumentOfLatestVersion(repositoryId, versionSeriesId);
}
- exceptionService.objectNotFound(DomainType.OBJECT, document,
- versionSeriesId);
- exceptionService.permissionDenied(context,
- repositoryId, PermissionMapping.CAN_GET_PROPERTIES_OBJECT, document);
+
+ Lock lock = threadLockService.getReadLock(repositoryId, document.getId());
+
+ try{
+ lock.lock();
+
+ exceptionService.objectNotFound(DomainType.OBJECT, document,
+ versionSeriesId);
+ exceptionService.permissionDenied(context,
+ repositoryId, PermissionMapping.CAN_GET_PROPERTIES_OBJECT, document);
- // //////////////////
- // Body of the method
- // //////////////////
- ObjectData objectData = compileService.compileObjectData(context,
- repositoryId, document, filter,
- includeAllowableActions, includeRelationships, renditionFilter, includeAcl);
- return objectData;
+ // //////////////////
+ // Body of the method
+ // //////////////////
+ ObjectData objectData = compileService.compileObjectData(context,
+ repositoryId, document, filter,
+ includeAllowableActions, includeRelationships, renditionFilter, includeAcl);
+ return objectData;
+
+ }finally{
+ lock.unlock();
+ }
}
@Override
@@ -217,34 +268,38 @@ public List getAllVersions(CallContext context,
.getAllVersions(context, repositoryId, versionSeriesId);
exceptionService.objectNotFoundVersionSeries(versionSeriesId,
allVersions);
- // Sort by the descending order
- Collections.sort(allVersions, new VersionComparator());
-
- //Permissions filter
- /*exceptionService.permissionDenied(context,
- PermissionMapping.CAN_GET_ALL_VERSIONS_VERSION_SERIES,
- allVersions.get(0));
- */
- Document latest = allVersions.get(0);
- if(latest.isPrivateWorkingCopy()){
- VersionSeries vs = contentService.getVersionSeries(repositoryId, latest);
- if(!context.getUsername().equals(vs.getVersionSeriesCheckedOutBy())){
- allVersions.remove(latest);
- }
- }
- // //////////////////
- // Body of the method
- // //////////////////
- List result = new ArrayList();
- for (Content content : allVersions) {
- ObjectData objectData = compileService.compileObjectData(
- context, repositoryId, content, filter,
- includeAllowableActions, IncludeRelationships.NONE, null, true);
- result.add(objectData);
- }
+ List locks = threadLockService.readLocks(repositoryId, allVersions);
+ try{
+ threadLockService.bulkLock(locks);
+
+ // Sort by the descending order
+ Collections.sort(allVersions, new VersionComparator());
+
+ Document latest = allVersions.get(0);
+ if(latest.isPrivateWorkingCopy()){
+ VersionSeries vs = contentService.getVersionSeries(repositoryId, latest);
+ if(!context.getUsername().equals(vs.getVersionSeriesCheckedOutBy())){
+ allVersions.remove(latest);
+ }
+ }
+
+ // //////////////////
+ // Body of the method
+ // //////////////////
+ List result = new ArrayList();
+ for (Content content : allVersions) {
+ ObjectData objectData = compileService.compileObjectData(
+ context, repositoryId, content, filter,
+ includeAllowableActions, IncludeRelationships.NONE, null, true);
+ result.add(objectData);
+ }
- return result;
+ return result;
+
+ }finally{
+ threadLockService.bulkUnlock(locks);
+ }
}
/**
@@ -284,4 +339,8 @@ public void setExceptionService(ExceptionService exceptionService) {
public void setNemakiCachePool(NemakiCachePool nemakiCachePool) {
this.nemakiCachePool = nemakiCachePool;
}
+
+ public void setThreadLockService(ThreadLockService threadLockService) {
+ this.threadLockService = threadLockService;
+ }
}
diff --git a/core/src/main/java/jp/aegif/nemaki/dao/ContentDaoService.java b/core/src/main/java/jp/aegif/nemaki/dao/ContentDaoService.java
index 97ec084c..2907e953 100644
--- a/core/src/main/java/jp/aegif/nemaki/dao/ContentDaoService.java
+++ b/core/src/main/java/jp/aegif/nemaki/dao/ContentDaoService.java
@@ -271,7 +271,7 @@ public interface ContentDaoService {
*
* @return
*/
- List getLatestChildrenIndex(String repositoryId, String parentId);
+ List getChildren(String repositoryId, String parentId);
/**
* Get a child content by name
@@ -407,6 +407,8 @@ public interface ContentDaoService {
* @return the newly updated document
*/
Document update(String repositoryId, Document document);
+
+ Document move(String repositoryId, Document document, String sourceId);
/**
* Update a version series
@@ -425,6 +427,8 @@ public interface ContentDaoService {
* @return the newly updated folder
*/
Folder update(String repositoryId, Folder folder);
+
+ Folder move(String repositoryId, Folder folder, String sourceId);
/**
* Update a relationship
@@ -640,6 +644,8 @@ public interface ContentDaoService {
*/
void deleteArchive(String repositoryId, String archiveId);
+ void refreshCmisObjectData(String repositoryId, String objectId);
+
/**
* Restore a content from its archive
* @param repositoryId TODO
diff --git a/core/src/main/java/jp/aegif/nemaki/dao/impl/cached/ContentDaoServiceImpl.java b/core/src/main/java/jp/aegif/nemaki/dao/impl/cached/ContentDaoServiceImpl.java
index da2d0407..d36c3a62 100644
--- a/core/src/main/java/jp/aegif/nemaki/dao/impl/cached/ContentDaoServiceImpl.java
+++ b/core/src/main/java/jp/aegif/nemaki/dao/impl/cached/ContentDaoServiceImpl.java
@@ -21,6 +21,10 @@
******************************************************************************/
package jp.aegif.nemaki.dao.impl.cached;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.GregorianCalendar;
import java.util.List;
import jp.aegif.nemaki.dao.ContentDaoService;
@@ -39,11 +43,11 @@
import jp.aegif.nemaki.model.Relationship;
import jp.aegif.nemaki.model.Rendition;
import jp.aegif.nemaki.model.VersionSeries;
-import jp.aegif.nemaki.util.cache.NemakiCache;
import jp.aegif.nemaki.util.cache.NemakiCachePool;
+import jp.aegif.nemaki.util.cache.model.NemakiCache;
+import jp.aegif.nemaki.util.cache.model.Tree;
import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;
-
import org.apache.chemistry.opencmis.commons.data.ContentStream;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
@@ -59,11 +63,11 @@ public class ContentDaoServiceImpl implements ContentDaoService {
private ContentDaoService nonCachedContentDaoService;
private NemakiCachePool nemakiCachePool;
-
+
private final String TOKEN_CACHE_LATEST_CHANGE_TOKEN = "lc";
public ContentDaoServiceImpl() {
-
+
}
// ///////////////////////////////////////
@@ -78,8 +82,7 @@ public List getTypeDefinitions(String repositoryId) {
return (List) v.getObjectValue();
}
- List result = nonCachedContentDaoService
- .getTypeDefinitions(repositoryId);
+ List result = nonCachedContentDaoService.getTypeDefinitions(repositoryId);
if (CollectionUtils.isEmpty(result)) {
return null;
@@ -111,20 +114,16 @@ public NemakiTypeDefinition getTypeDefinition(String repositoryId, String typeId
}
@Override
- public NemakiTypeDefinition createTypeDefinition(
- String repositoryId, NemakiTypeDefinition typeDefinition) {
- NemakiTypeDefinition nt = nonCachedContentDaoService
- .createTypeDefinition(repositoryId, typeDefinition);
+ public NemakiTypeDefinition createTypeDefinition(String repositoryId, NemakiTypeDefinition typeDefinition) {
+ NemakiTypeDefinition nt = nonCachedContentDaoService.createTypeDefinition(repositoryId, typeDefinition);
Cache typeCache = nemakiCachePool.get(repositoryId).getTypeCache();
typeCache.remove("typedefs");
return nt;
}
@Override
- public NemakiTypeDefinition updateTypeDefinition(
- String repositoryId, NemakiTypeDefinition typeDefinition) {
- NemakiTypeDefinition nt = nonCachedContentDaoService
- .updateTypeDefinition(repositoryId, typeDefinition);
+ public NemakiTypeDefinition updateTypeDefinition(String repositoryId, NemakiTypeDefinition typeDefinition) {
+ NemakiTypeDefinition nt = nonCachedContentDaoService.updateTypeDefinition(repositoryId, typeDefinition);
Cache typeCache = nemakiCachePool.get(repositoryId).getTypeCache();
typeCache.remove("typedefs");
return nt;
@@ -149,44 +148,38 @@ public NemakiPropertyDefinitionCore getPropertyDefinitionCore(String repositoryI
}
@Override
- public NemakiPropertyDefinitionCore getPropertyDefinitionCoreByPropertyId(
- String repositoryId, String propertyId) {
- return nonCachedContentDaoService
- .getPropertyDefinitionCoreByPropertyId(repositoryId, propertyId);
+ public NemakiPropertyDefinitionCore getPropertyDefinitionCoreByPropertyId(String repositoryId, String propertyId) {
+ return nonCachedContentDaoService.getPropertyDefinitionCoreByPropertyId(repositoryId, propertyId);
}
@Override
- public NemakiPropertyDefinitionDetail getPropertyDefinitionDetail(
- String repositoryId, String nodeId) {
+ public NemakiPropertyDefinitionDetail getPropertyDefinitionDetail(String repositoryId, String nodeId) {
return nonCachedContentDaoService.getPropertyDefinitionDetail(repositoryId, nodeId);
}
@Override
- public List getPropertyDefinitionDetailByCoreNodeId(
- String repositoryId, String coreNodeId) {
- return nonCachedContentDaoService
- .getPropertyDefinitionDetailByCoreNodeId(repositoryId, coreNodeId);
+ public List getPropertyDefinitionDetailByCoreNodeId(String repositoryId,
+ String coreNodeId) {
+ return nonCachedContentDaoService.getPropertyDefinitionDetailByCoreNodeId(repositoryId, coreNodeId);
}
@Override
- public NemakiPropertyDefinitionCore createPropertyDefinitionCore(
- String repositoryId, NemakiPropertyDefinitionCore propertyDefinitionCore) {
- return nonCachedContentDaoService
- .createPropertyDefinitionCore(repositoryId, propertyDefinitionCore);
+ public NemakiPropertyDefinitionCore createPropertyDefinitionCore(String repositoryId,
+ NemakiPropertyDefinitionCore propertyDefinitionCore) {
+ return nonCachedContentDaoService.createPropertyDefinitionCore(repositoryId, propertyDefinitionCore);
}
@Override
- public NemakiPropertyDefinitionDetail createPropertyDefinitionDetail(
- String repositoryId, NemakiPropertyDefinitionDetail propertyDefinitionDetail) {
- return nonCachedContentDaoService
- .createPropertyDefinitionDetail(repositoryId, propertyDefinitionDetail);
+ public NemakiPropertyDefinitionDetail createPropertyDefinitionDetail(String repositoryId,
+ NemakiPropertyDefinitionDetail propertyDefinitionDetail) {
+ return nonCachedContentDaoService.createPropertyDefinitionDetail(repositoryId, propertyDefinitionDetail);
}
@Override
- public NemakiPropertyDefinitionDetail updatePropertyDefinitionDetail(
- String repositoryId, NemakiPropertyDefinitionDetail propertyDefinitionDetail) {
- NemakiPropertyDefinitionDetail np = nonCachedContentDaoService
- .updatePropertyDefinitionDetail(repositoryId, propertyDefinitionDetail);
+ public NemakiPropertyDefinitionDetail updatePropertyDefinitionDetail(String repositoryId,
+ NemakiPropertyDefinitionDetail propertyDefinitionDetail) {
+ NemakiPropertyDefinitionDetail np = nonCachedContentDaoService.updatePropertyDefinitionDetail(repositoryId,
+ propertyDefinitionDetail);
Cache typeCache = nemakiCachePool.get(repositoryId).getTypeCache();
typeCache.remove("typedefs");
return np;
@@ -232,12 +225,13 @@ public boolean existContent(String repositoryId, String objectTypeId) {
@Override
public Document getDocument(String repositoryId, String objectId) {
Cache contentCache = nemakiCachePool.get(repositoryId).getContentCache();
+
Element v = contentCache.get(objectId);
if (v != null) {
- try{
+ try {
return (Document) v.getObjectValue();
- }catch(ClassCastException e){
+ } catch (ClassCastException e) {
throw e;
}
}
@@ -253,8 +247,7 @@ public Document getDocument(String repositoryId, String objectId) {
@Override
public List getCheckedOutDocuments(String repositoryId, String parentFolderId) {
- return nonCachedContentDaoService
- .getCheckedOutDocuments(repositoryId, parentFolderId);
+ return nonCachedContentDaoService.getCheckedOutDocuments(repositoryId, parentFolderId);
}
@Override
@@ -281,14 +274,12 @@ public List getAllVersions(String repositoryId, String versionSeriesId
@Override
public Document getDocumentOfLatestVersion(String repositoryId, String versionSeriesId) {
- return nonCachedContentDaoService
- .getDocumentOfLatestVersion(repositoryId, versionSeriesId);
+ return nonCachedContentDaoService.getDocumentOfLatestVersion(repositoryId, versionSeriesId);
}
@Override
public Document getDocumentOfLatestMajorVersion(String repositoryId, String versionSeriesId) {
- return nonCachedContentDaoService
- .getDocumentOfLatestMajorVersion(repositoryId, versionSeriesId);
+ return nonCachedContentDaoService.getDocumentOfLatestMajorVersion(repositoryId, versionSeriesId);
}
@Override
@@ -297,9 +288,9 @@ public Folder getFolder(String repositoryId, String objectId) {
Element v = contentCache.get(objectId);
if (v != null) {
- try{
+ try {
return (Folder) v.getObjectValue();
- }catch(ClassCastException e){
+ } catch (ClassCastException e) {
throw e;
}
}
@@ -320,8 +311,18 @@ public Folder getFolderByPath(String repositoryId, String path) {
}
@Override
- public List getLatestChildrenIndex(String repositoryId, String parentId) {
- return nonCachedContentDaoService.getLatestChildrenIndex(repositoryId, parentId);
+ public List getChildren(String repositoryId, String parentId) {
+ Tree tree = getOrCreateTreeCache(repositoryId, parentId);
+
+ List result = new ArrayList<>();
+ for(String childId : tree.getChildren()){
+ Content child = getContent(repositoryId, childId);
+ if(child != null){
+ result.add(child);
+ }
+ }
+
+ return result;
}
@Override
@@ -338,10 +339,10 @@ public List getChildrenNames(String repositoryId, String parentId) {
public Relationship getRelationship(String repositoryId, String objectId) {
Cache cache = nemakiCachePool.get(repositoryId).getContentCache();
Element v = cache.get(objectId);
-
- if(v == null){
- return (Relationship)v.getObjectValue();
- }else{
+
+ if (v == null) {
+ return (Relationship) v.getObjectValue();
+ } else {
return nonCachedContentDaoService.getRelationship(repositoryId, objectId);
}
}
@@ -373,9 +374,93 @@ public Item getItem(String repositoryId, String objectId) {
@Override
public Document create(String repositoryId, Document document) {
- Document d = nonCachedContentDaoService.create(repositoryId, document);
- nemakiCachePool.get(repositoryId).getContentCache().put(new Element(d.getId(), d));
- return d;
+ Document created = nonCachedContentDaoService.create(repositoryId, document);
+ nemakiCachePool.get(repositoryId).getContentCache().put(new Element(created.getId(), created));
+
+ //Tree cache
+ addToTreeCache(repositoryId, created);
+
+ return created;
+ }
+
+ private Tree getOrCreateTreeCache(String repositoryId, String parentId){
+ NemakiCache treeCache = nemakiCachePool.get(repositoryId).getTreeCache();
+ Tree tree = treeCache.get(parentId);
+ if(tree == null){
+ List list = nonCachedContentDaoService.getChildren(repositoryId, parentId);
+ tree = new Tree(parentId);
+ if(org.apache.commons.collections.CollectionUtils.isNotEmpty(list)){
+ for(Content child : list){
+ tree.add(child.getId());
+ }
+ }
+ treeCache.put(tree.getParent(), tree);
+ }
+
+ return tree;
+ }
+
+ private void addToTreeCache(String repositoryId, Content content){
+ Tree tree = getOrCreateTreeCache(repositoryId, content.getParentId());
+
+ if(content instanceof Document){
+ Document doc = (Document)content;
+ if(doc.isPrivateWorkingCopy()){
+ //do nothing
+ return;
+ }else{
+ List versions = getAllVersions(repositoryId, doc.getVersionSeriesId());
+ if(versions != null){
+ Collections.sort(versions, new VersionComparator());
+ for(Document version : versions){
+ if(version.getId().equals(doc.getId())){
+ tree.add(doc.getId());
+ }else{
+ tree.remove(version.getId());
+ }
+ }
+ }
+ }
+ }else if(content instanceof Folder || content instanceof Item){
+ tree.add(content.getId());
+ }
+ }
+
+ private class VersionComparator implements Comparator {
+ @Override
+ public int compare(Content content0, Content content1) {
+ // TODO when created time is not set
+ GregorianCalendar created0 = content0.getCreated();
+ GregorianCalendar created1 = content1.getCreated();
+
+ if (created0.before(created1)) {
+ return 1;
+ } else if (created0.after(created1)) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+ }
+
+ /**
+ *
+ * @param repositoryId
+ * @param doc
+ * @return Previous version. If previous version does not exist, return null.
+ * @throws Exception
+ */
+ private Document getPreviousVersion(String repositoryId, Document doc) throws Exception{
+ String vsId = doc.getVersionSeriesId();
+ List docs = getAllVersions(repositoryId, vsId);
+ if(CollectionUtils.isEmpty(docs)){
+ throw new Exception(String.format("Version series of document[%s] is broken!", doc.getId()));
+ }else if(docs.size() <=1){
+ return null;
+ }else{
+ Document previous = docs.get(docs.size() - 2);
+ return previous;
+ }
}
@Override
@@ -391,7 +476,8 @@ public Change create(String repositoryId, Change change) {
Change created = nonCachedContentDaoService.create(repositoryId, change);
Change latest = nonCachedContentDaoService.getLatestChange(repositoryId);
nemakiCachePool.get(repositoryId).getLatestChangeTokenCache().removeAll();
- nemakiCachePool.get(repositoryId).getLatestChangeTokenCache().put(new Element(TOKEN_CACHE_LATEST_CHANGE_TOKEN, latest));
+ nemakiCachePool.get(repositoryId).getLatestChangeTokenCache()
+ .put(new Element(TOKEN_CACHE_LATEST_CHANGE_TOKEN, latest));
return created;
}
@@ -399,6 +485,8 @@ public Change create(String repositoryId, Change change) {
public Folder create(String repositoryId, Folder folder) {
Folder created = nonCachedContentDaoService.create(repositoryId, folder);
nemakiCachePool.get(repositoryId).getContentCache().put(new Element(created.getId(), created));
+ addToTreeCache(repositoryId, created);
+
return created;
}
@@ -420,6 +508,7 @@ public Policy create(String repositoryId, Policy policy) {
public Item create(String repositoryId, Item item) {
Item created = nonCachedContentDaoService.create(repositoryId, item);
nemakiCachePool.get(repositoryId).getContentCache().put(new Element(created.getId(), created));
+ addToTreeCache(repositoryId, created);
return created;
}
@@ -427,13 +516,20 @@ public Item create(String repositoryId, Item item) {
public Document update(String repositoryId, Document document) {
Document updated = nonCachedContentDaoService.update(repositoryId, document);
nemakiCachePool.get(repositoryId).getContentCache().put(new Element(updated.getId(), updated));
+ nemakiCachePool.get(repositoryId).getObjectDataCache().remove(updated.getId());
return updated;
}
+
+ @Override
+ public Document move(String repositoryId, Document document, String sourceId) {
+ moveTreeCache(repositoryId, document, sourceId);
+ nemakiCachePool.get(repositoryId).getObjectDataCache().remove(document.getId());
+ return update(repositoryId, document);
+ }
@Override
public VersionSeries update(String repositoryId, VersionSeries versionSeries) {
- VersionSeries updated = nonCachedContentDaoService
- .update(repositoryId, versionSeries);
+ VersionSeries updated = nonCachedContentDaoService.update(repositoryId, versionSeries);
Cache versionSeriesCache = nemakiCachePool.get(repositoryId).getVersionSeriesCache();
versionSeriesCache.put(new Element(updated.getId(), updated));
return updated;
@@ -443,13 +539,37 @@ public VersionSeries update(String repositoryId, VersionSeries versionSeries) {
public Folder update(String repositoryId, Folder folder) {
Folder updated = nonCachedContentDaoService.update(repositoryId, folder);
nemakiCachePool.get(repositoryId).getContentCache().put(new Element(updated.getId(), updated));
+ nemakiCachePool.get(repositoryId).getObjectDataCache().remove(updated.getId());
return updated;
}
+
+ @Override
+ public Folder move(String repositoryId, Folder folder, String sourceId) {
+ moveTreeCache(repositoryId, folder, sourceId);
+ return update(repositoryId, folder);
+ }
+ private void moveTreeCache(String repositoryId, Content updated, String sourceId){
+ String targetId = updated.getParentId();
+
+ NemakiCache cache = nemakiCachePool.get(repositoryId).getTreeCache();
+ if(!sourceId.equals(targetId)){
+ //Remove from source
+ Tree souceTree = cache.get(sourceId);
+ if(souceTree != null){
+ souceTree.remove(updated.getId());
+ }
+
+ //Add to target
+ addToTreeCache(repositoryId, updated);
+ }
+ }
+
@Override
public Relationship update(String repositoryId, Relationship relationship) {
Relationship updated = nonCachedContentDaoService.update(repositoryId, relationship);
nemakiCachePool.get(repositoryId).getContentCache().put(new Element(updated.getId(), updated));
+ nemakiCachePool.get(repositoryId).getObjectDataCache().remove(updated.getId());
return updated;
}
@@ -457,6 +577,7 @@ public Relationship update(String repositoryId, Relationship relationship) {
public Policy update(String repositoryId, Policy policy) {
Policy updated = nonCachedContentDaoService.update(repositoryId, policy);
nemakiCachePool.get(repositoryId).getContentCache().put(new Element(updated.getId(), updated));
+ nemakiCachePool.get(repositoryId).getObjectDataCache().remove(updated.getId());
return updated;
}
@@ -464,6 +585,7 @@ public Policy update(String repositoryId, Policy policy) {
public Item update(String repositoryId, Item item) {
Item updated = nonCachedContentDaoService.update(repositoryId, item);
nemakiCachePool.get(repositoryId).getContentCache().put(new Element(updated.getId(), updated));
+ nemakiCachePool.get(repositoryId).getObjectDataCache().remove(updated.getId());
return updated;
}
@@ -471,24 +593,61 @@ public Item update(String repositoryId, Item item) {
public void delete(String repositoryId, String objectId) {
NodeBase nb = nonCachedContentDaoService.getNodeBase(repositoryId, objectId);
+ if(nb == null){
+ return;
+ }
+
+ //read document in advance
+ Document doc = null;
+ Document previous = null;
+ if(nb.isDocument()){
+ doc = (Document)getDocument(repositoryId, objectId);
+ try {
+ previous = getPreviousVersion(repositoryId, doc);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ //read tree in advance
+ Tree tree = null;
+ if(nb.isDocument() || nb.isFolder()){
+ Content _c = getContent(repositoryId, objectId);
+ tree = getOrCreateTreeCache(repositoryId, _c.getParentId());
+ }
+
// remove from database
nonCachedContentDaoService.delete(repositoryId, objectId);
-
+
// remove from cache
- String id = objectId;
-
- if (nb.isDocument()) {
- Document d = this.getDocument(repositoryId, objectId);
- // we can delete versionSeries or not?
- nemakiCachePool.get(repositoryId).getVersionSeriesCache().remove(d.getVersionSeriesId());
- nemakiCachePool.get(repositoryId).getAttachmentCache().remove(d.getAttachmentNodeId());
- }else if (nb.isAttachment()) {
- nemakiCachePool.get(repositoryId).getAttachmentCache().remove(id);
+ if(doc == null){
+ if (nb.isAttachment()) {
+ nemakiCachePool.get(repositoryId).getAttachmentCache().remove(objectId);
+ }else{
+ nemakiCachePool.get(repositoryId).getContentCache().remove(objectId);
+ nemakiCachePool.get(repositoryId).getObjectDataCache().remove(objectId);
+ if(tree != null){
+ tree.remove(objectId);
+ }
+ }
+ }else{
+ //DOCUMENT case
+ if(doc.isPrivateWorkingCopy()){
+ //delete just pwc-related cache
+ nemakiCachePool.get(repositoryId).getAttachmentCache().remove(doc.getAttachmentNodeId());
+ nemakiCachePool.get(repositoryId).getContentCache().remove(doc.getId());
+ nemakiCachePool.get(repositoryId).getObjectDataCache().remove(doc.getId());
+ nemakiCachePool.get(repositoryId).getVersionSeriesCache().remove(doc.getVersionSeriesId());
+ }else{
+ nemakiCachePool.get(repositoryId).getAttachmentCache().remove(doc.getAttachmentNodeId());
+ nemakiCachePool.get(repositoryId).getContentCache().remove(doc.getId());
+ nemakiCachePool.get(repositoryId).getObjectDataCache().remove(doc.getId());
+ tree.remove(doc.getId());
+ nemakiCachePool.get(repositoryId).getVersionSeriesCache().remove(doc.getVersionSeriesId());
+ }
}
-
- nemakiCachePool.get(repositoryId).getContentCache().remove(id);
}
-
+
// ///////////////////////////////////////
// Attachment
// ///////////////////////////////////////
@@ -531,8 +690,7 @@ public Rendition getRendition(String repositoryId, String objectId) {
}
@Override
- public String createRendition(String repositoryId,
- Rendition rendition, ContentStream contentStream) {
+ public String createRendition(String repositoryId, Rendition rendition, ContentStream contentStream) {
return nonCachedContentDaoService.createRendition(repositoryId, rendition, contentStream);
}
@@ -542,15 +700,13 @@ public String createAttachment(String repositoryId, AttachmentNode attachment, C
}
@Override
- public void updateAttachment(String repositoryId,
- AttachmentNode attachment, ContentStream contentStream) {
+ public void updateAttachment(String repositoryId, AttachmentNode attachment, ContentStream contentStream) {
Cache attachmentCache = nemakiCachePool.get(repositoryId).getAttachmentCache();
Element v = attachmentCache.get(attachment.getId());
if (v != null) {
attachmentCache.remove(attachment.getId());
}
nonCachedContentDaoService.updateAttachment(repositoryId, attachment, contentStream);
-
}
// //////////////////////////////////////////////////////////////////////////////
@@ -563,19 +719,20 @@ public Change getChangeEvent(String repositoryId, String changeTokenId) {
@Override
public Change getLatestChange(String repositoryId) {
- Change change =null;
-
+ Change change = null;
+
Element v = nemakiCachePool.get(repositoryId).getLatestChangeTokenCache().get(TOKEN_CACHE_LATEST_CHANGE_TOKEN);
if (v != null) {
- change = (Change)v.getObjectValue();
+ change = (Change) v.getObjectValue();
}
-
+
if (change != null) {
return change;
} else {
change = nonCachedContentDaoService.getLatestChange(repositoryId);
if (change != null) {
- nemakiCachePool.get(repositoryId).getLatestChangeTokenCache().put(new Element(TOKEN_CACHE_LATEST_CHANGE_TOKEN, change));
+ nemakiCachePool.get(repositoryId).getLatestChangeTokenCache()
+ .put(new Element(TOKEN_CACHE_LATEST_CHANGE_TOKEN, change));
}
return change;
}
@@ -583,8 +740,7 @@ public Change getLatestChange(String repositoryId) {
@Override
public List getLatestChanges(String repositoryId, String startToken, int maxItems) {
- return nonCachedContentDaoService
- .getLatestChanges(repositoryId, startToken, maxItems);
+ return nonCachedContentDaoService.getLatestChanges(repositoryId, startToken, maxItems);
}
// //////////////////////////////////////////////////////////////////////////////
@@ -612,8 +768,7 @@ public List getChildArchives(String repositoryId, Archive archive) {
@Override
public List getArchivesOfVersionSeries(String repositoryId, String versionSeriesId) {
- return nonCachedContentDaoService
- .getArchivesOfVersionSeries(repositoryId, versionSeriesId);
+ return nonCachedContentDaoService.getArchivesOfVersionSeries(repositoryId, versionSeriesId);
}
@Override
@@ -623,8 +778,7 @@ public List getAllArchives(String repositoryId) {
@Override
public Archive createArchive(String repositoryId, Archive archive, Boolean deletedWithParent) {
- return nonCachedContentDaoService.createArchive(repositoryId,
- archive, deletedWithParent);
+ return nonCachedContentDaoService.createArchive(repositoryId, archive, deletedWithParent);
}
@Override
@@ -647,11 +801,18 @@ public void restoreAttachment(String repositoryId, Archive archive) {
nonCachedContentDaoService.restoreAttachment(repositoryId, archive);
}
+ // //////////////////////////////////////////////////////////////////////////////
+ // Cache management
+ // //////////////////////////////////////////////////////////////////////////////
+ public void refreshCmisObjectData(String repositoryId, String objectId){
+ nemakiCachePool.get(repositoryId).getObjectDataCache().remove(objectId);
+ }
+
+
// //////////////////////////////////////////////////////////////////////////////
// Spring
// //////////////////////////////////////////////////////////////////////////////
- public void setNonCachedContentDaoService(
- ContentDaoService nonCachedContentDaoService) {
+ public void setNonCachedContentDaoService(ContentDaoService nonCachedContentDaoService) {
this.nonCachedContentDaoService = nonCachedContentDaoService;
}
diff --git a/core/src/main/java/jp/aegif/nemaki/dao/impl/couch/ContentDaoServiceImpl.java b/core/src/main/java/jp/aegif/nemaki/dao/impl/couch/ContentDaoServiceImpl.java
index 582ad188..9f81bfd8 100644
--- a/core/src/main/java/jp/aegif/nemaki/dao/impl/couch/ContentDaoServiceImpl.java
+++ b/core/src/main/java/jp/aegif/nemaki/dao/impl/couch/ContentDaoServiceImpl.java
@@ -432,7 +432,7 @@ public Folder getFolderByPath(String repositoryId, String path) {
}
@Override
- public List getLatestChildrenIndex(String repositoryId, String parentId) {
+ public List getChildren(String repositoryId, String parentId) {
ViewQuery query = new ViewQuery().designDocId(DESIGN_DOCUMENT).viewName("children").key(parentId);
List list = connectorPool.get(repositoryId).queryView(query, CouchContent.class);
@@ -618,6 +618,11 @@ public Document update(String repositoryId, Document document) {
connectorPool.get(repositoryId).update(update);
return update.convert();
}
+
+ @Override
+ public Document move(String repositoryId, Document document, String sourceId){
+ return update(repositoryId, document);
+ }
@Override
public VersionSeries update(String repositoryId, VersionSeries versionSeries) {
@@ -642,6 +647,11 @@ public Folder update(String repositoryId, Folder folder) {
return update.convert();
}
+
+ @Override
+ public Folder move(String repositoryId, Folder folder, String sourceId){
+ return update(repositoryId, folder);
+ }
@Override
public Relationship update(String repositoryId, Relationship relationship) {
@@ -1026,4 +1036,8 @@ public void setRepositoryInfoMap(RepositoryInfoMap repositoryInfoMap) {
this.repositoryInfoMap = repositoryInfoMap;
}
+ @Override
+ public void refreshCmisObjectData(String repositoryId, String objectId) {
+ // this method is for cached service
+ }
}
diff --git a/core/src/main/java/jp/aegif/nemaki/util/YamlManager.java b/core/src/main/java/jp/aegif/nemaki/util/YamlManager.java
index 21f4f948..41bc5547 100644
--- a/core/src/main/java/jp/aegif/nemaki/util/YamlManager.java
+++ b/core/src/main/java/jp/aegif/nemaki/util/YamlManager.java
@@ -39,7 +39,7 @@ public YamlManager(String baseModelFile){
this.baseModelFile = baseModelFile;
}
- public synchronized Object loadYml(){
+ public Object loadYml(){
InputStream is = getClass().getClassLoader().getResourceAsStream(baseModelFile);
if (is == null) {
log.error("yaml file not found");
diff --git a/core/src/main/java/jp/aegif/nemaki/util/cache/impl/NemakiCacheImpl.java b/core/src/main/java/jp/aegif/nemaki/util/cache/CacheService.java
similarity index 53%
rename from core/src/main/java/jp/aegif/nemaki/util/cache/impl/NemakiCacheImpl.java
rename to core/src/main/java/jp/aegif/nemaki/util/cache/CacheService.java
index bc12898a..2f7ffb51 100644
--- a/core/src/main/java/jp/aegif/nemaki/util/cache/impl/NemakiCacheImpl.java
+++ b/core/src/main/java/jp/aegif/nemaki/util/cache/CacheService.java
@@ -1,21 +1,27 @@
-package jp.aegif.nemaki.util.cache.impl;
-
+package jp.aegif.nemaki.util.cache;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
+
+import java.util.Map;
+import java.util.Map.Entry;
+
import jp.aegif.nemaki.util.PropertyManager;
-import jp.aegif.nemaki.util.cache.CustomCache;
-import jp.aegif.nemaki.util.cache.NemakiCache;
+import jp.aegif.nemaki.util.YamlManager;
+import jp.aegif.nemaki.util.cache.model.NemakiCache;
+import jp.aegif.nemaki.util.cache.model.Tree;
+import jp.aegif.nemaki.util.cache.CacheService;
import jp.aegif.nemaki.util.constant.PropertyKey;
-public class NemakiCacheImpl implements NemakiCache{
+public class CacheService {
private boolean cacheEnabled;
-
+
private final CacheManager cacheManager;
private final String OBJECT_DATA_CACHE = "objectDataCache";
private final String PROPERTIES_CACHE = "propertisCache";
private final String TYPE_CACHE = "typeCache";
private final String CONTENT_CACHE = "contentCache";
+ private final String TREE_CACHE = "treeCache";
private final String VERSION_SERIES_CACHE = "versionSeriesCache";
private final String ATTACHMENTS_CACHE = "attachmentCache";
private final String CHANGE_EVENT_CACHE = "changeEventCache";
@@ -24,93 +30,118 @@ public class NemakiCacheImpl implements NemakiCache{
private final String USERS_CACHE = "usersCache";
private final String GROUP_CACHE = "groupCache";
private final String GROUPS_CACHE = "groupsCache";
-
+
private final String repositoryId;
-
- public NemakiCacheImpl(String repositoryId, PropertyManager propertyManager) {
+
+ public CacheService(String repositoryId, PropertyManager propertyManager) {
this.repositoryId = repositoryId;
-
+
cacheEnabled = propertyManager.readBoolean(PropertyKey.CACHE_CMIS_ENABLED);
-
+
cacheManager = CacheManager.newInstance();
- cacheManager.addCache(new Cache(repositoryId + "_" + OBJECT_DATA_CACHE, 10000, false, false, 60 * 60, 60 * 60));
- cacheManager.addCache(new Cache(repositoryId + "_" + PROPERTIES_CACHE, 10000, false, false, 60 * 60, 60 * 60));
- cacheManager.addCache(new Cache(repositoryId + "_" + TYPE_CACHE, 1, false, false, 60 * 60, 60 * 60));
- cacheManager.addCache(new Cache(repositoryId + "_" + CONTENT_CACHE, 10000, false, false, 60 * 60, 60 * 60));
- cacheManager.addCache(new Cache(repositoryId + "_" + VERSION_SERIES_CACHE, 10000, false, false, 60 * 60, 60 * 60));
- cacheManager.addCache(new Cache(repositoryId + "_" + ATTACHMENTS_CACHE, 10000, false, false, 60 * 60, 60 * 60));
- cacheManager.addCache(new Cache(repositoryId + "_" + CHANGE_EVENT_CACHE, 10000, false, false, 60 * 60, 60 * 60));
- cacheManager.addCache(new Cache(repositoryId + "_" + LATEST_CHANGE_TOKEN_CACHE, 10000, false, false, 60 * 60, 60 * 60));
- cacheManager.addCache(new Cache(repositoryId + "_" + USER_CACHE, 10000, false, false, 60 * 60, 60 * 60));
- cacheManager.addCache(new Cache(repositoryId + "_" + USERS_CACHE, 10000, false, false, 60 * 60, 60 * 60));
- cacheManager.addCache(new Cache(repositoryId + "_" + GROUP_CACHE, 10000, false, false, 60 * 60, 60 * 60));
- cacheManager.addCache(new Cache(repositoryId + "_" + GROUPS_CACHE, 10000, false, false, 60 * 60, 60 * 60));
- }
-
- @Override
+ loadConfig(propertyManager);
+ }
+
+ private void loadConfig(PropertyManager propertyManager) {
+ String configFile = propertyManager.readValue(PropertyKey.CACHE_CONFIG);
+ YamlManager manager = new YamlManager(configFile);
+ Map> yml = (Map>) manager.loadYml();
+
+ // default
+ Map defaultConfigMap = yml.get("default");
+ yml.remove("default");
+
+ for (Entry> configMap : yml.entrySet()) {
+ NemakiCacheConfig config = new NemakiCacheConfig();
+ config.override(defaultConfigMap);
+ if(configMap.getValue() != null){
+ config.override(configMap.getValue());
+ }
+
+ Cache cache = new Cache(repositoryId + "_" + configMap.getKey(), config.maxElementsInMemory.intValue(),
+ config.overflowToDisc, config.eternal, config.timeToLiveSeconds, config.timeToIdleSeconds);
+ cacheManager.addCache(cache);
+ }
+
+ }
+
+ private class NemakiCacheConfig {
+ private Long maxElementsInMemory;
+ private Boolean overflowToDisc;
+ private Boolean eternal;
+ private Long timeToLiveSeconds;
+ private Long timeToIdleSeconds;
+
+ private void override(Map map) {
+ if (map.get("maxElementsInMemory") != null)
+ maxElementsInMemory = (Long) map.get("maxElementsInMemory");
+ if (map.get("overflowToDisc") != null)
+ overflowToDisc = (Boolean) map.get("overflowToDisc");
+ if (map.get("eternal") != null)
+ eternal = (Boolean) map.get("eternal");
+ if (map.get("timeToLiveSeconds") != null)
+ timeToLiveSeconds = (Long) map.get("timeToLiveSeconds");
+ if (map.get("timeToIdleSeconds") != null)
+ timeToIdleSeconds = (Long) map.get("timeToIdleSeconds");
+ }
+ }
+
public CustomCache getObjectDataCache() {
CustomCache cc = new CustomCache(cacheEnabled);
cc.setCache(cacheManager.getCache(repositoryId + "_" + OBJECT_DATA_CACHE));
- return cc;
+ return cc;
}
- @Override
public Cache getPropertiesCache() {
return cacheManager.getCache(repositoryId + "_" + PROPERTIES_CACHE);
}
- @Override
public Cache getTypeCache() {
return cacheManager.getCache(repositoryId + "_" + TYPE_CACHE);
}
- @Override
public Cache getContentCache() {
return cacheManager.getCache(repositoryId + "_" + CONTENT_CACHE);
}
- @Override
+ public NemakiCache getTreeCache() {
+ NemakiCache cache = new NemakiCache(true, cacheManager.getCache(repositoryId + "_" + TREE_CACHE));
+ return cache;
+ }
+
public Cache getVersionSeriesCache() {
return cacheManager.getCache(repositoryId + "_" + VERSION_SERIES_CACHE);
}
- @Override
public Cache getAttachmentCache() {
return cacheManager.getCache(repositoryId + "_" + ATTACHMENTS_CACHE);
}
- @Override
public Cache getChangeEventCache() {
return cacheManager.getCache(repositoryId + "_" + CHANGE_EVENT_CACHE);
}
- @Override
public Cache getLatestChangeTokenCache() {
return cacheManager.getCache(repositoryId + "_" + LATEST_CHANGE_TOKEN_CACHE);
}
- @Override
public Cache getUserCache() {
return cacheManager.getCache(repositoryId + "_" + USER_CACHE);
}
- @Override
public Cache getUsersCache() {
return cacheManager.getCache(repositoryId + "_" + USERS_CACHE);
}
- @Override
public Cache getGroupCache() {
return cacheManager.getCache(repositoryId + "_" + GROUP_CACHE);
}
- @Override
public Cache getGroupsCache() {
return cacheManager.getCache(repositoryId + "_" + GROUPS_CACHE);
}
- @Override
public void removeCmisCache(String objectId) {
getPropertiesCache().remove(objectId);
getObjectDataCache().remove(objectId);
diff --git a/core/src/main/java/jp/aegif/nemaki/util/cache/NemakiCache.java b/core/src/main/java/jp/aegif/nemaki/util/cache/NemakiCache.java
deleted file mode 100644
index 2193e7b8..00000000
--- a/core/src/main/java/jp/aegif/nemaki/util/cache/NemakiCache.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package jp.aegif.nemaki.util.cache;
-
-import net.sf.ehcache.Cache;
-
-public interface NemakiCache {
- public CustomCache getObjectDataCache();
- public Cache getPropertiesCache();
- public Cache getTypeCache();
- public Cache getContentCache();
- public Cache getVersionSeriesCache();
- public Cache getAttachmentCache();
- public Cache getChangeEventCache();
- public Cache getLatestChangeTokenCache();
- public Cache getUserCache();
- public Cache getUsersCache();
- public Cache getGroupCache();
- public Cache getGroupsCache();
-
- public void removeCmisCache(String objectId);
-}
diff --git a/core/src/main/java/jp/aegif/nemaki/util/cache/NemakiCachePool.java b/core/src/main/java/jp/aegif/nemaki/util/cache/NemakiCachePool.java
index ad28b634..87036ab6 100644
--- a/core/src/main/java/jp/aegif/nemaki/util/cache/NemakiCachePool.java
+++ b/core/src/main/java/jp/aegif/nemaki/util/cache/NemakiCachePool.java
@@ -1,7 +1,7 @@
package jp.aegif.nemaki.util.cache;
public interface NemakiCachePool {
- NemakiCache get(String repositoryId);
+ CacheService get(String repositoryId);
void add(String repositoryId);
void remove(String repositoryId);
void removeAll();
diff --git a/core/src/main/java/jp/aegif/nemaki/util/cache/impl/NemakiCachePoolImpl.java b/core/src/main/java/jp/aegif/nemaki/util/cache/impl/NemakiCachePoolImpl.java
index 903700e6..5eb3aeb5 100644
--- a/core/src/main/java/jp/aegif/nemaki/util/cache/impl/NemakiCachePoolImpl.java
+++ b/core/src/main/java/jp/aegif/nemaki/util/cache/impl/NemakiCachePoolImpl.java
@@ -5,13 +5,13 @@
import jp.aegif.nemaki.cmis.factory.info.RepositoryInfoMap;
import jp.aegif.nemaki.util.PropertyManager;
-import jp.aegif.nemaki.util.cache.NemakiCache;
+import jp.aegif.nemaki.util.cache.CacheService;
import jp.aegif.nemaki.util.cache.NemakiCachePool;
public class NemakiCachePoolImpl implements NemakiCachePool{
- private Map pool = new HashMap();
- private NemakiCache nullCache;
+ private Map pool = new HashMap();
+ private CacheService nullCache;
private RepositoryInfoMap repositoryInfoMap;
private PropertyManager propertyManager;
@@ -25,12 +25,12 @@ public void init(){
add(key);
}
- nullCache = new NemakiCacheImpl(null, propertyManager);
+ nullCache = new CacheService(null, propertyManager);
}
@Override
- public NemakiCache get(String repositoryId) {
- NemakiCache cache = pool.get(repositoryId);
+ public CacheService get(String repositoryId) {
+ CacheService cache = pool.get(repositoryId);
if (cache == null){
return nullCache;
@@ -41,7 +41,7 @@ public NemakiCache get(String repositoryId) {
@Override
public void add(String repositoryId) {
- pool.put(repositoryId, new NemakiCacheImpl(repositoryId, propertyManager));
+ pool.put(repositoryId, new CacheService(repositoryId, propertyManager));
}
@Override
@@ -56,13 +56,13 @@ public void removeAll() {
@Override
public void clear(String repositoryId) {
- pool.put(repositoryId, new NemakiCacheImpl(repositoryId, propertyManager));
+ pool.put(repositoryId, new CacheService(repositoryId, propertyManager));
}
@Override
public void clearAll() {
for(String key : pool.keySet()){
- pool.put(key, new NemakiCacheImpl(key, propertyManager));
+ pool.put(key, new CacheService(key, propertyManager));
}
}
diff --git a/core/src/main/java/jp/aegif/nemaki/util/cache/model/NemakiCache.java b/core/src/main/java/jp/aegif/nemaki/util/cache/model/NemakiCache.java
new file mode 100644
index 00000000..b28ce7a1
--- /dev/null
+++ b/core/src/main/java/jp/aegif/nemaki/util/cache/model/NemakiCache.java
@@ -0,0 +1,64 @@
+package jp.aegif.nemaki.util.cache.model;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+public class NemakiCache {
+ private Cache cache;
+ private final boolean cacheEnabled;
+ private static final Log log = LogFactory.getLog(NemakiCache.class);
+
+ public NemakiCache(boolean cacheEnabled, Cache cache){
+ this.cacheEnabled = cacheEnabled;
+ this.cache = cache;
+ }
+
+ public T get(String key){
+ if(cacheEnabled){
+ Element element = cache.get(key);
+ if(element == null || element.getObjectValue() == null){
+ return null;
+ }else{
+ return (T)element.getObjectValue();
+ }
+ }else{
+ return null;
+ }
+ }
+
+ public void put(String key, T data){
+ if(cacheEnabled){
+ Element element = new Element(key, data);
+ cache.put(element);
+ }
+ }
+
+ public void put(Element element){
+ if(cacheEnabled){
+ cache.put(element);
+ }
+ }
+
+ public void remove(String key){
+ if(cacheEnabled){
+ cache.remove(key);
+ }
+ }
+
+ public void removeAll(){
+ if(cacheEnabled){
+ cache.removeAll();
+ }
+ }
+
+ public Cache getCache(){
+ return this.cache;
+ }
+
+ public void setCache(Cache cache){
+ this.cache = cache;
+ }
+}
diff --git a/core/src/main/java/jp/aegif/nemaki/util/cache/model/Tree.java b/core/src/main/java/jp/aegif/nemaki/util/cache/model/Tree.java
new file mode 100644
index 00000000..eec87164
--- /dev/null
+++ b/core/src/main/java/jp/aegif/nemaki/util/cache/model/Tree.java
@@ -0,0 +1,40 @@
+package jp.aegif.nemaki.util.cache.model;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class Tree {
+ private String parent;
+ private Set children;
+
+ public Tree(String parent){
+ this.parent = parent;
+ this.children = new HashSet<>();
+ }
+
+ public void add(String objectId){
+ children.add(objectId);
+ }
+
+ public void remove(String objectId){
+ children.remove(objectId);
+ }
+
+ public String getParent() {
+ return parent;
+ }
+
+ public void setParent(String parent) {
+ this.parent = parent;
+ }
+
+ public Set getChildren() {
+ return children;
+ }
+
+ public void setChildren(Set children) {
+ this.children = children;
+ }
+}
diff --git a/core/src/main/java/jp/aegif/nemaki/util/constant/PropertyKey.java b/core/src/main/java/jp/aegif/nemaki/util/constant/PropertyKey.java
index 00e0e042..a7531160 100644
--- a/core/src/main/java/jp/aegif/nemaki/util/constant/PropertyKey.java
+++ b/core/src/main/java/jp/aegif/nemaki/util/constant/PropertyKey.java
@@ -254,6 +254,7 @@ public interface PropertyKey {
final String LOG_CALLCONTEXT = "log.callcontext";
//Cache
+ final String CACHE_CONFIG = "cache.config";
final String CACHE_CMIS_ENABLED = "cache.cmis.enabled";
//Auth token
diff --git a/core/src/main/java/jp/aegif/nemaki/util/lock/ThreadLockService.java b/core/src/main/java/jp/aegif/nemaki/util/lock/ThreadLockService.java
new file mode 100644
index 00000000..bed783cb
--- /dev/null
+++ b/core/src/main/java/jp/aegif/nemaki/util/lock/ThreadLockService.java
@@ -0,0 +1,16 @@
+package jp.aegif.nemaki.util.lock;
+
+import java.util.List;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+
+import jp.aegif.nemaki.model.Content;
+
+public interface ThreadLockService {
+ public ReadWriteLock get(String repositoryId, String objectId);
+ public Lock getWriteLock(String repositoryId, String objectId);
+ public Lock getReadLock(String repositoryId, String objectId);
+ public List readLocks(String repositoryId, List contents);
+ public void bulkLock(List locks);
+ public void bulkUnlock(List locks);
+}
diff --git a/core/src/main/java/jp/aegif/nemaki/util/lock/UniqueObjectId.java b/core/src/main/java/jp/aegif/nemaki/util/lock/UniqueObjectId.java
new file mode 100644
index 00000000..bc3c9de2
--- /dev/null
+++ b/core/src/main/java/jp/aegif/nemaki/util/lock/UniqueObjectId.java
@@ -0,0 +1,58 @@
+package jp.aegif.nemaki.util.lock;
+
+public class UniqueObjectId {
+ private String repositoryId;
+ private String objectId;
+
+ public UniqueObjectId(String repositoryId, String objectId) {
+ super();
+ this.repositoryId = repositoryId;
+ this.objectId = objectId;
+ }
+
+ public String getRepositoryId() {
+ return repositoryId;
+ }
+ public void setRepositoryId(String repositoryId) {
+ this.repositoryId = repositoryId;
+ }
+ public String getObjectId() {
+ return objectId;
+ }
+ public void setObjectId(String objectId) {
+ this.objectId = objectId;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((objectId == null) ? 0 : objectId.hashCode());
+ result = prime * result + ((repositoryId == null) ? 0 : repositoryId.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ UniqueObjectId other = (UniqueObjectId) obj;
+ if (objectId == null) {
+ if (other.objectId != null)
+ return false;
+ } else if (!objectId.equals(other.objectId))
+ return false;
+ if (repositoryId == null) {
+ if (other.repositoryId != null)
+ return false;
+ } else if (!repositoryId.equals(other.repositoryId))
+ return false;
+ return true;
+ }
+
+
+}
diff --git a/core/src/main/java/jp/aegif/nemaki/util/lock/impl/ThreadLockServiceImpl.java b/core/src/main/java/jp/aegif/nemaki/util/lock/impl/ThreadLockServiceImpl.java
new file mode 100644
index 00000000..7d06333d
--- /dev/null
+++ b/core/src/main/java/jp/aegif/nemaki/util/lock/impl/ThreadLockServiceImpl.java
@@ -0,0 +1,62 @@
+package jp.aegif.nemaki.util.lock.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+
+import org.apache.commons.collections.CollectionUtils;
+
+import com.google.common.util.concurrent.Striped;
+
+import jp.aegif.nemaki.model.Content;
+import jp.aegif.nemaki.util.lock.ThreadLockService;
+import jp.aegif.nemaki.util.lock.UniqueObjectId;
+
+public class ThreadLockServiceImpl implements ThreadLockService{
+
+ private final Striped locks = Striped.lazyWeakReadWriteLock(4096);
+
+ @Override
+ public ReadWriteLock get(String repositoryId, String objectId) {
+ ReadWriteLock lock = locks.get(new UniqueObjectId(repositoryId, objectId));
+ return lock;
+ }
+
+ @Override
+ public Lock getWriteLock(String repositoryId, String objectId) {
+ return get(repositoryId, objectId).writeLock();
+ }
+
+ @Override
+ public Lock getReadLock(String repositoryId, String objectId) {
+ return get(repositoryId, objectId).readLock();
+ }
+
+ @Override
+ public List readLocks(String repositoryId, List contents){
+ List locks = new ArrayList<>();
+ if(CollectionUtils.isNotEmpty(contents)){
+ for(T content : contents){
+ Lock lock = getReadLock(repositoryId, content.getId());
+ locks.add(lock);
+ }
+ }
+
+ return locks;
+ }
+
+ @Override
+ public void bulkLock(List locks){
+ for(Lock lock : locks){
+ lock.lock();
+ }
+ }
+
+ @Override
+ public void bulkUnlock(List locks){
+ for(Lock lock : locks){
+ lock.unlock();
+ }
+ }
+}
diff --git a/core/src/main/webapp/WEB-INF/classes/ehcache.xml b/core/src/main/webapp/WEB-INF/classes/ehcache.xml
index e5e74081..1eae0fdd 100644
--- a/core/src/main/webapp/WEB-INF/classes/ehcache.xml
+++ b/core/src/main/webapp/WEB-INF/classes/ehcache.xml
@@ -1,15 +1,15 @@
-
-
-
+
+
+
+
\ No newline at end of file
diff --git a/core/src/main/webapp/WEB-INF/classes/ehcache.yml b/core/src/main/webapp/WEB-INF/classes/ehcache.yml
new file mode 100644
index 00000000..9b7e2e60
--- /dev/null
+++ b/core/src/main/webapp/WEB-INF/classes/ehcache.yml
@@ -0,0 +1,36 @@
+default:
+ maxElementsInMemory: 10000
+ overflowToDisc: false
+ eternal: false
+ timeToLiveSeconds: 3600
+ timeToIdleSeconds: 3600
+
+objectDataCache:
+
+propertisCache:
+ maxElementsInMemory: 100
+
+typeCache:
+ maxElementsInMemory: 1
+
+contentCache:
+ maxElementsInMemory: 1000000
+ eternal: true
+treeCache:
+
+versionSeriesCache:
+
+attachmentCache:
+
+changeEventCache:
+
+latestChangeTokenCache:
+ maxElementsInMemory: 1
+
+userCache:
+
+usersCache:
+
+groupCache:
+
+groupsCache:
diff --git a/core/src/main/webapp/WEB-INF/classes/nemakiware.properties b/core/src/main/webapp/WEB-INF/classes/nemakiware.properties
index bb04c73e..22ea3704 100644
--- a/core/src/main/webapp/WEB-INF/classes/nemakiware.properties
+++ b/core/src/main/webapp/WEB-INF/classes/nemakiware.properties
@@ -68,6 +68,8 @@ log.after=false
log.callcontext=false
###Cache
+#Caches are created programmatically.
+cache.config=ehcache.yml
cache.cmis.enabled=true
###Auth token
diff --git a/core/src/main/webapp/WEB-INF/classes/serviceContext.xml b/core/src/main/webapp/WEB-INF/classes/serviceContext.xml
index 806529b1..d6906c1d 100644
--- a/core/src/main/webapp/WEB-INF/classes/serviceContext.xml
+++ b/core/src/main/webapp/WEB-INF/classes/serviceContext.xml
@@ -168,11 +168,11 @@
-
+
-
-
+
+
@@ -218,6 +218,9 @@
${thread.max}
+
+
+
@@ -238,6 +241,9 @@
+
+
+
@@ -292,6 +298,9 @@
+
+
+
@@ -324,6 +333,9 @@
+
+
+
@@ -347,6 +359,9 @@
+
+
+
@@ -376,12 +391,12 @@
+
+
+
-
-
-
@@ -477,6 +492,9 @@
${capability.extended.include.relationships}
+
+
+
@@ -568,12 +586,12 @@
+
+
+
-
-
-
@@ -648,4 +666,19 @@
+
+
+
+
+
+ jp.aegif.nemaki.util.lock.ThreadLockService
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/core/src/test/java/jp/aegif/nemaki/test/cmis/AllTest.java b/core/src/test/java/jp/aegif/nemaki/test/cmis/AllTest.java
deleted file mode 100644
index a7c53ef9..00000000
--- a/core/src/test/java/jp/aegif/nemaki/test/cmis/AllTest.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package jp.aegif.nemaki.test.cmis;
-
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
-
-import jp.aegif.nemaki.test.cmis.tests.BasicsTestGroup;
-import jp.aegif.nemaki.test.cmis.tests.ControlTestGroup;
-import jp.aegif.nemaki.test.cmis.tests.CrudTestGroup;
-import jp.aegif.nemaki.test.cmis.tests.FilingTestGroup;
-import jp.aegif.nemaki.test.cmis.tests.QueryTestGroup;
-import jp.aegif.nemaki.test.cmis.tests.TypesTestGroup;
-import jp.aegif.nemaki.test.cmis.tests.VersioningTestGroup;
-
-@RunWith( Suite.class )
-@Suite.SuiteClasses( {
- BasicsTestGroup.class,
- ControlTestGroup.class,
- CrudTestGroup.class,
- FilingTestGroup.class,
- QueryTestGroup.class,
- TypesTestGroup.class,
- VersioningTestGroup.class,
-} )
-public class AllTest extends TckSuite{
-
-}
diff --git a/core/src/test/java/jp/aegif/nemaki/test/nemaki/MultiThreadTest.java b/core/src/test/java/jp/aegif/nemaki/test/nemaki/MultiThreadTest.java
new file mode 100644
index 00000000..aaa77eae
--- /dev/null
+++ b/core/src/test/java/jp/aegif/nemaki/test/nemaki/MultiThreadTest.java
@@ -0,0 +1,163 @@
+package jp.aegif.nemaki.test.nemaki;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import org.apache.chemistry.opencmis.client.api.CmisObject;
+import org.apache.chemistry.opencmis.client.api.Document;
+import org.apache.chemistry.opencmis.client.api.Folder;
+import org.apache.chemistry.opencmis.client.api.ItemIterable;
+import org.apache.chemistry.opencmis.client.api.ObjectId;
+import org.apache.chemistry.opencmis.client.api.OperationContext;
+import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
+import org.apache.chemistry.opencmis.commons.enums.UnfileObject;
+import org.joda.time.DateTime;
+import org.joda.time.Duration;
+import org.junit.Test;
+
+public class MultiThreadTest extends TestBase{
+
+ @Test
+ public void checkOutTest_single(){
+ String folderId = createTestFolder();
+ String docId = createDocument(folderId, "test.txt", "This is test");
+ Document doc = (Document) session.getObject(docId);
+
+ long start = System.currentTimeMillis();
+ doc.checkOut();
+ long end = System.currentTimeMillis();
+
+ System.out.println("start=" + start + ", end=" + end);
+
+ Folder folder = (Folder) session.getObject(folderId);
+ folder.deleteTree(true, UnfileObject.DELETE, true);
+ }
+
+ @Test
+ public void checkOutTest_All() throws InterruptedException, ExecutionException{
+ //document ids
+ Folder folder = (Folder) session.getObject(testFolderId);
+ OperationContext oc = simpleOperationContext();
+ oc.setMaxItemsPerPage(Integer.MAX_VALUE);
+
+ ItemIterable children = folder.getChildren(oc);
+
+ Iterator itr = children.iterator();
+ List docs = new ArrayList<>();
+ while(itr.hasNext()){
+ CmisObject child = itr.next();
+ if(child.getBaseTypeId() == BaseTypeId.CMIS_DOCUMENT){
+ docs.add((Document)child);
+ }
+ }
+
+ //checkout
+ List threads = new ArrayList<>();
+ Integer taskNum = 1;
+ for(Document doc : docs){
+ threads.add(new Thread(new CheckOutTask(taskNum, doc)));
+ taskNum++;
+ }
+
+ for(Thread thread : threads){
+ thread.start();
+ }
+
+ for(Thread thread : threads){
+ thread.join();
+ }
+
+ System.out.println("test end: " + threads.size() + " threads processed");
+ }
+
+ public class CheckOutTask implements Runnable{
+ private int taskId;
+ private Document doc;
+
+ public CheckOutTask(int taskId, Document doc){
+ this.taskId = taskId;
+ this.doc = doc;
+ }
+
+ @Override
+ public void run() {
+ DateTime start = new DateTime();
+
+ ObjectId objectId = doc.checkOut();
+
+ DateTime end = new DateTime();
+
+ Duration duration = new Duration(start, end);
+
+ System.out.println(taskId + ", " + duration.getMillis() + ", " + doc.getId() + ", " + start + ", " + end);
+ }
+ }
+
+ @Test
+ public void readTest_All() throws InterruptedException{
+ //document ids
+ Folder folder = (Folder) session.getObject(testFolderId);
+ OperationContext oc = simpleOperationContext();
+ oc.setMaxItemsPerPage(Integer.MAX_VALUE);
+
+ ItemIterable children = folder.getChildren(oc);
+
+ Iterator itr = children.iterator();
+ List docs = new ArrayList<>();
+ while(itr.hasNext()){
+ CmisObject child = itr.next();
+ if(child.getBaseTypeId() == BaseTypeId.CMIS_DOCUMENT){
+ docs.add((Document)child);
+ }
+ }
+
+ //checkout
+ List threads = new ArrayList<>();
+ Integer taskNum = 1;
+ for(Document doc : docs){
+ threads.add(new Thread(new ReadTask(taskNum, doc.getId())));
+ taskNum++;
+ }
+
+ for(Thread thread : threads){
+ thread.start();
+ }
+
+ for(Thread thread : threads){
+ thread.join();
+ }
+
+ System.out.println("test end: " + threads.size() + " threads processed");
+ }
+
+ public class ReadTask implements Runnable{
+ private int taskId;
+ private String objectId;
+
+ public ReadTask(int taskId, String objectId){
+ this.taskId = taskId;
+ this.objectId = objectId;
+ }
+
+ @Override
+ public void run() {
+ System.out.println(taskId + ":start");
+
+ OperationContext oc = simpleOperationContext();
+ oc.setCacheEnabled(false);
+
+ DateTime start = new DateTime();
+
+ Document doc = (Document) session.getObject(objectId, oc);
+
+ DateTime end = new DateTime();
+
+ System.out.println(taskId + ":end");
+
+ Duration duration = new Duration(start, end);
+
+ System.out.println(taskId + ", " + duration.getMillis() + ", " + doc.getId() + ", " + start + ", " + end);
+ }
+ }
+}
diff --git a/core/src/test/java/jp/aegif/nemaki/test/nemaki/SessionUtil.java b/core/src/test/java/jp/aegif/nemaki/test/nemaki/SessionUtil.java
new file mode 100644
index 00000000..c8df9712
--- /dev/null
+++ b/core/src/test/java/jp/aegif/nemaki/test/nemaki/SessionUtil.java
@@ -0,0 +1,51 @@
+package jp.aegif.nemaki.test.nemaki;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.chemistry.opencmis.client.api.OperationContext;
+import org.apache.chemistry.opencmis.client.api.Session;
+import org.apache.chemistry.opencmis.client.api.SessionFactory;
+import org.apache.chemistry.opencmis.client.runtime.SessionFactoryImpl;
+import org.apache.chemistry.opencmis.commons.SessionParameter;
+import org.apache.chemistry.opencmis.commons.enums.BindingType;
+import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships;
+
+public class SessionUtil {
+
+ public static Session createCmisSession(String repositoryId, String userId, String password){
+ Map parameter = new HashMap();
+
+ // user credentials
+ //TODO enable change a user
+ parameter.put(SessionParameter.USER, userId);
+ parameter.put(SessionParameter.PASSWORD, password);
+
+ // session locale
+ parameter.put(SessionParameter.LOCALE_ISO3166_COUNTRY, "");
+ parameter.put(SessionParameter.LOCALE_ISO639_LANGUAGE, "");
+
+ // repository
+ parameter.put(SessionParameter.REPOSITORY_ID, repositoryId);
+ //parameter.put(org.apache.chemistry.opencmis.commons.impl.Constants.PARAM_REPOSITORY_ID, NemakiConfig.getValue(PropertyKey.NEMAKI_CORE_URI_REPOSITORY));
+
+ parameter. put(SessionParameter.BINDING_TYPE, BindingType.ATOMPUB.value());
+
+ String coreAtomUri = "http://localhost:8080/core/atom/" + repositoryId; //TODO
+ parameter.put(SessionParameter.ATOMPUB_URL, coreAtomUri);
+
+ //timeout
+ parameter.put(SessionParameter.CONNECT_TIMEOUT, "30000");
+ parameter.put(SessionParameter.READ_TIMEOUT, "30000");
+
+
+
+ SessionFactory f = SessionFactoryImpl.newInstance();
+ Session session = f.createSession(parameter);
+ OperationContext operationContext = session.createOperationContext(null,
+ true, true, false, IncludeRelationships.BOTH, null, false, null, true, 100);
+ session.setDefaultContext(operationContext);
+
+ return session;
+ }
+}
diff --git a/core/src/test/java/jp/aegif/nemaki/test/nemaki/TestBase.java b/core/src/test/java/jp/aegif/nemaki/test/nemaki/TestBase.java
new file mode 100644
index 00000000..57dc80ab
--- /dev/null
+++ b/core/src/test/java/jp/aegif/nemaki/test/nemaki/TestBase.java
@@ -0,0 +1,202 @@
+package jp.aegif.nemaki.test.nemaki;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import javax.activation.FileTypeMap;
+
+import org.apache.chemistry.opencmis.client.api.Folder;
+import org.apache.chemistry.opencmis.client.api.ObjectId;
+import org.apache.chemistry.opencmis.client.api.OperationContext;
+import org.apache.chemistry.opencmis.client.api.Session;
+import org.apache.chemistry.opencmis.client.runtime.ObjectIdImpl;
+import org.apache.chemistry.opencmis.client.runtime.OperationContextImpl;
+import org.apache.chemistry.opencmis.commons.PropertyIds;
+import org.apache.chemistry.opencmis.commons.data.ContentStream;
+import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
+import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships;
+import org.apache.chemistry.opencmis.commons.enums.UnfileObject;
+import org.apache.chemistry.opencmis.commons.enums.VersioningState;
+import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+public class TestBase {
+ protected static Session session;
+ protected static String testFolderId;
+
+ @BeforeClass
+ public static void before() throws Exception {
+ session = SessionUtil.createCmisSession("bedroom", "admin", "admin");
+ testFolderId = prepareData();
+ }
+
+ @AfterClass
+ public static void after() throws Exception {
+ Folder folder = (Folder) session.getObject(testFolderId);
+ folder.deleteTree(true, UnfileObject.DELETE, true);
+ }
+
+ public static String prepareData() throws Exception{
+ int itemNumber = 100;
+
+ String rootFolderId = session.getRepositoryInfo().getRootFolderId();
+
+ String testFolderId = createFolder(rootFolderId, "test_general_" + System.currentTimeMillis());
+
+ List tasks = new ArrayList<>();
+ for(int i=1; i<=itemNumber; i++){
+ tasks.add(new CreateDocumentTask("task_" + i , testFolderId, "task_" + i + ".txt", "これはテストです"));
+ }
+
+ List> _results = new ArrayList<>();
+ ExecutorService executor = Executors.newCachedThreadPool();
+ _results = executor.invokeAll(tasks);
+
+ List results = new ArrayList<>();
+ for(Future _result : _results){
+ results.add(_result.get());
+ }
+
+ System.out.println("data initilization completed: " + results.size() + " items");
+ return testFolderId;
+ }
+
+ public static String createFolder(String parentId, String name){
+ Mapmap = new HashMap<>();
+ map.put(PropertyIds.OBJECT_TYPE_ID, BaseTypeId.CMIS_FOLDER.value());
+ map.put(PropertyIds.PARENT_ID, parentId);
+ map.put(PropertyIds.NAME, name);
+ ObjectId result = session.createFolder(map, new ObjectIdImpl(parentId));
+ return result.getId();
+ }
+
+ private static class CreateDocumentTask implements Callable{
+ String taskId;
+ String parentId;
+ String name;
+ String text;
+
+ public CreateDocumentTask(String taskId, String parentId, String name, String text) {
+ super();
+ this.taskId = taskId;
+ this.parentId = parentId;
+ this.name = name;
+ this.text = text;
+ }
+ @Override
+ public String call() throws Exception {
+ String objectId = createDocument(parentId, name, text);
+ System.out.println(objectId + "(" + name + ") is created.");
+ return objectId;
+ }
+ }
+
+ protected static OperationContext simpleOperationContext(String...filters ){
+ OperationContextImpl oc = new OperationContextImpl();
+ if(filters.length > 0){
+ Set _filters = new HashSet(Arrays.asList(PropertyIds.OBJECT_ID));
+ oc.setFilter(_filters);
+ }
+ oc.setIncludeAllowableActions(false);
+ oc.setIncludeAcls(false);
+ oc.setIncludePolicies(false);
+ oc.setIncludeRelationships(IncludeRelationships.NONE);
+
+ return oc;
+ }
+
+ public static String createTestFolder(){
+ String rootFolderId = session.getRepositoryInfo().getRootFolderId();
+
+ Mapmap = new HashMap<>();
+ map.put(PropertyIds.OBJECT_TYPE_ID, BaseTypeId.CMIS_FOLDER.value());
+ map.put(PropertyIds.PARENT_ID, rootFolderId);
+ map.put(PropertyIds.NAME, "testFolder_" + System.currentTimeMillis());
+ ObjectId result = session.createFolder(map, new ObjectIdImpl(rootFolderId));
+ return result.getId();
+ }
+
+ public static String createDocument(String parentId, String name, File file){
+ Mapmap = new HashMap<>();
+ map.put(PropertyIds.OBJECT_TYPE_ID, BaseTypeId.CMIS_DOCUMENT.value());
+ map.put(PropertyIds.NAME, name);
+
+ ContentStream contentStream = convertFileToContentStream(session, file);
+
+ ObjectId objectId = session.createDocument(map, new ObjectIdImpl(parentId), contentStream, VersioningState.MAJOR);
+
+ return objectId.getId();
+ }
+
+ public static String createDocument(String parentId, String name, String string){
+ Mapmap = new HashMap<>();
+ map.put(PropertyIds.OBJECT_TYPE_ID, BaseTypeId.CMIS_DOCUMENT.value());
+ map.put(PropertyIds.NAME, name);
+
+ ContentStream contentStream = new ContentStreamImpl(name, "text/plain", string);
+
+ ObjectId objectId = session.createDocument(map, new ObjectIdImpl(parentId), contentStream, VersioningState.MAJOR);
+
+ return objectId.getId();
+ }
+
+ public static File convertInputStreamToFile(InputStream inputStream)
+ throws IOException {
+
+ File file = File.createTempFile(
+ String.valueOf(System.currentTimeMillis()), null);
+ file.deleteOnExit();
+
+ OutputStream out = new FileOutputStream(file);
+ try {
+ int read = 0;
+ byte[] bytes = new byte[1024];
+ while ((read = inputStream.read(bytes)) != -1) {
+ out.write(bytes, 0, read);
+ }
+ } catch (IOException e) {
+ System.out.println(e.getMessage());
+ }finally{
+ inputStream.close();
+
+ out.close();
+ }
+
+ return file;
+ }
+
+ public static ContentStream convertFileToContentStream(Session session,
+ File file) {
+ FileInputStream fis = null;
+ try {
+ fis = new FileInputStream(file);
+ } catch (FileNotFoundException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ FileTypeMap filetypeMap = FileTypeMap.getDefaultFileTypeMap();
+ String mimetype = filetypeMap.getContentType(file);
+
+ ContentStream cs = session.getObjectFactory().createContentStream(
+ file.getName(), file.length(), mimetype, fis);
+ return cs;
+ }
+}
diff --git a/core/src/test/java/jp/aegif/nemaki/test/tck/AllTest.java b/core/src/test/java/jp/aegif/nemaki/test/tck/AllTest.java
new file mode 100644
index 00000000..58747c49
--- /dev/null
+++ b/core/src/test/java/jp/aegif/nemaki/test/tck/AllTest.java
@@ -0,0 +1,26 @@
+package jp.aegif.nemaki.test.tck;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+import jp.aegif.nemaki.test.tck.tests.BasicsTestGroup;
+import jp.aegif.nemaki.test.tck.tests.ControlTestGroup;
+import jp.aegif.nemaki.test.tck.tests.CrudTestGroup;
+import jp.aegif.nemaki.test.tck.tests.FilingTestGroup;
+import jp.aegif.nemaki.test.tck.tests.QueryTestGroup;
+import jp.aegif.nemaki.test.tck.tests.TypesTestGroup;
+import jp.aegif.nemaki.test.tck.tests.VersioningTestGroup;
+
+@RunWith( Suite.class )
+@Suite.SuiteClasses( {
+ BasicsTestGroup.class,
+ ControlTestGroup.class,
+ CrudTestGroup.class,
+ FilingTestGroup.class,
+ QueryTestGroup.class,
+ TypesTestGroup.class,
+ VersioningTestGroup.class,
+} )
+public class AllTest extends TckSuite{
+
+}
diff --git a/core/src/test/java/jp/aegif/nemaki/test/cmis/TckSuite.java b/core/src/test/java/jp/aegif/nemaki/test/tck/TckSuite.java
similarity index 90%
rename from core/src/test/java/jp/aegif/nemaki/test/cmis/TckSuite.java
rename to core/src/test/java/jp/aegif/nemaki/test/tck/TckSuite.java
index 2e7534bd..e54a2d28 100644
--- a/core/src/test/java/jp/aegif/nemaki/test/cmis/TckSuite.java
+++ b/core/src/test/java/jp/aegif/nemaki/test/tck/TckSuite.java
@@ -1,4 +1,4 @@
-package jp.aegif.nemaki.test.cmis;
+package jp.aegif.nemaki.test.tck;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -14,13 +14,14 @@
import org.apache.chemistry.opencmis.tck.report.XmlReport;
import org.junit.ClassRule;
import org.junit.rules.ExternalResource;
-import jp.aegif.nemaki.test.cmis.tests.BasicsTestGroup;
-import jp.aegif.nemaki.test.cmis.tests.ControlTestGroup;
-import jp.aegif.nemaki.test.cmis.tests.CrudTestGroup;
-import jp.aegif.nemaki.test.cmis.tests.FilingTestGroup;
-import jp.aegif.nemaki.test.cmis.tests.QueryTestGroup;
-import jp.aegif.nemaki.test.cmis.tests.TypesTestGroup;
-import jp.aegif.nemaki.test.cmis.tests.VersioningTestGroup;
+
+import jp.aegif.nemaki.test.tck.tests.BasicsTestGroup;
+import jp.aegif.nemaki.test.tck.tests.ControlTestGroup;
+import jp.aegif.nemaki.test.tck.tests.CrudTestGroup;
+import jp.aegif.nemaki.test.tck.tests.FilingTestGroup;
+import jp.aegif.nemaki.test.tck.tests.QueryTestGroup;
+import jp.aegif.nemaki.test.tck.tests.TypesTestGroup;
+import jp.aegif.nemaki.test.tck.tests.VersioningTestGroup;
public class TckSuite extends TestGroupBase{
diff --git a/core/src/test/java/jp/aegif/nemaki/test/cmis/TestGroupBase.java b/core/src/test/java/jp/aegif/nemaki/test/tck/TestGroupBase.java
similarity index 99%
rename from core/src/test/java/jp/aegif/nemaki/test/cmis/TestGroupBase.java
rename to core/src/test/java/jp/aegif/nemaki/test/tck/TestGroupBase.java
index 1d27adc9..d77bf54f 100644
--- a/core/src/test/java/jp/aegif/nemaki/test/cmis/TestGroupBase.java
+++ b/core/src/test/java/jp/aegif/nemaki/test/tck/TestGroupBase.java
@@ -1,4 +1,4 @@
-package jp.aegif.nemaki.test.cmis;
+package jp.aegif.nemaki.test.tck;
import java.io.File;
import java.io.FileInputStream;
diff --git a/core/src/test/java/jp/aegif/nemaki/test/cmis/TestHelper.java b/core/src/test/java/jp/aegif/nemaki/test/tck/TestHelper.java
similarity index 97%
rename from core/src/test/java/jp/aegif/nemaki/test/cmis/TestHelper.java
rename to core/src/test/java/jp/aegif/nemaki/test/tck/TestHelper.java
index 3845600c..7e3bea0e 100644
--- a/core/src/test/java/jp/aegif/nemaki/test/cmis/TestHelper.java
+++ b/core/src/test/java/jp/aegif/nemaki/test/tck/TestHelper.java
@@ -1,4 +1,4 @@
-package jp.aegif.nemaki.test.cmis;
+package jp.aegif.nemaki.test.tck;
import java.io.File;
import java.util.Map;
@@ -12,7 +12,7 @@
import org.apache.chemistry.opencmis.tck.runner.AbstractRunner;
import org.junit.Assert;
-import jp.aegif.nemaki.test.cmis.tests.BasicsTestGroup;
+import jp.aegif.nemaki.test.tck.tests.BasicsTestGroup;
public class TestHelper {
private static final String PARAMETERS_FILE_NAME = "cmis-tck-parameters.properties";
diff --git a/core/src/test/java/jp/aegif/nemaki/test/cmis/tests/BasicsTestGroup.java b/core/src/test/java/jp/aegif/nemaki/test/tck/tests/BasicsTestGroup.java
similarity index 88%
rename from core/src/test/java/jp/aegif/nemaki/test/cmis/tests/BasicsTestGroup.java
rename to core/src/test/java/jp/aegif/nemaki/test/tck/tests/BasicsTestGroup.java
index 8ded561a..7b4f84f1 100644
--- a/core/src/test/java/jp/aegif/nemaki/test/cmis/tests/BasicsTestGroup.java
+++ b/core/src/test/java/jp/aegif/nemaki/test/tck/tests/BasicsTestGroup.java
@@ -1,10 +1,11 @@
-package jp.aegif.nemaki.test.cmis.tests;
+package jp.aegif.nemaki.test.tck.tests;
import org.apache.chemistry.opencmis.tck.tests.basics.RepositoryInfoTest;
import org.apache.chemistry.opencmis.tck.tests.basics.RootFolderTest;
import org.apache.chemistry.opencmis.tck.tests.basics.SecurityTest;
import org.junit.Test;
-import jp.aegif.nemaki.test.cmis.TckSuite;
+
+import jp.aegif.nemaki.test.tck.TckSuite;
public class BasicsTestGroup extends TckSuite{
diff --git a/core/src/test/java/jp/aegif/nemaki/test/cmis/tests/ControlTestGroup.java b/core/src/test/java/jp/aegif/nemaki/test/tck/tests/ControlTestGroup.java
similarity index 75%
rename from core/src/test/java/jp/aegif/nemaki/test/cmis/tests/ControlTestGroup.java
rename to core/src/test/java/jp/aegif/nemaki/test/tck/tests/ControlTestGroup.java
index 43d811db..0dda627f 100644
--- a/core/src/test/java/jp/aegif/nemaki/test/cmis/tests/ControlTestGroup.java
+++ b/core/src/test/java/jp/aegif/nemaki/test/tck/tests/ControlTestGroup.java
@@ -1,9 +1,9 @@
-package jp.aegif.nemaki.test.cmis.tests;
+package jp.aegif.nemaki.test.tck.tests;
import org.apache.chemistry.opencmis.tck.tests.control.ACLSmokeTest;
import org.junit.Test;
-import jp.aegif.nemaki.test.cmis.TckSuite;
+import jp.aegif.nemaki.test.tck.TckSuite;
public class ControlTestGroup extends TckSuite{
@Test
diff --git a/core/src/test/java/jp/aegif/nemaki/test/cmis/tests/CrudTestGroup.java b/core/src/test/java/jp/aegif/nemaki/test/tck/tests/CrudTestGroup.java
similarity index 97%
rename from core/src/test/java/jp/aegif/nemaki/test/cmis/tests/CrudTestGroup.java
rename to core/src/test/java/jp/aegif/nemaki/test/tck/tests/CrudTestGroup.java
index e903c0de..2343ef57 100644
--- a/core/src/test/java/jp/aegif/nemaki/test/cmis/tests/CrudTestGroup.java
+++ b/core/src/test/java/jp/aegif/nemaki/test/tck/tests/CrudTestGroup.java
@@ -1,4 +1,4 @@
-package jp.aegif.nemaki.test.cmis.tests;
+package jp.aegif.nemaki.test.tck.tests;
import org.apache.chemistry.opencmis.tck.tests.crud.BulkUpdatePropertiesTest;
import org.apache.chemistry.opencmis.tck.tests.crud.ChangeTokenTest;
@@ -21,7 +21,7 @@
import org.apache.chemistry.opencmis.tck.tests.crud.WhitespaceInNameTest;
import org.junit.Test;
-import jp.aegif.nemaki.test.cmis.TckSuite;
+import jp.aegif.nemaki.test.tck.TckSuite;
public class CrudTestGroup extends TckSuite{
diff --git a/core/src/test/java/jp/aegif/nemaki/test/cmis/tests/FilingTestGroup.java b/core/src/test/java/jp/aegif/nemaki/test/tck/tests/FilingTestGroup.java
similarity index 84%
rename from core/src/test/java/jp/aegif/nemaki/test/cmis/tests/FilingTestGroup.java
rename to core/src/test/java/jp/aegif/nemaki/test/tck/tests/FilingTestGroup.java
index 87971a1a..04a107bb 100644
--- a/core/src/test/java/jp/aegif/nemaki/test/cmis/tests/FilingTestGroup.java
+++ b/core/src/test/java/jp/aegif/nemaki/test/tck/tests/FilingTestGroup.java
@@ -1,10 +1,10 @@
-package jp.aegif.nemaki.test.cmis.tests;
+package jp.aegif.nemaki.test.tck.tests;
import org.apache.chemistry.opencmis.tck.tests.filing.MultifilingTest;
import org.apache.chemistry.opencmis.tck.tests.filing.UnfilingTest;
import org.junit.Test;
-import jp.aegif.nemaki.test.cmis.TckSuite;
+import jp.aegif.nemaki.test.tck.TckSuite;
public class FilingTestGroup extends TckSuite{
@Test
diff --git a/core/src/test/java/jp/aegif/nemaki/test/cmis/tests/QueryTestGroup.java b/core/src/test/java/jp/aegif/nemaki/test/tck/tests/QueryTestGroup.java
similarity index 93%
rename from core/src/test/java/jp/aegif/nemaki/test/cmis/tests/QueryTestGroup.java
rename to core/src/test/java/jp/aegif/nemaki/test/tck/tests/QueryTestGroup.java
index 03355d70..a2831f0b 100644
--- a/core/src/test/java/jp/aegif/nemaki/test/cmis/tests/QueryTestGroup.java
+++ b/core/src/test/java/jp/aegif/nemaki/test/tck/tests/QueryTestGroup.java
@@ -1,4 +1,4 @@
-package jp.aegif.nemaki.test.cmis.tests;
+package jp.aegif.nemaki.test.tck.tests;
import org.apache.chemistry.opencmis.tck.tests.query.ContentChangesSmokeTest;
import org.apache.chemistry.opencmis.tck.tests.query.QueryForObject;
@@ -8,7 +8,7 @@
import org.apache.chemistry.opencmis.tck.tests.query.QuerySmokeTest;
import org.junit.Test;
-import jp.aegif.nemaki.test.cmis.TckSuite;
+import jp.aegif.nemaki.test.tck.TckSuite;
public class QueryTestGroup extends TckSuite{
@Test
diff --git a/core/src/test/java/jp/aegif/nemaki/test/cmis/tests/TypesTestGroup.java b/core/src/test/java/jp/aegif/nemaki/test/tck/tests/TypesTestGroup.java
similarity index 89%
rename from core/src/test/java/jp/aegif/nemaki/test/cmis/tests/TypesTestGroup.java
rename to core/src/test/java/jp/aegif/nemaki/test/tck/tests/TypesTestGroup.java
index b3aa3fbc..45f9b3cf 100644
--- a/core/src/test/java/jp/aegif/nemaki/test/cmis/tests/TypesTestGroup.java
+++ b/core/src/test/java/jp/aegif/nemaki/test/tck/tests/TypesTestGroup.java
@@ -1,11 +1,11 @@
-package jp.aegif.nemaki.test.cmis.tests;
+package jp.aegif.nemaki.test.tck.tests;
import org.apache.chemistry.opencmis.tck.tests.types.BaseTypesTest;
import org.apache.chemistry.opencmis.tck.tests.types.CreateAndDeleteTypeTest;
import org.apache.chemistry.opencmis.tck.tests.types.SecondaryTypesTest;
import org.junit.Test;
-import jp.aegif.nemaki.test.cmis.TckSuite;
+import jp.aegif.nemaki.test.tck.TckSuite;
public class TypesTestGroup extends TckSuite{
@Test
diff --git a/core/src/test/java/jp/aegif/nemaki/test/cmis/tests/VersioningTestGroup.java b/core/src/test/java/jp/aegif/nemaki/test/tck/tests/VersioningTestGroup.java
similarity index 91%
rename from core/src/test/java/jp/aegif/nemaki/test/cmis/tests/VersioningTestGroup.java
rename to core/src/test/java/jp/aegif/nemaki/test/tck/tests/VersioningTestGroup.java
index 7478d2fa..2ffed53b 100644
--- a/core/src/test/java/jp/aegif/nemaki/test/cmis/tests/VersioningTestGroup.java
+++ b/core/src/test/java/jp/aegif/nemaki/test/tck/tests/VersioningTestGroup.java
@@ -1,4 +1,4 @@
-package jp.aegif.nemaki.test.cmis.tests;
+package jp.aegif.nemaki.test.tck.tests;
import org.apache.chemistry.opencmis.tck.tests.versioning.CheckedOutTest;
import org.apache.chemistry.opencmis.tck.tests.versioning.VersionDeleteTest;
@@ -6,7 +6,7 @@
import org.apache.chemistry.opencmis.tck.tests.versioning.VersioningStateCreateTest;
import org.junit.Test;
-import jp.aegif.nemaki.test.cmis.TckSuite;
+import jp.aegif.nemaki.test.tck.TckSuite;
public class VersioningTestGroup extends TckSuite{
@Test
diff --git a/core/src/test/resources/cmis-tck-filters.properties b/core/src/test/resources/cmis-tck-filters.properties
index fc0f0f39..f5efb702 100644
--- a/core/src/test/resources/cmis-tck-filters.properties
+++ b/core/src/test/resources/cmis-tck-filters.properties
@@ -45,7 +45,7 @@ TypesTestGroup=true
secondaryTypesTest=true
VersioningTestGroup=true
- versioningSmokeTest=false
+ versioningSmokeTest=true
versionDeleteTest=true
versioningStateCreateTest=true
checkedOutTest=true
\ No newline at end of file
diff --git a/setup/installer/install.xml b/setup/installer/install.xml
index c7dbccac..15607965 100755
--- a/setup/installer/install.xml
+++ b/setup/installer/install.xml
@@ -3,7 +3,7 @@
NemakiWare
- 2.3.12
+ 2.3.13
http://www.nemakiware.com/