Dave Ward c3a622e3c4 Merged V4.0-BUG-FIX to HEAD
33836: Fix for ALF-10651 Fix patches that trigger reindexing and ALF-10656 SOLR: Patches execute search during bootstrap causing deadlock
   33842: Fixes ALF-12797: i18n strings in activiti-admin login-screen escaped properly
   33844: Fix for ALF-10651 Fix patches that trigger reindexing and ALF-10656 SOLR: Patches execute search during bootstrap causing deadlock
   - batch touch to limit the in clause size generated
   33845: Manually added extra core Share extensions needed for the V4.0 Records Management module from the development branch.
   - Refactored JSON property decorators for the Document Library data webscripts
   - Document List banners (e.g. working copy) moved into metadata template config
   - Ability to override default document/folder title within Document Library (<title> element in metadata template - unused in core code)
   - Additional extension point in surf-doclist to override remote data URL
   - Better handling for missing content property
   33852: ALF-12725: Merged V3.4-BUG-FIX (3.4.9) to V4.0-BUG-FIX (4.0.1)
      33849: Merged V3 (3.4.8) to V3.4-BUG-FIX (3.4.9)
         33848: ALF-10976 (relates to ALF-10412)
            Fixed bug to do with preview being stuck as always being 'Content cannot be previewed.
            Do you wish to download?' or a 'blank preview after a transformer is not found' for all
            content with the same mimetype. Cache in ThumbnailRegistory.getThumbnailDefinitions()
            now understands that transformers may have an upper content size limit. The choice between
            the two options was based on the size of the first file previewed of each mimetype.
            Needed to add getMaxSourceSizeBytes() to support this (see below).
            - refactored (previous refactor was incomplete) ContentTransformer so that
              the two halfs of isTransformable is now split into sub methods
              isTransformableMimetypes and isTransformableSize.
              This is why there are so many files changed.
            - Moved getMaxSourceSizeBytes() from AbstractContentTransformerLimits to ContentTransformer as
              there were becomming too many places in the code that needed needed to check if the ContentTransformer
              was an instanceof AbstractContentTransformerLimits before calling this method.
            - TransformerDebug now uses KB MB GB values in output to make it simpler to read.
            - TransformerDebug now uses thousand separaters in millisecond values to make it simpler to read.
            - TransformerDebug now reports the 'parent' transformer name rather than the sub-transformer name 
              when an unavailable transformer is found. Makes it simpler to tie up with the 'available transformer'
              list with the new pushIsTransformableSize() calls.
            - TransformerDebug now uses trace logging for calls from ThumbnailRegistory.isThumbnailDefinitionAvailable()
              as it is normally followed by a ContentService.transform() which is logged at debug level anyway.
            - TransformerDebug now turns logging level to trace if the file size is 0 bytes. Request from Jan.
              Not sure how one uploads such a file!
            - Modified ComplexContentTransformer.isTransformable() so that it checks the mimetypes before the sizes
              so that TransformerDebug does not report 'unavailable transformers' that don't support the
              mimetype conversion.
            - Modified ComplexContentTransformer.getLimits and ComplexContentTransformer.isPageLimitSupported()
              to include the limits from the first sub transformer.
              Was not an issue until ContentTransformer.getMaxSourceSizeBytes() was introduced.
            - Added logger to RhinoScriptProcessor to debug requests run javascript on the server.
            - Dropped the sourceUrl parameter from ThumbnailRegistry.getThumbnailDefinitions() which was
              introduced with limits as it is logicall not needed.
   33853: DiskInterface.renameFile() can now throw PermissionDeniedException to return a different status to the client. Part of ALF-12717.
   33856: Merged V3.4-BUG-FIX to V4.0-BUG-FIX
      33835: ALF-12546: Remove references to retired RegPaths.exe from installed apply_amps.bat script
      33843: Fix for ALF-12775
      33855: Merged V3.4 to V3.4-BUG-FIX
         33851: ALF-12588: Documents Intermittently Do Not Appear in Share
            - Fix by Alex Busel for regression I accidentally caused in 3.4.6
            - Simple typo in mergeDeletions() caused path deletions to sometimes not get applied or get processed twice
            - Yikes!


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@33857 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
2012-02-13 12:24:24 +00:00

226 lines
9.1 KiB
Java

/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.thumbnail;
import java.io.Serializable;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.ContentServicePolicies;
import org.alfresco.repo.policy.JavaBehaviour;
import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.TransactionListenerAdapter;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.cmr.thumbnail.ThumbnailService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
/**
* A simplistic policy that generates all applicable thumbnails for content as it is added or updated. As this is done
* synchronously, this is not recommended for production use.
*
* @author dward
*/
public class SimpleThumbnailer extends TransactionListenerAdapter implements
ContentServicePolicies.OnContentUpdatePolicy, InitializingBean
{
private static final Log logger = LogFactory.getLog(SimpleThumbnailer.class);
/** The key under which nodes to thumbnail at the end of the transaction are stored. */
private static final String KEY_POST_TXN_NODES_TO_THUMBNAIL = "SimpleThumbnailer.KEY_POST_TXN_NODES_TO_THUMBNAIL";
/** The component to register the behaviour with. */
private PolicyComponent policyComponent;
/** The node service. */
private NodeService nodeService;
/** The transaction service. */
private TransactionService transactionService;
/** The thumbnail service. */
private ThumbnailService thumbnailService;
/**
* Sets the policy component.
*
* @param policyComponent
* used for registrations
*/
public void setPolicyComponent(PolicyComponent policyComponent)
{
this.policyComponent = policyComponent;
}
/**
* Sets the node service.
*
* @param nodeService
* the node service
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* Sets the transaction service.
*
* @param transactionService
* the transaction service
*/
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
}
/**
* Sets the thumbnail service.
*
* @param thumbnailService
* the thumbnail service
*/
public void setThumbnailService(ThumbnailService thumbnailService)
{
this.thumbnailService = thumbnailService;
}
/**
* Registers the policy behaviour methods.
*/
public void afterPropertiesSet()
{
this.policyComponent.bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI, "onContentUpdate"),
this, new JavaBehaviour(this, "onContentUpdate"));
}
/**
* When content changes, thumbnails are (re)generated.
*
* @param nodeRef
* the node ref
* @param newContent
* is the content new?
*/
public void onContentUpdate(NodeRef nodeRef, boolean newContent)
{
if (!this.nodeService.getType(nodeRef).equals(ContentModel.TYPE_THUMBNAIL)
&& this.nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT) != null)
{
// Bind this service to the transaction and add the node to the set of nodes to thumbnail post txn
AlfrescoTransactionSupport.bindListener(this);
getPostTxnNodesToThumbnail().add(nodeRef);
}
}
/*
* (non-Javadoc)
* @see org.alfresco.repo.transaction.TransactionListenerAdapter#afterCommit()
*/
@Override
public void afterCommit()
{
for (final NodeRef nodeRef : getPostTxnNodesToThumbnail())
{
if (!this.nodeService.exists(nodeRef))
{
continue;
}
Serializable value = this.nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT);
ContentData contentData = DefaultTypeConverter.INSTANCE.convert(ContentData.class, value);
List<ThumbnailDefinition> thumbnailDefinitions = this.thumbnailService.getThumbnailRegistry()
.getThumbnailDefinitions(contentData.getMimetype(), contentData.getSize());
for (final ThumbnailDefinition thumbnailDefinition : thumbnailDefinitions)
{
final NodeRef existingThumbnail = this.thumbnailService.getThumbnailByName(nodeRef,
ContentModel.PROP_CONTENT, thumbnailDefinition.getName());
try
{
// Generate each thumbnail in its own transaction, so that we can recover if one of them goes wrong
this.transactionService.getRetryingTransactionHelper().doInTransaction(
new RetryingTransactionCallback<Object>()
{
public Object execute() throws Throwable
{
if (existingThumbnail == null)
{
if (SimpleThumbnailer.logger.isDebugEnabled())
{
SimpleThumbnailer.logger.debug("Creating thumbnail \""
+ thumbnailDefinition.getName() + "\" for node " + nodeRef.getId());
}
SimpleThumbnailer.this.thumbnailService.createThumbnail(nodeRef,
ContentModel.PROP_CONTENT, thumbnailDefinition.getMimetype(),
thumbnailDefinition.getTransformationOptions(), thumbnailDefinition
.getName());
}
else
{
SimpleThumbnailer.logger.debug("Updating thumbnail \""
+ thumbnailDefinition.getName() + "\" for node " + nodeRef.getId());
SimpleThumbnailer.this.thumbnailService.updateThumbnail(existingThumbnail,
thumbnailDefinition.getTransformationOptions());
}
return null;
}
}, false, true);
}
catch (Exception e)
{
SimpleThumbnailer.logger.warn("Failed to generate thumbnail \"" + thumbnailDefinition.getName()
+ "\" for node " + nodeRef.getId(), e);
}
}
}
}
/**
* Gets the txn-bound set of nodes that need thumbnailing.
*
* @return the set of nodes that need thumbnailing
*/
private Set<NodeRef> getPostTxnNodesToThumbnail()
{
@SuppressWarnings("unchecked")
Set<NodeRef> nodesToThumbnail = (Set<NodeRef>) AlfrescoTransactionSupport
.getResource(SimpleThumbnailer.KEY_POST_TXN_NODES_TO_THUMBNAIL);
if (nodesToThumbnail == null)
{
nodesToThumbnail = new LinkedHashSet<NodeRef>(11);
AlfrescoTransactionSupport
.bindResource(SimpleThumbnailer.KEY_POST_TXN_NODES_TO_THUMBNAIL, nodesToThumbnail);
}
return nodesToThumbnail;
}
}