diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml index 9382e98557..b7f3c779e0 100644 --- a/config/alfresco/core-services-context.xml +++ b/config/alfresco/core-services-context.xml @@ -90,6 +90,12 @@ + + ${alfresco.jgroups.bind_address} + + + ${alfresco.jgroups.bind_interface} + ${alfresco.tcp.start_port} @@ -718,7 +724,7 @@ - + diff --git a/config/alfresco/hibernate-context.xml b/config/alfresco/hibernate-context.xml index 3e0589f87c..d16e598ddf 100644 --- a/config/alfresco/hibernate-context.xml +++ b/config/alfresco/hibernate-context.xml @@ -32,10 +32,6 @@ - - - - @@ -54,16 +50,6 @@ false - - - - - - - - - - @@ -497,9 +483,4 @@ - - - - - \ No newline at end of file diff --git a/config/alfresco/mimetype/mimetype-map.xml b/config/alfresco/mimetype/mimetype-map.xml index 06d22aa0fc..cd6fc2debb 100644 --- a/config/alfresco/mimetype/mimetype-map.xml +++ b/config/alfresco/mimetype/mimetype-map.xml @@ -258,6 +258,8 @@ ppt + pps + pot ras diff --git a/config/alfresco/model-specific-services-context.xml b/config/alfresco/model-specific-services-context.xml index 05f52e88ca..a564cef384 100644 --- a/config/alfresco/model-specific-services-context.xml +++ b/config/alfresco/model-specific-services-context.xml @@ -66,6 +66,7 @@ + diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties index 027f41e83d..2d5a8f0eff 100644 --- a/config/alfresco/repository.properties +++ b/config/alfresco/repository.properties @@ -70,10 +70,12 @@ alfresco.cluster.name= # Use this property to select which communication method should be used. # The JGroups configuration file is build up using the protocol string alfresco.jgroups.defaultProtocol=UDP +# The bind address and interface for JGroups to use; equivalent to -Djgroups.bind_addr and -Djgroups.bind_interface +alfresco.jgroups.bind_address= +alfresco.jgroups.bind_interface= # JGroups configuration (http://www.jgroups.org) # The location of the JGroups configuration file alfresco.jgroups.configLocation=classpath:alfresco/jgroups/alfresco-jgroups-${alfresco.jgroups.defaultProtocol}.xml -#alfresco.jgroups.configLocation=alfresco/jgroups/alfresco-jgroups-${alfresco.jgroups.defaultProtocol}.xml # # How long should shutdown wait to complete normally before diff --git a/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java b/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java index d49c6b462f..737f3ef224 100644 --- a/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java +++ b/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java @@ -2457,9 +2457,15 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa if ( renState.hasRenameState()) renState.getRenameState().setNodeRef(nodeToMoveRef); - // Remove the file state for the old file name - ctx.getStateTable().removeFileState(oldName); + //Fix for ETHREEOH-1951 + //Set delete on close state for a short period to avoid loosing of version history + //if "always create a backup copy" option is enabled in MS Word. + FileState oldState = ctx.getStateTable().findFileState(oldName, false, true); + oldState.setNodeRef(nodeToMoveRef); + oldState.setFileStatus(FileStateStatus.DeleteOnClose); + oldState.setExpiryTime(System.currentTimeMillis() + FileState.RenameTimeout); + // Get, or create, a file state for the new file path @@ -2500,7 +2506,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa String newNameNorm = newName.toLowerCase(); boolean isTempFile = false; - if ( newNameNorm.endsWith(".tmp") || newNameNorm.endsWith(".temp")) { + if ( newNameNorm.endsWith(".tmp") || newNameNorm.endsWith(".temp") || newNameNorm.endsWith(".wbk")) { // Add the temporary aspect, also prevents versioning @@ -2524,7 +2530,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa String oldNameNorm = oldName.toLowerCase(); - if ( isTempFile == false && (oldNameNorm.endsWith(".tmp") || oldNameNorm.endsWith(".temp"))) { + if ( isTempFile == false && (oldNameNorm.endsWith(".tmp") || oldNameNorm.endsWith(".temp") || oldNameNorm.endsWith(".wbk"))) { // Remove the temporary aspect diff --git a/source/java/org/alfresco/repo/content/metadata/PdfBoxMetadataExtracter.java b/source/java/org/alfresco/repo/content/metadata/PdfBoxMetadataExtracter.java index b64f40dc70..7bbecaee14 100644 --- a/source/java/org/alfresco/repo/content/metadata/PdfBoxMetadataExtracter.java +++ b/source/java/org/alfresco/repo/content/metadata/PdfBoxMetadataExtracter.java @@ -27,13 +27,17 @@ package org.alfresco.repo.content.metadata; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; +import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Calendar; +import java.util.Date; import java.util.HashSet; import java.util.Map; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.service.cmr.repository.ContentReader; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocumentInformation; @@ -51,6 +55,8 @@ import org.apache.pdfbox.pdmodel.PDDocumentInformation; */ public class PdfBoxMetadataExtracter extends AbstractMappingMetadataExtracter { + protected static Log pdfLogger = LogFactory.getLog(PdfBoxMetadataExtracter.class); + private static final String KEY_AUTHOR = "author"; private static final String KEY_TITLE = "title"; private static final String KEY_SUBJECT = "subject"; @@ -92,10 +98,32 @@ public class PdfBoxMetadataExtracter extends AbstractMappingMetadataExtracter putRawValue(KEY_CREATED, created.getTime(), rawProperties); } } - catch (IOException e) + catch (IOException iox) { // This sometimes fails because the date is a string: ETHREEOH-1936 - } + // Alfresco bug ETHREEOH-801 refers to a bug in PDFBox (http://issues.apache.org/jira/browse/PDFBOX-145) + // where the above call to docInfo.getCreationDate() throws an IOException for some PDFs. + // + // The code below is a workaround for that issue. + + // This creationDate has format: D:20080429+01'00' + String creationDate = docInfo.getCustomMetadataValue("CreationDate"); + + if (pdfLogger.isWarnEnabled()) + { + pdfLogger.warn("IOException caught when extracting metadata from pdf file."); + pdfLogger.warn("This may be caused by a PDFBox bug that can often be worked around. The stack trace below is provided for information purposes only."); + pdfLogger.warn("", iox); + } + + final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); + if (creationDate != null && creationDate.length() > 10) // 10 allows for "D:yyyyMMdd" + { + String dateWithoutLeadingDColon = creationDate.substring(2); + Date parsedDate = sdf.parse(dateWithoutLeadingDColon); + putRawValue(KEY_CREATED, parsedDate, rawProperties); + } + } } } finally diff --git a/source/java/org/alfresco/repo/deploy/DeploymentServiceImpl.java b/source/java/org/alfresco/repo/deploy/DeploymentServiceImpl.java index 1599c2b73e..0fc7d8d7de 100644 --- a/source/java/org/alfresco/repo/deploy/DeploymentServiceImpl.java +++ b/source/java/org/alfresco/repo/deploy/DeploymentServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2007 Alfresco Software Limited. + * Copyright (C) 2005-2009 Alfresco Software Limited. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -425,8 +425,12 @@ public class DeploymentServiceImpl implements DeploymentService String name = entry.getKey(); AVMNodeDescriptor srcNode = entry.getValue(); - if(isStale(srcNode)) + if (isStale(srcNode)) { + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug("Stale child found: " + srcNode); + } srcList.remove(name); } } @@ -642,9 +646,12 @@ public class DeploymentServiceImpl implements DeploymentService /** * Temporary work around for staleness. */ - if(isStale(child)) + if (isStale(child)) { - fgLogger.debug("stale file ignored" + child); + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug("Stale child found: " + child); + } continue; } @@ -1245,11 +1252,11 @@ public class DeploymentServiceImpl implements DeploymentService * Correct fix would be to remove stale files from the snapshot. * Code becomes obsolete once stale files are not part of the snapshot. */ - if(isStale(src)) + if (isStale(src)) { if (fgLogger.isDebugEnabled()) { - fgLogger.debug("Stale content found" + src); + fgLogger.debug("Stale child found: " + src); } src = null; continue; @@ -1433,6 +1440,14 @@ public class DeploymentServiceImpl implements DeploymentService { if (!excluded(matcher, child.getPath(), null)) { + if (isStale(child)) + { + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug("Stale child found: " + child); + } + continue; + } createOnFSR(service, ticket, version, child, dstPath, matcher, sendQueue); } } diff --git a/source/java/org/alfresco/repo/domain/hibernate/BulkLoader.java b/source/java/org/alfresco/repo/domain/hibernate/BulkLoader.java deleted file mode 100644 index 019cc5fda1..0000000000 --- a/source/java/org/alfresco/repo/domain/hibernate/BulkLoader.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2005-2007 Alfresco Software Limited. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: - * http://www.alfresco.com/legal/licensing" - */ -package org.alfresco.repo.domain.hibernate; - -import java.util.Collection; - -import org.alfresco.service.cmr.repository.NodeRef; - -/** - * @author andyh - * - */ -public interface BulkLoader -{ - public void loadIntoCache(Collection nodeRefs); -} diff --git a/source/java/org/alfresco/repo/domain/hibernate/HibernateL1CacheBulkLoader.java b/source/java/org/alfresco/repo/domain/hibernate/HibernateL1CacheBulkLoader.java deleted file mode 100644 index c2b15cacb5..0000000000 --- a/source/java/org/alfresco/repo/domain/hibernate/HibernateL1CacheBulkLoader.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2005-2007 Alfresco Software Limited. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: - * http://www.alfresco.com/legal/licensing" - */ -package org.alfresco.repo.domain.hibernate; - -import java.util.Collection; -import java.util.Map; - -import org.alfresco.service.cmr.repository.NodeRef; -import org.hibernate.CacheMode; -import org.hibernate.Criteria; -import org.hibernate.EntityMode; -import org.hibernate.FetchMode; -import org.hibernate.FlushMode; -import org.hibernate.Session; -import org.hibernate.criterion.Restrictions; -import org.hibernate.metadata.ClassMetadata; -import org.hibernate.metadata.CollectionMetadata; -import org.springframework.orm.hibernate3.support.HibernateDaoSupport; - -/** - * Pre-populates Node entities for a given set of references. - * - * @author andyh - * @since 3.0 - */ -public class HibernateL1CacheBulkLoader extends HibernateDaoSupport implements BulkLoader -{ - public void loadIntoCache(Collection nodeRefs) - { - Session session = getSession(); - DirtySessionMethodInterceptor.flushSession(session); - - String[] guids = new String[nodeRefs.size()]; - int index = 0; - for (NodeRef nodeRef : nodeRefs) - { - guids[index++] = nodeRef.getId(); - } - - Criteria criteria = getSession().createCriteria(NodeImpl.class, "node"); - criteria.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP); - criteria.add(Restrictions.in("uuid", guids)); - criteria.createAlias("node.store", "store"); - criteria.setFetchMode("node.aspects", FetchMode.SELECT); - criteria.setFetchMode("node.properties", FetchMode.JOIN); - criteria.setFetchMode("node.store", FetchMode.SELECT); - criteria.setCacheMode(CacheMode.IGNORE); - criteria.setFlushMode(FlushMode.MANUAL); - - criteria.list(); - - criteria = getSession().createCriteria(NodeImpl.class, "node"); - criteria.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP); - criteria.add(Restrictions.in("uuid", guids)); - criteria.createAlias("node.store", "store"); - criteria.setFetchMode("node.aspects", FetchMode.JOIN); - criteria.setFetchMode("node.properties", FetchMode.SELECT); - criteria.setFetchMode("node.store", FetchMode.SELECT); - criteria.setCacheMode(CacheMode.IGNORE); - criteria.setFlushMode(FlushMode.MANUAL); - - criteria.list(); - } - - @SuppressWarnings("unchecked") - public void clear() - { - getSession().flush(); - getSession().clear(); - Map classes = getSessionFactory().getAllClassMetadata(); - for (ClassMetadata clazz : classes.values()) - { - getSessionFactory().evict(clazz.getMappedClass(EntityMode.POJO)); - } - Map collections = getSessionFactory().getAllCollectionMetadata(); - for (CollectionMetadata clazz : collections.values()) - { - getSessionFactory().evictCollection(clazz.getRole()); - } - - } -} diff --git a/source/java/org/alfresco/repo/domain/hibernate/HibernateLoadListener.java b/source/java/org/alfresco/repo/domain/hibernate/HibernateLoadListener.java deleted file mode 100644 index e1a445042e..0000000000 --- a/source/java/org/alfresco/repo/domain/hibernate/HibernateLoadListener.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.alfresco.repo.domain.hibernate; - -import org.hibernate.HibernateException; -import org.hibernate.event.LoadEvent; -import org.hibernate.event.LoadEventListener; -import org.hibernate.proxy.HibernateProxy; -import net.sf.cglib.proxy.Enhancer; - -public class HibernateLoadListener implements LoadEventListener -{ - - public void onLoad(LoadEvent event, LoadType loadType) throws HibernateException - { - Object obj = event.getResult(); - if (obj instanceof HibernateProxy) { - Enhancer.registerCallbacks(obj.getClass(),null); - } - - - } - -} diff --git a/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java b/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java index 6014f3ed4b..6b2f566fe8 100644 --- a/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java +++ b/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java @@ -41,6 +41,7 @@ import java.util.Set; import javax.transaction.UserTransaction; import org.alfresco.model.ContentModel; +import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.dictionary.DictionaryComponent; import org.alfresco.repo.dictionary.DictionaryDAO; @@ -84,6 +85,8 @@ import org.apache.commons.collections.map.SingletonMap; import org.hibernate.Session; import org.springframework.context.ApplicationContext; +import sun.security.action.GetBooleanAction; + /** * Provides a base set of tests of the various {@link org.alfresco.service.cmr.repository.NodeService} * implementations. @@ -1820,6 +1823,39 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest RegexQNamePattern.MATCH_ALL); } + @SuppressWarnings("unchecked") + public void testParentAssocsCacheOnNewChildAssoc() throws Exception + { + Map assocRefs = buildNodeGraph(); + final ChildAssociationRef n3pn6Ref = assocRefs.get(QName.createQName(BaseNodeServiceTest.NAMESPACE, "n3_p_n6")); + + setComplete(); + endTransaction(); + + SimpleCache parentAssocsSharedCache = + (SimpleCache) applicationContext.getBean("parentAssocsSharedCache"); + parentAssocsSharedCache.clear(); + + // Create a secondary association between two nodes + RetryingTransactionCallback testCallback = new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + nodeService.addChild( + n3pn6Ref.getParentRef(), + n3pn6Ref.getChildRef(), + ASSOC_TYPE_QNAME_TEST_CHILDREN, + QName.createQName("pathA")); + // Now get it back + ChildAssociationRef checkRef = nodeService.getPrimaryParent(n3pn6Ref.getChildRef()); + assertNotNull("ParentAssocsCache not holding primary assoc", checkRef); + assertEquals("Primary parent assoc not correct", n3pn6Ref, checkRef); + return null; + } + }; + retryingTransactionHelper.doInTransaction(testCallback, false, true); + } + public void testGetChildAssocs() throws Exception { Map assocRefs = buildNodeGraph(); diff --git a/source/java/org/alfresco/repo/node/NodeBulkLoader.java b/source/java/org/alfresco/repo/node/NodeBulkLoader.java index d799204c76..9eb0bcaa98 100644 --- a/source/java/org/alfresco/repo/node/NodeBulkLoader.java +++ b/source/java/org/alfresco/repo/node/NodeBulkLoader.java @@ -46,4 +46,9 @@ public interface NodeBulkLoader * @param nodeRefs the nodes that will be cached. */ public void cacheNodes(List nodeRefs); + + /** + * Clears the cached nodes + */ + public void clear(); } diff --git a/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java b/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java index 2c5ecba83c..90d8852cbb 100644 --- a/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java +++ b/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java @@ -2055,17 +2055,23 @@ public class HibernateNodeDaoServiceImpl childNameUnique.getFirst()); // Add it to the cache - Set parentAssocIds = parentAssocsCache.get(childNode.getId()); + Set parentAssocIds = parentAssocsCache.get(childNodeId); if (parentAssocIds == null) { + // There isn't an entry in the cache, so go and make one + Collection parentAssocs = getParentAssocsInternal(childNodeId); parentAssocIds = new HashSet(3); + for (ChildAssoc childAssoc : parentAssocs) + { + parentAssocIds.add(childAssoc.getId()); + } } else { // Copy the list when we add to it parentAssocIds = new HashSet(parentAssocIds); + parentAssocIds.add(assocId); } - parentAssocIds.add(assocId); parentAssocsCache.put(childNodeId, parentAssocIds); if (isDebugParentAssocCacheEnabled) { @@ -2988,6 +2994,20 @@ public class HibernateNodeDaoServiceImpl } } + /** + * {@inheritDoc} + *

+ * Clears the L1 cache, the parentAssocsCache and storeAndNodeIdCache + */ + public void clear() + { + Session session = getSession(); + DirtySessionMethodInterceptor.flushSession(session, true); + session.clear(); + parentAssocsCache.clear(); + storeAndNodeIdCache.clear(); + } + /** * {@inheritDoc} *

@@ -2995,6 +3015,11 @@ public class HibernateNodeDaoServiceImpl */ public void cacheNodes(List nodeRefs) { + if (nodeRefs.size() == 0) + { + // Nothing to cache + return; + } // Group the nodes by store so that we don't *have* to eagerly join to store to get query performance Map> uuidsByStore = new HashMap>(3); for (NodeRef nodeRef : nodeRefs) @@ -3071,6 +3096,12 @@ public class HibernateNodeDaoServiceImpl nodeIds.add(nodeId); } + if (nodeIds.size() == 0) + { + // Can't query + return; + } + criteria = getSession().createCriteria(ChildAssocImpl.class, "parentAssoc"); criteria.setResultTransformer(Criteria.ROOT_ENTITY); criteria.add(Restrictions.in("child.id", nodeIds)); diff --git a/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneTest.java b/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneTest.java index 2b5a9d4acc..33b5faec54 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneTest.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneTest.java @@ -61,9 +61,9 @@ import org.alfresco.repo.dictionary.DictionaryDAO; import org.alfresco.repo.dictionary.DictionaryNamespaceComponent; import org.alfresco.repo.dictionary.M2Model; import org.alfresco.repo.dictionary.NamespaceDAOImpl; -import org.alfresco.repo.domain.hibernate.HibernateL1CacheBulkLoader; import org.alfresco.repo.domain.hibernate.SessionSizeResourceManager; import org.alfresco.repo.node.BaseNodeServiceTest; +import org.alfresco.repo.node.NodeBulkLoader; import org.alfresco.repo.search.MLAnalysisMode; import org.alfresco.repo.search.QueryParameterDefImpl; import org.alfresco.repo.search.QueryRegisterComponent; @@ -217,7 +217,7 @@ public class ADMLuceneTest extends TestCase private Date testDate; - private HibernateL1CacheBulkLoader hibernateL1CacheBulkLoader; + private NodeBulkLoader hibernateL1CacheBulkLoader; private QueryEngine queryEngine; @@ -245,7 +245,7 @@ public class ADMLuceneTest extends TestCase tenantService = (TenantService) ctx.getBean("tenantService"); queryEngine = (QueryEngine) ctx.getBean("adm.luceneQueryEngineImpl"); - hibernateL1CacheBulkLoader = (HibernateL1CacheBulkLoader) ctx.getBean("hibernateL1CacheBulkLoader"); + hibernateL1CacheBulkLoader = (NodeBulkLoader) ctx.getBean("nodeDaoServiceImpl"); serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY); diff --git a/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerAndSearcherFactory.java b/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerAndSearcherFactory.java index fdcd8dc270..dd52954162 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerAndSearcherFactory.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerAndSearcherFactory.java @@ -47,7 +47,7 @@ import javax.transaction.xa.XAResource; import javax.transaction.xa.Xid; import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.repo.domain.hibernate.BulkLoader; +import org.alfresco.repo.node.NodeBulkLoader; import org.alfresco.repo.search.IndexerException; import org.alfresco.repo.search.MLAnalysisMode; import org.alfresco.repo.search.QueryRegisterComponent; @@ -149,7 +149,7 @@ public abstract class AbstractLuceneIndexerAndSearcherFactory implements LuceneI private ThreadPoolExecutor threadPoolExecutor; - private BulkLoader bulkLoader; + private NodeBulkLoader bulkLoader; private int maxDocIdCacheSize = 10000; @@ -289,12 +289,12 @@ public abstract class AbstractLuceneIndexerAndSearcherFactory implements LuceneI return maxAtomicTransformationTime; } - public BulkLoader getBulkLoader() + public NodeBulkLoader getBulkLoader() { return bulkLoader; } - public void setBulkLoader(BulkLoader bulkLoader) + public void setBulkLoader(NodeBulkLoader bulkLoader) { this.bulkLoader = bulkLoader; } diff --git a/source/java/org/alfresco/repo/search/impl/lucene/LuceneConfig.java b/source/java/org/alfresco/repo/search/impl/lucene/LuceneConfig.java index 4e92ccd51b..ea887faa0f 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/LuceneConfig.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/LuceneConfig.java @@ -26,7 +26,7 @@ package org.alfresco.repo.search.impl.lucene; import java.util.concurrent.ThreadPoolExecutor; -import org.alfresco.repo.domain.hibernate.BulkLoader; +import org.alfresco.repo.node.NodeBulkLoader; import org.alfresco.repo.search.MLAnalysisMode; import org.springframework.context.ConfigurableApplicationContext; @@ -90,10 +90,8 @@ public interface LuceneConfig /** * Get preloader - may be null if preloading is not supported - * - * @return */ - public BulkLoader getBulkLoader(); + public NodeBulkLoader getBulkLoader(); /** * Use the nio memory mapping (work arounf for bugs with some JVMs) diff --git a/source/java/org/alfresco/repo/search/impl/lucene/LuceneResultSet.java b/source/java/org/alfresco/repo/search/impl/lucene/LuceneResultSet.java index 77cf2e7951..f79cdedc62 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/LuceneResultSet.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/LuceneResultSet.java @@ -27,7 +27,9 @@ package org.alfresco.repo.search.impl.lucene; import java.io.IOException; import java.util.ArrayList; import java.util.BitSet; +import java.util.List; +import org.alfresco.repo.node.NodeBulkLoader; import org.alfresco.repo.search.AbstractResultSet; import org.alfresco.repo.search.ResultSetRowIterator; import org.alfresco.repo.search.SearcherException; @@ -109,6 +111,7 @@ public class LuceneResultSet extends AbstractResultSet { try { + prefetch(n); // We have to get the document to resolve this // It is possible the store ref is also stored in the index if (searcher instanceof ClosingIndexSearcher) @@ -150,11 +153,8 @@ public class LuceneResultSet extends AbstractResultSet { try { + prefetch(n); Document doc = hits.doc(n); - if (!prefetch.get(n)) - { - fetch(n); - } return doc; } catch (IOException e) @@ -162,48 +162,48 @@ public class LuceneResultSet extends AbstractResultSet throw new SearcherException("IO Error reading reading document from the result set", e); } } - - private void fetch(int n) + + private void prefetch(int n) throws IOException { - if (searchParameters.getBulkFetch() && (config.getBulkLoader() != null)) + NodeBulkLoader bulkLoader = config.getBulkLoader(); + if (!searchParameters.getBulkFetch() || (bulkLoader == null)) { - while (!prefetch.get(n)) - { - fetch(); - } + // No prefetching + return; } - } - - private void fetch() - { - try + if (prefetch.get(n)) { - if (searchParameters.getBulkFetch() && (config.getBulkLoader() != null)) - { - for (int i = 0; (i < hits.length()); i += searchParameters.getBulkFecthSize()) - { - if (!prefetch.get(i)) - { - ArrayList fetchList = new ArrayList(searchParameters.getBulkFecthSize()); - for (int j = i; (j < i + searchParameters.getBulkFecthSize()) && (j < hits.length()); j++) - { - Document doc = hits.doc(j); - String id = doc.get("ID"); - NodeRef nodeRef = tenantService.getBaseName(new NodeRef(id)); - fetchList.add(nodeRef); - } - config.getBulkLoader().loadIntoCache(fetchList); - for (int j = i; j < i + searchParameters.getBulkFecthSize(); j++) - { - prefetch.set(j); - } - } - } - } + // The document was already processed + return; } - catch (IOException e) + // Start at 'n' and process the the next bulk set + int bulkFetchSize = searchParameters.getBulkFecthSize(); + List fetchList = new ArrayList(bulkFetchSize); + int totalHits = hits.length(); + for (int i = 0; i < bulkFetchSize; i++) { - throw new SearcherException("IO Error reading reading document from the result set", e); + int next = n + i; + if (next >= totalHits) + { + // We've hit the end + break; + } + if (prefetch.get(next)) + { + // This one is in there already + continue; + } + // We store the node and mark it as prefetched + prefetch.set(next); + Document doc = hits.doc(next); + String nodeRefStr = doc.get("ID"); + NodeRef nodeRef = tenantService.getBaseName(new NodeRef(nodeRefStr)); + fetchList.add(nodeRef); + } + // Now bulk fetch + if (fetchList.size() > 1) + { + bulkLoader.cacheNodes(fetchList); } } diff --git a/source/java/org/alfresco/repo/tenant/MultiTNodeServiceInterceptor.java b/source/java/org/alfresco/repo/tenant/MultiTNodeServiceInterceptor.java index 4efd12ad5e..30980e888c 100644 --- a/source/java/org/alfresco/repo/tenant/MultiTNodeServiceInterceptor.java +++ b/source/java/org/alfresco/repo/tenant/MultiTNodeServiceInterceptor.java @@ -122,31 +122,7 @@ public class MultiTNodeServiceInterceptor extends DelegatingIntroductionIntercep for (int i = 0; i < args.length; i++) { Object arg = args[i]; - Object newArg = arg; - if (arg == null) - { - // No conversion possible - } - if (arg instanceof StoreRef) - { - StoreRef ref = (StoreRef) arg; - newArg = tenantService.getName(ref); - } - else if (arg instanceof NodeRef) - { - NodeRef ref = (NodeRef) arg; - newArg = tenantService.getName(ref); - } - else if (arg instanceof ChildAssociationRef) - { - ChildAssociationRef ref = (ChildAssociationRef) arg; - newArg = tenantService.getName(ref); - } - else if (arg instanceof AssociationRef) - { - AssociationRef ref = (AssociationRef) arg; - newArg = tenantService.getName(ref); - } + Object newArg = convertInboundValue(arg); if (logger.isDebugEnabled()) { @@ -258,8 +234,9 @@ public class MultiTNodeServiceInterceptor extends DelegatingIntroductionIntercep } /** - * Convert outbound collection to spoofed (no tenant prefix) values. + * Convert outbound collection to spoofed (ie. without tenant prefix) values. */ + @SuppressWarnings("unchecked") private Collection convertOutboundValues(Collection rawValues) { /* @@ -289,7 +266,7 @@ public class MultiTNodeServiceInterceptor extends DelegatingIntroductionIntercep } /** - * Convert outbound single value to spoofed (no tenant prefix) value. + * Convert outbound single value to spoofed (ie. without tenant prefix) value. */ @SuppressWarnings("unchecked") private Object convertOutboundValue(Object rawValue) @@ -299,10 +276,10 @@ public class MultiTNodeServiceInterceptor extends DelegatingIntroductionIntercep return null; } - // Deal with collections Object value = rawValue; if (rawValue instanceof Collection) { + // Deal with collections value = convertOutboundValues((Collection)rawValue); } else if (rawValue instanceof StoreRef) @@ -344,4 +321,93 @@ public class MultiTNodeServiceInterceptor extends DelegatingIntroductionIntercep // Done return value; } + + /** + * Convert inbound collection to non-spoofed (ie. with tenant prefix) values. + */ + @SuppressWarnings("unchecked") + private Collection convertInboundValues(Collection rawValues) + { + /* + * Return types can be Lists or Sets, so cater for both. + */ + final Collection convertedValues; + if (rawValues instanceof List) + { + convertedValues = new ArrayList(rawValues.size()); + } + else if (rawValues instanceof Set) + { + convertedValues = new HashSet(rawValues.size(), 1.0F); + } + else + { + throw new IllegalArgumentException("Interceptor can only handle List and Set return types."); + } + + for (Object rawValue : rawValues) + { + Object convertedValue = convertInboundValue(rawValue); + convertedValues.add(convertedValue); + } + // Done + return convertedValues; + } + + /** + * Convert outbound single value to non-spoofed (ie. with tenant prefix) value. + */ + @SuppressWarnings("unchecked") + private Object convertInboundValue(Object rawValue) + { + if (rawValue == null) + { + return null; + } + + Object value = rawValue; + if (rawValue instanceof StoreRef) + { + StoreRef ref = (StoreRef) rawValue; + value = tenantService.getName(ref); + } + else if (rawValue instanceof NodeRef) + { + NodeRef ref = (NodeRef) rawValue; + value = tenantService.getName(ref); + } + else if (rawValue instanceof ChildAssociationRef) + { + ChildAssociationRef ref = (ChildAssociationRef) rawValue; + value = tenantService.getName(ref); + } + else if (rawValue instanceof AssociationRef) + { + AssociationRef ref = (AssociationRef) rawValue; + value = tenantService.getName(ref); + } + else if (rawValue instanceof Collection) + { + // Deal with collections + value = convertInboundValues((Collection)rawValue); + } + else if (rawValue instanceof Path) + { + Path ref = (Path)rawValue; + Path inboundPath = new Path(); + Iterator itr = ref.iterator(); + while (itr.hasNext()) + { + Path.Element pathElement = itr.next(); + if (pathElement instanceof Path.ChildAssocElement) + { + pathElement = new Path.ChildAssocElement(tenantService.getName(((Path.ChildAssocElement)pathElement).getRef())); + } + inboundPath.append(pathElement); + } + value = inboundPath; + } + // Done + return value; + } } diff --git a/source/java/org/alfresco/repo/usage/ContentUsageImpl.java b/source/java/org/alfresco/repo/usage/ContentUsageImpl.java index 1b07d2f25c..96eb696763 100644 --- a/source/java/org/alfresco/repo/usage/ContentUsageImpl.java +++ b/source/java/org/alfresco/repo/usage/ContentUsageImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2007 Alfresco Software Limited. + * Copyright (C) 2005-2009 Alfresco Software Limited. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -25,6 +25,7 @@ package org.alfresco.repo.usage; import java.io.Serializable; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -56,7 +57,8 @@ import org.apache.commons.logging.LogFactory; */ public class ContentUsageImpl implements ContentUsageService, NodeServicePolicies.OnUpdatePropertiesPolicy, - NodeServicePolicies.BeforeDeleteNodePolicy + NodeServicePolicies.BeforeDeleteNodePolicy, + NodeServicePolicies.OnAddAspectPolicy { // Logger private static Log logger = LogFactory.getLog(ContentUsageImpl.class); @@ -127,7 +129,7 @@ public class ContentUsageImpl implements ContentUsageService, } /** - * The initialise method + * The initialise method */ public void init() { @@ -135,21 +137,28 @@ public class ContentUsageImpl implements ContentUsageService, { // Register interest in the onUpdateProperties policy - for content policyComponent.bindClassBehaviour( - QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"), - ContentModel.TYPE_CONTENT, + QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"), + ContentModel.TYPE_CONTENT, new JavaBehaviour(this, "onUpdateProperties")); // Register interest in the beforeDeleteNode policy - for content policyComponent.bindClassBehaviour( - QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"), + QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"), ContentModel.TYPE_CONTENT, new JavaBehaviour(this, "beforeDeleteNode")); // Register interest in the beforeDeleteNode policy - for folder policyComponent.bindClassBehaviour( - QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"), + QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"), ContentModel.TYPE_FOLDER, new JavaBehaviour(this, "beforeDeleteNode")); + + // Register interest in the onAddAspect policy - for ownable + policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onAddAspect"), + ContentModel.ASPECT_OWNABLE, + new JavaBehaviour(this, "onAddAspect")); + } } @@ -260,7 +269,7 @@ public class ContentUsageImpl implements ContentUsageService, { incrementUserUsage(ownerAfter, contentSizeAfter, nodeRef); recordUpdate(nodeRef); - } + } } else { @@ -282,8 +291,8 @@ public class ContentUsageImpl implements ContentUsageService, else if (ownerBefore != null && ownerAfter != null && ownerBefore.equals(ownerAfter) == false) { // owner has changed (size has not) - if (logger.isDebugEnabled()) logger.debug("onUpdateProperties: updateOwner ("+ownerBefore+" -> "+ownerAfter+"): nodeRef="+nodeRef+", contentSize="+contentSizeBefore); - + if (logger.isDebugEnabled()) logger.debug("onUpdateProperties: updateOwner ("+ownerBefore+" -> "+ownerAfter+"): nodeRef="+nodeRef+", contentSize="+contentSizeBefore); + if (contentSizeBefore != 0) { decrementUserUsage(ownerBefore, contentSizeBefore, nodeRef); @@ -333,7 +342,7 @@ public class ContentUsageImpl implements ContentUsageService, decrementUserUsage(owner, contentSize, nodeRef); recordDelete(nodeRef); } - } + } } else if (type.equals(ContentModel.TYPE_FOLDER)) { @@ -343,6 +352,38 @@ public class ContentUsageImpl implements ContentUsageService, } } + /** + * Called after an cm:ownable aspect has been added to a node + * + * @param nodeRef the node to which the aspect was added + * @param aspectTypeQName the type of the aspect + */ + public void onAddAspect(NodeRef nodeRef, QName aspectTypeQName) + { + if ((stores.contains(tenantService.getBaseName(nodeRef.getStoreRef()).toString())) && + (aspectTypeQName.equals(ContentModel.ASPECT_OWNABLE))) + { + String newOwner = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_OWNER); + String creator = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_CREATOR); + + if ((newOwner != null) && (! newOwner.equals(creator))) + { + ContentData content = (ContentData)nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT); + + Map before = new HashMap(2); + Map after = new HashMap(2); + + after.put(ContentModel.PROP_OWNER, newOwner); + after.put(ContentModel.PROP_CONTENT, content); + + before.put(ContentModel.PROP_CREATOR, creator); + before.put(ContentModel.PROP_CONTENT, content); + + onUpdateProperties(nodeRef, before, after); + } + } + } + private void incrementUserUsage(String userName, long contentSize, NodeRef contentNodeRef) { if (! authenticationContext.isSystemUserName(userName)) @@ -393,7 +434,7 @@ public class ContentUsageImpl implements ContentUsageService, logger.debug("User (" + userName + ") has negative usage (" + newSize + ") - reset to 0"); } } - + NodeRef personNodeRef = getPerson(userName); if (personNodeRef != null) { @@ -417,10 +458,10 @@ public class ContentUsageImpl implements ContentUsageService, } public long getUserStoredUsage(NodeRef personNodeRef) - { + { Long currentUsage = null; if (personNodeRef != null) - { + { currentUsage = (Long)nodeService.getProperty(personNodeRef, ContentModel.PROP_SIZE_CURRENT); } @@ -435,7 +476,7 @@ public class ContentUsageImpl implements ContentUsageService, NodeRef personNodeRef = getPerson(userName); if (personNodeRef != null) - { + { currentUsage = getUserStoredUsage(personNodeRef); } @@ -453,7 +494,7 @@ public class ContentUsageImpl implements ContentUsageService, currentUsage = 0; } } - + return currentUsage; } @@ -468,7 +509,7 @@ public class ContentUsageImpl implements ContentUsageService, { NodeRef personNodeRef = getPerson(userName); if (personNodeRef != null) - { + { nodeService.setProperty(personNodeRef, ContentModel.PROP_SIZE_QUOTA, new Long(currentQuota)); } } @@ -479,7 +520,7 @@ public class ContentUsageImpl implements ContentUsageService, NodeRef personNodeRef = getPerson(userName); if (personNodeRef != null) - { + { currentQuota = (Long)nodeService.getProperty(personNodeRef, ContentModel.PROP_SIZE_QUOTA); } diff --git a/source/java/org/alfresco/repo/usage/UserUsageTest.java b/source/java/org/alfresco/repo/usage/UserUsageTest.java index 78b993f11b..c93f93add2 100644 --- a/source/java/org/alfresco/repo/usage/UserUsageTest.java +++ b/source/java/org/alfresco/repo/usage/UserUsageTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Alfresco Software Limited. + * Copyright (C) 2005-2009 Alfresco Software Limited. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -46,6 +46,7 @@ import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.security.AuthenticationService; +import org.alfresco.service.cmr.security.OwnableService; import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.usage.UsageService; import org.alfresco.service.namespace.NamespaceService; @@ -60,35 +61,23 @@ import org.springframework.context.ApplicationContext; public class UserUsageTest extends TestCase { private static ApplicationContext applicationContext = ApplicationContextHelper.getApplicationContext(); - + protected NodeService nodeService; - protected FileFolderService fileFolderService; - protected AuthenticationService authenticationService; - private MutableAuthenticationDao authenticationDAO; - protected NodeRef rootNodeRef; - protected NodeRef systemNodeRef; - protected NodeRef personNodeRef; - protected AuthenticationComponent authenticationComponent; - private UserTransaction testTX; - private TransactionService transactionService; - private ContentService contentService; - private PersonService personService; - private ContentUsageImpl contentUsageImpl; - private UsageService usageService; - + private OwnableService ownableService; + private static final String TEST_USER = "userUsageTestUser"; protected void setUp() throws Exception @@ -98,7 +87,7 @@ public class UserUsageTest extends TestCase authenticationService = (AuthenticationService) applicationContext.getBean("authenticationService"); authenticationComponent = (AuthenticationComponent) applicationContext.getBean("authenticationComponent"); - + authenticationComponent.setCurrentUser(authenticationComponent.getSystemUserName()); authenticationDAO = (MutableAuthenticationDao) applicationContext.getBean("authenticationDao"); transactionService = (TransactionService) applicationContext.getBean("transactionComponent"); @@ -108,11 +97,13 @@ public class UserUsageTest extends TestCase contentUsageImpl = (ContentUsageImpl) applicationContext.getBean("contentUsageImpl"); usageService = (UsageService) applicationContext.getBean("usageService"); - + + ownableService = (OwnableService) applicationContext.getBean("ownableService"); + testTX = transactionService.getUserTransaction(); testTX.begin(); this.authenticationComponent.setSystemUserAsCurrentUser(); - + // get default store (as configured for content usage service) StoreRef storeRef = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore"); rootNodeRef = nodeService.getRootNode(storeRef); @@ -125,17 +116,17 @@ public class UserUsageTest extends TestCase Map props = createPersonProperties(TEST_USER); personNodeRef = personService.createPerson(props); - + // create an authentication object e.g. the user if (authenticationDAO.userExists(TEST_USER)) { authenticationService.deleteAuthentication(TEST_USER); } authenticationService.createAuthentication(TEST_USER, TEST_USER.toCharArray()); - + authenticationComponent.clearCurrentSecurityContext(); } - + protected void tearDown() throws Exception { try @@ -154,21 +145,21 @@ public class UserUsageTest extends TestCase super.tearDown(); } } - + protected void runAs(String userName) { authenticationService.authenticate(userName, userName.toCharArray()); assertNotNull(authenticationService.getCurrentUserName()); } - + private Map createPersonProperties(String userName) { HashMap properties = new HashMap(); properties.put(ContentModel.PROP_USERNAME, userName); return properties; } - - public void testCreateUpdatedeleteInTx() throws Exception + + public void testCreateUpdateDeleteInTx() throws Exception { if(!contentUsageImpl.getEnabled()) { @@ -222,7 +213,7 @@ public class UserUsageTest extends TestCase assertEquals(0, contentUsageImpl.getUserUsage(TEST_USER)); } - public void testCreateUpdatedeleteAcrossTx() throws Exception + public void testCreateUpdateDeleteAcrossTx() throws Exception { if(!contentUsageImpl.getEnabled()) { @@ -290,7 +281,7 @@ public class UserUsageTest extends TestCase assertEquals(0, contentUsageImpl.getUserUsage(TEST_USER)); } - public void testCreateCopydeleteInTx() throws Exception + public void testCreateCopyDeleteInTx() throws Exception { if(!contentUsageImpl.getEnabled()) { @@ -325,7 +316,7 @@ public class UserUsageTest extends TestCase assertEquals(129, contentUsageImpl.getUserUsage(TEST_USER)); // delete content - + delete(content2); // - 43 assertEquals(86, contentUsageImpl.getUserUsage(TEST_USER)); @@ -336,7 +327,7 @@ public class UserUsageTest extends TestCase assertEquals(0, contentUsageImpl.getUserUsage(TEST_USER)); } - public void testCreateCopydeleteAcrossTx() throws Exception + public void testCreateCopyDeleteAcrossTx() throws Exception { if(!contentUsageImpl.getEnabled()) { @@ -385,7 +376,7 @@ public class UserUsageTest extends TestCase assertEquals(129, contentUsageImpl.getUserUsage(TEST_USER)); // delete content - + delete(content2); // - 43 assertEquals(86, contentUsageImpl.getUserUsage(TEST_USER)); @@ -433,12 +424,12 @@ public class UserUsageTest extends TestCase assertEquals(290, contentUsageImpl.getUserUsage(TEST_USER)); // delete copied folder - + delete(folder2); // - 145 assertEquals(145, contentUsageImpl.getUserUsage(TEST_USER)); // delete original folder - + delete(folder1); // - 145 assertEquals(0, contentUsageImpl.getUserUsage(TEST_USER)); } @@ -492,7 +483,7 @@ public class UserUsageTest extends TestCase runAs(TEST_USER); // delete copied folder - + delete(folder2); // - 145 assertEquals(145, contentUsageImpl.getUserUsage(TEST_USER)); @@ -501,31 +492,161 @@ public class UserUsageTest extends TestCase testTX = transactionService.getUserTransaction(); testTX.begin(); runAs(TEST_USER); - + // delete original folder - + delete(folder1); // - 145 assertEquals(0, contentUsageImpl.getUserUsage(TEST_USER)); } + public void testCreateTakeOwnershipInTx() throws Exception + { + if(!contentUsageImpl.getEnabled()) + { + return; + } + + runAs(TEST_USER); + + assertEquals(0, contentUsageImpl.getUserUsage(TEST_USER)); + + // Create a folder + Map folderProps = new HashMap(1); + folderProps.put(ContentModel.PROP_NAME, "testFolder"); + NodeRef folder = this.nodeService.createNode( + this.rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "testFolder"), + ContentModel.TYPE_FOLDER).getChildRef(); + + // add content (in this case, some "panagrams") + + NodeRef content1 = addTextContent(folder, "text1.txt", "The quick brown fox jumps over the lazy dog"); // + 43 + assertEquals(43, contentUsageImpl.getUserUsage(TEST_USER)); + + NodeRef content2 = addTextContent(folder, "text2.txt", "Amazingly few discotheques provide jukeboxes"); // + 44 + assertEquals(87, contentUsageImpl.getUserUsage(TEST_USER)); + + NodeRef content3 = addTextContent(folder, "text3.txt", "All questions asked by five watch experts amazed the judge"); // + 58 + assertEquals(145, contentUsageImpl.getUserUsage(TEST_USER)); + + String ADMIN = AuthenticationUtil.getAdminUserName(); + + runAs(ADMIN); + + long before = contentUsageImpl.getUserUsage(ADMIN); + + takeOwnership(content1); // +/- 43 (test user -> admin) + + assertEquals(102, contentUsageImpl.getUserUsage(TEST_USER)); + assertEquals(before+43, contentUsageImpl.getUserUsage(ADMIN)); + + takeOwnership(content2); // +/- 44 (test user -> admin) + + assertEquals(58, contentUsageImpl.getUserUsage(TEST_USER)); + assertEquals(before+87, contentUsageImpl.getUserUsage(ADMIN)); + + runAs(TEST_USER); + + takeOwnership(content1); // +/- 43 (admin -> test user) + + assertEquals(101, contentUsageImpl.getUserUsage(TEST_USER)); + assertEquals(before+44, contentUsageImpl.getUserUsage(ADMIN)); + + takeOwnership(content3); // note: already the creator + + assertEquals(101, contentUsageImpl.getUserUsage(TEST_USER)); + assertEquals(before+44, contentUsageImpl.getUserUsage(ADMIN)); + } + + public void testCreateTakeOwnershipAcrossTx() throws Exception + { + if(!contentUsageImpl.getEnabled()) + { + return; + } + + runAs(TEST_USER); + + assertEquals(0, contentUsageImpl.getUserUsage(TEST_USER)); + + // Create a folder + Map folderProps = new HashMap(1); + folderProps.put(ContentModel.PROP_NAME, "testFolder"); + NodeRef folder = this.nodeService.createNode( + this.rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "testFolder"), + ContentModel.TYPE_FOLDER).getChildRef(); + + // add content (in this case, some "panagrams") + + NodeRef content1 = addTextContent(folder, "text1.txt", "The quick brown fox jumps over the lazy dog"); // + 43 + assertEquals(43, contentUsageImpl.getUserUsage(TEST_USER)); + + NodeRef content2 = addTextContent(folder, "text2.txt", "Amazingly few discotheques provide jukeboxes"); // + 44 + assertEquals(87, contentUsageImpl.getUserUsage(TEST_USER)); + + NodeRef content3 = addTextContent(folder, "text3.txt", "All questions asked by five watch experts amazed the judge"); // + 58 + assertEquals(145, contentUsageImpl.getUserUsage(TEST_USER)); + + testTX.commit(); + + String ADMIN = AuthenticationUtil.getAdminUserName(); + + testTX = transactionService.getUserTransaction(); + testTX.begin(); + + runAs(ADMIN); + + long before = contentUsageImpl.getUserUsage(ADMIN); + + takeOwnership(content1); // +/- 43 (test user -> admin) + + assertEquals(102, contentUsageImpl.getUserUsage(TEST_USER)); + assertEquals(before+43, contentUsageImpl.getUserUsage(ADMIN)); + + takeOwnership(content2); // +/- 44 (test user -> admin) + + assertEquals(58, contentUsageImpl.getUserUsage(TEST_USER)); + assertEquals(before+87, contentUsageImpl.getUserUsage(ADMIN)); + + testTX.commit(); + + testTX = transactionService.getUserTransaction(); + testTX.begin(); + + runAs(TEST_USER); + + takeOwnership(content1); // +/- 43 (admin -> test user) + + assertEquals(101, contentUsageImpl.getUserUsage(TEST_USER)); + assertEquals(before+44, contentUsageImpl.getUserUsage(ADMIN)); + + takeOwnership(content3); // note: already the creator + + assertEquals(101, contentUsageImpl.getUserUsage(TEST_USER)); + assertEquals(before+44, contentUsageImpl.getUserUsage(ADMIN)); + } + private NodeRef addTextContent(NodeRef folderRef, String name, String textData) { Map contentProps = new HashMap(); contentProps.put(ContentModel.PROP_NAME, name); - + ChildAssociationRef association = nodeService.createNode(folderRef, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name), ContentModel.TYPE_CONTENT, contentProps); - + NodeRef content = association.getChildRef(); - + ContentWriter writer = contentService.getWriter(content, ContentModel.PROP_CONTENT, true); - + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); writer.setEncoding("UTF-8"); - + writer.putContent(textData); return content; @@ -534,10 +655,10 @@ public class UserUsageTest extends TestCase private void updateTextContent(NodeRef contentRef, String textData) { ContentWriter writer = contentService.getWriter(contentRef, ContentModel.PROP_CONTENT, true); - + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); writer.setEncoding("UTF-8"); - + writer.putContent(textData); } @@ -550,4 +671,9 @@ public class UserUsageTest extends TestCase { return fileFolderService.copy(sourceFolderOrContentRef, targetFolderRef, newName).getNodeRef(); } + + private void takeOwnership(NodeRef nodeRef) + { + ownableService.takeOwnership(nodeRef); + } }