mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-07 18:25:23 +00:00
33119: Merge V3.4-BUG-FIX (3.4.8) to V4.0-BUG-FIX (4.0.1) 33099: ALF-10412 Nonreducing 100% CPU Uploading Large Files to Share Site Document Library ALF-10976 Excel files bigger than 2mb cause soffice.exe to take 100% of one CPU for more than 2 minutes in previews. - Polish TransformerDebug - Better config for txt and xlsx to swf 33095: ALF-10412 Nonreducing 100% CPU Uploading Large Files to Share Site Document Library ALF-10976 Excel files bigger than 2mb cause soffice.exe to take 100% of one CPU for more than 2 minutes in previews. - Improvements to TransformerDebug so that calls to getTransformers use trace rather than debug level logging allowing one to see the wood for the trees 33016: ALF-10412 Nonreducing 100% CPU Uploading Large Files to Share Site Document Library ALF-10976 Excel files bigger than 2mb cause soffice.exe to take 100% of one CPU for more than 2 minutes in previews. - fix build errors - may not get all of them as not tested on Linux 33005: ALF-10412 Nonreducing 100% CPU Uploading Large Files to Share Site Document Library ALF-10976 Excel files bigger than 2mb cause soffice.exe to take 100% of one CPU for more than 2 minutes in previews. - Disable transformers if the source txt or xlsx is too large - avoids transforms that don't finish txt limit is 5MB xlsx limit is 1MB - Added a general 2 minute timeout added (ignored by JOD transformers - which already have a 2 minute timeout and OpenOffice transformers - would require more work) - Previous commit already limited txt -> pdf -> png so that only the 1st pdf page was created when creating a doclib icon - Earlier commit already reduced the priority of the background Thread used for transformations so that user interaction did not suffer. 33004: ALF-10412 Nonreducing 100% CPU Uploading Large Files to Share Site Document Library ALF-10976 Excel files bigger than 2mb cause soffice.exe to take 100% of one CPU for more than 2 minutes in previews. - Added time, size and page limits to transformer configuration to allow one to avoid costly transformations and to stop them if they do occur. Limits avoid a transformer being selected if the source is too large, or make it throw and Exception or discard data after a given time, KBytes read or pages created. - Page limits currently only used by TextToPdfContentTransformer for thumbnail (icon) creation. - Time, Size and Page limits are currently ignored by JodContentTransformer and OpenOfficeContentTransformerWorker once running but the max source size limits may be used to avoid the selection of the transformer in the first place. - TransformerDebug added to be able to see what is going on. A real eye opener! log4j org.alfresco.repo.content.transform.TransformerDebug 32136: ALF-10412 Nonreducing 100% CPU Uploading Large Files to Share Site Document Library Reducing the priority of the async thread pool that is used to perform the transformations so that normal activity (and even garbage collection) is not interrupted by transformations. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@33223 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
348 lines
13 KiB
Java
348 lines
13 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.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 */
|
|
private Map<String, List<ThumbnailDefinition>> mimetypeMap = new HashMap<String, List<ThumbnailDefinition>>(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(null, mimetype, -1);
|
|
}
|
|
|
|
public List<ThumbnailDefinition> getThumbnailDefinitions(String sourceUrl, String mimetype, long sourceSize)
|
|
{
|
|
List<ThumbnailDefinition> result = this.mimetypeMap.get(mimetype);
|
|
|
|
if (result == null)
|
|
{
|
|
boolean foundAtLeastOneTransformer = false;
|
|
result = new ArrayList<ThumbnailDefinition>(7);
|
|
|
|
for (ThumbnailDefinition thumbnailDefinition : this.thumbnailDefinitions.values())
|
|
{
|
|
if (isThumbnailDefinitionAvailable(sourceUrl, mimetype, sourceSize, thumbnailDefinition))
|
|
{
|
|
result.add(thumbnailDefinition);
|
|
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, result);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
}
|
|
}
|
|
}
|