ALF-11237 - imap folders and modification.

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@32796 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Mark Rogers
2011-12-15 17:35:54 +00:00
parent ba9dcab4ab
commit beb261ed4e

View File

@@ -85,6 +85,7 @@ import org.alfresco.service.cmr.model.FileNotFoundException;
import org.alfresco.service.cmr.model.SubFolderFilter; import org.alfresco.service.cmr.model.SubFolderFilter;
import org.alfresco.service.cmr.preference.PreferenceService; import org.alfresco.service.cmr.preference.PreferenceService;
import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.InvalidNodeRefException; import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.MimetypeService; import org.alfresco.service.cmr.repository.MimetypeService;
@@ -97,6 +98,7 @@ import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.site.SiteInfo; import org.alfresco.service.cmr.site.SiteInfo;
import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.alfresco.util.EqualsHelper;
import org.alfresco.util.GUID; import org.alfresco.util.GUID;
import org.alfresco.util.MaxSizeMap; import org.alfresco.util.MaxSizeMap;
import org.alfresco.util.Pair; import org.alfresco.util.Pair;
@@ -141,7 +143,8 @@ public class ImapServiceImpl implements ImapService, OnCreateChildAssociationPol
private NamespaceService namespaceService; private NamespaceService namespaceService;
private SearchService searchService; private SearchService searchService;
// Note that this cache need not be cluster synchronized, as it is keyed by the cluster-safe change token // Note that this cache need not be cluster synchronized, as it is keyed by the cluster-safe
// change token. Key is username, changeToken
private Map<Pair<String, String>, FolderStatus> folderCache; private Map<Pair<String, String>, FolderStatus> folderCache;
private int folderCacheSize = 1000; private int folderCacheSize = 1000;
private ReentrantReadWriteLock folderCacheLock = new ReentrantReadWriteLock(); private ReentrantReadWriteLock folderCacheLock = new ReentrantReadWriteLock();
@@ -730,7 +733,7 @@ public class ImapServiceImpl implements ImapService, OnCreateChildAssociationPol
{ {
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {
logger.debug("Search mails contextNodeRef=" + contextNodeRef + ", viewMode=" + viewMode); logger.debug("getFolderStatus contextNodeRef=" + contextNodeRef + ", viewMode=" + viewMode);
} }
// No need to ACL check the change token read // No need to ACL check the change token read
@@ -838,11 +841,19 @@ public class ImapServiceImpl implements ImapService, OnCreateChildAssociationPol
FolderStatus oldResult = this.folderCache.get(cacheKey); FolderStatus oldResult = this.folderCache.get(cacheKey);
if (oldResult != null) if (oldResult != null)
{ {
if(logger.isDebugEnabled())
{
logger.debug("At end of getFolderStatus. Found info in cache, changeToken:" + changeToken);
}
return oldResult; return oldResult;
} }
this.folderCache.put(cacheKey, result); this.folderCache.put(cacheKey, result);
logger.debug("Found files:" + currentSearch.size()); if(logger.isDebugEnabled())
{
logger.debug("At end of getFolderStatus. Found files:" + currentSearch.size() + ", changeToken:" + changeToken);
}
return result; return result;
} }
finally finally
@@ -944,6 +955,10 @@ public class ImapServiceImpl implements ImapService, OnCreateChildAssociationPol
} }
else else
{ {
if(logger.isDebugEnabled())
{
logger.debug("set flag nodeRef:" + nodeRef + ",flag:" + flagToQname.get(flag) + ", value:" + value);
}
nodeService.setProperty(nodeRef, flagToQname.get(flag), value); nodeService.setProperty(nodeRef, flagToQname.get(flag), value);
} }
messageCache.remove(nodeRef); messageCache.remove(nodeRef);
@@ -1598,14 +1613,53 @@ public class ImapServiceImpl implements ImapService, OnCreateChildAssociationPol
} }
@Override @Override
public void onUpdateProperties(final NodeRef nodeRef, Map<QName, Serializable> before, public void onUpdateProperties(final NodeRef nodeRef, final Map<QName, Serializable> before,
Map<QName, Serializable> after) final Map<QName, Serializable> after)
{ {
doAsSystem(new RunAsWork<Void>() doAsSystem(new RunAsWork<Void>()
{ {
@Override @Override
public Void doWork() throws Exception public Void doWork() throws Exception
{ {
/**
* Imap only cares about a few properties however if those properties
* change then the uidvalidity needs to be reset otherwise the new content
* won't get re-loaded. This is nonsense for an email server, but needed for
* modifiable repository. Also we need to ignore certain properties.
*/
boolean hasChanged = false;
if(!hasChanged)
{
hasChanged = !EqualsHelper.nullSafeEquals(before.get(ContentModel.PROP_NAME), after.get(ContentModel.PROP_NAME));
}
if(!hasChanged)
{
hasChanged = !EqualsHelper.nullSafeEquals(before.get(ContentModel.PROP_AUTHOR), after.get(ContentModel.PROP_AUTHOR));
}
if(!hasChanged)
{
hasChanged = !EqualsHelper.nullSafeEquals(before.get(ContentModel.PROP_TITLE), after.get(ContentModel.PROP_TITLE));
}
if(!hasChanged)
{
hasChanged = !EqualsHelper.nullSafeEquals(before.get(ContentModel.PROP_DESCRIPTION), after.get(ContentModel.PROP_DESCRIPTION));
}
if(!hasChanged)
{
Serializable s1 = before.get(ContentModel.PROP_CONTENT);
Serializable s2 = after.get(ContentModel.PROP_CONTENT);
if(s1 != null && s2 != null)
{
ContentData c1 = (ContentData)s1;
ContentData c2 = (ContentData)s2;
hasChanged = !EqualsHelper.nullSafeEquals(c1.getContentUrl(), c2.getContentUrl());
}
}
for (ChildAssociationRef parentAssoc : nodeService.getParentAssocs(nodeRef)) for (ChildAssociationRef parentAssoc : nodeService.getParentAssocs(nodeRef))
{ {
NodeRef folderRef = parentAssoc.getParentRef(); NodeRef folderRef = parentAssoc.getParentRef();
@@ -1613,8 +1667,15 @@ public class ImapServiceImpl implements ImapService, OnCreateChildAssociationPol
{ {
messageCache.remove(nodeRef); messageCache.remove(nodeRef);
// Force generation of a new change token // Force generation of a new change token for the parent folders
getUidValidityTransactionListener(folderRef); UidValidityTransactionListener listener = getUidValidityTransactionListener(folderRef);
// if we have a significant change then we need to force a new uidvalidity.
if(hasChanged)
{
logger.debug("message has changed - force new uidvalidity for the parent folder");
listener.forceNewUidvalidity();
}
} }
} }
return null; return null;
@@ -1669,6 +1730,7 @@ public class ImapServiceImpl implements ImapService, OnCreateChildAssociationPol
private NodeRef folderNodeRef; private NodeRef folderNodeRef;
private Long minUid; private Long minUid;
private Long maxUid; private Long maxUid;
private boolean forceNewUidValidity = false;
public UidValidityTransactionListener(NodeRef folderNodeRef, NodeService nodeService) public UidValidityTransactionListener(NodeRef folderNodeRef, NodeService nodeService)
{ {
@@ -1676,6 +1738,11 @@ public class ImapServiceImpl implements ImapService, OnCreateChildAssociationPol
this.nodeService = nodeService; this.nodeService = nodeService;
} }
public void forceNewUidvalidity()
{
this.forceNewUidValidity = true;
}
public void recordNewUid(long newUid) public void recordNewUid(long newUid)
{ {
if (this.minUid == null) if (this.minUid == null)
@@ -1705,23 +1772,26 @@ public class ImapServiceImpl implements ImapService, OnCreateChildAssociationPol
@Override @Override
public Void doWork() throws Exception public Void doWork() throws Exception
{ {
if (UidValidityTransactionListener.this.minUid != null) if (UidValidityTransactionListener.this.forceNewUidValidity || UidValidityTransactionListener.this.minUid != null)
{ {
long modifDate = System.currentTimeMillis(); long modifDate = System.currentTimeMillis();
Long oldMax = (Long)UidValidityTransactionListener.this.nodeService.getProperty(folderNodeRef, ImapModel.PROP_MAXUID); Long oldMax = (Long)UidValidityTransactionListener.this.nodeService.getProperty(folderNodeRef, ImapModel.PROP_MAXUID);
// Only update UIDVALIDITY if a new node has and ID that is smaller than the old maximum (as UIDs are always meant to increase) // Only update UIDVALIDITY if a new node has and ID that is smaller than the old maximum (as UIDs are always meant to increase)
if (oldMax == null || UidValidityTransactionListener.this.minUid < oldMax) if (UidValidityTransactionListener.this.forceNewUidValidity || oldMax == null || UidValidityTransactionListener.this.minUid < oldMax)
{ {
UidValidityTransactionListener.this.nodeService.setProperty(folderNodeRef, ImapModel.PROP_UIDVALIDITY, modifDate); UidValidityTransactionListener.this.nodeService.setProperty(folderNodeRef, ImapModel.PROP_UIDVALIDITY, modifDate);
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {
logger.debug("UIDVALIDITY was modified for " + folderNodeRef); logger.debug("UIDVALIDITY was modified for folder, nodeRef:" + folderNodeRef);
} }
} }
if(UidValidityTransactionListener.this.maxUid != null)
{
UidValidityTransactionListener.this.nodeService.setProperty(folderNodeRef, ImapModel.PROP_MAXUID, UidValidityTransactionListener.this.maxUid); UidValidityTransactionListener.this.nodeService.setProperty(folderNodeRef, ImapModel.PROP_MAXUID, UidValidityTransactionListener.this.maxUid);
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {
logger.debug("MAXUID was modified for " + folderNodeRef); logger.debug("MAXUID was modified for folder, nodeRef:" + folderNodeRef);
}
} }
} }
UidValidityTransactionListener.this.nodeService.setProperty(folderNodeRef, ImapModel.PROP_CHANGE_TOKEN, changeToken); UidValidityTransactionListener.this.nodeService.setProperty(folderNodeRef, ImapModel.PROP_CHANGE_TOKEN, changeToken);