(id, entity.getEncoding().toUpperCase());
}
}
diff --git a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java
index 403ad1a048..81851059bb 100644
--- a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java
+++ b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java
@@ -79,8 +79,8 @@ import org.alfresco.util.GUID;
import org.alfresco.util.Pair;
import org.alfresco.util.PropertyCheck;
import org.alfresco.util.ReadWriteLockExecuter;
+import org.alfresco.util.SerializationUtils;
import org.alfresco.util.EqualsHelper.MapValueComparison;
-import org.apache.commons.lang.SerializationUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.ConcurrencyFailureException;
@@ -94,10 +94,6 @@ import org.springframework.util.Assert;
* for CRUD operations.
*
* TODO: Timestamp propagation
- * TODO: Local retries for certain operations that might benefit
- * TODO: Take out joins to parent nodes for selectChildAssoc queries (it's static data)
- * TODO: Child nodes' cache invalidation must use a leaner query
- * TODO: Bulk loading of caches
*
* @author Derek Hulley
* @since 3.4
@@ -357,6 +353,13 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
{
return serverIdStorage.get();
}
+ // Avoid write operations in read-only transactions
+ // ALF-5456: IP address change can cause read-write errors on startup
+ if (AlfrescoTransactionSupport.getTransactionReadState() == TxnReadState.TXN_READ_ONLY)
+ {
+ return null;
+ }
+
// Server IP address
String ipAddress = null;
try
@@ -386,7 +389,8 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
}
/**
- * Get the ID of the current server
+ * Get the ID of the current server, or null if there is no ID for the current
+ * server and one can't be created.
*
* @see ServerIdCallback
*/
@@ -792,14 +796,14 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
*
* @param nodeId the node
* @return Returns the fully populated node
- * @throws DataIntegrityViolationException if the ID doesn't reference a live node
+ * @throws ConcurrencyFailureException if the ID doesn't reference a live node
*/
private Node getNodeNotNull(Long nodeId)
{
Pair pair = nodesCache.getByKey(nodeId);
if (pair == null || pair.getSecond().getDeleted())
{
- throw new DataIntegrityViolationException("No live node exists for ID " + nodeId);
+ throw new ConcurrencyFailureException("No live node exists for ID " + nodeId);
}
else
{
diff --git a/source/java/org/alfresco/repo/domain/propval/PropertyValueDAOTest.java b/source/java/org/alfresco/repo/domain/propval/PropertyValueDAOTest.java
index 1d4e67d69a..1ef1281642 100644
--- a/source/java/org/alfresco/repo/domain/propval/PropertyValueDAOTest.java
+++ b/source/java/org/alfresco/repo/domain/propval/PropertyValueDAOTest.java
@@ -171,7 +171,7 @@ public class PropertyValueDAOTest extends TestCase
public void testPropertyStringValue() throws Exception
{
- final String stringValue = "One Two Three - " + System.currentTimeMillis();
+ final String stringValue = "One Two Three - àâæçéèêëîïôœùûüÿñ - " + System.currentTimeMillis();
final String stringValueUpper = stringValue.toUpperCase();
final String stringValueLower = stringValue.toLowerCase();
RetryingTransactionCallback> createStringCallback = new RetryingTransactionCallback>()
diff --git a/source/java/org/alfresco/repo/forms/FormServiceImplTest.java b/source/java/org/alfresco/repo/forms/FormServiceImplTest.java
index 5de79d3191..33e4dd2c3e 100644
--- a/source/java/org/alfresco/repo/forms/FormServiceImplTest.java
+++ b/source/java/org/alfresco/repo/forms/FormServiceImplTest.java
@@ -222,7 +222,7 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
aspectProps.put(ContentModel.PROP_ADDRESSEES, (Serializable)addressees);
aspectProps.put(ContentModel.PROP_SUBJECT, VALUE_SUBJECT);
aspectProps.put(ContentModel.PROP_SENTDATE, VALUE_SENT_DATE);
- this.nodeService.addAspect(this.document, ContentModel.ASPECT_MAILED, aspectProps);
+ this.nodeService.addAspect(this.document, ContentModel.ASPECT_EMAILED, aspectProps);
// add referencing aspect (has association)
aspectProps.clear();
diff --git a/source/java/org/alfresco/repo/forms/processor/node/ContentModelFormProcessor.java b/source/java/org/alfresco/repo/forms/processor/node/ContentModelFormProcessor.java
index c006b296dd..e4a544be4b 100644
--- a/source/java/org/alfresco/repo/forms/processor/node/ContentModelFormProcessor.java
+++ b/source/java/org/alfresco/repo/forms/processor/node/ContentModelFormProcessor.java
@@ -24,6 +24,8 @@ import static org.alfresco.repo.forms.processor.node.FormFieldConstants.ASSOC_DA
import static org.alfresco.repo.forms.processor.node.FormFieldConstants.DEFAULT_CONTENT_MIMETYPE;
import static org.alfresco.repo.forms.processor.node.FormFieldConstants.ON;
import static org.alfresco.repo.forms.processor.node.FormFieldConstants.PROP_DATA_PREFIX;
+import static org.alfresco.repo.forms.processor.node.FormFieldConstants.DOT_CHARACTER;
+import static org.alfresco.repo.forms.processor.node.FormFieldConstants.DOT_CHARACTER_REPLACEMENT;
import java.io.Serializable;
import java.util.ArrayList;
@@ -300,7 +302,7 @@ public abstract class ContentModelFormProcessor extends
getLogger().debug("Processing field " + fieldData + " for property persistence");
// match and extract the prefix and name parts
- Matcher m = this.propertyNamePattern.matcher(fieldData.getName());
+ Matcher m = this.propertyNamePattern.matcher(fieldData.getName().replaceAll(DOT_CHARACTER_REPLACEMENT, DOT_CHARACTER));
if (m.matches())
{
String qNamePrefix = m.group(1);
@@ -483,7 +485,7 @@ public abstract class ContentModelFormProcessor extends
getLogger().debug("Processing field " + fieldData + " for association persistence");
String fieldName = fieldData.getName();
- Matcher m = this.associationNamePattern.matcher(fieldName);
+ Matcher m = this.associationNamePattern.matcher(fieldName.replaceAll(DOT_CHARACTER_REPLACEMENT, DOT_CHARACTER));
if (m.matches())
{
String qNamePrefix = m.group(1);
diff --git a/source/java/org/alfresco/repo/forms/processor/node/FormFieldConstants.java b/source/java/org/alfresco/repo/forms/processor/node/FormFieldConstants.java
index 199a561888..e9fd0209e5 100644
--- a/source/java/org/alfresco/repo/forms/processor/node/FormFieldConstants.java
+++ b/source/java/org/alfresco/repo/forms/processor/node/FormFieldConstants.java
@@ -48,6 +48,10 @@ public interface FormFieldConstants
public static final String REMOVED = "removed";
+ public static final String DOT_CHARACTER = ".";
+
+ public static final String DOT_CHARACTER_REPLACEMENT = "#dot#";
+
/** Protected constants */
public static final String DEFAULT_CONTENT_MIMETYPE = "text/plain";
diff --git a/source/java/org/alfresco/repo/forms/processor/workflow/TaskFormPersister.java b/source/java/org/alfresco/repo/forms/processor/workflow/TaskFormPersister.java
index 606d12f965..631ba58e21 100644
--- a/source/java/org/alfresco/repo/forms/processor/workflow/TaskFormPersister.java
+++ b/source/java/org/alfresco/repo/forms/processor/workflow/TaskFormPersister.java
@@ -161,9 +161,6 @@ public class TaskFormPersister extends ContentModelFormPersister
}
else
{
- // update the task first
- updater.update();
-
if (transitionId.length() == 0)
{
// transition with the default transition
diff --git a/source/java/org/alfresco/repo/forms/processor/workflow/TaskFormProcessor.java b/source/java/org/alfresco/repo/forms/processor/workflow/TaskFormProcessor.java
index 2308ad7ae7..52c3a00a7c 100644
--- a/source/java/org/alfresco/repo/forms/processor/workflow/TaskFormProcessor.java
+++ b/source/java/org/alfresco/repo/forms/processor/workflow/TaskFormProcessor.java
@@ -35,6 +35,7 @@ import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.dictionary.TypeDefinition;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.AuthenticationService;
+import org.alfresco.service.cmr.security.NoSuchPersonException;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.workflow.WorkflowService;
import org.alfresco.service.cmr.workflow.WorkflowTask;
@@ -217,7 +218,15 @@ public class TaskFormProcessor extends AbstractWorkflowFormProcessor accessPermissions = permissionService.getAllSetPermissions(nodeRef);
diff --git a/source/java/org/alfresco/repo/i18n/MessageServiceImpl.java b/source/java/org/alfresco/repo/i18n/MessageServiceImpl.java
index 58e181a537..d5d6ef425e 100644
--- a/source/java/org/alfresco/repo/i18n/MessageServiceImpl.java
+++ b/source/java/org/alfresco/repo/i18n/MessageServiceImpl.java
@@ -41,16 +41,19 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.cache.SimpleCache;
+import org.alfresco.repo.dictionary.RepositoryLocation;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.tenant.TenantService;
+import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
-import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.NamespaceService;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.service.namespace.RegexQNamePattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.extensions.surf.util.I18NUtil;
@@ -76,7 +79,6 @@ public class MessageServiceImpl implements MessageService
// dependencies
private TenantService tenantService;
- private SearchService searchService;
private ContentService contentService;
private NamespaceService namespaceService;
private NodeService nodeService;
@@ -104,15 +106,11 @@ public class MessageServiceImpl implements MessageService
this.namespaceService = namespaceService;
}
- public void setSearchService(SearchService searchService) {
- this.searchService = searchService;
- }
-
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
-
+
public void setTenantService(TenantService tenantService)
{
this.tenantService = tenantService;
@@ -530,28 +528,21 @@ public class MessageServiceImpl implements MessageService
NodeRef rootNode = nodeService.getRootNode(storeRef);
// first attempt - with locale
- List nodeRefs = searchService.selectNodes(rootNode, path+"_"+locale+PROPERTIES_FILE_SUFFIX, null, namespaceService, false);
+ NodeRef nodeRef = getNode(rootNode, path+"_"+locale+PROPERTIES_FILE_SUFFIX);
- if ((nodeRefs == null) || (nodeRefs.size() == 0))
+ if (nodeRef == null)
{
// second attempt - basename
- nodeRefs = searchService.selectNodes(rootNode, path+PROPERTIES_FILE_SUFFIX, null, namespaceService, false);
-
- if ((nodeRefs == null) || (nodeRefs.size() == 0))
- {
- logger.debug("Could not find message resource bundle " + storeRef + "/" + path);
- return null;
- }
+ nodeRef = getNode(rootNode, path+PROPERTIES_FILE_SUFFIX);
}
- if (nodeRefs.size() > 1)
+ if (nodeRef == null)
{
- throw new RuntimeException("Found more than one message resource bundle " + storeRef + path);
+ logger.debug("Could not find message resource bundle " + storeRef + "/" + path);
+ return null;
}
- NodeRef messageResourceNodeRef = nodeRefs.get(0);
-
- ContentReader cr = contentService.getReader(messageResourceNodeRef, ContentModel.PROP_CONTENT);
+ ContentReader cr = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT);
ResourceBundle resBundle = new MessagePropertyResourceBundle(
new InputStreamReader(cr.getContentInputStream(), cr.getEncoding()));
return resBundle;
@@ -911,4 +902,65 @@ public class MessageServiceImpl implements MessageService
return bundleBaseName;
}
+
+ protected NodeRef getNode(NodeRef rootNodeRef, String path)
+ {
+ RepositoryLocation repositoryLocation = new RepositoryLocation(rootNodeRef.getStoreRef(), path, RepositoryLocation.LANGUAGE_PATH);
+ String[] pathElements = repositoryLocation.getPathElements();
+
+ NodeRef nodeRef = rootNodeRef;
+ if (pathElements.length > 0)
+ {
+ nodeRef = resolveQNamePath(rootNodeRef, pathElements);
+ }
+
+ return nodeRef;
+ }
+
+ // TODO refactor (see also DictionaryRepositoryBootstrap)
+ protected NodeRef resolveQNamePath(NodeRef rootNodeRef, String[] pathPrefixQNameStrings)
+ {
+ if (pathPrefixQNameStrings.length == 0)
+ {
+ throw new IllegalArgumentException("Path array is empty");
+ }
+ // walk the path
+ NodeRef parentNodeRef = rootNodeRef;
+ for (int i = 0; i < pathPrefixQNameStrings.length; i++)
+ {
+ String pathPrefixQNameString = pathPrefixQNameStrings[i];
+
+ QName pathQName = null;
+ if (AuthenticationUtil.isMtEnabled())
+ {
+ String[] parts = QName.splitPrefixedQName(pathPrefixQNameString);
+ if ((parts.length == 2) && (parts[0].equals(NamespaceService.APP_MODEL_PREFIX)))
+ {
+ String pathUriQNameString = new StringBuilder(64).
+ append(QName.NAMESPACE_BEGIN).
+ append(NamespaceService.APP_MODEL_1_0_URI).
+ append(QName.NAMESPACE_END).
+ append(parts[1]).toString();
+
+ pathQName = QName.createQName(pathUriQNameString);
+ }
+ else
+ {
+ pathQName = QName.createQName(pathPrefixQNameString, namespaceService);
+ }
+ }
+ else
+ {
+ pathQName = QName.createQName(pathPrefixQNameString, namespaceService);
+ }
+
+ List childAssocRefs = nodeService.getChildAssocs(parentNodeRef, RegexQNamePattern.MATCH_ALL, pathQName);
+ if (childAssocRefs.size() != 1)
+ {
+ return null;
+ }
+ parentNodeRef = childAssocRefs.get(0).getChildRef();
+ }
+ return parentNodeRef;
+ }
}
diff --git a/source/java/org/alfresco/repo/imap/AbstractImapFolder.java b/source/java/org/alfresco/repo/imap/AbstractImapFolder.java
index 1a260bbc13..558489374b 100644
--- a/source/java/org/alfresco/repo/imap/AbstractImapFolder.java
+++ b/source/java/org/alfresco/repo/imap/AbstractImapFolder.java
@@ -383,7 +383,7 @@ public abstract class AbstractImapFolder implements MailFolder
return getUidNextInternal();
}
};
- return command.run(true);
+ return command.run(false);
}
/**
@@ -400,7 +400,7 @@ public abstract class AbstractImapFolder implements MailFolder
return getUidValidityInternal();
}
};
- return command.run(true);
+ return command.run(false);
}
/**
diff --git a/source/java/org/alfresco/repo/imap/AlfrescoImapConst.java b/source/java/org/alfresco/repo/imap/AlfrescoImapConst.java
index 9ba7ad8bfa..a41d223d54 100644
--- a/source/java/org/alfresco/repo/imap/AlfrescoImapConst.java
+++ b/source/java/org/alfresco/repo/imap/AlfrescoImapConst.java
@@ -48,6 +48,7 @@ public interface AlfrescoImapConst
public static final String NAMESPACE_PREFIX = "#";
public static final String USER_NAMESPACE = "#mail";
public static final String INBOX_NAME = "INBOX";
+ public static final String TRASH_NAME = "Trash";
public static final String BODY_TEXT_PLAIN_NAME = "Body.txt";
public static final String BODY_TEXT_HTML_NAME = "Body.html";
diff --git a/source/java/org/alfresco/repo/imap/AlfrescoImapFolder.java b/source/java/org/alfresco/repo/imap/AlfrescoImapFolder.java
index 72a0e46c68..465d06b012 100644
--- a/source/java/org/alfresco/repo/imap/AlfrescoImapFolder.java
+++ b/source/java/org/alfresco/repo/imap/AlfrescoImapFolder.java
@@ -20,6 +20,7 @@ package org.alfresco.repo.imap;
import java.io.IOException;
import java.io.OutputStream;
+import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collection;
@@ -44,6 +45,8 @@ import javax.mail.internet.MimeUtility;
import org.alfresco.model.ContentModel;
import org.alfresco.model.ImapModel;
import org.alfresco.repo.imap.AlfrescoImapConst.ImapViewMode;
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.model.FileExistsException;
import org.alfresco.service.cmr.model.FileFolderService;
@@ -53,6 +56,7 @@ import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.AccessStatus;
+import org.alfresco.service.namespace.QName;
import org.alfresco.util.GUID;
import org.alfresco.util.Utf7;
import org.apache.commons.logging.Log;
@@ -71,10 +75,17 @@ import com.icegreen.greenmail.store.SimpleStoredMessage;
* Implementation of greenmail MailFolder. It represents an Alfresco content folder and handles
* appendMessage, copyMessage, expunge (delete), getMessages, getMessage and so requests.
*
+ * The folder is identified by a qualifiedMailboxName and versioned with a version number called UIDVALIDITY.
+ *
* @author Mike Shavnev
*/
-public class AlfrescoImapFolder extends AbstractImapFolder
+public class AlfrescoImapFolder extends AbstractImapFolder implements Serializable
{
+ /**
+ *
+ */
+ private static final long serialVersionUID = -7223111284066976111L;
+
private final static long YEAR_2005 = 1101765600000L;
private static Log logger = LogFactory.getLog(AlfrescoImapFolder.class);
@@ -124,12 +135,16 @@ public class AlfrescoImapFolder extends AbstractImapFolder
*/
private Boolean readOnly;
+ /**
+ * The UIDValidity
+ */
+ private long uidValidity = 0;
+
private boolean extractAttachmentsEnabled;
private Map messages = new TreeMap();
private Map msnCache = new HashMap();
-
- private Map messagesCache = Collections.synchronizedMap(new MaxSizeMap(10, CACHE_SIZE));
+ private Map messagesCache = Collections.synchronizedMap(new MaxSizeMap(10, MESSAGE_CACHE_SIZE));
/**
* Map that ejects the last recently used element(s) to keep the size to a
@@ -140,6 +155,8 @@ public class AlfrescoImapFolder extends AbstractImapFolder
*/
private class MaxSizeMap extends LinkedHashMap
{
+ private static final long serialVersionUID = 1L;
+
private int maxSize;
public MaxSizeMap(int initialSize, int maxSize)
@@ -156,7 +173,7 @@ public class AlfrescoImapFolder extends AbstractImapFolder
}
}
- private final static int CACHE_SIZE = 20;
+ private final static int MESSAGE_CACHE_SIZE = 40;
private static final Flags PERMANENT_FLAGS = new Flags();
@@ -238,6 +255,8 @@ public class AlfrescoImapFolder extends AbstractImapFolder
{
this.imapService = serviceRegistry.getImapService();
}
+
+ this.uidValidity = generateUidValidity();
// MailFolder object can be null if it is used to obtain hierarchy delimiter by LIST command:
// Example:
@@ -339,6 +358,8 @@ public class AlfrescoImapFolder extends AbstractImapFolder
{
serviceRegistry.getFileFolderService().copy(sourceMessageFileInfo.getNodeRef(), destFolderNodeRef, null);
}
+ removeMessageFromCache(uid);
+
}
/**
@@ -359,8 +380,7 @@ public class AlfrescoImapFolder extends AbstractImapFolder
imapService.setFlag(fileInfo, Flags.Flag.DELETED, true);
// comment out to physically remove content.
// fileFolderService.delete(fileInfo.getNodeRef());
- messages.remove(mess.getUid());
- msnCache.remove(mess.getUid());
+ removeMessageFromCache(mess.getUid());
}
}
@@ -384,6 +404,7 @@ public class AlfrescoImapFolder extends AbstractImapFolder
{
NodeRef nodeRef = ((AbstractMimeMessage) mess.getMimeMessage()).getMessageInfo().getNodeRef();
serviceRegistry.getFileFolderService().delete(nodeRef);
+ removeMessageFromCache(mess.getUid());
}
}
}
@@ -424,7 +445,7 @@ public class AlfrescoImapFolder extends AbstractImapFolder
if (logger.isDebugEnabled())
{
- logger.debug("[getFullNameInternal] entry");
+ logger.debug("[getFullNameInternal] " + this);
}
StringBuilder fullName = new StringBuilder();
@@ -472,6 +493,10 @@ public class AlfrescoImapFolder extends AbstractImapFolder
@Override
protected SimpleStoredMessage getMessageInternal(long uid) throws MessagingException
{
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("[getMessageInternal] " + this);
+ }
AbstractMimeMessage mes = (AbstractMimeMessage) messages.get(uid).getMimeMessage();
FileInfo mesInfo = mes.getMessageInfo();
@@ -517,7 +542,7 @@ public class AlfrescoImapFolder extends AbstractImapFolder
{
if (logger.isDebugEnabled())
{
- logger.debug("[getMessageCountInternal] entry");
+ logger.debug("[getMessageCountInternal] " + this);
}
if (messages.size() == 0 && folderInfo != null)
@@ -542,8 +567,9 @@ public class AlfrescoImapFolder extends AbstractImapFolder
{
if (logger.isDebugEnabled())
{
- logger.debug("[getMessageUidsInternal] entry");
+ logger.debug("[getMessageUidsInternal] " + this);
}
+
if (messages == null || messages.size() == 0 && folderInfo != null)
{
List fileInfos = imapService.searchMails(folderInfo.getNodeRef(), viewMode);
@@ -570,7 +596,7 @@ public class AlfrescoImapFolder extends AbstractImapFolder
{
if (logger.isDebugEnabled())
{
- logger.debug("[getMessagesInternal] entry");
+ logger.debug("[getMessagesInternal] " + this);
}
List fileInfos = imapService.searchMails(folderInfo.getNodeRef(), viewMode);
return convertToMessages(fileInfos);
@@ -580,7 +606,7 @@ public class AlfrescoImapFolder extends AbstractImapFolder
{
if (logger.isDebugEnabled())
{
- logger.debug("[convertToMessages] entry");
+ logger.debug("[convertToMessages] " + this);
}
if (fileInfos == null || fileInfos.size() == 0)
{
@@ -616,6 +642,7 @@ public class AlfrescoImapFolder extends AbstractImapFolder
protected SimpleStoredMessage createImapMessage(FileInfo fileInfo, Long key, boolean generateBody) throws MessagingException
{
+ // TODO MER 26/11/2010- this test should really be that the content of the node is of type message/RFC822
if (serviceRegistry.getNodeService().hasAspect(fileInfo.getNodeRef(), ImapModel.ASPECT_IMAP_CONTENT))
{
return new SimpleStoredMessage(new ImapModelMessage(fileInfo, serviceRegistry, generateBody), new Date(), key);
@@ -637,7 +664,7 @@ public class AlfrescoImapFolder extends AbstractImapFolder
{
if (logger.isDebugEnabled())
{
- logger.debug("[getMessagesInternal] entry");
+ logger.debug("[getMessagesInternal] " + this);
}
if (messages == null || messages.size() == 0)
{
@@ -653,11 +680,6 @@ public class AlfrescoImapFolder extends AbstractImapFolder
}
}
- if (logger.isDebugEnabled())
- {
- logger.debug("[getMessagesInternal] exit");
- }
-
return ret;
}
@@ -700,7 +722,7 @@ public class AlfrescoImapFolder extends AbstractImapFolder
{
if (logger.isDebugEnabled())
{
- logger.debug("[getNonDeletedMessagesInternal] entry");
+ logger.debug("[getNonDeletedMessagesInternal] " + this);
}
List result = new ArrayList();
@@ -750,7 +772,7 @@ public class AlfrescoImapFolder extends AbstractImapFolder
{
if (logger.isDebugEnabled())
{
- logger.debug("[getRecentCountInternal] entry");
+ logger.debug("[getRecentCountInternal] " + this);
}
if (messages.size() == 0 && folderInfo != null)
{
@@ -790,6 +812,18 @@ public class AlfrescoImapFolder extends AbstractImapFolder
{
return getUidValidity();
}
+
+ public boolean isStale()
+ {
+ if(uidValidity == generateUidValidity())
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+ }
/**
* Returns UIDVALIDITY value of the folder.
@@ -799,8 +833,25 @@ public class AlfrescoImapFolder extends AbstractImapFolder
@Override
protected long getUidValidityInternal()
{
- long modifDate = ((Date) serviceRegistry.getNodeService().getProperty(folderInfo.getNodeRef(), ContentModel.PROP_MODIFIED)).getTime();
- return (modifDate - YEAR_2005)/1000;
+ return uidValidity;
+ //return (Long) AuthenticationUtil.runAs(new GetUidValidityWork(folderInfo.getNodeRef(), serviceRegistry.getNodeService()), AuthenticationUtil.getSystemUserName());
+ }
+
+ /**
+ * Generates UIDVALIDITY value of the folder.
+ *
+ * @return UIDVALIDITY value.
+ */
+ private long generateUidValidity()
+ {
+ if(folderInfo != null)
+ {
+ return (Long) AuthenticationUtil.runAs(new GetUidValidityWork(folderInfo.getNodeRef(), serviceRegistry.getNodeService()), AuthenticationUtil.getSystemUserName());
+ }
+ else
+ {
+ return 0;
+ }
}
/**
@@ -813,7 +864,7 @@ public class AlfrescoImapFolder extends AbstractImapFolder
{
if (logger.isDebugEnabled())
{
- logger.debug("[getUnseenCountInternal] entry");
+ logger.debug("[getUnseenCountInternal] " + this);
}
if (messages.size() == 0 && folderInfo != null)
{
@@ -1115,6 +1166,13 @@ public class AlfrescoImapFolder extends AbstractImapFolder
FileCopyUtils.copy(part.getInputStream(), os);
}
+ private void removeMessageFromCache(long uid)
+ {
+ messages.remove(uid);
+ msnCache.remove(uid);
+ messagesCache.remove(uid);
+ }
+
class CacheItem
{
private Date modified;
@@ -1147,4 +1205,45 @@ public class AlfrescoImapFolder extends AbstractImapFolder
}
}
+ public class GetUidValidityWork implements RunAsWork
+ {
+
+ private NodeService nodeService;
+ private NodeRef folderNodeRef;
+
+ public GetUidValidityWork(NodeRef folderNodeRef, NodeService nodeService)
+ {
+ this.folderNodeRef = folderNodeRef;
+ this.nodeService = nodeService;
+ }
+
+ @Override
+ public Long doWork() throws Exception
+ {
+ if(nodeService.exists(folderNodeRef))
+ {
+ long modifDate = 0L;
+ if (nodeService.hasAspect(folderNodeRef, ImapModel.ASPECT_IMAP_FOLDER))
+ {
+ modifDate = ((Long) nodeService.getProperty(folderNodeRef, ImapModel.PROP_UIDVALIDITY));
+ }
+ else
+ {
+ Date currDate = new Date();
+ modifDate = currDate.getTime();
+ Map aspectProperties = new HashMap(1, 1);
+ aspectProperties.put(ImapModel.PROP_UIDVALIDITY, modifDate);
+ nodeService.addAspect(folderNodeRef, ImapModel.ASPECT_IMAP_FOLDER, aspectProperties);
+ }
+ return (modifDate - YEAR_2005) / 1000;
+ }
+ else
+ {
+ return new Long(0);
+ }
+ }
+
+ }
+
+
}
diff --git a/source/java/org/alfresco/repo/imap/AlfrescoImapFolderAccessInterceptor.java b/source/java/org/alfresco/repo/imap/AlfrescoImapFolderAccessInterceptor.java
new file mode 100644
index 0000000000..a07be00971
--- /dev/null
+++ b/source/java/org/alfresco/repo/imap/AlfrescoImapFolderAccessInterceptor.java
@@ -0,0 +1,89 @@
+package org.alfresco.repo.imap;
+
+import java.lang.reflect.Method;
+
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.security.AccessStatus;
+import org.alfresco.service.cmr.security.PermissionService;
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ *
+ * @author arsenyko
+ *
+ */
+public class AlfrescoImapFolderAccessInterceptor implements MethodInterceptor
+{
+ private Log logger = LogFactory.getLog(AlfrescoImapFolderAccessInterceptor.class);
+
+ private PermissionService permissionService;
+
+ @Override
+ public Object invoke(MethodInvocation mi) throws Throwable
+ {
+ Object[] args = mi.getArguments();
+ Method method = mi.getMethod();
+ String methodName = method.getName();
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Check the cache [" + methodName + "]");
+ }
+ if ("contains".equals(methodName))
+ {
+ //XXX: Do we need a check for permissions here?
+ String mailboxName = (String) args[0];
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Check the cache [" + methodName + "] for '" + mailboxName + "'");
+ }
+ boolean containsResult = (Boolean) mi.proceed();
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("The cache " + (containsResult ? "contains" : "does't contain") + " '" + mailboxName + "'");
+ }
+ return containsResult;
+ }
+ else if ("get".equals(methodName))
+ {
+ String mailboxName = (String) args[0];
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Check the cache [" + methodName + "] for '" + mailboxName + "'");
+ }
+ AlfrescoImapFolder folder = (AlfrescoImapFolder) mi.proceed();
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("The cache " + (folder != null ? "contains" : "does't contain") + " '" + mailboxName + "'");
+ }
+ if (folder != null)
+ {
+ NodeRef nodeRef = folder.getFolderInfo().getNodeRef();
+ boolean accessAllowed = permissionService.hasPermission(nodeRef, PermissionService.READ) == AccessStatus.ALLOWED
+ && permissionService.hasPermission(nodeRef, PermissionService.READ_CHILDREN) == AccessStatus.ALLOWED;
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Access " + (accessAllowed ? "allowed" : "denied") + " to '" + mailboxName + "' for user '" + AuthenticationUtil.getFullyAuthenticatedUser() + "'");
+ }
+ if (!accessAllowed)
+ return null;
+ }
+ return folder;
+ }
+ return mi.proceed();
+ }
+
+ public void setPermissionService(PermissionService permissionService)
+ {
+ this.permissionService = permissionService;
+ }
+
+ public PermissionService getPermissionService()
+ {
+ return permissionService;
+ }
+
+}
diff --git a/source/java/org/alfresco/repo/imap/AlfrescoImapServer.java b/source/java/org/alfresco/repo/imap/AlfrescoImapServer.java
index 6eaa1842b0..193e486d6b 100644
--- a/source/java/org/alfresco/repo/imap/AlfrescoImapServer.java
+++ b/source/java/org/alfresco/repo/imap/AlfrescoImapServer.java
@@ -52,6 +52,11 @@ public class AlfrescoImapServer extends AbstractLifecycleBean
{
this.imapServerEnabled = imapServerEnabled;
}
+
+ public boolean isImapServerEnabled()
+ {
+ return imapServerEnabled;
+ }
public void setPort(int port)
{
@@ -86,6 +91,27 @@ public class AlfrescoImapServer extends AbstractLifecycleBean
protected void onBootstrap(ApplicationEvent event)
{
if (imapServerEnabled)
+ {
+ startup();
+ }
+ else
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("IMAP service is disabled.");
+ }
+ }
+ }
+
+ protected void onShutdown(ApplicationEvent event)
+ {
+ shutdown();
+
+ }
+
+ public void startup()
+ {
+ if(serverImpl == null)
{
Managers imapManagers = new Managers()
{
@@ -111,15 +137,19 @@ public class AlfrescoImapServer extends AbstractLifecycleBean
{
if (logger.isDebugEnabled())
{
- logger.debug("IMAP service is disabled.");
+ logger.debug("IMAP server already running.");
}
}
}
-
- protected void onShutdown(ApplicationEvent event)
+
+ public void shutdown()
{
if (serverImpl != null)
{
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("IMAP service stopping.");
+ }
serverImpl.stopService(null);
}
}
diff --git a/source/java/org/alfresco/repo/imap/ImapContentPolicy.java b/source/java/org/alfresco/repo/imap/ImapContentPolicy.java
index 6256b2b0e1..0e02370dd9 100644
--- a/source/java/org/alfresco/repo/imap/ImapContentPolicy.java
+++ b/source/java/org/alfresco/repo/imap/ImapContentPolicy.java
@@ -18,15 +18,24 @@
*/
package org.alfresco.repo.imap;
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.Map;
+
import org.alfresco.model.ImapModel;
+import org.alfresco.repo.copy.CopyBehaviourCallback;
+import org.alfresco.repo.copy.CopyDetails;
+import org.alfresco.repo.copy.CopyServicePolicies.OnCopyNodePolicy;
+import org.alfresco.repo.copy.DefaultCopyBehaviourCallback;
+import org.alfresco.repo.node.NodeServicePolicies.OnAddAspectPolicy;
+import org.alfresco.repo.policy.Behaviour.NotificationFrequency;
import org.alfresco.repo.policy.JavaBehaviour;
import org.alfresco.repo.policy.PolicyComponent;
-import org.alfresco.repo.policy.Behaviour.NotificationFrequency;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.repository.NodeRef;
-import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
+import org.alfresco.util.Pair;
import org.alfresco.util.PropertyCheck;
@@ -46,9 +55,16 @@ public class ImapContentPolicy
/**
* Bind policies
*/
- this.getPolicyComponent().bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI, "onAddAspect"),
+ this.getPolicyComponent().bindClassBehaviour(OnAddAspectPolicy.QNAME,
ImapModel.ASPECT_IMAP_CONTENT,
new JavaBehaviour(this, "onAddAspect", NotificationFrequency.TRANSACTION_COMMIT));
+
+ /**
+ * Bind policies
+ */
+ this.getPolicyComponent().bindClassBehaviour(OnCopyNodePolicy.QNAME ,
+ ImapModel.ASPECT_IMAP_CONTENT,
+ new JavaBehaviour(this, "getCopyCallback", NotificationFrequency.EVERY_EVENT));
}
/**
@@ -69,6 +85,58 @@ public class ImapContentPolicy
}
}
}
+
+ /**
+ * Extends the default copy behaviour to prevent copying of the imap attatchments.
+ *
+ * @author Mark Rogers
+ * @since 3.3
+ */
+ private static class ImapContentCopyBehaviourCallback extends DefaultCopyBehaviourCallback
+ {
+ private static final CopyBehaviourCallback INSTANCE = new ImapContentCopyBehaviourCallback();
+
+ /**
+ * @return Returns an empty map
+ */
+ public Map getCopyProperties(
+ QName classQName, CopyDetails copyDetails, Map properties)
+ {
+ return Collections.emptyMap();
+ }
+
+ /**
+ * Don't copy IMAP attachments or IMAP folder assocs since they belong to the "source" message, not the "destination" message.
+ *
+ * @return Returns
+ * {@link AssocCopySourceAction#IGNORE} and
+ * {@link AssocCopyTargetAction#USE_COPIED_OTHERWISE_ORIGINAL_TARGET}
+ */
+ /*
+ * Note : MER 30/11/2010 For RM this is the correct action since extract attachments is run by the user.
+ *
+ * For non RM use cases, we may be expected to extract the attachments automatically for the target node depending upon
+ * the IMAP configuration and destination of the copy, this is not yet attempted since it depends on a bigger re-factor
+ * of the AlfrescoImapFolder ALF-3153
+ */
+ @Override
+ public Pair getAssociationCopyAction(
+ QName classQName,
+ CopyDetails copyDetails,
+ CopyAssociationDetails assocCopyDetails)
+ {
+ return new Pair(
+ AssocCopySourceAction.IGNORE,
+ AssocCopyTargetAction.USE_COPIED_OTHERWISE_ORIGINAL_TARGET);
+ }
+ }
+
+
+ public CopyBehaviourCallback getCopyCallback(QName classRef, CopyDetails copyDetails)
+ {
+ return ImapContentCopyBehaviourCallback.INSTANCE;
+ }
+
public void setActionService(ActionService actionService)
{
diff --git a/source/java/org/alfresco/repo/imap/ImapMessageTest.java b/source/java/org/alfresco/repo/imap/ImapMessageTest.java
index 575605243e..5265b6a0df 100644
--- a/source/java/org/alfresco/repo/imap/ImapMessageTest.java
+++ b/source/java/org/alfresco/repo/imap/ImapMessageTest.java
@@ -19,25 +19,39 @@
package org.alfresco.repo.imap;
import java.io.BufferedInputStream;
+import java.io.FileInputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.io.SequenceInputStream;
+import java.io.Serializable;
+import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.Properties;
+import javax.mail.Address;
import javax.mail.Folder;
+import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Store;
+import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
+import javax.mail.internet.MimeUtility;
import javax.transaction.UserTransaction;
+import junit.framework.TestCase;
+
import org.alfresco.model.ContentModel;
+import org.alfresco.model.ImapModel;
import org.alfresco.repo.importer.ACPImportPackageHandler;
import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory;
import org.alfresco.repo.node.integrity.IntegrityChecker;
+import org.alfresco.repo.search.QueryParameterDefImpl;
import org.alfresco.service.ServiceRegistry;
+import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileFolderUtil;
import org.alfresco.service.cmr.model.FileInfo;
@@ -45,20 +59,24 @@ import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
+import org.alfresco.service.cmr.search.QueryParameterDefinition;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.MutableAuthenticationService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.view.ImporterService;
import org.alfresco.service.cmr.view.Location;
import org.alfresco.service.namespace.NamespaceService;
+import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ApplicationContextHelper;
+import org.alfresco.util.GUID;
import org.alfresco.util.PropertyMap;
import org.alfresco.util.config.RepositoryFolderConfigBean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.ClassPathResource;
+import org.springframework.mail.javamail.MimeMessageHelper;
import com.sun.mail.iap.ProtocolException;
import com.sun.mail.iap.Response;
@@ -66,9 +84,10 @@ import com.sun.mail.imap.IMAPFolder;
import com.sun.mail.imap.protocol.BODY;
import com.sun.mail.imap.protocol.FetchResponse;
import com.sun.mail.imap.protocol.IMAPProtocol;
+import com.sun.mail.imap.protocol.IMAPResponse;
+import com.sun.mail.imap.protocol.RFC822DATA;
import com.sun.mail.imap.protocol.UID;
-
-import junit.framework.TestCase;
+import com.sun.mail.util.ASCIIUtility;
public class ImapMessageTest extends TestCase
{
@@ -76,8 +95,8 @@ public class ImapMessageTest extends TestCase
// IMAP client settings
private static final String PROTOCOL = "imap";
- //private static final String HOST = "localhost";
- //private static final int PORT = 143;
+ private static final String HOST = "localhost";
+ private static final int PORT = 7143;
private static final String ADMIN_USER_NAME = "admin";
private static final String ADMIN_USER_PASSWORD = "admin";
@@ -88,6 +107,7 @@ public class ImapMessageTest extends TestCase
private IMAPFolder folder = null;
private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
+ private ServiceRegistry serviceRegistry;
private TransactionService transactionService;
private NodeService nodeService;
private ImporterService importerService;
@@ -96,6 +116,7 @@ public class ImapMessageTest extends TestCase
private NamespaceService namespaceService;
private FileFolderService fileFolderService;
private MutableAuthenticationService authenticationService;
+ private AlfrescoImapServer imapServer;
String anotherUserName;
private NodeRef testImapFolderNodeRef;
@@ -107,104 +128,112 @@ public class ImapMessageTest extends TestCase
private static final String TEST_FILE = "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + IMAP_FOLDER_NAME + "/" + NamespaceService.CONTENT_MODEL_PREFIX
+ ":___-___folder_a/" + NamespaceService.CONTENT_MODEL_PREFIX + ":___-___folder_a_a/" + NamespaceService.CONTENT_MODEL_PREFIX + ":___-___file_a_a";
- public void testAllTestsCommentedOut()
- {
- }
-
@Override
public void setUp() throws Exception
{
-// ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean("ServiceRegistry");
-// transactionService = serviceRegistry.getTransactionService();
-// nodeService = serviceRegistry.getNodeService();
-// importerService = serviceRegistry.getImporterService();
-// personService = serviceRegistry.getPersonService();
-// authenticationService = serviceRegistry.getAuthenticationService();
-// searchService = serviceRegistry.getSearchService();
-// namespaceService = serviceRegistry.getNamespaceService();
-// fileFolderService = serviceRegistry.getFileFolderService();
-//
-//
-// // start the transaction
-// UserTransaction txn = transactionService.getUserTransaction();
-// txn.begin();
-// authenticationService.authenticate(ADMIN_USER_NAME, ADMIN_USER_PASSWORD.toCharArray());
-//
-// // downgrade integrity
-// IntegrityChecker.setWarnInTransaction();
-//
-// anotherUserName = "user" + System.currentTimeMillis();
-//
-// PropertyMap testUser = new PropertyMap();
-// testUser.put(ContentModel.PROP_USERNAME, anotherUserName);
-// testUser.put(ContentModel.PROP_FIRSTNAME, anotherUserName);
-// testUser.put(ContentModel.PROP_LASTNAME, anotherUserName);
-// testUser.put(ContentModel.PROP_EMAIL, anotherUserName + "@alfresco.com");
-// testUser.put(ContentModel.PROP_JOBTITLE, "jobTitle");
-//
-// personService.createPerson(testUser);
-//
-// // create the ACEGI Authentication instance for the new user
-// authenticationService.createAuthentication(anotherUserName, anotherUserName.toCharArray());
-//
-// StoreRef storeRef = new StoreRef(storePath);
-// storeRootNodeRef = nodeService.getRootNode(storeRef);
-//
-// List nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore, null, namespaceService, false);
-// NodeRef companyHomeNodeRef = nodeRefs.get(0);
-//
-// nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore + "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + IMAP_FOLDER_NAME, null,
-// namespaceService, false);
-// if (nodeRefs != null && nodeRefs.size() > 0)
-// {
-// fileFolderService.delete(nodeRefs.get(0));
-// }
-//
-// ChildApplicationContextFactory imap = (ChildApplicationContextFactory) ctx.getBean("imap");
-// ApplicationContext imapCtx = imap.getApplicationContext();
-// ImapServiceImpl imapServiceImpl = (ImapServiceImpl) imapCtx.getBean("imapService");
-// AlfrescoImapServer imapServer = (AlfrescoImapServer) imapCtx.getBean("imapServer");
-//
-// // Creating IMAP test folder for IMAP root
-// LinkedList folders = new LinkedList();
-// folders.add(IMAP_FOLDER_NAME);
-// FileFolderUtil.makeFolders(fileFolderService, companyHomeNodeRef, folders, ContentModel.TYPE_FOLDER);
-//
-// // Setting IMAP root
-// RepositoryFolderConfigBean imapHome = new RepositoryFolderConfigBean();
-// imapHome.setStore(storePath);
-// imapHome.setRootPath(companyHomePathInStore);
-// imapHome.setFolderPath(IMAP_FOLDER_NAME);
-// imapServiceImpl.setImapHome(imapHome);
-//
-// // Starting IMAP
-// imapServiceImpl.startup();
-//
-// nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore + "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + IMAP_FOLDER_NAME, null,
-// namespaceService, false);
-// testImapFolderNodeRef = nodeRefs.get(0);
-//
-// /*
-// * Importing test folders: Test folder contains: "___-___folder_a" "___-___folder_a" contains: "___-___folder_a_a", "___-___file_a", "Message_485.eml" (this is IMAP
-// * Message) "___-___folder_a_a" contains: "____-____file_a_a"
-// */
-// importInternal("imap/imapservice_test_folder_a.acp", testImapFolderNodeRef);
-//
-// txn.commit();
-//
-// // Init mail client session
-// Properties props = new Properties();
-// props.setProperty("mail.imap.partialfetch", "false");
-// this.session = Session.getDefaultInstance(props, null);
-//
-// // Get the store
-// this.store = session.getStore(PROTOCOL);
-// //this.store.connect(HOST, PORT, anotherUserName, anotherUserName);
-// this.store.connect(imapServer.getHost(), imapServer.getPort(), anotherUserName, anotherUserName);
-//
-// // Get folder
-// folder = (IMAPFolder) store.getFolder(TEST_FOLDER);
-// folder.open(Folder.READ_ONLY);
+ logger.debug("In SetUp");
+ serviceRegistry = (ServiceRegistry) ctx.getBean("ServiceRegistry");
+ transactionService = serviceRegistry.getTransactionService();
+ nodeService = serviceRegistry.getNodeService();
+ importerService = serviceRegistry.getImporterService();
+ personService = serviceRegistry.getPersonService();
+ authenticationService = serviceRegistry.getAuthenticationService();
+ searchService = serviceRegistry.getSearchService();
+ namespaceService = serviceRegistry.getNamespaceService();
+ fileFolderService = serviceRegistry.getFileFolderService();
+
+
+ // start the transaction
+ UserTransaction txn = transactionService.getUserTransaction();
+ txn.begin();
+ authenticationService.authenticate(ADMIN_USER_NAME, ADMIN_USER_PASSWORD.toCharArray());
+
+ // downgrade integrity
+ IntegrityChecker.setWarnInTransaction();
+
+ anotherUserName = "user" + System.currentTimeMillis();
+
+ PropertyMap testUser = new PropertyMap();
+ testUser.put(ContentModel.PROP_USERNAME, anotherUserName);
+ testUser.put(ContentModel.PROP_FIRSTNAME, anotherUserName);
+ testUser.put(ContentModel.PROP_LASTNAME, anotherUserName);
+ testUser.put(ContentModel.PROP_EMAIL, anotherUserName + "@alfresco.com");
+ testUser.put(ContentModel.PROP_JOBTITLE, "jobTitle");
+
+ personService.createPerson(testUser);
+
+ // create the ACEGI Authentication instance for the new user
+ authenticationService.createAuthentication(anotherUserName, anotherUserName.toCharArray());
+
+ StoreRef storeRef = new StoreRef(storePath);
+ storeRootNodeRef = nodeService.getRootNode(storeRef);
+
+ List nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore, null, namespaceService, false);
+ NodeRef companyHomeNodeRef = nodeRefs.get(0);
+
+ nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore + "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + IMAP_FOLDER_NAME, null,
+ namespaceService, false);
+ if (nodeRefs != null && nodeRefs.size() > 0)
+ {
+ fileFolderService.delete(nodeRefs.get(0));
+ }
+
+ ChildApplicationContextFactory imap = (ChildApplicationContextFactory) ctx.getBean("imap");
+ ApplicationContext imapCtx = imap.getApplicationContext();
+ ImapServiceImpl imapServiceImpl = (ImapServiceImpl) imapCtx.getBean("imapService");
+ imapServer = (AlfrescoImapServer) imapCtx.getBean("imapServer");
+
+ if(!imapServer.isImapServerEnabled())
+ {
+ imapServer.setImapServerEnabled(true);
+ imapServer.setHost(HOST);
+ imapServer.setPort(PORT);
+ imapServer.startup();
+ }
+
+ // Creating IMAP test folder for IMAP root
+ LinkedList folders = new LinkedList();
+ folders.add(IMAP_FOLDER_NAME);
+ FileFolderUtil.makeFolders(fileFolderService, companyHomeNodeRef, folders, ContentModel.TYPE_FOLDER);
+
+ // Setting IMAP root
+ RepositoryFolderConfigBean imapHome = new RepositoryFolderConfigBean();
+ imapHome.setStore(storePath);
+ imapHome.setRootPath(companyHomePathInStore);
+ imapHome.setFolderPath(IMAP_FOLDER_NAME);
+ imapServiceImpl.setImapHome(imapHome);
+
+
+ // Starting IMAP
+ imapServiceImpl.startup();
+
+ nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore + "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + IMAP_FOLDER_NAME, null,
+ namespaceService, false);
+ testImapFolderNodeRef = nodeRefs.get(0);
+
+ /*
+ * Importing test folders: Test folder contains: "___-___folder_a" "___-___folder_a" contains: "___-___folder_a_a", "___-___file_a", "Message_485.eml" (this is IMAP
+ * Message) "___-___folder_a_a" contains: "____-____file_a_a"
+ */
+ importInternal("imap/imapservice_test_folder_a.acp", testImapFolderNodeRef);
+
+ txn.commit();
+
+ // Init mail client session
+ Properties props = new Properties();
+ props.setProperty("mail.imap.partialfetch", "false");
+ this.session = Session.getDefaultInstance(props, null);
+
+ // Get the store
+ this.store = session.getStore(PROTOCOL);
+ //this.store.connect(HOST, PORT, anotherUserName, anotherUserName);
+ this.store.connect(imapServer.getHost(), imapServer.getPort(), anotherUserName, anotherUserName);
+
+ // Get folder
+ folder = (IMAPFolder) store.getFolder(TEST_FOLDER);
+ folder.open(Folder.READ_ONLY);
+
+ logger.debug("End SetUp");
}
@@ -217,170 +246,332 @@ public class ImapMessageTest extends TestCase
importerService.importView(acpHandler, importLocation, null, null);
}
-// public void testMessageModifiedBetweenReads() throws Exception
-// {
-// // Get test message UID
-// final Long uid = getMessageUid(folder, 1);
-// // Get Message size
-// final int count = getMessageSize(folder, uid);
-//
-// // Get first part
-// BODY body = getMessageBodyPart(folder, uid, 0, count - 100);
-//
-// // Modify message. The size of letter describing the node may change
-// // These changes should be committed because it should be visible from client
-// NodeRef contentNode = findNode(companyHomePathInStore + TEST_FILE);
-// UserTransaction txn = transactionService.getUserTransaction();
-// txn.begin();
-// ContentWriter writer = fileFolderService.getWriter(contentNode);
-// StringBuffer sb = new StringBuffer();
-// for (int i = 0; i < 2000; i++)
-// {
-// sb.append("test string");
-// }
-// writer.putContent(sb.toString());
-// txn.commit();
-//
-// // Read second message part
-// BODY bodyRest = getMessageBodyPart(folder, uid, count - 10, 10);
-//
-// // Creating and parsing message from 2 parts
-// MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties()), new SequenceInputStream(new BufferedInputStream(body.getByteArrayInputStream()),
-// new BufferedInputStream(bodyRest.getByteArrayInputStream())));
-//
-// // Reading first part - should be successful
-// MimeMultipart content = (MimeMultipart) message.getContent();
-// assertNotNull(content.getBodyPart(0).getContent());
-//
-// try
-// {
-// // Reading second part cause error
-// content.getBodyPart(1).getContent();
-// fail("Should raise an IOException");
-// }
-// catch (IOException e)
-// {
-// }
-// }
-//
-// public void testMessageRenamedBetweenReads() throws Exception
-// {
-// // Get test message UID
-// final Long uid = getMessageUid(folder, 1);
-// // Get Message size
-// final int count = getMessageSize(folder, uid);
-//
-// // Get first part
-// BODY body = getMessageBodyPart(folder, uid, 0, count - 100);
-//
-// // Rename message. The size of letter describing the node will change
-// // These changes should be committed because it should be visible from client
-// NodeRef contentNode = findNode(companyHomePathInStore + TEST_FILE);
-// UserTransaction txn = transactionService.getUserTransaction();
-// txn.begin();
-// fileFolderService.rename(contentNode, "testtesttesttesttesttesttesttesttesttest");
-// txn.commit();
-//
-// // Read second message part
-// BODY bodyRest = getMessageBodyPart(folder, uid, count - 100, 100);
-//
-// // Creating and parsing message from 2 parts
-// MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties()), new SequenceInputStream(new BufferedInputStream(body.getByteArrayInputStream()),
-// new BufferedInputStream(bodyRest.getByteArrayInputStream())));
-//
-// // Reading first part - should be successful
-// MimeMultipart content = (MimeMultipart) message.getContent();
-// assertNotNull(content.getBodyPart(0).getContent());
-//
-// try
-// {
-// // Reading second part cause error
-// content.getBodyPart(1).getContent();
-// fail("Should raise an IOException");
-// }
-// catch (IOException e)
-// {
-// }
-// }
-//
-// public void testMessageCache() throws Exception
-// {
-//
-// // Create messages
-// NodeRef contentNode = findNode(companyHomePathInStore + TEST_FILE);
-// UserTransaction txn = transactionService.getUserTransaction();
-// txn.begin();
-//
-// // Create messages more than cache capacity
-// for (int i = 0; i < 51; i++)
-// {
-// FileInfo fi = fileFolderService.create(nodeService.getParentAssocs(contentNode).get(0).getParentRef(), "test" + i, ContentModel.TYPE_CONTENT);
-// ContentWriter writer = fileFolderService.getWriter(fi.getNodeRef());
-// writer.putContent("test");
-// }
-//
-// txn.commit();
-//
-// // Reload folder
-// folder.close(false);
-// folder = (IMAPFolder) store.getFolder(TEST_FOLDER);
-// folder.open(Folder.READ_ONLY);
-//
-// // Read all messages
-// for (int i = 1; i < 51; i++)
-// {
-// // Get test message UID
-// final Long uid = getMessageUid(folder, i);
-// // Get Message size
-// final int count = getMessageSize(folder, uid);
-//
-// // Get first part
-// BODY body = getMessageBodyPart(folder, uid, 0, count - 100);
-// // Read second message part
-// BODY bodyRest = getMessageBodyPart(folder, uid, count - 100, 100);
-//
-// // Creating and parsing message from 2 parts
-// MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties()), new SequenceInputStream(new BufferedInputStream(body.getByteArrayInputStream()),
-// new BufferedInputStream(bodyRest.getByteArrayInputStream())));
-//
-// // Reading first part - should be successful
-// MimeMultipart content = (MimeMultipart) message.getContent();
-// assertNotNull(content.getBodyPart(0).getContent());
-// assertNotNull(content.getBodyPart(1).getContent());
-// }
-// }
-//
-//
-//
-// public void testUnmodifiedMessage() throws Exception
-// {
-// // Get test message UID
-// final Long uid = getMessageUid(folder, 1);
-// // Get Message size
-// final int count = getMessageSize(folder, uid);
-//
-// // Make multiple message reading
-// for (int i = 0; i < 100; i++)
-// {
-// // Get random offset
-// int n = (int) ((int) 100 * Math.random());
-//
-// // Get first part
-// BODY body = getMessageBodyPart(folder, uid, 0, count - n);
-// // Read second message part
-// BODY bodyRest = getMessageBodyPart(folder, uid, count - n, n);
-//
-// // Creating and parsing message from 2 parts
-// MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties()), new SequenceInputStream(new BufferedInputStream(body.getByteArrayInputStream()),
-// new BufferedInputStream(bodyRest.getByteArrayInputStream())));
-//
-// MimeMultipart content = (MimeMultipart) message.getContent();
-// // Reading first part - should be successful
-// assertNotNull(content.getBodyPart(0).getContent());
-// // Reading second part - should be successful
-// assertNotNull(content.getBodyPart(1).getContent());
-// }
-// }
+ public void testMessageModifiedBetweenReads() throws Exception
+ {
+ // Get test message UID
+ final Long uid = getMessageUid(folder, 1);
+ // Get Message size
+ final int count = getMessageSize(folder, uid);
+
+ // Get first part
+ BODY body = getMessageBodyPart(folder, uid, 0, count - 100);
+
+ // Modify message. The size of letter describing the node may change
+ // These changes should be committed because it should be visible from client
+ NodeRef contentNode = findNode(companyHomePathInStore + TEST_FILE);
+ UserTransaction txn = transactionService.getUserTransaction();
+ txn.begin();
+ ContentWriter writer = fileFolderService.getWriter(contentNode);
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < 2000; i++)
+ {
+ sb.append("test string");
+ }
+ writer.putContent(sb.toString());
+ txn.commit();
+
+ // Read second message part
+ BODY bodyRest = getMessageBodyPart(folder, uid, count - 10, 10);
+
+ // Creating and parsing message from 2 parts
+ MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties()), new SequenceInputStream(new BufferedInputStream(body.getByteArrayInputStream()),
+ new BufferedInputStream(bodyRest.getByteArrayInputStream())));
+
+ // Reading first part - should be successful
+ MimeMultipart content = (MimeMultipart) message.getContent();
+ assertNotNull(content.getBodyPart(0).getContent());
+
+ try
+ {
+ // Reading second part cause error
+ content.getBodyPart(1).getContent();
+ fail("Should raise an IOException");
+ }
+ catch (IOException e)
+ {
+ }
+ }
+
+ public void testMessageRenamedBetweenReads() throws Exception
+ {
+ // Get test message UID
+ final Long uid = getMessageUid(folder, 1);
+ // Get Message size
+ final int count = getMessageSize(folder, uid);
+
+ // Get first part
+ BODY body = getMessageBodyPart(folder, uid, 0, count - 100);
+
+ // Rename message. The size of letter describing the node will change
+ // These changes should be committed because it should be visible from client
+ NodeRef contentNode = findNode(companyHomePathInStore + TEST_FILE);
+ UserTransaction txn = transactionService.getUserTransaction();
+ txn.begin();
+ fileFolderService.rename(contentNode, "testtesttesttesttesttesttesttesttesttest");
+ txn.commit();
+
+ // Read second message part
+ BODY bodyRest = getMessageBodyPart(folder, uid, count - 100, 100);
+
+ // Creating and parsing message from 2 parts
+ MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties()), new SequenceInputStream(new BufferedInputStream(body.getByteArrayInputStream()),
+ new BufferedInputStream(bodyRest.getByteArrayInputStream())));
+
+ // Reading first part - should be successful
+ MimeMultipart content = (MimeMultipart) message.getContent();
+ assertNotNull(content.getBodyPart(0).getContent());
+
+ try
+ {
+ // Reading second part cause error
+ content.getBodyPart(1).getContent();
+ fail("Should raise an IOException");
+ }
+ catch (IOException e)
+ {
+ }
+ }
+
+ public void testMessageCache() throws Exception
+ {
+
+ // Create messages
+ NodeRef contentNode = findNode(companyHomePathInStore + TEST_FILE);
+ UserTransaction txn = transactionService.getUserTransaction();
+ txn.begin();
+
+ // Create messages more than cache capacity
+ for (int i = 0; i < 51; i++)
+ {
+ FileInfo fi = fileFolderService.create(nodeService.getParentAssocs(contentNode).get(0).getParentRef(), "test" + i, ContentModel.TYPE_CONTENT);
+ ContentWriter writer = fileFolderService.getWriter(fi.getNodeRef());
+ writer.putContent("test");
+ }
+
+ txn.commit();
+
+ // Reload folder
+ folder.close(false);
+ folder = (IMAPFolder) store.getFolder(TEST_FOLDER);
+ folder.open(Folder.READ_ONLY);
+
+ // Read all messages
+ for (int i = 1; i < 51; i++)
+ {
+ // Get test message UID
+ final Long uid = getMessageUid(folder, i);
+ // Get Message size
+ final int count = getMessageSize(folder, uid);
+
+ // Get first part
+ BODY body = getMessageBodyPart(folder, uid, 0, count - 100);
+ // Read second message part
+ BODY bodyRest = getMessageBodyPart(folder, uid, count - 100, 100);
+
+ // Creating and parsing message from 2 parts
+ MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties()), new SequenceInputStream(new BufferedInputStream(body.getByteArrayInputStream()),
+ new BufferedInputStream(bodyRest.getByteArrayInputStream())));
+
+ // Reading first part - should be successful
+ MimeMultipart content = (MimeMultipart) message.getContent();
+ assertNotNull(content.getBodyPart(0).getContent());
+ assertNotNull(content.getBodyPart(1).getContent());
+ }
+ }
+
+
+
+ public void testUnmodifiedMessage() throws Exception
+ {
+ // Get test message UID
+ final Long uid = getMessageUid(folder, 1);
+ // Get Message size
+ final int count = getMessageSize(folder, uid);
+
+ // Make multiple message reading
+ for (int i = 0; i < 100; i++)
+ {
+ // Get random offset
+ int n = (int) ((int) 100 * Math.random());
+
+ // Get first part
+ BODY body = getMessageBodyPart(folder, uid, 0, count - n);
+ // Read second message part
+ BODY bodyRest = getMessageBodyPart(folder, uid, count - n, n);
+
+ // Creating and parsing message from 2 parts
+ MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties()), new SequenceInputStream(new BufferedInputStream(body.getByteArrayInputStream()),
+ new BufferedInputStream(bodyRest.getByteArrayInputStream())));
+
+ MimeMultipart content = (MimeMultipart) message.getContent();
+ // Reading first part - should be successful
+ assertNotNull(content.getBodyPart(0).getContent());
+ // Reading second part - should be successful
+ assertNotNull(content.getBodyPart(1).getContent());
+ }
+ }
+
+ public void testEncodedFromToAddresses() throws Exception
+ {
+ // RFC1342
+ String addressString = "ars.kov@gmail.com";
+ String personalString = "�?р�?ений Ковальчук";
+ InternetAddress address = new InternetAddress(addressString, personalString, "UTF-8");
+
+ // Following method returns the address with quoted personal aka <["�?р�?ений Ковальчук"] >
+ // NOTE! This should be coincided with RFC822MetadataExtracter. Would 'addresses' be quoted or not?
+ // String decodedAddress = address.toUnicodeString();
+ // So, just using decode, for now
+ String decodedAddress = MimeUtility.decodeText(address.toString());
+
+ // InternetAddress.toString(new Address[] {address}) - is used in the RFC822MetadataExtracter
+ // So, compare with that
+ assertFalse("Non ASCII characters in the address should be encoded", decodedAddress.equals(InternetAddress.toString(new Address[] {address})));
+
+ MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties()));
+
+ MimeMessageHelper messageHelper = new MimeMessageHelper(message, false, "UTF-8");
+
+ messageHelper.setText("This is a sample message for ALF-5647");
+ messageHelper.setSubject("This is a sample message for ALF-5647");
+ messageHelper.setFrom(address);
+ messageHelper.addTo(address);
+ messageHelper.addCc(address);
+
+ // Creating the message node in the repository
+ String name = AlfrescoImapConst.MESSAGE_PREFIX + GUID.generate();
+ FileInfo messageFile = fileFolderService.create(testImapFolderNodeRef, name, ContentModel.TYPE_CONTENT);
+ // Writing a content.
+ new IncomingImapMessage(messageFile, serviceRegistry, message);
+
+ // Getting the transformed properties from the repository
+ // cm:originator, cm:addressee, cm:addressees, imap:messageFrom, imap:messageTo, imap:messageCc
+ Map properties = nodeService.getProperties(messageFile.getNodeRef());
+
+ String cmOriginator = (String) properties.get(ContentModel.PROP_ORIGINATOR);
+ String cmAddressee = (String) properties.get(ContentModel.PROP_ADDRESSEE);
+ // TODO cm:addressees is returned as list
+ //String cmAddressees = (String) properties.get(ContentModel.PROP_ADDRESSEES);
+ String imapMessageFrom = (String) properties.get(ImapModel.PROP_MESSAGE_FROM);
+ String imapMessageTo = (String) properties.get(ImapModel.PROP_MESSAGE_TO);
+ String imapMessageCc = (String) properties.get(ImapModel.PROP_MESSAGE_CC);
+
+ assertNotNull(cmOriginator);
+ assertEquals(decodedAddress, cmOriginator);
+ assertNotNull(cmAddressee);
+ assertEquals(decodedAddress, cmAddressee);
+ //assertNotNull(cmAddressees);
+ //assertEquals(cmAddressees, encodedAddress);
+ assertNotNull(imapMessageFrom);
+ assertEquals(decodedAddress, imapMessageFrom);
+ assertNotNull(imapMessageTo);
+ assertEquals(decodedAddress, imapMessageTo);
+ assertNotNull(imapMessageCc);
+ assertEquals(decodedAddress, imapMessageCc);
+ }
+
+ public void testEightBitMessage() throws Exception
+ {
+
+ Store lstore = session.getStore(PROTOCOL);
+ lstore.connect(imapServer.getHost(), imapServer.getPort(), ADMIN_USER_NAME, ADMIN_USER_PASSWORD);
+
+ String folderName = "Alfresco IMAP/" + IMAP_FOLDER_NAME;
+
+ UserTransaction txn = transactionService.getUserTransaction();
+
+ IMAPFolder lfolder = (IMAPFolder) lstore.getFolder(folderName);
+ lfolder.open(Folder.READ_WRITE);
+
+
+
+ InputStream messageFileInputStream1 = null;
+ InputStream messageFileInputStream2 = null;
+ try
+ {
+ ClassPathResource fileResource = new ClassPathResource("imap/test-8bit-message.eml");
+ messageFileInputStream1 = new FileInputStream(fileResource.getFile());
+ Message message = new MimeMessage(Session.getDefaultInstance(new Properties()), messageFileInputStream1);
+ String subject = message.getSubject();
+
+ // get original bytes for further comparation
+ messageFileInputStream2 = new FileInputStream(fileResource.getFile());
+ byte[] original = ASCIIUtility.getBytes(messageFileInputStream2);
+
+ Message[] messages = {message};
+
+ lfolder.appendMessages(messages);
+
+
+
+ // The search is not implemented.
+ // SearchTerm term = new HeaderTerm("X-Alfresco-Unique", "test8bit");
+ // messages = folder.search(term);
+
+ // So wee need to get our test message's UID from the repo
+
+ String messageXPath = companyHomePathInStore + "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + IMAP_FOLDER_NAME + "/*[like(@cm:title, $cm:title, true)]";
+
+ QueryParameterDefinition[] params = new QueryParameterDefinition[1];
+ params[0] = new QueryParameterDefImpl(
+ ContentModel.PROP_TITLE,
+ serviceRegistry.getDictionaryService().getDataType(DataTypeDefinition.TEXT),
+ true,
+ subject);
+
+ List nodeRefs = searchService.selectNodes(storeRootNodeRef, messageXPath, params, namespaceService, true);
+
+
+ // does the message exist
+ assertEquals(1, nodeRefs.size());
+
+ NodeRef messageNodeRef = nodeRefs.get(0);
+
+ // get message UID
+ Long dbid = (Long) nodeService.getProperty(messageNodeRef, ContentModel.PROP_NODE_DBID);
+
+ // fetch the massage
+ RFC822DATA data = getRFC822Message(lfolder, dbid);
+
+ assertNotNull("Can't fetch a message from the repositiry", data);
+
+ byte[] processed = ASCIIUtility.getBytes(data.getByteArrayInputStream());
+
+ assertTrue("Original message doesn't coincide to the message processed by the repository", Arrays.equals(original, processed));
+ }
+ finally
+ {
+ if (messageFileInputStream1 != null) messageFileInputStream1.close();
+ if (messageFileInputStream2 != null) messageFileInputStream2.close();
+ }
+
+ // close connection
+ lfolder.close(true);
+ lstore.close();
+
+ }
+
+
+ private static RFC822DATA getRFC822Message(final IMAPFolder folder, final long uid) throws MessagingException
+ {
+ return (RFC822DATA) folder.doCommand(new IMAPFolder.ProtocolCommand()
+ {
+ public Object doCommand(IMAPProtocol p) throws ProtocolException
+ {
+ Response[] r = p.command("UID FETCH " + uid + " (RFC822)", null);
+ logResponse(r);
+ Response response = r[r.length - 1];
+ if (!response.isOK())
+ {
+ throw new ProtocolException("Unable to retrieve message in RFC822 format");
+ }
+
+ FetchResponse fetchResponse = (FetchResponse) r[0];
+ return fetchResponse.getItem(RFC822DATA.class);
+ }
+ });
+
+ }
/**
* Returns BODY object containing desired message fragment
@@ -442,7 +633,8 @@ public class ImapMessageTest extends TestCase
{
public Object doCommand(IMAPProtocol p) throws ProtocolException
{
- Response[] r = p.command("FETCH " + msn + " (UID)", null);
+ String command = "FETCH " + msn + " (UID)";
+ Response[] r = p.command(command, null);
logResponse(r);
Response response = r[r.length - 1];
@@ -451,9 +643,39 @@ public class ImapMessageTest extends TestCase
{
throw new ProtocolException("Unable to retrieve message UID");
}
- FetchResponse fetchResponse = (FetchResponse) r[0];
- UID uid = (UID) fetchResponse.getItem(UID.class);
- return uid.uid;
+
+ for(int i = 0 ; i < r.length; i++)
+ {
+ if(r[i] instanceof FetchResponse)
+ {
+ FetchResponse fetchResponse = (FetchResponse) r[0];
+ UID uid = (UID) fetchResponse.getItem(UID.class);
+ logger.debug("MSGNO=" + uid.msgno + ", UID="+uid.uid);
+ return uid.uid;
+ }
+ }
+
+ /**
+ * Uh-oh - this is where we would intermittently fall over with a class cast exception.
+ * The following code probes why we don't have a FetchResponse
+ */
+ StringBuffer sb = new StringBuffer();
+ sb.append("command="+command);
+ sb.append('\n');
+ sb.append("resp length=" + r.length);
+ sb.append('\n');
+ for(int i = 0 ; i < r.length; i++)
+ {
+ logger.error(r[i]);
+ sb.append("class=" + r[i].getClass().getName());
+ IMAPResponse unexpected = (IMAPResponse)r[i];
+ sb.append("key=" + unexpected.getKey());
+ sb.append("number=" + unexpected.getNumber());
+ sb.append("rest=" + unexpected.getRest());
+
+ sb.append("r[" + i + "]=" + r[i] + '\n');
+ }
+ throw new ProtocolException("getMessageUid: "+ sb.toString());
}
});
}
@@ -498,33 +720,34 @@ public class ImapMessageTest extends TestCase
for (int i = 0; i < r.length; i++)
{
logger.debug(r[i]);
- //logger.info(r[i]);
}
}
@Override
public void tearDown() throws Exception
{
-// // Deleting created test environment
-//
-// UserTransaction txn = transactionService.getUserTransaction();
-// txn.begin();
-//
-// List nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore + "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + IMAP_FOLDER_NAME, null,
-// namespaceService, false);
-// if (nodeRefs != null && nodeRefs.size() > 0)
-// {
-// fileFolderService.delete(nodeRefs.get(0));
-// }
-//
-// authenticationService.deleteAuthentication(anotherUserName);
-// personService.deletePerson(anotherUserName);
-//
-// txn.commit();
-//
-// // Closing client connection
-// folder.close(false);
-// store.close();
+ // Deleting created test environment
+ logger.debug("tearDown ");
+
+ UserTransaction txn = transactionService.getUserTransaction();
+ txn.begin();
+
+ List nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore + "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + IMAP_FOLDER_NAME, null,
+ namespaceService, false);
+ if (nodeRefs != null && nodeRefs.size() > 0)
+ {
+ fileFolderService.delete(nodeRefs.get(0));
+ }
+
+ authenticationService.deleteAuthentication(anotherUserName);
+ personService.deletePerson(anotherUserName);
+
+ txn.commit();
+
+ // Closing client connection
+ folder.close(false);
+ store.close();
+ logger.debug("tearDown end");
}
}
diff --git a/source/java/org/alfresco/repo/imap/ImapServiceImpl.java b/source/java/org/alfresco/repo/imap/ImapServiceImpl.java
index 75bb8a5ab6..89d67dedd9 100644
--- a/source/java/org/alfresco/repo/imap/ImapServiceImpl.java
+++ b/source/java/org/alfresco/repo/imap/ImapServiceImpl.java
@@ -23,6 +23,7 @@ import static org.alfresco.repo.imap.AlfrescoImapConst.DICTIONARY_TEMPLATE_PREFI
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
@@ -38,12 +39,22 @@ import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.model.ImapModel;
import org.alfresco.repo.admin.SysAdminParams;
+import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.repo.imap.AlfrescoImapConst.ImapViewMode;
import org.alfresco.repo.imap.config.ImapConfigMountPointsBean;
+import org.alfresco.repo.node.NodeServicePolicies.OnCreateChildAssociationPolicy;
+import org.alfresco.repo.node.NodeServicePolicies.OnDeleteChildAssociationPolicy;
+import org.alfresco.repo.policy.JavaBehaviour;
+import org.alfresco.repo.policy.PolicyComponent;
+import org.alfresco.repo.policy.Behaviour.NotificationFrequency;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.security.permissions.AccessDeniedException;
+import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.site.SiteModel;
import org.alfresco.repo.site.SiteServiceException;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
+import org.alfresco.repo.transaction.RetryingTransactionHelper;
+import org.alfresco.repo.transaction.TransactionListenerAdapter;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.lock.NodeLockedException;
@@ -77,7 +88,7 @@ import org.springframework.extensions.surf.util.I18NUtil;
* @author Arseny Kovalchuk
* @since 3.2
*/
-public class ImapServiceImpl implements ImapService
+public class ImapServiceImpl implements ImapService, OnCreateChildAssociationPolicy, OnDeleteChildAssociationPolicy
{
private Log logger = LogFactory.getLog(ImapServiceImpl.class);
@@ -88,6 +99,10 @@ public class ImapServiceImpl implements ImapService
private static final String CHECKED_NODES = "imap.flaggable.aspect.checked.list";
private static final String FAVORITE_SITES = "imap.favorite.sites.list";
+ private static final String UIDVALIDITY_LISTENER_ALREADY_BOUND = "imap.uidvalidity.already.bound";
+
+ private static final String INBOX = "INBOX";
+ private static final String TRASH = "TRASH";
private SysAdminParams sysAdminParams;
private FileFolderService fileFolderService;
@@ -95,6 +110,13 @@ public class ImapServiceImpl implements ImapService
private PermissionService permissionService;
private ServiceRegistry serviceRegistry;
+ /**
+ * Folders cache
+ *
+ * Key : folder name, Object : AlfrescoImapFolder
+ */
+ private SimpleCache foldersCache;
+
private Map imapConfigMountPoints;
private RepositoryFolderConfigBean[] ignoreExtractionFoldersBeans;
private RepositoryFolderConfigBean imapHomeConfigBean;
@@ -175,6 +197,16 @@ public class ImapServiceImpl implements ImapService
this.sysAdminParams = sysAdminParams;
}
+ public void setFoldersCache(SimpleCache foldersCache)
+ {
+ this.foldersCache = foldersCache;
+ }
+
+ public SimpleCache getFoldersCache()
+ {
+ return foldersCache;
+ }
+
public FileFolderService getFileFolderService()
{
return fileFolderService;
@@ -282,6 +314,8 @@ public class ImapServiceImpl implements ImapService
public void startup()
{
+ bindBeahaviour();
+
final NamespaceService namespaceService = serviceRegistry.getNamespaceService();
final SearchService searchService = serviceRegistry.getSearchService();
@@ -334,6 +368,35 @@ public class ImapServiceImpl implements ImapService
{
}
+ protected void bindBeahaviour()
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("[bindBeahaviour] Binding behaviours");
+ }
+ PolicyComponent policyComponent = (PolicyComponent) serviceRegistry.getService(QName.createQName(NamespaceService.ALFRESCO_URI, "policyComponent"));
+ policyComponent.bindAssociationBehaviour(
+ OnCreateChildAssociationPolicy.QNAME,
+ ContentModel.TYPE_FOLDER,
+ ContentModel.ASSOC_CONTAINS,
+ new JavaBehaviour(this, "onCreateChildAssociation", NotificationFrequency.TRANSACTION_COMMIT));
+ policyComponent.bindAssociationBehaviour(
+ OnCreateChildAssociationPolicy.QNAME,
+ ContentModel.TYPE_CONTENT,
+ ContentModel.ASSOC_CONTAINS,
+ new JavaBehaviour(this, "onCreateChildAssociation", NotificationFrequency.TRANSACTION_COMMIT));
+ policyComponent.bindAssociationBehaviour(
+ OnDeleteChildAssociationPolicy.QNAME,
+ ContentModel.TYPE_FOLDER,
+ ContentModel.ASSOC_CONTAINS,
+ new JavaBehaviour(this, "onDeleteChildAssociation", NotificationFrequency.TRANSACTION_COMMIT));
+ policyComponent.bindAssociationBehaviour(
+ OnDeleteChildAssociationPolicy.QNAME,
+ ContentModel.TYPE_CONTENT,
+ ContentModel.ASSOC_CONTAINS,
+ new JavaBehaviour(this, "onDeleteChildAssociation", NotificationFrequency.TRANSACTION_COMMIT));
+ }
+
// ---------------------- Service Methods --------------------------------
public List listSubscribedMailboxes(AlfrescoImapUser user, String mailboxPattern)
@@ -347,7 +410,7 @@ public class ImapServiceImpl implements ImapService
mailboxPattern = getMailPathInRepo(mailboxPattern);
if (logger.isDebugEnabled())
{
- logger.debug("Listing subscribed mailboxes: mailboxPattern in Alfresco=" + mailboxPattern);
+ logger.debug("Listing subscribed mailboxes: mailbox path in Alfresco=" + mailboxPattern);
}
return listMailboxes(user, mailboxPattern, true);
}
@@ -363,7 +426,7 @@ public class ImapServiceImpl implements ImapService
mailboxPattern = getMailPathInRepo(mailboxPattern);
if (logger.isDebugEnabled())
{
- logger.debug("Listing mailboxes: mailboxPattern in Alfresco=" + mailboxPattern);
+ logger.debug("Listing mailboxes: mailbox path in Alfresco Repository = " + mailboxPattern);
}
return listMailboxes(user, mailboxPattern, false);
@@ -399,7 +462,7 @@ public class ImapServiceImpl implements ImapService
throw new AlfrescoRuntimeException(ERROR_PERMISSION_DENIED);
}
FileInfo mailFolder = serviceRegistry.getFileFolderService().create(parentNodeRef, folderName, ContentModel.TYPE_FOLDER);
- return new AlfrescoImapFolder(
+ AlfrescoImapFolder resultFolder = new AlfrescoImapFolder(
user.getQualifiedMailboxName(),
mailFolder,
folderName,
@@ -408,6 +471,8 @@ public class ImapServiceImpl implements ImapService
getMountPointName(mailboxName),
isExtractionEnabled(mailFolder.getNodeRef()),
serviceRegistry);
+ foldersCache.put(mailboxName, resultFolder);
+ return resultFolder;
}
else
{
@@ -463,6 +528,7 @@ public class ImapServiceImpl implements ImapService
throw new AlfrescoRuntimeException(mailboxName + " - Can't delete a non-selectable store with children.");
}
}
+ foldersCache.remove(mailboxName);
}
public void renameMailbox(AlfrescoImapUser user, String oldMailboxName, String newMailboxName)
@@ -491,17 +557,30 @@ public class ImapServiceImpl implements ImapService
folderName = folderNames[i];
if (i == (folderNames.length - 1)) // is it the last element
{
+ FileInfo newFileInfo = null;
if (oldMailboxName.equalsIgnoreCase(AlfrescoImapConst.INBOX_NAME))
{
// If you trying to rename INBOX
// - just copy it to another folder with new name
// and leave INBOX (with children) intact.
- fileFolderService.copy(sourceNode.getFolderInfo().getNodeRef(), parentNodeRef, folderName);
+ newFileInfo = fileFolderService.copy(sourceNode.getFolderInfo().getNodeRef(), parentNodeRef, folderName);
}
else
{
- fileFolderService.move(sourceNode.getFolderInfo().getNodeRef(), parentNodeRef, folderName);
+ newFileInfo = fileFolderService.move(sourceNode.getFolderInfo().getNodeRef(), parentNodeRef, folderName);
}
+
+ foldersCache.remove(oldMailboxName);
+ AlfrescoImapFolder resultFolder = new AlfrescoImapFolder(
+ user.getQualifiedMailboxName(),
+ newFileInfo,
+ folderName,
+ getViewMode(newMailboxName),
+ root,
+ getMountPointName(newMailboxName),
+ isExtractionEnabled(newFileInfo.getNodeRef()),
+ serviceRegistry);
+ foldersCache.put(newMailboxName, resultFolder);
}
else
{
@@ -554,6 +633,10 @@ public class ImapServiceImpl implements ImapService
*/
public AlfrescoImapFolder getFolder(AlfrescoImapUser user, String mailboxName)
{
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Folders cache size is " + foldersCache.getKeys().size());
+ }
mailboxName = Utf7.decode(mailboxName, Utf7.UTF7_MODIFIED);
if (logger.isDebugEnabled())
{
@@ -570,95 +653,203 @@ public class ImapServiceImpl implements ImapService
{
logger.debug("Request for the hierarchy delimiter");
}
- return new AlfrescoImapFolder(user.getQualifiedMailboxName(), serviceRegistry);
- }
-
- ImapViewMode viewMode = getViewMode(mailboxName);
- String mountPointName = getMountPointName(mailboxName);
-
- NodeRef root = getMailboxRootRef(mailboxName, user.getLogin());
- NodeRef nodeRef = root; // initial top folder
-
- String[] folderNames = getMailPathInRepo(mailboxName).split(String.valueOf(AlfrescoImapConst.HIERARCHY_DELIMITER));
-
- if(folderNames.length == 1 && folderNames[0].length() == 0)
- {
- // This is the root of the mount point e.g "Alfresco IMAP" which has a path from root of ""
- FileInfo folderFileInfo = fileFolderService.getFileInfo(root);
-
- AlfrescoImapFolder folder = new AlfrescoImapFolder(
- user.getQualifiedMailboxName(),
- folderFileInfo,
- mountPointName,
- viewMode,
- root,
- mountPointName,
- isExtractionEnabled(folderFileInfo.getNodeRef()),
- serviceRegistry);
-
+ AlfrescoImapFolder hierarhyFolder = (AlfrescoImapFolder) foldersCache.get("hierarhy.delimeter");
if (logger.isDebugEnabled())
{
- logger.debug("Returning root folder '" + mailboxName + "'");
+ logger.debug("Got a hierarhy delimeter from cache: " + hierarhyFolder);
}
- return folder;
+ if (hierarhyFolder == null)
+ {
+ hierarhyFolder = new AlfrescoImapFolder(user.getQualifiedMailboxName(), serviceRegistry);
+ // TEMP Comment out putting this into the same cache as the "real folders" Causes NPE in
+ // Security Interceptor
+ //foldersCache.put("hierarhy.delimeter", hierarhyFolder);
+ }
+ return hierarhyFolder;
}
-
- for (int i = 0; i < folderNames.length; i++)
+ else if (AlfrescoImapConst.INBOX_NAME.equalsIgnoreCase(mailboxName) || AlfrescoImapConst.TRASH_NAME.equalsIgnoreCase(mailboxName))
{
- if (logger.isDebugEnabled())
- {
- logger.debug("Processing of " + folderNames[i]);
- }
+ String cacheKey = user.getLogin() + '.' + mailboxName;
+ AlfrescoImapFolder imapSystemFolder = (AlfrescoImapFolder) foldersCache.get(cacheKey);
- NodeRef targetNode = fileFolderService.searchSimple(nodeRef, folderNames[i]);
-
- if (targetNode == null)
- {
- AlfrescoImapFolder folder = new AlfrescoImapFolder(user.getQualifiedMailboxName(), serviceRegistry);
+ if(imapSystemFolder != null)
+ {
if (logger.isDebugEnabled())
{
- logger.debug("Returning empty folder '" + folderNames[i]);
- }
- return folder;
- }
-
- if (i == (folderNames.length - 1)) // is last
- {
- FileInfo folderFileInfo = fileFolderService.getFileInfo(targetNode);
-
- if (logger.isDebugEnabled())
- {
- logger.debug("Found folder to list: " + folderFileInfo.getName());
+ logger.debug("Got a system folder '" + mailboxName + "' from cache: " + imapSystemFolder);
}
- AlfrescoImapFolder folder = new AlfrescoImapFolder(
+ /**
+ * Check whether resultFolder is stale
+ */
+ if(imapSystemFolder.isStale())
+ {
+ logger.debug("system folder is stale");
+ imapSystemFolder = null;
+ }
+ }
+
+ if (imapSystemFolder == null)
+ {
+ NodeRef userImapRoot = getUserImapHomeRef(user.getLogin());
+ NodeRef mailBoxRef = nodeService.getChildByName(userImapRoot, ContentModel.ASSOC_CONTAINS, mailboxName);
+ if (mailBoxRef != null)
+ {
+ FileInfo mailBoxFileInfo = fileFolderService.getFileInfo(mailBoxRef);
+ imapSystemFolder = new AlfrescoImapFolder(
+ user.getQualifiedMailboxName(),
+ mailBoxFileInfo,
+ mailBoxFileInfo.getName(),
+ getViewMode(mailboxName),
+ userImapRoot,
+ getMountPointName(mailboxName),
+ isExtractionEnabled(mailBoxFileInfo.getNodeRef()),
+ serviceRegistry);
+ foldersCache.put(cacheKey, imapSystemFolder);
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Returning folder '" + mailboxName + "'");
+ }
+ }
+ else
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Cannot get a folder '" + mailboxName + "'");
+ }
+ throw new AlfrescoRuntimeException(ERROR_CANNOT_GET_A_FOLDER, new String[] { mailboxName });
+ }
+ }
+ return imapSystemFolder;
+
+ }
+
+ /**
+ * Folder is not hierarchy.delimiter or a "System" folder (INBOX or TRASH)
+ */
+ AlfrescoImapFolder resultFolder = (AlfrescoImapFolder) foldersCache.get(mailboxName);
+
+ if(resultFolder != null)
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Got a folder '" + mailboxName + "' from cache: " + resultFolder);
+ }
+
+ /**
+ * Check whether resultFolder is stale
+ */
+ if(resultFolder.isStale())
+ {
+ logger.debug("folder is stale");
+ resultFolder = null;
+ }
+ }
+
+ if (resultFolder == null)
+ {
+ ImapViewMode viewMode = getViewMode(mailboxName);
+ String mountPointName = getMountPointName(mailboxName);
+
+ NodeRef root = getMailboxRootRef(mailboxName, user.getLogin());
+ NodeRef nodeRef = root; // initial top folder
+
+ String[] folderNames = getMailPathInRepo(mailboxName).split(String.valueOf(AlfrescoImapConst.HIERARCHY_DELIMITER));
+
+ if(folderNames.length == 1 && folderNames[0].length() == 0)
+ {
+ // This is the root of the mount point e.g "Alfresco IMAP" which has a path from root of ""
+ FileInfo folderFileInfo = fileFolderService.getFileInfo(root);
+
+ resultFolder = new AlfrescoImapFolder(
user.getQualifiedMailboxName(),
folderFileInfo,
- folderFileInfo.getName(),
+ mountPointName,
viewMode,
root,
mountPointName,
isExtractionEnabled(folderFileInfo.getNodeRef()),
serviceRegistry);
-
+
if (logger.isDebugEnabled())
{
- logger.debug("Returning folder '" + mailboxName + "'");
+ logger.debug("Returning root folder '" + mailboxName + "'");
+ }
+ }
+ else
+ {
+
+ for (int i = 0; i < folderNames.length; i++)
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Processing of '" + folderNames[i] + "'");
+ }
+
+ NodeRef targetNode = fileFolderService.searchSimple(nodeRef, folderNames[i]);
+
+ if (targetNode == null)
+ {
+ resultFolder = new AlfrescoImapFolder(user.getQualifiedMailboxName(), serviceRegistry);
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Returning empty folder '" + folderNames[i] + "'");
+ }
+ // skip cache for root
+ return resultFolder;
+ }
+
+ if (i == (folderNames.length - 1)) // is last
+ {
+ FileInfo folderFileInfo = fileFolderService.getFileInfo(targetNode);
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Found folder to list: " + folderFileInfo.getName());
+ }
+
+ resultFolder = new AlfrescoImapFolder(
+ user.getQualifiedMailboxName(),
+ folderFileInfo,
+ folderFileInfo.getName(),
+ viewMode,
+ root,
+ mountPointName,
+ isExtractionEnabled(folderFileInfo.getNodeRef()),
+ serviceRegistry);
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Returning folder '" + mailboxName + "'");
+ }
+ }
+
+ /**
+ * End of loop - next element in path
+ */
+ nodeRef = targetNode;
+ }
+ }
+
+ if (resultFolder == null)
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Cannot get a folder '" + mailboxName + "'");
+ }
+ throw new AlfrescoRuntimeException(ERROR_CANNOT_GET_A_FOLDER, new String[] { mailboxName });
+ }
+ else
+ {
+ foldersCache.put(mailboxName, resultFolder);
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Put a folder '" + mailboxName + "' to cache: " + resultFolder);
}
- return folder;
}
-
- /**
- * End of loop - next element in path
- */
- nodeRef = targetNode;
}
-
- if (logger.isDebugEnabled())
- {
- logger.debug("Cannot get a folder '" + mailboxName + "'");
- }
- throw new AlfrescoRuntimeException(ERROR_CANNOT_GET_A_FOLDER, new String[] { mailboxName });
+ return resultFolder;
+
}
/**
@@ -894,6 +1085,8 @@ public class ImapServiceImpl implements ImapService
public synchronized Flags getFlags(FileInfo messageInfo)
{
Flags flags = new Flags();
+ if (nodeService.exists(messageInfo.getNodeRef()))
+ {
checkForFlaggableAspect(messageInfo.getNodeRef());
Map props = nodeService.getProperties(messageInfo.getNodeRef());
@@ -905,6 +1098,7 @@ public class ImapServiceImpl implements ImapService
flags.add(qNameToFlag.get(key));
}
}
+ }
return flags;
}
@@ -972,10 +1166,12 @@ public class ImapServiceImpl implements ImapService
FileInfo mountPointFileInfo = fileFolderService.getFileInfo(mountPoint);
NodeRef mountParent = nodeService.getParentAssocs(mountPoint).get(0).getParentRef();
ImapViewMode viewMode = imapConfigMountPoints.get(mountPointName).getMode();
+ /* FIX for ALF-2793 Reinstated
if (!mailboxPattern.equals("*"))
{
mountPoint = mountParent;
}
+ */
List folders = expandFolder(mountPoint, mountPoint, user, mailboxPattern, listSubscribed, viewMode);
if (folders != null)
{
@@ -1327,20 +1523,51 @@ public class ImapServiceImpl implements ImapService
Set mountPointNodeRefs = new HashSet(5);
Map mountPoints = new HashMap();
- NamespaceService namespaceService = serviceRegistry.getNamespaceService();
- SearchService searchService = serviceRegistry.getSearchService();
- for (ImapConfigMountPointsBean config : imapConfigMountPoints.values())
+ final NamespaceService namespaceService = serviceRegistry.getNamespaceService();
+ final SearchService searchService = serviceRegistry.getSearchService();
+ for (final ImapConfigMountPointsBean config : imapConfigMountPoints.values())
{
- // Get node reference
- NodeRef nodeRef = config.getFolderPath(namespaceService, nodeService, searchService, fileFolderService);
-
- if (!mountPointNodeRefs.add(nodeRef))
+ try
{
- throw new IllegalArgumentException(
- "A mount point has been defined twice: \n" +
- " Mount point: " + config);
+ // Get node reference. Do it in new transaction to avoid RollBack in case when AccessDeniedException is thrown.
+ NodeRef nodeRef = serviceRegistry.getTransactionService().getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback()
+ {
+ public NodeRef execute() throws Exception
+ {
+ try
+ {
+ return config.getFolderPath(namespaceService, nodeService, searchService, fileFolderService);
+ }
+ catch (AccessDeniedException e)
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("A mount point is skipped due to Access Dennied. \n" + " Mount point: " + config + "\n" + " User: "
+ + AuthenticationUtil.getFullyAuthenticatedUser());
+ }
+ }
+
+ return null;
+ }
+ }, true, true);
+
+ if (nodeRef != null)
+ {
+ if (!mountPointNodeRefs.add(nodeRef))
+ {
+ throw new IllegalArgumentException("A mount point has been defined twice: \n" + " Mount point: " + config);
+ }
+ mountPoints.put(config.getMountPointName(), nodeRef);
+ }
+ }
+ catch (AccessDeniedException e)
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("A mount point is skipped due to Access Dennied. \n" + " Mount point: " + config + "\n" + " User: "
+ + AuthenticationUtil.getFullyAuthenticatedUser());
+ }
}
- mountPoints.put(config.getMountPointName(), nodeRef);
}
return mountPoints;
}
@@ -1467,10 +1694,12 @@ public class ImapServiceImpl implements ImapService
private List createMailFolderList(AlfrescoImapUser user, Collection list, NodeRef imapUserHomeRef)
{
List result = new LinkedList();
-
+ // XXX : put folders into the cache and get them in next lookups.
+ // The question is to get a mailBoxName for the cache key... And what if a folders list was changed?
+ // So, for now keep it as is.
for (FileInfo folderInfo : list)
{
- // folderName, viewMode, mountPointName will be setted in listSubscribedMailboxes() method
+ // folderName, viewMode, mountPointName will be set in listSubscribedMailboxes() method
result.add(
new AlfrescoImapFolder(
user.getQualifiedMailboxName(),
@@ -1813,4 +2042,97 @@ public class ImapServiceImpl implements ImapService
}
}
+
+ @Override
+ public void onCreateChildAssociation(ChildAssociationRef childAssocRef, boolean isNewNode)
+ {
+ // Add a listener once, when a lots of messsages were created/moved into the folder
+ if (AlfrescoTransactionSupport.getResource(UIDVALIDITY_LISTENER_ALREADY_BOUND) == null)
+ {
+ AlfrescoTransactionSupport.bindListener(new UidValidityTransactionListener(childAssocRef.getParentRef(), nodeService));
+ AlfrescoTransactionSupport.bindResource(UIDVALIDITY_LISTENER_ALREADY_BOUND, true);
+ }
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("[onCreateChildAssociation] Association " + childAssocRef + " created. UIDVALIDITY will be changed.");
+ }
+ }
+
+ public void onDeleteChildAssociation(ChildAssociationRef childAssocRef)
+ {
+ // Add a listener once, when a lots of messsages were created/moved into the folder
+ if (AlfrescoTransactionSupport.getResource(UIDVALIDITY_LISTENER_ALREADY_BOUND) == null)
+ {
+ AlfrescoTransactionSupport.bindListener(new UidValidityTransactionListener(childAssocRef.getParentRef(), nodeService));
+ AlfrescoTransactionSupport.bindResource(UIDVALIDITY_LISTENER_ALREADY_BOUND, true);
+ }
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("[onDeleteChildAssociation] Association " + childAssocRef + " removed. UIDVALIDITY will be changed.");
+ }
+ }
+
+ private class UidValidityTransactionListener extends TransactionListenerAdapter
+ {
+
+ private RunAsWork work;
+
+ UidValidityTransactionListener(NodeRef folderNodeRef, NodeService nodeService)
+ {
+ this.work = new IncrementUidValidityWork(folderNodeRef, nodeService);
+ }
+
+ @Override
+ public void afterCommit()
+ {
+ AuthenticationUtil.runAs(this.work, AuthenticationUtil.getSystemUserName());
+ }
+
+ }
+
+ private class IncrementUidValidityWork implements RunAsWork
+ {
+ private NodeService nodeService;
+ private NodeRef folderNodeRef;
+
+ public IncrementUidValidityWork(NodeRef folderNodeRef, NodeService nodeService)
+ {
+ this.folderNodeRef = folderNodeRef;
+ this.nodeService = nodeService;
+ }
+
+ @Override
+ public Long doWork() throws Exception
+ {
+ RetryingTransactionHelper txnHelper = serviceRegistry.getRetryingTransactionHelper();
+ return txnHelper.doInTransaction(new RetryingTransactionCallback(){
+
+ @Override
+ public Long execute() throws Throwable
+ {
+ long modifDate = new Date().getTime();
+
+ if (!IncrementUidValidityWork.this.nodeService.hasAspect(folderNodeRef, ImapModel.ASPECT_IMAP_FOLDER))
+ {
+ Map aspectProperties = new HashMap(1, 1);
+ aspectProperties.put(ImapModel.PROP_UIDVALIDITY, modifDate);
+ IncrementUidValidityWork.this.nodeService.addAspect(folderNodeRef, ImapModel.ASPECT_IMAP_FOLDER, aspectProperties);
+ }
+ else
+ {
+ IncrementUidValidityWork.this.nodeService.setProperty(folderNodeRef, ImapModel.PROP_UIDVALIDITY, modifDate);
+ }
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("UIDVALIDITY was modified");
+ }
+ return modifDate;
+ }
+
+ }, false, true);
+ }
+
+ }
+
+
}
diff --git a/source/java/org/alfresco/repo/invitation/InvitationServiceImpl.java b/source/java/org/alfresco/repo/invitation/InvitationServiceImpl.java
index 0338a0c91a..65e7788908 100644
--- a/source/java/org/alfresco/repo/invitation/InvitationServiceImpl.java
+++ b/source/java/org/alfresco/repo/invitation/InvitationServiceImpl.java
@@ -102,6 +102,8 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli
private int maxUserNameGenRetries = MAX_NUM_INVITEE_USER_NAME_GEN_TRIES;
+ // Property determining whether emails should be sent.
+ private boolean sendEmails = true;
/**
* Set the policy component
*
@@ -1451,4 +1453,20 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli
return I18NUtil.getMessage(messageId, siteTitle);
}
+
+ /**
+ * @param sendEmails the sendEmails to set
+ */
+ public void setSendEmails(boolean sendEmails)
+ {
+ this.sendEmails = sendEmails;
+ }
+
+ /**
+ * @return true if emails are sent on invite.
+ */
+ public boolean isSendEmails()
+ {
+ return sendEmails;
+ }
}
diff --git a/source/java/org/alfresco/repo/invitation/InvitationServiceImplTest.java b/source/java/org/alfresco/repo/invitation/InvitationServiceImplTest.java
index ad2fb05292..45d9b7e8da 100644
--- a/source/java/org/alfresco/repo/invitation/InvitationServiceImplTest.java
+++ b/source/java/org/alfresco/repo/invitation/InvitationServiceImplTest.java
@@ -89,7 +89,7 @@ public class InvitationServiceImplTest extends BaseAlfrescoSpringTest
this.personService = (PersonService) this.applicationContext.getBean("PersonService");
this.authenticationComponent = (AuthenticationComponent) this.applicationContext
.getBean("authenticationComponent");
-
+
// TODO MER 20/11/2009 Bodge - turn off email sending to prevent errors
// during unit testing
// (or sending out email by accident from tests)
diff --git a/source/java/org/alfresco/repo/invitation/ModeratedActionApprove.java b/source/java/org/alfresco/repo/invitation/ModeratedActionApprove.java
index 8964d3544f..7f4ab8c798 100644
--- a/source/java/org/alfresco/repo/invitation/ModeratedActionApprove.java
+++ b/source/java/org/alfresco/repo/invitation/ModeratedActionApprove.java
@@ -19,16 +19,11 @@
package org.alfresco.repo.invitation;
-import org.alfresco.repo.invitation.WorkflowModelNominatedInvitation;
-import org.alfresco.repo.invitation.site.AcceptInviteAction;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
-import org.alfresco.repo.security.authentication.MutableAuthenticationDao;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.workflow.jbpm.JBPMSpringActionHandler;
import org.alfresco.service.ServiceRegistry;
-import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.site.SiteService;
-import org.alfresco.service.cmr.workflow.WorkflowService;
import org.jbpm.graph.exe.ExecutionContext;
import org.springframework.beans.factory.BeanFactory;
@@ -39,37 +34,27 @@ public class ModeratedActionApprove extends JBPMSpringActionHandler
{
private static final long serialVersionUID = 4377660284993206875L;
- private MutableAuthenticationDao mutableAuthenticationDao;
- private PersonService personService;
- private WorkflowService workflowService;
private SiteService siteService;
- /* (non-Javadoc)
- * @see org.alfresco.repo.workflow.jbpm.JBPMSpringActionHandler#initialiseHandler(org.springframework.beans.factory.BeanFactory)
+ /**
+ * {@inheritDoc}
*/
@Override
protected void initialiseHandler(BeanFactory factory)
{
ServiceRegistry services = (ServiceRegistry)factory.getBean(ServiceRegistry.SERVICE_REGISTRY);
- mutableAuthenticationDao = (MutableAuthenticationDao) factory.getBean("authenticationDao");
- personService = (PersonService) services.getPersonService();
- workflowService = (WorkflowService) services.getWorkflowService();
siteService = services.getSiteService();
}
- /* (non-Javadoc)
- * @see org.jbpm.graph.def.ActionHandler#execute(org.jbpm.graph.exe.ExecutionContext)
- * Approve Moderated
+ /**
+ * {@inheritDoc}
**/
- @SuppressWarnings("unchecked")
public void execute(final ExecutionContext executionContext) throws Exception
{
- final String resourceType = (String)executionContext.getVariable(WorkflowModelModeratedInvitation.wfVarResourceType);
final String resourceName = (String)executionContext.getVariable(WorkflowModelModeratedInvitation.wfVarResourceName);
final String inviteeUserName = (String)executionContext.getVariable(WorkflowModelModeratedInvitation.wfVarInviteeUserName);
final String inviteeRole = (String)executionContext.getVariable(WorkflowModelModeratedInvitation.wfVarInviteeRole);
final String reviewer = (String)executionContext.getVariable(WorkflowModelModeratedInvitation.wfVarReviewer);
- final String reviewComments = (String)executionContext.getVariable(WorkflowModelModeratedInvitation.wfVarReviewComments);
/**
* Add invitee to the site
diff --git a/source/java/org/alfresco/repo/invitation/ModeratedActionReject.java b/source/java/org/alfresco/repo/invitation/ModeratedActionReject.java
index 0395c70b8f..4c6f825264 100644
--- a/source/java/org/alfresco/repo/invitation/ModeratedActionReject.java
+++ b/source/java/org/alfresco/repo/invitation/ModeratedActionReject.java
@@ -23,15 +23,11 @@ import java.util.HashMap;
import java.util.Map;
import org.alfresco.repo.action.executer.MailActionExecuter;
-import org.alfresco.repo.invitation.site.RejectInviteAction;
-import org.alfresco.repo.security.authentication.MutableAuthenticationDao;
import org.alfresco.repo.workflow.jbpm.JBPMSpringActionHandler;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.repository.TemplateService;
-import org.alfresco.service.cmr.security.PersonService;
-import org.alfresco.service.cmr.workflow.WorkflowService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jbpm.graph.exe.ExecutionContext;
@@ -45,35 +41,36 @@ public class ModeratedActionReject extends JBPMSpringActionHandler
private static final long serialVersionUID = 4377660284993206875L;
private static final Log logger = LogFactory.getLog(ModeratedActionReject.class);
- private MutableAuthenticationDao mutableAuthenticationDao;
- private PersonService personService;
- private WorkflowService workflowService;
private ActionService actionService;
private TemplateService templateService;
//private String rejectTemplate = " PATH:\"app:company_home/app:dictionary/app:email_templates/cm:invite/cm:moderated-reject-email.ftl\"";
private String rejectTemplate = "/alfresco/bootstrap/invite/moderated-reject-email.ftl";
- /* (non-Javadoc)
- * @see org.alfresco.repo.workflow.jbpm.JBPMSpringActionHandler#initialiseHandler(org.springframework.beans.factory.BeanFactory)
+ private boolean sendEmails = true;
+
+ /**
+ * {@inheritDoc}
*/
@Override
protected void initialiseHandler(BeanFactory factory)
{
ServiceRegistry services = (ServiceRegistry)factory.getBean(ServiceRegistry.SERVICE_REGISTRY);
- mutableAuthenticationDao = (MutableAuthenticationDao) factory.getBean("authenticationDao");
- personService = (PersonService) services.getPersonService();
- workflowService = (WorkflowService) services.getWorkflowService();
- templateService = (TemplateService) services.getTemplateService();
- actionService = (ActionService) services.getActionService();
+ templateService = services.getTemplateService();
+ actionService = services.getActionService();
+ sendEmails = services.getInvitationService().isSendEmails();
}
- /* (non-Javadoc)
- * @see org.jbpm.graph.def.ActionHandler#execute(org.jbpm.graph.exe.ExecutionContext)
- * Reject Moderated
+ /**
+ * {@inheritDoc}
*/
- @SuppressWarnings("unchecked")
public void execute(final ExecutionContext executionContext) throws Exception
{
+ //Do nothing if emails disabled.
+ if(sendEmails == false)
+ {
+ return;
+ }
+
final String resourceType = (String)executionContext.getVariable(WorkflowModelModeratedInvitation.wfVarResourceType);
final String resourceName = (String)executionContext.getVariable(WorkflowModelModeratedInvitation.wfVarResourceName);
final String inviteeUserName = (String)executionContext.getVariable(WorkflowModelModeratedInvitation.wfVarInviteeUserName);
diff --git a/source/java/org/alfresco/repo/invitation/site/CancelInviteAction.java b/source/java/org/alfresco/repo/invitation/site/CancelInviteAction.java
index 66910c1a8f..bfd4c0fe43 100644
--- a/source/java/org/alfresco/repo/invitation/site/CancelInviteAction.java
+++ b/source/java/org/alfresco/repo/invitation/site/CancelInviteAction.java
@@ -57,15 +57,14 @@ public class CancelInviteAction extends JBPMSpringActionHandler
{
ServiceRegistry services = (ServiceRegistry)factory.getBean(ServiceRegistry.SERVICE_REGISTRY);
mutableAuthenticationDao = (MutableAuthenticationDao) factory.getBean("authenticationDao");
- personService = (PersonService) services.getPersonService();
- workflowService = (WorkflowService) services.getWorkflowService();
- siteService = (SiteService) services.getSiteService();
+ personService = services.getPersonService();
+ workflowService = services.getWorkflowService();
+ siteService = services.getSiteService();
}
- /* (non-Javadoc)
- * @see org.jbpm.graph.def.ActionHandler#execute(org.jbpm.graph.exe.ExecutionContext)
+ /**
+ * {@inheritDoc}
*/
- @SuppressWarnings("unchecked")
public void execute(final ExecutionContext executionContext) throws Exception
{
// get the invitee user name and site short name variables off the execution context
diff --git a/source/java/org/alfresco/repo/invitation/site/SendInviteAction.java b/source/java/org/alfresco/repo/invitation/site/SendInviteAction.java
index f3cabe5765..f8097eabc6 100644
--- a/source/java/org/alfresco/repo/invitation/site/SendInviteAction.java
+++ b/source/java/org/alfresco/repo/invitation/site/SendInviteAction.java
@@ -19,7 +19,15 @@
package org.alfresco.repo.invitation.site;
-import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.*;
+import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarAcceptUrl;
+import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarInviteTicket;
+import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarInviteeGenPassword;
+import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarInviteeUserName;
+import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarInviterUserName;
+import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarRejectUrl;
+import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarResourceName;
+import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarRole;
+import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarServerPath;
import java.util.Arrays;
import java.util.Collection;
@@ -32,6 +40,7 @@ import org.alfresco.repo.model.Repository;
import org.alfresco.repo.workflow.WorkflowModel;
import org.alfresco.repo.workflow.jbpm.JBPMSpringActionHandler;
import org.alfresco.service.ServiceRegistry;
+import org.alfresco.service.cmr.invitation.InvitationService;
import org.alfresco.service.namespace.NamespaceService;
import org.jbpm.graph.exe.ExecutionContext;
import org.springframework.beans.factory.BeanFactory;
@@ -44,6 +53,8 @@ public class SendInviteAction extends JBPMSpringActionHandler
private InviteSender inviteSender;
private NamespaceService namespaceService;
+ private boolean sendEmails;
+
@Override
protected void initialiseHandler(BeanFactory factory)
{
@@ -52,11 +63,14 @@ public class SendInviteAction extends JBPMSpringActionHandler
MessageService messageService= (MessageService) factory.getBean("messageService");
inviteSender = new InviteSender(services, repository, messageService);
namespaceService = services.getNamespaceService();
+ InvitationService invitationService = services.getInvitationService();
+ sendEmails = invitationService.isSendEmails();
}
public void execute(final ExecutionContext context) throws Exception
{
-
+ if(sendEmails == false)
+ return;
Collection propertyNames = Arrays.asList(wfVarInviteeUserName,//
wfVarResourceName,//
wfVarInviterUserName,//
diff --git a/source/java/org/alfresco/repo/jscript/ScriptNode.java b/source/java/org/alfresco/repo/jscript/ScriptNode.java
index 0db313cb7b..5d4d71a473 100644
--- a/source/java/org/alfresco/repo/jscript/ScriptNode.java
+++ b/source/java/org/alfresco/repo/jscript/ScriptNode.java
@@ -89,6 +89,7 @@ import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.util.GUID;
+import org.alfresco.util.ISO9075;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONException;
@@ -170,6 +171,7 @@ public class ScriptNode implements Serializable, Scopeable, NamespacePrefixResol
private Boolean isContainer = null;
private Boolean isLinkToDocument = null;
private Boolean isLinkToContainer = null;
+ private Boolean hasChildren = null;
private String displayPath = null;
protected TemplateImageResolver imageResolver = null;
protected ScriptNode parent = null;
@@ -388,11 +390,25 @@ public class ScriptNode implements Serializable, Scopeable, NamespacePrefixResol
children[i] = newInstance(childRefs.get(i).getChildRef(), this.services, this.scope);
}
this.children = Context.getCurrentContext().newArray(this.scope, children);
+ this.hasChildren = (children.length != 0);
}
return this.children;
}
+ /**
+ * @return true if the Node has children
+ */
+ public boolean getHasChildren()
+ {
+ if (this.hasChildren == null)
+ {
+ this.hasChildren = !this.services.getNodeService().getChildAssocs(
+ getNodeRef(), RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false).isEmpty();
+ }
+ return hasChildren;
+ }
+
/**
* childByNamePath returns the Node at the specified 'cm:name' based Path walking the children of this Node.
* So a valid call might be:
@@ -1651,7 +1667,8 @@ public class ScriptNode implements Serializable, Scopeable, NamespacePrefixResol
}
/**
- * Add an existing node as a child of this node.
+ * Creates a new secondary association between the current node and the specified child node.
+ * The association is given the same name as the child node's primary association.
*
* @param node node to add as a child of this node
*/
@@ -1790,6 +1807,39 @@ public class ScriptNode implements Serializable, Scopeable, NamespacePrefixResol
return true;
}
+ /**
+ * Move this Node from specified parent to a new parent destination.
+ *
+ * @param source Node
+ * @param destination Node
+ * @return true on successful move, false on failure to move.
+ */
+ public boolean move(ScriptNode source, ScriptNode destination)
+ {
+ ParameterCheck.mandatory("Destination Node", destination);
+
+ if (source == null)
+ {
+ return move(destination);
+ }
+ else
+ {
+ try
+ {
+ this.services.getFileFolderService().move(this.nodeRef, source.getNodeRef(), destination.getNodeRef(), null);
+ }
+ catch (Exception e)
+ {
+ throw new ScriptException("Can't move node", e);
+ }
+ }
+
+ // reset cached values
+ reset();
+
+ return true;
+ }
+
/**
* Add an aspect to the Node. As no properties are provided in this call, it can only be used to add aspects that do not require any mandatory properties.
*
@@ -2404,6 +2454,17 @@ public class ScriptNode implements Serializable, Scopeable, NamespacePrefixResol
throw new ScriptException("The thumbnail name '" + thumbnailName + "' is not registered");
}
+ // If there's nothing currently registered to generate thumbnails for the
+ // specified mimetype, then log a message and bail out
+ String nodeMimeType = getMimetype();
+ if (!registry.isThumbnailDefinitionAvailable(nodeMimeType, details))
+ {
+ logger.info("Unable to create thumbnail '" + details.getName() + "' for " +
+ nodeMimeType + " as no transformer is currently available");
+ return null;
+ }
+
+ // Have the thumbnail created
if (async == false)
{
// Create the thumbnail
@@ -2729,7 +2790,8 @@ public class ScriptNode implements Serializable, Scopeable, NamespacePrefixResol
Path.Element siteName = path.get(i+1);
// remove the "cm:" prefix and add to result object
- this.siteName = siteName.getPrefixedString(this.services.getNamespaceService()).substring(3);
+ this.siteName = ISO9075.decode(siteName.getPrefixedString(
+ this.services.getNamespaceService()).substring(3));
}
break;
@@ -2917,6 +2979,7 @@ public class ScriptNode implements Serializable, Scopeable, NamespacePrefixResol
this.sourceAssocs = null;
this.childAssocs = null;
this.children = null;
+ this.hasChildren = null;
this.parentAssocs = null;
this.displayPath = null;
this.isDocument = null;
diff --git a/source/java/org/alfresco/repo/jscript/Search.java b/source/java/org/alfresco/repo/jscript/Search.java
index 363b848845..99ee5e9e4a 100644
--- a/source/java/org/alfresco/repo/jscript/Search.java
+++ b/source/java/org/alfresco/repo/jscript/Search.java
@@ -33,6 +33,7 @@ import org.alfresco.repo.search.impl.lucene.LuceneQueryParser;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.LimitBy;
import org.alfresco.service.cmr.search.ResultSet;
@@ -743,11 +744,15 @@ public class Search extends BaseScopableProcessorExtension
if (results.length() != 0)
{
+ NodeService nodeService = this.services.getNodeService();
set = new LinkedHashSet(results.length(), 1.0f);
for (ResultSetRow row: results)
{
NodeRef nodeRef = row.getNodeRef();
- set.add(new ScriptNode(nodeRef, this.services, getScope()));
+ if (nodeService.exists(nodeRef))
+ {
+ set.add(new ScriptNode(nodeRef, this.services, getScope()));
+ }
}
}
}
diff --git a/source/java/org/alfresco/repo/management/subsystems/ChildApplicationContextFactory.java b/source/java/org/alfresco/repo/management/subsystems/ChildApplicationContextFactory.java
index 46c869515e..43f552c0b7 100644
--- a/source/java/org/alfresco/repo/management/subsystems/ChildApplicationContextFactory.java
+++ b/source/java/org/alfresco/repo/management/subsystems/ChildApplicationContextFactory.java
@@ -43,7 +43,12 @@ import org.springframework.beans.factory.config.ListFactoryBean;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.event.ApplicationEventMulticaster;
+import org.springframework.context.event.ContextClosedEvent;
+import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.support.ClassPathXmlApplicationContext;
+import org.springframework.util.Assert;
/**
* A factory allowing initialization of an entire 'subsystem' in a child application context. As with other
@@ -280,7 +285,7 @@ public class ChildApplicationContextFactory extends AbstractPropertyBackedBean i
overrideFactory.setSystemPropertiesMode(PropertyPlaceholderConfigurer.SYSTEM_PROPERTIES_MODE_OVERRIDE);
overrideFactory.setLocations(getParent().getResources(
ChildApplicationContextFactory.EXTENSION_CLASSPATH_PREFIX + getCategory() + '/' + getTypeName() + '/'
- + idList.get(idList.size() - 1) + '/' + ChildApplicationContextFactory.PROPERTIES_SUFFIX));
+ + idList.get(idList.size() - 1) + ChildApplicationContextFactory.PROPERTIES_SUFFIX));
overrideFactory.setProperties(((ApplicationContextState) state).properties);
overrideFactory.afterPropertiesSet();
((ApplicationContextState) state).properties = (Properties) overrideFactory.getObject();
@@ -370,8 +375,7 @@ public class ChildApplicationContextFactory extends AbstractPropertyBackedBean i
+ '/'
+ getTypeName()
+ '/'
- + ChildApplicationContextFactory.this.getId().get(
- ChildApplicationContextFactory.this.getId().size() - 1) + '/'
+ + ChildApplicationContextFactory.this.getId().get(ChildApplicationContextFactory.this.getId().size() - 1)
+ ChildApplicationContextFactory.CONTEXT_SUFFIX
}, false, ChildApplicationContextFactory.this.getParent());
@@ -436,6 +440,22 @@ public class ChildApplicationContextFactory extends AbstractPropertyBackedBean i
}
});
}
+
+ @Override
+ public void publishEvent(ApplicationEvent event)
+ {
+ Assert.notNull(event, "Event must not be null");
+ if (logger.isTraceEnabled())
+ {
+ logger.trace("Publishing event in " + getDisplayName() + ": " + event);
+ }
+ ((ApplicationEventMulticaster) getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)).multicastEvent(event);
+
+ if (!(getParent() == null || event instanceof ContextRefreshedEvent || event instanceof ContextClosedEvent))
+ {
+ getParent().publishEvent(event);
+ }
+ }
}
/**
diff --git a/source/java/org/alfresco/repo/management/subsystems/test/SampleService.java b/source/java/org/alfresco/repo/management/subsystems/test/SampleService.java
new file mode 100644
index 0000000000..a408e3ad84
--- /dev/null
+++ b/source/java/org/alfresco/repo/management/subsystems/test/SampleService.java
@@ -0,0 +1,5 @@
+package org.alfresco.repo.management.subsystems.test;
+
+public class SampleService {
+
+}
diff --git a/source/java/org/alfresco/repo/management/subsystems/test/SubsystemsTest.java b/source/java/org/alfresco/repo/management/subsystems/test/SubsystemsTest.java
index 8947ff78d3..9a06ea9929 100644
--- a/source/java/org/alfresco/repo/management/subsystems/test/SubsystemsTest.java
+++ b/source/java/org/alfresco/repo/management/subsystems/test/SubsystemsTest.java
@@ -22,6 +22,7 @@ import org.alfresco.repo.management.subsystems.ApplicationContextFactory;
import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory;
import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.BaseSpringTest;
+import org.apache.cxf.endpoint.ServerRegistryImpl;
import org.springframework.context.ConfigurableApplicationContext;
/**
@@ -94,4 +95,16 @@ public class SubsystemsTest extends BaseSpringTest
assertEquals("Global Instance Default", testBeans[2].getAnotherStringProperty());
}
+ public void testALF6058() throws Exception
+ {
+ ServerRegistryImpl serverRegistry = getApplicationContext().getBean(ServerRegistryImpl.class);
+ ApplicationContextFactory subsystem = (ApplicationContextFactory) getApplicationContext().getBean("testsubsystem");
+ int beforeStop = serverRegistry.getServers().size();
+ subsystem.stop();
+ //Make sure CXF doesn't remove its endpoints after subsystem stops
+ assertEquals(beforeStop, serverRegistry.getServers().size());
+ subsystem.start();
+
+ }
+
}
diff --git a/source/java/org/alfresco/repo/model/filefolder/FileFolderPerformanceTester.java b/source/java/org/alfresco/repo/model/filefolder/FileFolderPerformanceTester.java
index 37b0eb3da6..bf4c8cdc05 100644
--- a/source/java/org/alfresco/repo/model/filefolder/FileFolderPerformanceTester.java
+++ b/source/java/org/alfresco/repo/model/filefolder/FileFolderPerformanceTester.java
@@ -29,6 +29,7 @@ import junit.framework.TestCase;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
+import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.content.transform.AbstractContentTransformerTest;
import org.alfresco.repo.dictionary.DictionaryDAO;
import org.alfresco.repo.dictionary.M2Model;
@@ -422,7 +423,7 @@ public class FileFolderPerformanceTester extends TestCase
}
catch (Throwable e)
{
- System.out.println("Failed to run CifsHelper performance test");
+ System.out.println("Failed to run FileFolder performance test");
e.printStackTrace();
}
finally
@@ -495,6 +496,7 @@ public class FileFolderPerformanceTester extends TestCase
companyHomeNodeRef,
"TOP_FOLDER_" + System.currentTimeMillis(),
ContentModel.TYPE_FOLDER).getNodeRef();
+ System.out.println("Created folder " + folderNodeRef + " with user " + user);
}
finally
{
@@ -508,7 +510,7 @@ public class FileFolderPerformanceTester extends TestCase
folderNodeRef = selectedFolderNodeRef;
// Grant permissions
permissionService.setPermission(folderNodeRef, user, PermissionService.ALL_PERMISSIONS, true);
- System.out.println("Reusing folder " + folderNodeRef);
+ System.out.println("Reusing folder " + folderNodeRef + " with user " + user);
}
}
finally
@@ -541,6 +543,11 @@ public class FileFolderPerformanceTester extends TestCase
nodeRef,
QName.createQName("{test}aspect_"+m), null);
}
+ // write the content
+ ContentWriter writer = fileFolderService.getWriter(nodeRef);
+ writer.setEncoding("UTF-8");
+ writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN);
+ writer.putContent("Some small text data");
}
System.out.println("Created " + fileCount + " files in folder " + folderNodeRef);
diff --git a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java
index 0377453642..197fb10ea7 100644
--- a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java
+++ b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java
@@ -34,8 +34,6 @@ import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.rule.ruletrigger.RuleTrigger;
import org.alfresco.repo.search.QueryParameterDefImpl;
-import org.alfresco.repo.security.authentication.AuthenticationUtil;
-import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.transaction.TransactionalResourceHelper;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
@@ -698,7 +696,7 @@ public class FileFolderServiceImpl implements FileFolderService
String marker = sourceNodeRef.toString()+"rename";
nodeRefRenameSet.add(marker);
- return moveOrCopy(sourceNodeRef, null, newName, true);
+ return moveOrCopy(sourceNodeRef, null, null, newName, true);
}
/**
@@ -706,7 +704,15 @@ public class FileFolderServiceImpl implements FileFolderService
*/
public FileInfo move(NodeRef sourceNodeRef, NodeRef targetParentRef, String newName) throws FileExistsException, FileNotFoundException
{
- return moveOrCopy(sourceNodeRef, targetParentRef, newName, true);
+ return moveOrCopy(sourceNodeRef, null, targetParentRef, newName, true);
+ }
+
+ /**
+ * @see #moveOrCopy(NodeRef, NodeRef, String, boolean)
+ */
+ public FileInfo move(NodeRef sourceNodeRef, NodeRef sourceParentRef, NodeRef targetParentRef, String newName) throws FileExistsException, FileNotFoundException
+ {
+ return moveOrCopy(sourceNodeRef, sourceParentRef, targetParentRef, newName, true);
}
/**
@@ -714,7 +720,7 @@ public class FileFolderServiceImpl implements FileFolderService
*/
public FileInfo copy(NodeRef sourceNodeRef, NodeRef targetParentRef, String newName) throws FileExistsException, FileNotFoundException
{
- return moveOrCopy(sourceNodeRef, targetParentRef, newName, false);
+ return moveOrCopy(sourceNodeRef, null, targetParentRef, newName, false);
}
/**
@@ -722,7 +728,7 @@ public class FileFolderServiceImpl implements FileFolderService
*
* @param move true to move, otherwise false to copy
*/
- private FileInfo moveOrCopy(NodeRef sourceNodeRef, NodeRef targetParentRef, String newName, boolean move) throws FileExistsException, FileNotFoundException
+ private FileInfo moveOrCopy(NodeRef sourceNodeRef, NodeRef sourceParentRef, NodeRef targetParentRef, String newName, boolean move) throws FileExistsException, FileNotFoundException
{
// get file/folder in its current state
FileInfo beforeFileInfo = toFileInfo(sourceNodeRef, true);
@@ -731,11 +737,37 @@ public class FileFolderServiceImpl implements FileFolderService
{
newName = beforeFileInfo.getName();
}
-
+
boolean nameChanged = (newName.equals(beforeFileInfo.getName()) == false);
-
+
+ // check is primary parent
+ boolean isPrimaryParent = true;
+ if (sourceParentRef != null)
+ {
+ isPrimaryParent = sourceParentRef.equals(nodeService.getPrimaryParent(sourceNodeRef).getParentRef());
+ }
+
// we need the current association type
- ChildAssociationRef assocRef = nodeService.getPrimaryParent(sourceNodeRef);
+ ChildAssociationRef assocRef = null;
+ if (isPrimaryParent)
+ {
+ assocRef = nodeService.getPrimaryParent(sourceNodeRef);
+ }
+ else
+ {
+ List assocList = nodeService.getParentAssocs(sourceNodeRef);
+ if (assocList != null)
+ {
+ for (ChildAssociationRef assocListEntry : assocList)
+ {
+ if (sourceParentRef.equals(assocListEntry.getParentRef()))
+ {
+ assocRef = assocListEntry;
+ break;
+ }
+ }
+ }
+ }
if (targetParentRef == null)
{
targetParentRef = assocRef.getParentRef();
@@ -772,21 +804,29 @@ public class FileFolderServiceImpl implements FileFolderService
QName targetParentType = nodeService.getType(targetParentRef);
- // Fix AWC-1517
+ // Fix AWC-1517 & ALF-5569
QName assocTypeQname = null;
- if (dictionaryService.isSubClass(targetParentType, ContentModel.TYPE_FOLDER))
+ if (nameChanged && move)
{
- assocTypeQname = ContentModel.ASSOC_CONTAINS; // cm:folder -> cm:contains
- }
- else if (dictionaryService.isSubClass(targetParentType, ContentModel.TYPE_CONTAINER))
- {
- assocTypeQname = ContentModel.ASSOC_CHILDREN; // sys:container -> sys:children
+ // if it's a rename use the existing assoc type
+ assocTypeQname = assocRef.getTypeQName();
}
else
{
- throw new InvalidTypeException("Unexpected type (" + targetParentType + ") for target parent: " + targetParentRef);
+ if (dictionaryService.isSubClass(targetParentType, ContentModel.TYPE_FOLDER))
+ {
+ assocTypeQname = ContentModel.ASSOC_CONTAINS; // cm:folder -> cm:contains
+ }
+ else if (dictionaryService.isSubClass(targetParentType, ContentModel.TYPE_CONTAINER))
+ {
+ assocTypeQname = ContentModel.ASSOC_CHILDREN; // sys:container -> sys:children
+ }
+ else
+ {
+ throw new InvalidTypeException("Unexpected type (" + targetParentType + ") for target parent: " + targetParentRef);
+ }
}
-
+
// move or copy
NodeRef targetNodeRef = null;
if (move)
@@ -805,12 +845,19 @@ public class FileFolderServiceImpl implements FileFolderService
}
try
{
- // move the node so that the association moves as well
- ChildAssociationRef newAssocRef = nodeService.moveNode(
- sourceNodeRef,
- targetParentRef,
- assocTypeQname,
- qname);
+ ChildAssociationRef newAssocRef = null;
+
+ if (isPrimaryParent)
+ {
+ // move the node so that the association moves as well
+ newAssocRef = nodeService.moveNode(sourceNodeRef, targetParentRef, assocTypeQname, qname);
+ }
+ else
+ {
+ nodeService.removeChild(sourceParentRef, sourceNodeRef);
+ newAssocRef = nodeService.addChild(targetParentRef, sourceNodeRef, assocRef.getTypeQName(), assocRef.getQName());
+ }
+
targetNodeRef = newAssocRef.getChildRef();
}
catch (DuplicateChildNodeNameException e)
diff --git a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java
index a18cd21d7e..91ee67851d 100644
--- a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java
+++ b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java
@@ -32,6 +32,7 @@ import junit.framework.TestCase;
import org.alfresco.error.AlfrescoRuntimeException;
import org.springframework.extensions.surf.util.I18NUtil;
import org.alfresco.model.ContentModel;
+import org.alfresco.model.ForumModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.dictionary.DictionaryDAO;
import org.alfresco.repo.dictionary.M2Model;
@@ -44,6 +45,7 @@ import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileFolderServiceType;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.model.FileNotFoundException;
+import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.CyclicChildRelationshipException;
@@ -54,6 +56,7 @@ import org.alfresco.service.cmr.view.ImporterService;
import org.alfresco.service.cmr.view.Location;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
+import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ApplicationContextHelper;
import org.springframework.context.ApplicationContext;
@@ -78,6 +81,7 @@ public class FileFolderServiceImplTest extends TestCase
private static final String NAME_L1_FILE_C = "L1- File C (%_)";
private static final String NAME_CHECK_FILE = "CHECK_FILE";
private static final String NAME_CHECK_FOLDER = "CHECK_FOLDER";
+ private static final String NAME_DISCUSSION_FOLDER = "CHECK_DISCUSSION_RENAME";
private static final ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
@@ -363,6 +367,35 @@ public class FileFolderServiceImplTest extends TestCase
// expected
}
}
+
+ public void testRenameDiscussionALF5569() throws Exception
+ {
+ FileInfo fileInfo = getByName(NAME_L0_FILE_A, false);
+ assertNotNull(fileInfo);
+
+ // create a discussion for the file, this happens in a behaviour
+ // when adding the discussable aspect
+ nodeService.addAspect(fileInfo.getNodeRef(), ForumModel.ASPECT_DISCUSSABLE, null);
+ List destChildren = nodeService.getChildAssocs(
+ fileInfo.getNodeRef(),
+ ForumModel.ASSOC_DISCUSSION,
+ RegexQNamePattern.MATCH_ALL);
+ assertEquals(1, destChildren.size());
+
+ // get the first child
+ NodeRef discussionNodeRef = destChildren.get(0).getChildRef();
+
+ // check the current name
+ String currentName = (String)nodeService.getProperty(discussionNodeRef, ContentModel.PROP_NAME);
+ assertFalse(NAME_DISCUSSION_FOLDER.equals(currentName));
+
+ // rename the discussion node
+ FileInfo newFileInfo = fileFolderService.rename(discussionNodeRef, NAME_DISCUSSION_FOLDER);
+
+ // get the name now
+ String newName = (String)nodeService.getProperty(newFileInfo.getNodeRef(), ContentModel.PROP_NAME);
+ assertEquals(NAME_DISCUSSION_FOLDER, newName);
+ }
public void testMove() throws Exception
{
diff --git a/source/java/org/alfresco/repo/module/tool/ModuleManagementTool.java b/source/java/org/alfresco/repo/module/tool/ModuleManagementTool.java
index 3280eb1032..68159dfb9c 100644
--- a/source/java/org/alfresco/repo/module/tool/ModuleManagementTool.java
+++ b/source/java/org/alfresco/repo/module/tool/ModuleManagementTool.java
@@ -304,14 +304,14 @@ public class ModuleManagementTool
else if (compareValue == 0)
{
// Trying to install the same extension version again
- outputMessage("WARNING: This version of this module is already installed in the WAR");
- throw new ModuleManagementToolException("This version of this module is alreay installed. Use the 'force' parameter if you want to overwrite the current installation.");
+ outputMessage("WARNING: This version of this module is already installed in the WAR. Installation skipped.");
+ return;
}
else if (compareValue == 1)
{
// Trying to install an earlier version of the extension
- outputMessage("WARNING: A later version of this module is already installed in the WAR");
- throw new ModuleManagementToolException("A later version of this module is already installed. You must first unistall the current version before installing this version of the module.");
+ outputMessage("WARNING: A later version of this module is already installed in the WAR. Installation skipped.");
+ return;
}
}
diff --git a/source/java/org/alfresco/repo/module/tool/ModuleManagementToolTest.java b/source/java/org/alfresco/repo/module/tool/ModuleManagementToolTest.java
index 7dc7ec138a..9cbfa6a60c 100644
--- a/source/java/org/alfresco/repo/module/tool/ModuleManagementToolTest.java
+++ b/source/java/org/alfresco/repo/module/tool/ModuleManagementToolTest.java
@@ -98,7 +98,8 @@ public class ModuleManagementToolTest extends TestCase
try
{
this.manager.installModule(ampLocation, warLocation);
- fail("The module is already installed so an exception should have been raised since we are not forcing an overwite");
+ // Already installed is now a Warning rather than an Error and is now non fatal
+ // fail("The module is already installed so an exception should have been raised since we are not forcing an overwite");
}
catch(ModuleManagementToolException exception)
{
@@ -142,11 +143,18 @@ public class ModuleManagementToolTest extends TestCase
// Ensure the file has been reverted as it isnt updated in the v2.0
checkContentsOfFile(warLocation + orig, "ORIGIONAL");
- // Try and install and earlier version
+ /**
+ * Try and install an earlier version over a later version
+ */
try
{
this.manager.installModule(ampLocation, warLocation);
- fail("An earlier version of this module is already installed so an exception should have been raised since we are not forcing an overwite");
+ //fail("A later version of this module is already installed so an exception should have been raised since we are not forcing an overwite");
+ //this is now a warning rather than an error
+
+ // Check that the war has not been modified
+ checkForFileExistance(warLocation, files2);
+ checkForFileNonExistance(warLocation, files3);
}
catch(ModuleManagementToolException exception)
{
diff --git a/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java b/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java
index ad0b8ee429..684c29ace4 100644
--- a/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java
+++ b/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java
@@ -528,6 +528,24 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
QName checkType = nodeService.getType(childRef);
assertEquals("Child node type incorrect", ContentModel.TYPE_CONTAINER, checkType);
}
+
+ public void testCreateWithTooLongPathLocalname() throws Exception
+ {
+ try
+ {
+ ChildAssociationRef assocRef = nodeService.createNode(rootNodeRef, ASSOC_TYPE_QNAME_TEST_CHILDREN,
+ QName.createQName("Recognize that VSEPR theory states that nonbonded electrons (lone "
+ + "pairs) exert strong electrostatic repulsive forces against the bonded pairs "
+ + "of electrons and, as a result, the electron pairs arrange themselves as far "
+ + "apart as possible in order to minimize the repulsive forces"),
+ ContentModel.TYPE_CONTAINER);
+ fail("Expected too-long QName localname to have been kicked out as illegal argument.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // Expected
+ }
+ }
/**
* Tests node creation with a pre-determined {@link ContentModel#PROP_NODE_UUID uuid}.
@@ -1500,7 +1518,7 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
}
catch (IllegalArgumentException e)
{
- fail("Null property values are allowed");
+ fail("Null property values are allowed");
}
// try setting null value as part of complete set
try
@@ -2889,34 +2907,34 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
public static boolean behaviourExecuted = false;
public void testAR1303() throws Exception
- {
- Map props = new HashMap(1);
- props.put(ContentModel.PROP_NAME, "test.txt");
-
- NodeRef nodeRef = this.nodeService.createNode(
- this.rootNodeRef,
- ContentModel.ASSOC_CHILDREN,
- ContentModel.ASSOC_CHILDREN,
- ContentModel.TYPE_CONTENT,
- props).getChildRef();
-
- nodeService.addAspect(nodeRef, ContentModel.ASPECT_TITLED, null);
-
- nodeService.setProperty(nodeRef, ContentModel.PROP_DESCRIPTION, "my description");
- nodeService.setProperty(nodeRef, ContentModel.PROP_TITLE, "my title");
-
- JavaBehaviour behaviour = new JavaBehaviour(this, "onUpdateProperties");
- PolicyComponent policyComponent = (PolicyComponent)this.applicationContext.getBean("policyComponent");
- policyComponent.bindClassBehaviour(
- QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"),
- ContentModel.ASPECT_TITLED,
- behaviour);
-
- behaviourExecuted = false;
-
- // Update the title property and check that the behaviour has been fired
- nodeService.setProperty(nodeRef, ContentModel.PROP_TITLE, "changed title");
- assertTrue("The onUpdateProperties behaviour has not been fired.", behaviourExecuted);
+ {
+ Map props = new HashMap(1);
+ props.put(ContentModel.PROP_NAME, "test.txt");
+
+ NodeRef nodeRef = this.nodeService.createNode(
+ this.rootNodeRef,
+ ContentModel.ASSOC_CHILDREN,
+ ContentModel.ASSOC_CHILDREN,
+ ContentModel.TYPE_CONTENT,
+ props).getChildRef();
+
+ nodeService.addAspect(nodeRef, ContentModel.ASPECT_TITLED, null);
+
+ nodeService.setProperty(nodeRef, ContentModel.PROP_DESCRIPTION, "my description");
+ nodeService.setProperty(nodeRef, ContentModel.PROP_TITLE, "my title");
+
+ JavaBehaviour behaviour = new JavaBehaviour(this, "onUpdateProperties");
+ PolicyComponent policyComponent = (PolicyComponent)this.applicationContext.getBean("policyComponent");
+ policyComponent.bindClassBehaviour(
+ QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"),
+ ContentModel.ASPECT_TITLED,
+ behaviour);
+
+ behaviourExecuted = false;
+
+ // Update the title property and check that the behaviour has been fired
+ nodeService.setProperty(nodeRef, ContentModel.PROP_TITLE, "changed title");
+ assertTrue("The onUpdateProperties behaviour has not been fired.", behaviourExecuted);
}
public void onUpdateProperties(
diff --git a/source/java/org/alfresco/repo/node/ConcurrentNodeServiceTest.java b/source/java/org/alfresco/repo/node/ConcurrentNodeServiceTest.java
index a9d4b57e8e..d2cdf5c57b 100644
--- a/source/java/org/alfresco/repo/node/ConcurrentNodeServiceTest.java
+++ b/source/java/org/alfresco/repo/node/ConcurrentNodeServiceTest.java
@@ -19,18 +19,27 @@
package org.alfresco.repo.node;
import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+import javax.transaction.UserTransaction;
+
import junit.framework.TestCase;
+import org.alfresco.model.ContentModel;
import org.alfresco.repo.dictionary.DictionaryDAO;
import org.alfresco.repo.dictionary.M2Model;
import org.alfresco.repo.search.impl.lucene.fts.FullTextSearchIndexer;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.tagging.TaggingServiceImpl;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
+import org.alfresco.service.cmr.repository.MLText;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
@@ -42,41 +51,37 @@ import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ApplicationContextHelper;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
/**
* @author Andy Hind
+ * @author Nick Burch
+ * @author Derek Hulley
*/
@SuppressWarnings("unused")
public class ConcurrentNodeServiceTest extends TestCase
{
public static final String NAMESPACE = "http://www.alfresco.org/test/BaseNodeServiceTest";
-
public static final String TEST_PREFIX = "test";
-
public static final QName TYPE_QNAME_TEST_CONTENT = QName.createQName(NAMESPACE, "content");
-
public static final QName ASPECT_QNAME_TEST_TITLED = QName.createQName(NAMESPACE, "titled");
-
public static final QName PROP_QNAME_TEST_TITLE = QName.createQName(NAMESPACE, "title");
-
public static final QName PROP_QNAME_TEST_MIMETYPE = QName.createQName(NAMESPACE, "mimetype");
public static final int COUNT = 10;
-
public static final int REPEATS = 20;
+ private static Log logger = LogFactory.getLog(ConcurrentNodeServiceTest.class);
+
static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
private NodeService nodeService;
-
private TransactionService transactionService;
private RetryingTransactionHelper retryingTransactionHelper;
-
private NodeRef rootNodeRef;
-
private FullTextSearchIndexer luceneFTS;
-
private AuthenticationComponent authenticationComponent;
public ConcurrentNodeServiceTest()
@@ -334,7 +339,158 @@ public class ConcurrentNodeServiceTest extends TestCase
}
}
}
+ }
+
+ /**
+ * Tests that when multiple threads try to edit different
+ * properties on a node, that transactions + retries always
+ * mean that every change always ends up on the node.
+ *
+ * @since 3.4
+ */
+ public void testMultiThreadedNodePropertiesWrites() throws Exception
+ {
+ final List threads = new ArrayList();
+ final int loops = 200;
+ luceneFTS.pause();
+ // Have 5 threads, each trying to edit their own properties on the same node
+ // Loop repeatedly
+ final QName[] properties = new QName[] {
+ QName.createQName("test1", "MadeUp1"),
+ QName.createQName("test2", "MadeUp2"),
+ QName.createQName("test3", "MadeUp3"),
+ QName.createQName("test4", "MadeUp4"),
+ QName.createQName("test5", "MadeUp5")
+ };
+ for(QName prop : properties)
+ {
+ final QName property = prop;
+
+ // Zap the property if it is there
+ transactionService.getRetryingTransactionHelper().doInTransaction(
+ new RetryingTransactionCallback