diff --git a/config/alfresco/content-services-context.xml b/config/alfresco/content-services-context.xml index 5dcf26bf59..94a8fcf7f0 100644 --- a/config/alfresco/content-services-context.xml +++ b/config/alfresco/content-services-context.xml @@ -220,6 +220,9 @@ + + + diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml index 09d7e9abaf..0042f20f5f 100644 --- a/config/alfresco/core-services-context.xml +++ b/config/alfresco/core-services-context.xml @@ -641,7 +641,9 @@ fm:commentCount cm:expiryDate - + + sys:clientVisibilityMask + diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties index a2d4e59216..c5c30cc5b7 100644 --- a/config/alfresco/repository.properties +++ b/config/alfresco/repository.properties @@ -43,6 +43,10 @@ system.webdav.url.path.prefix= system.webdav.storeName=${protocols.storeName} system.webdav.rootPath=${protocols.rootPath} system.webdav.activities.enabled=true +# File name patterns that trigger rename shuffle detection +# pattern is used by move - tested against full path after it has been lower cased. +system.webdav.renameShufflePattern=(.*/\\..*)|(.*[a-f0-9]{8}+$)|(.*\\.tmp$)|(.*\\.wbk$)|(.*\\.bak$)|(.*\\~$) + # Is the JBPM Deploy Process Servlet enabled? # Default is false. Should not be enabled in production environments as the diff --git a/config/alfresco/subsystems/thirdparty/default/imagemagick-transform-context.xml b/config/alfresco/subsystems/thirdparty/default/imagemagick-transform-context.xml index 6e37fbb827..ba8b02b9a1 100644 --- a/config/alfresco/subsystems/thirdparty/default/imagemagick-transform-context.xml +++ b/config/alfresco/subsystems/thirdparty/default/imagemagick-transform-context.xml @@ -47,6 +47,10 @@ + + + 1,2,255,400,405,410,415,420,425,430,435,440,450,455,460,465,470,475,480,485,490,495,499,700,705,710,715,720,725,730,735,740,750,755,760,765,770,775,780,785,790,795,799 + diff --git a/pom.xml b/pom.xml index bf082d2974..a2999699f9 100644 --- a/pom.xml +++ b/pom.xml @@ -52,7 +52,7 @@ com.icegreen greenmail - 1.3-patched + 1.3-alfresco-patched commons-dbcp @@ -534,7 +534,7 @@ org.apache.poi poi - ${dependency.poi.version} + ${dependency.poi.version}-alfresco-patched org.apache.poi diff --git a/source/java/org/alfresco/repo/bulkimport/impl/FilesystemContentDataFactory.java b/source/java/org/alfresco/repo/bulkimport/impl/FilesystemContentDataFactory.java index 80ad8a4ec5..123982013c 100644 --- a/source/java/org/alfresco/repo/bulkimport/impl/FilesystemContentDataFactory.java +++ b/source/java/org/alfresco/repo/bulkimport/impl/FilesystemContentDataFactory.java @@ -59,96 +59,97 @@ import org.springframework.beans.factory.InitializingBean; */ public class FilesystemContentDataFactory implements ContentDataFactory, InitializingBean { - private static final Log logger = LogFactory.getLog(FilesystemContentDataFactory.class); - - private static final String PROTOCOL_DELIMITER = ContentStore.PROTOCOL_DELIMITER; - private static final String OS_FILE_SEPARATOR = System.getProperty("file.separator"); - - private MimetypeService mimetypeService; - private String defaultEncoding; - private String storeProtocol; - - public void setMimetypeService(MimetypeService mimetypeService) - { - this.mimetypeService = mimetypeService; - } - - public void setDefaultEncoding(String defaultEncoding) - { - this.defaultEncoding = defaultEncoding; - } - - public void setStoreProtocol(String storeProtocol) - { - this.storeProtocol = storeProtocol; - } + private static final Log logger = LogFactory.getLog(FilesystemContentDataFactory.class); + + private static final String PROTOCOL_DELIMITER = ContentStore.PROTOCOL_DELIMITER; + private static final String OS_FILE_SEPARATOR = System.getProperty("file.separator"); + + private MimetypeService mimetypeService; + private String defaultEncoding; + private String storeProtocol; + + public void setMimetypeService(MimetypeService mimetypeService) + { + this.mimetypeService = mimetypeService; + } + + public void setDefaultEncoding(String defaultEncoding) + { + this.defaultEncoding = defaultEncoding; + } + + public void setStoreProtocol(String storeProtocol) + { + this.storeProtocol = storeProtocol; + } - public void afterPropertiesSet() throws Exception - { - PropertyCheck.mandatory(this, "mimetypeService", mimetypeService); + public void afterPropertiesSet() throws Exception + { + PropertyCheck.mandatory(this, "mimetypeService", mimetypeService); PropertyCheck.mandatory(this, "defaultEncoding", defaultEncoding); PropertyCheck.mandatory(this, "storeProtocol", storeProtocol); - } - - /** - * Create a {@link ContentData} by combining the given {@link ContentStore}'s root location and the {@link File}'s path within that store. - * The given file must therefore be accessible within the content store's configured root location. - * The encoding and mimetype will be guessed from the given file. - * - * @param store The {@link ContentStore} in which the file should be - * @param contentFile The {@link File} to check - * @return the constructed {@link ContentData} - */ - public ContentData createContentData(ContentStore store, File contentFile) + } + + /** + * Create a {@link ContentData} by combining the given {@link ContentStore}'s root location and the {@link File}'s path within that store. + * The given file must therefore be accessible within the content store's configured root location. + * The encoding and mimetype will be guessed from the given file. + * + * @param store The {@link ContentStore} in which the file should be + * @param contentFile The {@link File} to check + * @return the constructed {@link ContentData} + */ + public ContentData createContentData(ContentStore store, File contentFile) { - if(!contentIsInStore(contentFile, store)) - { - throw new IllegalArgumentException("Can't create content URL : file '" + contentFile.getAbsolutePath() + - "' is not located within the store's tree ! The store's root is :'" + store.getRootLocation()); - } - - String relativeFilePath = contentFile.getAbsolutePath().replace(store.getRootLocation() + OS_FILE_SEPARATOR, ""); - String mimetype = mimetypeService.guessMimetype(contentFile.getName()); - String encoding = defaultEncoding; - if(!contentFile.isDirectory()) - { - encoding = guessEncoding(contentFile, mimetype); - } - + if(!contentIsInStore(contentFile, store)) + { + throw new IllegalArgumentException("Can't create content URL : file '" + contentFile.getAbsolutePath() + + "' is not located within the store's tree ! The store's root is :'" + store.getRootLocation()); + } + + String relativeFilePath = contentFile.getAbsolutePath().replace(store.getRootLocation() + OS_FILE_SEPARATOR, ""); + String mimetype = mimetypeService.guessMimetype(contentFile.getName()); + String encoding = defaultEncoding; + if(!contentFile.isDirectory()) + { + encoding = guessEncoding(contentFile, mimetype); + } + ContentData contentData = new ContentData(storeProtocol + PROTOCOL_DELIMITER + relativeFilePath, mimetype, contentFile.length(), encoding); Map contentProps = new HashMap(); contentProps.put(ContentModel.PROP_NAME, contentFile.getName()); contentProps.put(ContentModel.PROP_CONTENT, contentData); - return contentData; + return contentData; } - - /** - * Check if file is in the store's tree, by checking if the file path starts - * with the store's configured root location. - * - * @param store The {@link ContentStore} in which the file should be - * @param contentFile The {@link File} to check - * @return boolean : whether or not the file is in the expected file tree - */ - private boolean contentIsInStore(File contentFile,ContentStore store) - { - return contentFile.getAbsolutePath().startsWith(store.getRootLocation()); - } - - /** - * Attempt to guess file encoding. fall back to {@link #defaultEncoding} otherwise. - * - * @param file the {@link java.io.File} to test - * @param mimetype the file mimetype. used to first distinguish between binary and text files - * @return the encoding as a {@link String} - */ - private String guessEncoding(File file,String mimetype) - { + + /** + * Check if file is in the store's tree, by checking if the file path starts + * with the store's configured root location. + * + * @param store The {@link ContentStore} in which the file should be + * @param contentFile The {@link File} to check + * @return boolean : whether or not the file is in the expected file tree + */ + private boolean contentIsInStore(File contentFile,ContentStore store) + { + File contentStoreFile = new File(store.getRootLocation()); + return contentFile.getAbsolutePath().startsWith(contentStoreFile.getAbsolutePath()); + } + + /** + * Attempt to guess file encoding. fall back to {@link #defaultEncoding} otherwise. + * + * @param file the {@link java.io.File} to test + * @param mimetype the file mimetype. used to first distinguish between binary and text files + * @return the encoding as a {@link String} + */ + private String guessEncoding(File file,String mimetype) + { String encoding = defaultEncoding; // fallback default if(file.isDirectory()) - return defaultEncoding; // not necessary to guess folder encoding + return defaultEncoding; // not necessary to guess folder encoding InputStream is = null; ContentCharsetFinder charsetFinder = mimetypeService.getContentCharsetFinder(); @@ -159,7 +160,7 @@ public class FilesystemContentDataFactory implements ContentDataFactory, Initial } catch (Throwable e) { - if(logger.isWarnEnabled()) + if(logger.isWarnEnabled()) logger.warn("Failed to guess character encoding of file: '" + file.getName() + "'. Falling back to configured default encoding (" + defaultEncoding + ")"); } finally @@ -171,6 +172,6 @@ public class FilesystemContentDataFactory implements ContentDataFactory, Initial } return encoding; - } + } } diff --git a/source/java/org/alfresco/repo/content/metadata/AbstractMappingMetadataExtracter.java b/source/java/org/alfresco/repo/content/metadata/AbstractMappingMetadataExtracter.java index e829e19e9f..c4635e1f2c 100644 --- a/source/java/org/alfresco/repo/content/metadata/AbstractMappingMetadataExtracter.java +++ b/source/java/org/alfresco/repo/content/metadata/AbstractMappingMetadataExtracter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2012 Alfresco Software Limited. * * This file is part of Alfresco * @@ -53,6 +53,7 @@ import org.alfresco.service.namespace.QName; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.xmlbeans.impl.xb.xsdschema.All; +import org.springframework.beans.factory.BeanNameAware; import org.springframework.extensions.surf.util.ISO8601DateFormat; /** @@ -95,7 +96,7 @@ import org.springframework.extensions.surf.util.ISO8601DateFormat; * @author Jesper Steen Møller * @author Derek Hulley */ -abstract public class AbstractMappingMetadataExtracter implements MetadataExtracter, MetadataEmbedder +abstract public class AbstractMappingMetadataExtracter implements MetadataExtracter, MetadataEmbedder, BeanNameAware { public static final String NAMESPACE_PROPERTY_PREFIX = "namespace.prefix."; private static final String ERR_TYPE_CONVERSION = "metadata.extraction.err.type_conversion"; @@ -117,6 +118,8 @@ abstract public class AbstractMappingMetadataExtracter implements MetadataExtrac private boolean inheritDefaultMapping; private boolean inheritDefaultEmbedMapping; private boolean enableStringTagging; + private String beanName; + private Properties properties; /** * Default constructor. If this is called, then {@link #isSupported(String)} should @@ -227,7 +230,7 @@ abstract public class AbstractMappingMetadataExtracter implements MetadataExtrac */ public boolean isSupported(String sourceMimetype) { - return supportedMimetypes.contains(sourceMimetype); + return supportedMimetypes.contains(sourceMimetype) && isEnabled(sourceMimetype); } /** @@ -244,6 +247,27 @@ abstract public class AbstractMappingMetadataExtracter implements MetadataExtrac return supportedEmbedMimetypes.contains(sourceMimetype); } + private boolean isEnabled(String mimetype) + { + return properties == null || mimetypeService == null || + (getBooleanProperty(beanName+".enabled", true) && + getBooleanProperty(beanName+'.'+mimetypeService.getExtension(mimetype)+".enabled", true)); + } + + private boolean getBooleanProperty(String name, boolean defaultValue) + { + boolean value = defaultValue; + if (properties != null) + { + String property = properties.getProperty(name); + if (property != null) + { + value = property.trim().equalsIgnoreCase("true"); + } + } + return value; + } + /** * TODO - This doesn't appear to be used, so should be removed / deprecated / replaced * @return Returns 1.0 if the mimetype is supported, otherwise 0.0 @@ -354,6 +378,25 @@ abstract public class AbstractMappingMetadataExtracter implements MetadataExtrac this.inheritDefaultMapping = inheritDefaultMapping; } + @Override + public void setBeanName(String beanName) + { + this.beanName = beanName; + } + + public String getBeanName() + { + return beanName; + } + + /** + * The Alfresco global properties. + */ + public void setProperties(Properties properties) + { + this.properties = properties; + } + /** * Whether or not to enable the pass through of simple strings to cm:taggable tags * diff --git a/source/java/org/alfresco/repo/content/metadata/AbstractMetadataExtracter.java b/source/java/org/alfresco/repo/content/metadata/AbstractMetadataExtracter.java index e55782a18e..8768d9af1b 100644 --- a/source/java/org/alfresco/repo/content/metadata/AbstractMetadataExtracter.java +++ b/source/java/org/alfresco/repo/content/metadata/AbstractMetadataExtracter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005 Jesper Steen Møller + * Copyright (C) 2005-2012 Jesper Steen Møller * * This file is part of Alfresco * @@ -22,6 +22,7 @@ import java.io.Serializable; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Properties; import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; @@ -31,6 +32,7 @@ import org.alfresco.service.cmr.repository.MimetypeService; import org.alfresco.service.namespace.QName; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.BeanNameAware; /** * Support class for metadata extracters. @@ -40,7 +42,7 @@ import org.apache.commons.logging.LogFactory; * @author Jesper Steen Møller * @author Derek Hulley */ -abstract public class AbstractMetadataExtracter implements MetadataExtracter +abstract public class AbstractMetadataExtracter implements MetadataExtracter, BeanNameAware { protected static Log logger = LogFactory.getLog(AbstractMetadataExtracter.class); @@ -49,6 +51,8 @@ abstract public class AbstractMetadataExtracter implements MetadataExtracter private Set supportedMimetypes; private double reliability; private long extractionTime; + private String beanName; + private Properties properties; protected AbstractMetadataExtracter(String supportedMimetype, double reliability, long extractionTime) { @@ -84,6 +88,25 @@ abstract public class AbstractMetadataExtracter implements MetadataExtracter this.mimetypeService = mimetypeService; } + @Override + public void setBeanName(String beanName) + { + this.beanName = beanName; + } + + public String getBeanName() + { + return beanName; + } + + /** + * The Alfresco global properties. + */ + public void setProperties(Properties properties) + { + this.properties = properties; + } + /** * @return Returns the mimetype helper */ @@ -131,7 +154,28 @@ abstract public class AbstractMetadataExtracter implements MetadataExtracter public boolean isSupported(String mimetype) { double reliability = getReliability(mimetype); - return reliability > 0.0; + return reliability > 0.0 && isEnabled(mimetype); + } + + private boolean isEnabled(String mimetype) + { + return properties == null || mimetypeService == null || + (getBooleanProperty(beanName+".enabled", true) && + getBooleanProperty(beanName+'.'+mimetypeService.getExtension(mimetype)+".enabled", true)); + } + + private boolean getBooleanProperty(String name, boolean defaultValue) + { + boolean value = defaultValue; + if (properties != null) + { + String property = properties.getProperty(name); + if (property != null) + { + value = property.trim().equalsIgnoreCase("true"); + } + } + return value; } public long getExtractionTime() diff --git a/source/java/org/alfresco/repo/content/metadata/MetadataExtracterRegistry.java b/source/java/org/alfresco/repo/content/metadata/MetadataExtracterRegistry.java index 4566c84252..1aabf85e94 100644 --- a/source/java/org/alfresco/repo/content/metadata/MetadataExtracterRegistry.java +++ b/source/java/org/alfresco/repo/content/metadata/MetadataExtracterRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005 Jesper Steen Møller + * Copyright (C) 2005-2012 Jesper Steen Møller * * This file is part of Alfresco * @@ -119,6 +119,7 @@ public class MetadataExtracterRegistry */ public MetadataExtracter getExtracter(String sourceMimetype) { + logger.debug("Get extractors for " + sourceMimetype); List extractors = null; extracterCacheReadLock.lock(); try @@ -162,12 +163,26 @@ public class MetadataExtracterRegistry // An extractor may dynamically become unavailable if (!extractor.isSupported(sourceMimetype)) { + logger.debug("Get unsupported: "+getName(extractor)); continue; } + logger.debug("Get supported: "+getName(extractor)); liveExtractor = extractor; } + logger.debug("Get returning: "+getName(liveExtractor)); return liveExtractor; } + + private String getName(MetadataExtracter extractor) + { + return extractor == null + ? null + : extractor instanceof AbstractMetadataExtracter + ? ((AbstractMetadataExtracter)extractor).getBeanName() + : extractor instanceof AbstractMappingMetadataExtracter + ? ((AbstractMappingMetadataExtracter)extractor).getBeanName() + : extractor.getClass().getSimpleName(); + } /** * @param sourceMimetype The MIME type under examination @@ -184,10 +199,13 @@ public class MetadataExtracterRegistry if (!extractor.isSupported(sourceMimetype)) { // extraction not achievable + logger.debug("Find unsupported: "+getName(extractor)); continue; } + logger.debug("Find supported: "+getName(extractor)); extractors.add(extractor); } + logger.debug("Find returning: "+extractors); return extractors; } diff --git a/source/java/org/alfresco/repo/imap/AlfrescoImapFolder.java b/source/java/org/alfresco/repo/imap/AlfrescoImapFolder.java index 3ec0e7b6c7..dd1d3bd28a 100644 --- a/source/java/org/alfresco/repo/imap/AlfrescoImapFolder.java +++ b/source/java/org/alfresco/repo/imap/AlfrescoImapFolder.java @@ -30,8 +30,8 @@ import java.util.Map; import java.util.NavigableMap; import javax.mail.Flags; -import javax.mail.MessagingException; import javax.mail.Flags.Flag; +import javax.mail.MessagingException; import javax.mail.internet.MimeMessage; import org.alfresco.model.ContentModel; @@ -127,9 +127,9 @@ public class AlfrescoImapFolder extends AbstractImapFolder implements Serializab /** * Protected constructor for the hierarchy delimiter */ - AlfrescoImapFolder(String userName, ServiceRegistry serviceRegistry) + AlfrescoImapFolder(String userName, ImapService imapService, ServiceRegistry serviceRegistry) { - this(null, userName, "", "", null, serviceRegistry, false, false, 0); + this(null, userName, "", "", null, imapService, serviceRegistry, false, false, 0); } @@ -150,10 +150,11 @@ public class AlfrescoImapFolder extends AbstractImapFolder implements Serializab String folderPath, ImapViewMode viewMode, boolean extractAttachmentsEnabled, + ImapService imapService, ServiceRegistry serviceRegistry, int mountPointId) { - this(folderInfo, userName, folderName, folderPath, viewMode, serviceRegistry, null, extractAttachmentsEnabled, mountPointId); + this(folderInfo, userName, folderName, folderPath, viewMode, imapService, serviceRegistry, null, extractAttachmentsEnabled, mountPointId); } /** @@ -165,7 +166,7 @@ public class AlfrescoImapFolder extends AbstractImapFolder implements Serializab * @param viewMode - defines view mode. Can be one of the following: {@link AlfrescoImapConst#MODE_ARCHIVE} or {@link AlfrescoImapConst#MODE_VIRTUAL}. * @param rootNodeRef - reference to the root node of the store where folder is placed. * @param mountPointName - name of the mount point. - * @param imapService - reference to the {@link ImapHelper} object. + * @param imapService - the IMAP service. * @param selectable - defines whether the folder is selectable or not. */ public AlfrescoImapFolder( @@ -174,6 +175,7 @@ public class AlfrescoImapFolder extends AbstractImapFolder implements Serializab String folderName, String folderPath, ImapViewMode viewMode, + ImapService imapService, ServiceRegistry serviceRegistry, Boolean selectable, boolean extractAttachmentsEnabled, @@ -186,7 +188,7 @@ public class AlfrescoImapFolder extends AbstractImapFolder implements Serializab this.folderPath = folderPath; this.viewMode = viewMode != null ? viewMode : ImapViewMode.ARCHIVE; this.extractAttachmentsEnabled = extractAttachmentsEnabled; - this.imapService = serviceRegistry.getImapService(); + this.imapService = imapService; // MailFolder object can be null if it is used to obtain hierarchy delimiter by LIST command: // Example: diff --git a/source/java/org/alfresco/repo/imap/ImapServiceImpl.java b/source/java/org/alfresco/repo/imap/ImapServiceImpl.java index 4c19cd36df..e19368dd91 100644 --- a/source/java/org/alfresco/repo/imap/ImapServiceImpl.java +++ b/source/java/org/alfresco/repo/imap/ImapServiceImpl.java @@ -568,7 +568,7 @@ public class ImapServiceImpl implements ImapService, OnCreateChildAssociationPol // A request for the hierarchy delimiter if (mailboxName.length() == 0) { - return new AlfrescoImapFolder(user.getLogin(), serviceRegistry); + return new AlfrescoImapFolder(user.getLogin(), this, serviceRegistry); } final NodeRef root; final List pathElements; @@ -633,7 +633,7 @@ public class ImapServiceImpl implements ImapService, OnCreateChildAssociationPol } } return new AlfrescoImapFolder(mailFolder, user.getLogin(), pathElements.get(pathElements.size() - 1), mailboxName, viewMode, - serviceRegistry, true, isExtractionEnabled(mailFolder.getNodeRef()), mountPointId); + this, serviceRegistry, true, isExtractionEnabled(mailFolder.getNodeRef()), mountPointId); } public void deleteMailbox(AlfrescoImapUser user, String mailboxName) @@ -1053,12 +1053,12 @@ public class ImapServiceImpl implements ImapService, OnCreateChildAssociationPol if (!listSubscribed || !unsubscribedFodlers.contains(mountPointFileInfo.getNodeRef())) { result.add(new AlfrescoImapFolder(mountPointFileInfo, userName, mountPointName, mountPointName, viewMode, - isExtractionEnabled(mountPointFileInfo.getNodeRef()), serviceRegistry, mountPointId)); + isExtractionEnabled(mountPointFileInfo.getNodeRef()), this, serviceRegistry, mountPointId)); } else if (rootPath.endsWith("%") && !expandFolder(mountPoint, user, mountPointName, "%", true, viewMode, mountPointId).isEmpty()) // \NoSelect { result.add(new AlfrescoImapFolder(mountPointFileInfo, userName, mountPointName, mountPointName, viewMode, - serviceRegistry, false, isExtractionEnabled(mountPointFileInfo.getNodeRef()), mountPointId)); + this, serviceRegistry, false, isExtractionEnabled(mountPointFileInfo.getNodeRef()), mountPointId)); } if (rootPath.endsWith("*")) { @@ -1169,12 +1169,12 @@ public class ImapServiceImpl implements ImapService, OnCreateChildAssociationPol if (!listSubscribed || !unsubscribedFodlers.contains(fileInfo.getNodeRef())) { fullList.add(new AlfrescoImapFolder(fileInfo, userName, fileInfo.getName(), folderPath, viewMode, - isExtractionEnabled(fileInfo.getNodeRef()), serviceRegistry, mountPointId)); + isExtractionEnabled(fileInfo.getNodeRef()), this, serviceRegistry, mountPointId)); } else if (name.endsWith("%") && !expandFolder(fileInfo.getNodeRef(), user, folderPath, "%", true, viewMode, mountPointId).isEmpty()) // \NoSelect { fullList.add(new AlfrescoImapFolder(fileInfo, userName, fileInfo.getName(), folderPath, viewMode, - serviceRegistry, false, isExtractionEnabled(fileInfo.getNodeRef()), mountPointId)); + this, serviceRegistry, false, isExtractionEnabled(fileInfo.getNodeRef()), mountPointId)); } if (name.endsWith("*")) { diff --git a/source/java/org/alfresco/repo/jscript/ScriptableHashMap.java b/source/java/org/alfresco/repo/jscript/ScriptableHashMap.java index 59f1206169..e5f49a3105 100644 --- a/source/java/org/alfresco/repo/jscript/ScriptableHashMap.java +++ b/source/java/org/alfresco/repo/jscript/ScriptableHashMap.java @@ -21,6 +21,8 @@ package org.alfresco.repo.jscript; import java.util.Iterator; import java.util.LinkedHashMap; +import org.mozilla.javascript.Callable; +import org.mozilla.javascript.Context; import org.mozilla.javascript.Scriptable; /** @@ -51,6 +53,17 @@ public class ScriptableHashMap extends LinkedHashMap implements Scrip { return this.size(); } + else if ("hasOwnProperty".equals(name)) + { + return new Callable() + { + @Override + public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) + { + return (args.length > 0 ? hasOwnProperty(args[0]) : null); + } + }; + } else { return get(name); @@ -71,6 +84,17 @@ public class ScriptableHashMap extends LinkedHashMap implements Scrip } return value; } + + /** + * ECMAScript 5 hasOwnProperty method support. + * + * @param key Object key to test for + * @return true if found, false otherwise + */ + public boolean hasOwnProperty(Object key) + { + return containsKey(key); + } /** * @see org.mozilla.javascript.Scriptable#has(java.lang.String, org.mozilla.javascript.Scriptable) diff --git a/source/java/org/alfresco/repo/jscript/ScriptableQNameMap.java b/source/java/org/alfresco/repo/jscript/ScriptableQNameMap.java index 891b4b7b05..de855cad3c 100644 --- a/source/java/org/alfresco/repo/jscript/ScriptableQNameMap.java +++ b/source/java/org/alfresco/repo/jscript/ScriptableQNameMap.java @@ -18,9 +18,10 @@ */ package org.alfresco.repo.jscript; -import org.alfresco.service.namespace.NamespacePrefixResolver; import org.alfresco.service.namespace.NamespacePrefixResolverProvider; import org.alfresco.service.namespace.QNameMap; +import org.mozilla.javascript.Callable; +import org.mozilla.javascript.Context; import org.mozilla.javascript.Scriptable; /** @@ -43,7 +44,7 @@ public class ScriptableQNameMap extends QNameMap implements Scriptable { return "ScriptableQNameMap"; } - + /** * @see org.mozilla.javascript.Scriptable#get(java.lang.String, org.mozilla.javascript.Scriptable) */ @@ -54,6 +55,17 @@ public class ScriptableQNameMap extends QNameMap implements Scriptable { return this.size(); } + else if ("hasOwnProperty".equals(name)) + { + return new Callable() + { + @Override + public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) + { + return (args.length > 0 ? hasOwnProperty(args[0]) : null); + } + }; + } else { return get(name); @@ -68,6 +80,17 @@ public class ScriptableQNameMap extends QNameMap implements Scriptable return null; } + /** + * ECMAScript 5 hasOwnProperty method support. + * + * @param key Object key to test for + * @return true if found, false otherwise + */ + public boolean hasOwnProperty(Object key) + { + return containsKey(key); + } + /** * @see org.mozilla.javascript.Scriptable#has(java.lang.String, org.mozilla.javascript.Scriptable) */ diff --git a/source/java/org/alfresco/repo/management/subsystems/ChildApplicationContextFactory.java b/source/java/org/alfresco/repo/management/subsystems/ChildApplicationContextFactory.java index f7a0403214..2575197c9d 100644 --- a/source/java/org/alfresco/repo/management/subsystems/ChildApplicationContextFactory.java +++ b/source/java/org/alfresco/repo/management/subsystems/ChildApplicationContextFactory.java @@ -449,17 +449,11 @@ public class ChildApplicationContextFactory extends AbstractPropertyBackedBean i // Add a property placeholder configurer, with the subsystem-scoped default properties PropertyPlaceholderConfigurer configurer = new PropertyPlaceholderConfigurer(); - configurer.setProperties(properties); + configurer.setPropertiesArray(new Properties[] {ChildApplicationContextFactory.this.getPropertyDefaults(), properties}); configurer.setIgnoreUnresolvablePlaceholders(true); configurer.setSearchSystemEnvironment(false); addBeanFactoryPostProcessor(configurer); - // Add all the post processors of the parent, e.g. to make sure system placeholders get expanded properly - for (Object postProcessor : getParent().getBeansOfType(BeanFactoryPostProcessor.class, false, false).values()) - { - addBeanFactoryPostProcessor((BeanFactoryPostProcessor) postProcessor); - } - setClassLoader(ChildApplicationContextFactory.this.getParent().getClassLoader()); } diff --git a/source/java/org/alfresco/repo/node/NodeServiceTest.java b/source/java/org/alfresco/repo/node/NodeServiceTest.java index 0a3dd3c21b..672addd80f 100644 --- a/source/java/org/alfresco/repo/node/NodeServiceTest.java +++ b/source/java/org/alfresco/repo/node/NodeServiceTest.java @@ -321,6 +321,22 @@ public class NodeServiceTest extends TestCase assertEquals("", 3, paths.size()); } + static class InnerCallbackException extends RuntimeException + { + private final Throwable hiddenCause; + + public InnerCallbackException(Throwable hiddenCause) + { + super(hiddenCause.getMessage()); + this.hiddenCause = hiddenCause; + } + + public Throwable getHiddenCause() + { + return hiddenCause; + } + } + /** * Tests that two separate node trees can be deleted concurrently at the database level. * This is not a concurrent thread issue; instead we delete a hierarchy and hold the txn @@ -340,10 +356,10 @@ public class NodeServiceTest extends TestCase buildNodeHierarchy(workspaceRootNodeRef, nodesOne); final NodeRef[] nodesTwo = new NodeRef[10]; buildNodeHierarchy(workspaceRootNodeRef, nodesTwo); - + // Prime the root of the archive store (first child adds inherited ACL) nodeService.deleteNode(nodesPrimer[0]); - + RetryingTransactionCallback outerCallback = new RetryingTransactionCallback() { @Override @@ -351,18 +367,73 @@ public class NodeServiceTest extends TestCase { // Delete the first hierarchy nodeService.deleteNode(nodesOne[0]); + // Keep the txn hanging around to maintain DB locks // and start a second transaction to delete another hierarchy - RetryingTransactionCallback innerCallback = new RetryingTransactionCallback() + class InnerThread extends Thread { - @Override - public Void execute() throws Throwable + + private Throwable error; + + public InnerThread() { - nodeService.deleteNode(nodesTwo[0]); - return null; + setDaemon(true); } - }; - txnService.getRetryingTransactionHelper().doInTransaction(innerCallback, false, true); + + public Throwable getError() + { + return error; + } + + /* + * (non-Javadoc) + * @see java.lang.Thread#run() + */ + @Override + public void run() + { + AuthenticationUtil.setRunAsUserSystem(); + RetryingTransactionCallback innerCallback = new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + try + { + nodeService.deleteNode(nodesTwo[0]); + return null; + } + catch (Throwable t) + { + // Wrap throwables so they pass straight through the retry mechanism + throw new InnerCallbackException(t); + } + } + }; + try + { + txnService.getRetryingTransactionHelper().doInTransaction(innerCallback, false, true); + } + catch (InnerCallbackException e) + { + error = e.getHiddenCause(); + } + } + } + InnerThread innerThread = new InnerThread(); + innerThread.start(); + innerThread.join(30000); + if (innerThread.isAlive()) + { + innerThread.interrupt(); + fail("Transaction hung for 30 seconds. Test failed."); + } + // Rethrow potentially retryable exception + Throwable t = innerThread.getError(); + if (t != null) + { + throw t; + } return null; } }; diff --git a/source/java/org/alfresco/repo/rule/RuleServiceImplTest.java b/source/java/org/alfresco/repo/rule/RuleServiceImplTest.java index 2cd7aa0abd..569b74ddae 100644 --- a/source/java/org/alfresco/repo/rule/RuleServiceImplTest.java +++ b/source/java/org/alfresco/repo/rule/RuleServiceImplTest.java @@ -953,6 +953,7 @@ public class RuleServiceImplTest extends BaseRuleTest public void testDeleteSpaceWithExecuteScriptRule() throws Exception { + endTransaction(); // So we don't hang indefinitely waiting for the outer transaction transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() { public Object execute() diff --git a/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneIndexerImpl.java b/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneIndexerImpl.java index 4308495b4e..d8beb345b5 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneIndexerImpl.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneIndexerImpl.java @@ -193,7 +193,7 @@ public class ADMLuceneIndexerImpl extends AbstractLuceneIndexerImpl imp } /** - * Helper setter of the transformer debug. + * Setter of the transformer debug. * @param transformerDebug */ public void setTransformerDebug(TransformerDebug transformerDebug) @@ -1435,11 +1435,11 @@ public class ADMLuceneIndexerImpl extends AbstractLuceneIndexerImpl imp try { // get the transformer - TransformationOptions options = new TransformationOptions(); - options.setSourceNodeRef(nodeRef); - transformerDebug.pushAvailable(reader.getContentUrl(), reader.getMimetype(), MimetypeMap.MIMETYPE_TEXT_PLAIN, options); + TransformationOptions options = new TransformationOptions(); + options.setSourceNodeRef(nodeRef); + transformerDebug.pushAvailable(reader.getContentUrl(), reader.getMimetype(), MimetypeMap.MIMETYPE_TEXT_PLAIN, options); long sourceSize = reader.getSize(); - List transformers = contentService.getActiveTransformers(reader.getMimetype(), sourceSize, MimetypeMap.MIMETYPE_TEXT_PLAIN, options); + List transformers = contentService.getActiveTransformers(reader.getMimetype(), sourceSize, MimetypeMap.MIMETYPE_TEXT_PLAIN, options); transformerDebug.availableTransformers(transformers, sourceSize, "ADMLuceneIndexer"); if (transformers.isEmpty()) diff --git a/source/java/org/alfresco/repo/security/authentication/AuthenticationTest.java b/source/java/org/alfresco/repo/security/authentication/AuthenticationTest.java index 7a955d70cf..8f1b470ebd 100644 --- a/source/java/org/alfresco/repo/security/authentication/AuthenticationTest.java +++ b/source/java/org/alfresco/repo/security/authentication/AuthenticationTest.java @@ -559,6 +559,7 @@ public class AuthenticationTest extends TestCase public void testCreateAuthenticationWhileRunningAsSystem() throws Exception { + userTransaction.rollback(); RunAsWork authWorkAsMuppet = new RunAsWork() { public Object doWork() throws Exception diff --git a/source/java/org/alfresco/repo/transfer/TransferServiceImplTest.java b/source/java/org/alfresco/repo/transfer/TransferServiceImplTest.java index 708a4625cf..18552a2621 100644 --- a/source/java/org/alfresco/repo/transfer/TransferServiceImplTest.java +++ b/source/java/org/alfresco/repo/transfer/TransferServiceImplTest.java @@ -1485,6 +1485,7 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest */ public void testPathBasedUpdate() throws Exception { + endTransaction(); final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper(); final String CONTENT_TITLE = "ContentTitle"; @@ -1602,22 +1603,29 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest testContext.child = nodeService.createNode(testContext.guestHome, ContentModel.ASSOC_CONTAINS, TEST_QNAME, ContentModel.TYPE_CONTENT); testContext.newContentNodeRef = testContext.child.getChildRef(); nodeService.setProperty(testContext.newContentNodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE_UPDATED); - - /** - * Transfer our node which is a new node (so will not exist on the back end) with a path that already has a node. - */ - { - TransferDefinition definition = new TransferDefinition(); - Setnodes = new HashSet(); - nodes.add(testContext.newContentNodeRef); - definition.setNodes(nodes); - transferService.transfer(testContext.targetName, definition); - } return null; } }; tran.doInTransaction(transfer1CB); + RetryingTransactionCallback transfer1CB2 = new RetryingTransactionCallback() { + + @Override + public Void execute() throws Throwable + { + /** + * Transfer our node which is a new node (so will not exist on the back end) with a path that already has a node. + */ + TransferDefinition definition = new TransferDefinition(); + Setnodes = new HashSet(); + nodes.add(testContext.newContentNodeRef); + definition.setNodes(nodes); + transferService.transfer(testContext.targetName, definition); + return null; + } + }; + tran.doInTransaction(transfer1CB2); + RetryingTransactionCallback validateStep1CB = new RetryingTransactionCallback() { @Override diff --git a/source/java/org/alfresco/repo/workflow/AbstractWorkflowServiceIntegrationTest.java b/source/java/org/alfresco/repo/workflow/AbstractWorkflowServiceIntegrationTest.java index 5d1aafdb9c..b90b842e8c 100644 --- a/source/java/org/alfresco/repo/workflow/AbstractWorkflowServiceIntegrationTest.java +++ b/source/java/org/alfresco/repo/workflow/AbstractWorkflowServiceIntegrationTest.java @@ -35,6 +35,7 @@ import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.person.TestGroupManager; import org.alfresco.repo.security.person.TestPersonManager; +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.NodeRef; @@ -56,6 +57,7 @@ import org.alfresco.service.cmr.workflow.WorkflowTaskState; import org.alfresco.service.cmr.workflow.WorkflowTimer; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.BaseSpringTest; import org.alfresco.util.GUID; import org.alfresco.util.collections.CollectionUtils; @@ -83,6 +85,7 @@ public abstract class AbstractWorkflowServiceIntegrationTest extends BaseSpringT protected NodeService nodeService; private NodeRef companyHome; protected WorkflowTestHelper wfTestHelper; + protected TransactionService transactionService; public void testDeployWorkflowDefinition() { @@ -310,7 +313,7 @@ public abstract class AbstractWorkflowServiceIntegrationTest extends BaseSpringT WorkflowPath path = workflowService.startWorkflow(workflowDef.getId(), params); assertNotNull(path); assertTrue(path.isActive()); - String workflowInstanceId = path.getInstance().getId(); + final String workflowInstanceId = path.getInstance().getId(); // End start task to progress workflow WorkflowTask startTask = workflowService.getStartTask(workflowInstanceId); @@ -356,8 +359,19 @@ public abstract class AbstractWorkflowServiceIntegrationTest extends BaseSpringT assertFalse(workflowService.isTaskEditable(currentTask, USER3)); assertFalse(workflowService.isTaskReassignable(currentTask, USER3)); - // cancel the workflow - workflowService.cancelWorkflow(workflowInstanceId); + setComplete(); + endTransaction(); + transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + // cancel the workflow + workflowService.cancelWorkflow(workflowInstanceId); + return null; + } + }); + startNewTransaction(); } public void testPooledTaskCapabilities() @@ -384,7 +398,7 @@ public abstract class AbstractWorkflowServiceIntegrationTest extends BaseSpringT WorkflowPath path = workflowService.startWorkflow(workflowDef.getId(), params); assertNotNull(path); assertTrue(path.isActive()); - String workflowInstanceId = path.getInstance().getId(); + final String workflowInstanceId = path.getInstance().getId(); // End start task to progress workflow WorkflowTask startTask = workflowService.getStartTask(workflowInstanceId); @@ -510,8 +524,19 @@ public abstract class AbstractWorkflowServiceIntegrationTest extends BaseSpringT assertFalse(workflowService.isTaskReassignable(currentTask, USER2)); assertFalse(workflowService.isTaskReassignable(currentTask, USER3)); - // cancel the workflow - workflowService.cancelWorkflow(workflowInstanceId); + setComplete(); + endTransaction(); + transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + // cancel the workflow + workflowService.cancelWorkflow(workflowInstanceId); + return null; + } + }); + startNewTransaction(); } public void testGetWorkflowTaskDefinitions() @@ -1190,6 +1215,7 @@ public abstract class AbstractWorkflowServiceIntegrationTest extends BaseSpringT this.nodeService = registry.getNodeService(); Repository repositoryHelper = (Repository) applicationContext.getBean("repositoryHelper"); this.companyHome = repositoryHelper.getCompanyHome(); + this.transactionService = registry.getTransactionService(); MutableAuthenticationService authenticationService = registry.getAuthenticationService(); AuthorityService authorityService = registry.getAuthorityService(); @@ -1223,13 +1249,22 @@ public abstract class AbstractWorkflowServiceIntegrationTest extends BaseSpringT @Override protected void onTearDownInTransaction() throws Exception { - wfTestHelper.tearDown(); - authenticationComponent.setSystemUserAsCurrentUser(); - groupManager.clearGroups(); - personManager.clearPeople(); - authenticationComponent.clearCurrentSecurityContext(); + endTransaction(); + transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + wfTestHelper.tearDown(); + authenticationComponent.setSystemUserAsCurrentUser(); + groupManager.clearGroups(); + personManager.clearPeople(); + authenticationComponent.clearCurrentSecurityContext(); - super.onTearDownInTransaction(); + AbstractWorkflowServiceIntegrationTest.super.onTearDownInTransaction(); + return null; + } + }); } protected abstract String getEngine(); diff --git a/source/test-resources/subsystem-test-context.xml b/source/test-resources/subsystem-test-context.xml index 0187ce2a5c..23df0f587c 100644 --- a/source/test-resources/subsystem-test-context.xml +++ b/source/test-resources/subsystem-test-context.xml @@ -5,7 +5,7 @@ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> - +