mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-07 18:25:23 +00:00
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
408 lines
16 KiB
Java
408 lines
16 KiB
Java
/*
|
|
* Copyright (C) 2005-2012 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.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
import org.alfresco.repo.content.transform.ContentTransformer;
|
|
import org.alfresco.repo.content.transform.TransformerDebug;
|
|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
|
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
|
|
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
|
import org.alfresco.service.cmr.rendition.RenditionDefinition;
|
|
import org.alfresco.service.cmr.rendition.RenditionService;
|
|
import org.alfresco.service.cmr.repository.ContentService;
|
|
import org.alfresco.service.cmr.thumbnail.ThumbnailException;
|
|
import org.alfresco.service.transaction.TransactionService;
|
|
import org.apache.commons.logging.Log;
|
|
import org.apache.commons.logging.LogFactory;
|
|
import org.springframework.beans.BeansException;
|
|
import org.springframework.context.ApplicationContext;
|
|
import org.springframework.context.ApplicationContextAware;
|
|
import org.springframework.context.ApplicationEvent;
|
|
import org.springframework.context.ApplicationListener;
|
|
import org.springframework.context.event.ApplicationContextEvent;
|
|
import org.springframework.extensions.surf.util.AbstractLifecycleBean;
|
|
|
|
/**
|
|
* Registry of all the thumbnail details available
|
|
*
|
|
* @author Roy Wetherall
|
|
* @author Neil McErlean
|
|
*/
|
|
public class ThumbnailRegistry implements ApplicationContextAware, ApplicationListener<ApplicationContextEvent>
|
|
{
|
|
/** Logger */
|
|
private static Log logger = LogFactory.getLog(ThumbnailRegistry.class);
|
|
|
|
/** Content service */
|
|
private ContentService contentService;
|
|
|
|
/** Transaction service */
|
|
private TransactionService transactionService;
|
|
|
|
/** Rendition service */
|
|
private RenditionService renditionService;
|
|
|
|
/** Map of thumbnail definition */
|
|
private Map<String, ThumbnailDefinition> thumbnailDefinitions = new HashMap<String, ThumbnailDefinition>();
|
|
|
|
/** Cache to store mimetype to thumbnailDefinition mapping with max size limit */
|
|
private Map<String, List<ThumbnailDefinitionLimits>> mimetypeMap = new HashMap<String, List<ThumbnailDefinitionLimits>>(17);
|
|
|
|
private ThumbnailRenditionConvertor thumbnailRenditionConvertor;
|
|
|
|
private RegistryLifecycle lifecycle = new RegistryLifecycle();
|
|
|
|
public void setThumbnailRenditionConvertor(
|
|
ThumbnailRenditionConvertor thumbnailRenditionConvertor)
|
|
{
|
|
this.thumbnailRenditionConvertor = thumbnailRenditionConvertor;
|
|
}
|
|
|
|
public ThumbnailRenditionConvertor getThumbnailRenditionConvertor()
|
|
{
|
|
return thumbnailRenditionConvertor;
|
|
}
|
|
|
|
/**
|
|
* Content service
|
|
*
|
|
* @param contentService content service
|
|
*/
|
|
public void setContentService(ContentService contentService)
|
|
{
|
|
this.contentService = contentService;
|
|
}
|
|
|
|
/**
|
|
* Transaction service
|
|
*
|
|
* @param transactionService transaction service
|
|
*/
|
|
public void setTransactionService(TransactionService transactionService)
|
|
{
|
|
this.transactionService = transactionService;
|
|
}
|
|
|
|
/**
|
|
* Rendition service
|
|
*
|
|
* @param renditionService rendition service
|
|
*/
|
|
public void setRenditionService(RenditionService renditionService)
|
|
{
|
|
this.renditionService = renditionService;
|
|
}
|
|
|
|
/**
|
|
* This method is used to inject the thumbnail definitions.
|
|
* @param thumbnailDefinitions
|
|
*/
|
|
public void setThumbnailDefinitions(final List<ThumbnailDefinition> thumbnailDefinitions)
|
|
{
|
|
for (ThumbnailDefinition td : thumbnailDefinitions)
|
|
{
|
|
String thumbnailName = td.getName();
|
|
if (thumbnailName == null)
|
|
{
|
|
throw new ThumbnailException("When adding a thumbnail details object make sure the name is set.");
|
|
}
|
|
|
|
this.thumbnailDefinitions.put(thumbnailName, td);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Those thumbnail definitions that are injected by Spring are converted
|
|
* to rendition definitions and saved.
|
|
*/
|
|
private void initThumbnailDefinitions()
|
|
{
|
|
// If the database is in read-only mode, then do not persist the thumbnail definitions.
|
|
if (transactionService.isReadOnly())
|
|
{
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
logger.debug("TransactionService is in read-only mode. Therefore no thumbnail definitions have been initialised.");
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Otherwise we should go ahead and persist the thumbnail definitions.
|
|
// This is done during system startup. It needs to be done as the system user to ensure the thumbnail definitions get saved
|
|
// and also needs to be done within a transaction in order to support concurrent startup. See ALF-6271 for details.
|
|
RetryingTransactionHelper transactionHelper = transactionService.getRetryingTransactionHelper();
|
|
transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Void>()
|
|
{
|
|
@Override
|
|
public Void execute() throws Throwable
|
|
{
|
|
AuthenticationUtil.runAs(new RunAsWork<Void>() {
|
|
public Void doWork() throws Exception
|
|
{
|
|
for (String thumbnailDefName : thumbnailDefinitions.keySet())
|
|
{
|
|
final ThumbnailDefinition thumbnailDefinition = thumbnailDefinitions.get(thumbnailDefName);
|
|
|
|
// Built-in thumbnailDefinitions do not provide any non-standard values
|
|
// for the ThumbnailParentAssociationDetails object. Hence the null
|
|
RenditionDefinition renditionDef = thumbnailRenditionConvertor.convert(thumbnailDefinition, null);
|
|
|
|
// Thumbnail definitions are saved into the repository as actions
|
|
renditionService.saveRenditionDefinition(renditionDef);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}, AuthenticationUtil.getSystemUserName());
|
|
|
|
return null;
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get a list of all the thumbnail definitions
|
|
*
|
|
* @return Collection<ThumbnailDefinition> collection of thumbnail definitions
|
|
*/
|
|
public List<ThumbnailDefinition> getThumbnailDefinitions()
|
|
{
|
|
return new ArrayList<ThumbnailDefinition>(this.thumbnailDefinitions.values());
|
|
}
|
|
|
|
/**
|
|
* @deprecated use overloaded version with sourceSize parameter.
|
|
*/
|
|
public List<ThumbnailDefinition> getThumbnailDefinitions(String mimetype)
|
|
{
|
|
return getThumbnailDefinitions(mimetype, -1);
|
|
}
|
|
|
|
public List<ThumbnailDefinition> getThumbnailDefinitions(String mimetype, long sourceSize)
|
|
{
|
|
List<ThumbnailDefinitionLimits> thumbnailDefinitionsLimitsForMimetype = this.mimetypeMap.get(mimetype);
|
|
|
|
if (thumbnailDefinitionsLimitsForMimetype == null)
|
|
{
|
|
boolean foundAtLeastOneTransformer = false;
|
|
thumbnailDefinitionsLimitsForMimetype = new ArrayList<ThumbnailDefinitionLimits>(7);
|
|
|
|
for (ThumbnailDefinition thumbnailDefinition : this.thumbnailDefinitions.values())
|
|
{
|
|
long maxSourceSizeBytes = getMaxSourceSizeBytes(mimetype, thumbnailDefinition);
|
|
if (maxSourceSizeBytes != 0)
|
|
{
|
|
thumbnailDefinitionsLimitsForMimetype.add(new ThumbnailDefinitionLimits(thumbnailDefinition, maxSourceSizeBytes));
|
|
foundAtLeastOneTransformer = true;
|
|
}
|
|
}
|
|
|
|
// If we have found no transformers for the given MIME type then we do
|
|
// not cache the empty list. We prevent this because we want to allow for
|
|
// transformers only coming online *during* system operation - as opposed
|
|
// to coming online during startup.
|
|
//
|
|
// An example of such a transient transformer would be those that use OpenOffice.org.
|
|
// It is possible that the system might start without OOo-based transformers
|
|
// being available. Therefore we must not cache an empty list for the relevant
|
|
// MIME types - otherwise this class would hide the fact that OOo (soffice) has
|
|
// been launched and that new transformers are available.
|
|
if (foundAtLeastOneTransformer)
|
|
{
|
|
this.mimetypeMap.put(mimetype, thumbnailDefinitionsLimitsForMimetype);
|
|
}
|
|
}
|
|
|
|
// Only return ThumbnailDefinition for this specific source - may be limited on size.
|
|
List<ThumbnailDefinition> result = new ArrayList<ThumbnailDefinition>(thumbnailDefinitionsLimitsForMimetype.size());
|
|
for (ThumbnailDefinitionLimits thumbnailDefinitionLimits: thumbnailDefinitionsLimitsForMimetype)
|
|
{
|
|
long maxSourceSizeBytes = thumbnailDefinitionLimits.getMaxSourceSizeBytes();
|
|
if (sourceSize <= 0 || maxSourceSizeBytes < 0 || maxSourceSizeBytes >= sourceSize)
|
|
{
|
|
result.add(thumbnailDefinitionLimits.getThumbnailDefinition());
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param mimetype
|
|
* @return
|
|
* @deprecated Use {@link #getThumbnailDefinitions(String)} instead.
|
|
*/
|
|
@Deprecated
|
|
public List<ThumbnailDefinition> getThumnailDefintions(String mimetype)
|
|
{
|
|
return this.getThumbnailDefinitions(mimetype);
|
|
}
|
|
|
|
/**
|
|
* Checks to see if at this moment in time, the specified {@link ThumbnailDefinition}
|
|
* is able to thumbnail the source mimetype. Typically used with Thumbnail Definitions
|
|
* retrieved by name, and/or when dealing with transient {@link ContentTransformer}s.
|
|
* @param sourceUrl The URL of the source (optional)
|
|
* @param sourceMimetype The source mimetype
|
|
* @param sourceSize the size (in bytes) of the source. Use -1 if unknown.
|
|
* @param thumbnailDefinition The {@link ThumbnailDefinition} to check for
|
|
*/
|
|
public boolean isThumbnailDefinitionAvailable(String sourceUrl, String sourceMimetype, long sourceSize, ThumbnailDefinition thumbnailDefinition)
|
|
{
|
|
// Log the following getTransform() as trace so we can see the wood for the trees
|
|
boolean orig = TransformerDebug.setDebugOutput(false);
|
|
try
|
|
{
|
|
return this.contentService.getTransformer(
|
|
sourceUrl,
|
|
sourceMimetype,
|
|
sourceSize,
|
|
thumbnailDefinition.getMimetype(), thumbnailDefinition.getTransformationOptions()
|
|
) != null;
|
|
}
|
|
finally
|
|
{
|
|
TransformerDebug.setDebugOutput(orig);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the maximum source size of any content that may transformed between the supplied
|
|
* sourceMimetype and thumbnailDefinition's targetMimetype using its transformation options.
|
|
* @param sourceMimetype
|
|
* @param thumbnailDefinition
|
|
* @return 0 if there are no transformers, -1 if there is no limit or if positive the size in bytes.
|
|
*/
|
|
public long getMaxSourceSizeBytes(String sourceMimetype, ThumbnailDefinition thumbnailDefinition)
|
|
{
|
|
// Log the following getTransform() as trace so we can see the wood for the trees
|
|
boolean orig = TransformerDebug.setDebugOutput(false);
|
|
try
|
|
{
|
|
return contentService.getMaxSourceSizeBytes(sourceMimetype,
|
|
thumbnailDefinition.getMimetype(), thumbnailDefinition.getTransformationOptions());
|
|
}
|
|
finally
|
|
{
|
|
TransformerDebug.setDebugOutput(orig);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add a thumbnail details
|
|
*
|
|
* @param thumbnailDetails thumbnail details
|
|
*/
|
|
public void addThumbnailDefinition(ThumbnailDefinition thumbnailDetails)
|
|
{
|
|
String thumbnailName = thumbnailDetails.getName();
|
|
if (thumbnailName == null)
|
|
{
|
|
throw new ThumbnailException("When adding a thumbnail details object make sure the name is set.");
|
|
}
|
|
|
|
this.thumbnailDefinitions.put(thumbnailName, thumbnailDetails);
|
|
}
|
|
|
|
/**
|
|
* Get the definition of a named thumbnail
|
|
*
|
|
* @param thumbnailNam the thumbnail name
|
|
* @return ThumbnailDetails the details of the thumbnail
|
|
*/
|
|
public ThumbnailDefinition getThumbnailDefinition(String thumbnailName)
|
|
{
|
|
return this.thumbnailDefinitions.get(thumbnailName);
|
|
}
|
|
|
|
/* (non-Javadoc)
|
|
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
|
|
*/
|
|
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
|
|
{
|
|
lifecycle.setApplicationContext(applicationContext);
|
|
}
|
|
|
|
/* (non-Javadoc)
|
|
* @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent)
|
|
*/
|
|
public void onApplicationEvent(ApplicationContextEvent event)
|
|
{
|
|
lifecycle.onApplicationEvent(event);
|
|
}
|
|
|
|
/**
|
|
* This class hooks in to the spring application lifecycle and ensures that any
|
|
* ThumbnailDefinitions injected by spring are converted to RenditionDefinitions
|
|
* and saved.
|
|
*/
|
|
private class RegistryLifecycle extends AbstractLifecycleBean
|
|
{
|
|
/* (non-Javadoc)
|
|
* @see org.alfresco.util.AbstractLifecycleBean#onBootstrap(org.springframework.context.ApplicationEvent)
|
|
*/
|
|
@Override
|
|
protected void onBootstrap(ApplicationEvent event)
|
|
{
|
|
initThumbnailDefinitions();
|
|
}
|
|
|
|
/* (non-Javadoc)
|
|
* @see org.alfresco.util.AbstractLifecycleBean#onShutdown(org.springframework.context.ApplicationEvent)
|
|
*/
|
|
@Override
|
|
protected void onShutdown(ApplicationEvent event)
|
|
{
|
|
// Intentionally empty
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Links transformer limits (such as maximum size) to a ThumbnailDefinition.
|
|
*
|
|
*/
|
|
private class ThumbnailDefinitionLimits
|
|
{
|
|
private ThumbnailDefinition thumbnailDefinition;
|
|
private long maxSourceSizeBytes;
|
|
|
|
public ThumbnailDefinitionLimits(ThumbnailDefinition thumbnailDefinition, long maxSourceSizeBytes)
|
|
{
|
|
this.thumbnailDefinition = thumbnailDefinition;
|
|
this.maxSourceSizeBytes = maxSourceSizeBytes;
|
|
}
|
|
|
|
public ThumbnailDefinition getThumbnailDefinition()
|
|
{
|
|
return thumbnailDefinition;
|
|
}
|
|
|
|
public long getMaxSourceSizeBytes()
|
|
{
|
|
return maxSourceSizeBytes;
|
|
}
|
|
}
|
|
}
|