params = thumbnailRegistry.getThumbnailRenditionConvertor().convert(transformationOptions, assocDetails);
// Add the other parameters given in this method signature.
params.put(AbstractRenderingEngine.PARAM_SOURCE_CONTENT_PROPERTY, contentProperty);
params.put(AbstractRenderingEngine.PARAM_MIME_TYPE, mimetype);
// Set the parameters on the rendition definition.
definition.addParameterValues(params);
return definition;
}
private NodeRef createThumbnailNode(final NodeRef node, final QName contentProperty,
final String mimetype, final TransformationOptions transformationOptions, final String thumbnailName,
final ThumbnailParentAssociationDetails assocDetails)
{
// Get the name of the thumbnail and add to properties map
QName thumbnailQName = getThumbnailQName(thumbnailName);
RenditionDefinition definition = createRenditionDefinition(contentProperty, mimetype,
transformationOptions, thumbnailQName, assocDetails);
try
{
ChildAssociationRef thumbnailAssoc = renditionService.render(node, definition);
NodeRef thumbnail = getThumbnailNode(thumbnailAssoc);
setThumbnailNameProperty(thumbnail, thumbnailName);
return thumbnail;
} catch (RenditionServiceException rsx)
{
throw new ThumbnailException(rsx.getMessage(), rsx);
}
}
/**
* Sets the thumbnail name if the rendition is of type cm:thumbnail.
* @param thumbnail NodeRef
* @param thumbnailName String
*/
private void setThumbnailNameProperty(NodeRef thumbnail, String thumbnailName)
{
if (thumbnailName != null && thumbnailName.length() > 0)
{
if (ContentModel.TYPE_THUMBNAIL.equals(nodeService.getType(thumbnail)))
{
nodeService.setProperty(thumbnail, ContentModel.PROP_THUMBNAIL_NAME, thumbnailName);
}
}
}
/**
* Updates the parent of the supplied {@link NodeRef} to ensure that it has the "cm:thumbnailModification" aspect
* and sets the last modification data for it.
* @param nodeRef A {@link NodeRef} representing a thumbnail to provide last modification data for.
*/
@SuppressWarnings("unchecked")
private void addThumbnailModificationData(final NodeRef nodeRef, final String thumbnailName)
{
if (nodeService.exists(nodeRef))
{
if (thumbnailName != null && !nodeRef.toString().endsWith(thumbnailName))
{
Date modified = (Date)nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIED);
if (modified != null)
{
// Get the last modified value as a timestamp...
Long timestamp = modified.getTime();
// Create the value we want to set...
final String lastModifiedValue = thumbnailName + ":" + timestamp;
// Get the parent node (there should be only one) and apply the aspect and
// set the property to indicate which thumbnail the checksum refers to...
for (ChildAssociationRef parent: nodeService.getParentAssocs(nodeRef))
{
List thumbnailMods = null;
NodeRef parentNode = parent.getParentRef();
// we don't want to audit any changes to the parent here.
behaviourFilter.disableBehaviour(parentNode, ContentModel.ASPECT_AUDITABLE);
behaviourFilter.disableBehaviour(parentNode, ContentModel.ASPECT_VERSIONABLE);
try
{
if (nodeService.hasAspect(parentNode, ContentModel.ASPECT_THUMBNAIL_MODIFICATION))
{
// The node already has the aspect, check to see if the current thumbnail modification exists...
thumbnailMods = (List) nodeService.getProperty(parentNode, ContentModel.PROP_LAST_THUMBNAIL_MODIFICATION_DATA);
// If we have previously set last modified thumbnail data then it will exist as part of the multi-value
// property. The value will consist of the "cm:thumbnailName" value delimited with a ":" and then the
// timestamp. We need to find the appropriate entry in the multivalue property and then update it
String target = null;
for (String currThumbnailMod: thumbnailMods)
{
if (currThumbnailMod.startsWith(thumbnailName))
{
target = currThumbnailMod;
}
}
// Remove the previous value
if (target != null)
{
thumbnailMods.remove(target);
}
// Add the timestamp...
thumbnailMods.add(lastModifiedValue);
// Set the property...
if (logger.isDebugEnabled())
{
logger.debug("Setting thumbnail last modified date to " + lastModifiedValue +" on parent node: " + parentNode);
}
ruleService.disableRuleType(RuleType.UPDATE);
try
{
nodeService.setProperty(parentNode, ContentModel.PROP_LAST_THUMBNAIL_MODIFICATION_DATA, (Serializable) thumbnailMods);
}
finally
{
ruleService.enableRuleType(RuleType.UPDATE);
}
}
else
{
// If the aspect has not previously been added then we'll need to set it now...
thumbnailMods = new ArrayList();
thumbnailMods.add(lastModifiedValue);
// Add the aspect with the new property...
Map properties = new HashMap();
properties.put(ContentModel.PROP_LAST_THUMBNAIL_MODIFICATION_DATA, (Serializable) thumbnailMods);
if (logger.isDebugEnabled())
{
logger.debug("Adding " + ContentModel.ASPECT_THUMBNAIL_MODIFICATION + " aspect to parent node: " + parentNode);
}
ruleService.disableRuleType(RuleType.UPDATE);
try
{
nodeService.addAspect(parentNode, ContentModel.ASPECT_THUMBNAIL_MODIFICATION, properties);
}
finally
{
ruleService.enableRuleType(RuleType.UPDATE);
}
}
}
finally
{
behaviourFilter.enableBehaviour(parentNode, ContentModel.ASPECT_AUDITABLE);
behaviourFilter.enableBehaviour(parentNode, ContentModel.ASPECT_VERSIONABLE);
}
}
}
}
}
}
private class ThumbnailTransactionListenerAdapter extends TransactionListenerAdapter
{
@Override
public void afterCommit()
{
if (logger.isDebugEnabled())
{
logger.debug("Starting aftercommit listener execution.");
}
final Set childAssocs = TransactionalResourceHelper.getSet(THUMBNAIL_PARENT_NODES);
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork()
{
@Override
public Void doWork() throws Exception
{
// MNT-15135: Do the property update in a new transaction, in case the parent node was already changed (has a different version) in another transaction.
// This way the failures will not propagate up the retry stack.
RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper();
txnHelper.setForceWritable(true);
txnHelper.doInTransaction(new RetryingTransactionCallback()
{
@Override
public Void execute() throws Throwable
{
for (ChildAssociationRef childAssoc : childAssocs)
{
NodeRef thumbnailNodeRef = childAssoc.getChildRef();
NodeRef sourceNodeRef = childAssoc.getParentRef();
// check if thumbnail node exists
if (thumbnailNodeRef == null || !nodeService.exists(thumbnailNodeRef))
{
logger.debug("Thumbnail node " + thumbnailNodeRef + " does not exist. It will be skipped");
continue;
}
// check if source node exists
if (sourceNodeRef == null || !nodeService.exists(sourceNodeRef))
{
logger.debug("Parent node " + sourceNodeRef + " does not exist. It will be skipped");
continue;
}
String thumbnailName = (String) nodeService.getProperty(thumbnailNodeRef, ContentModel.PROP_NAME);
// Update the parent node with the thumbnail update...
if (logger.isDebugEnabled())
{
logger.debug("Found cached parent node " + sourceNodeRef + " in transactional resources");
logger.debug("Adding thumbnail modification data.");
}
addThumbnailModificationData(thumbnailNodeRef, thumbnailName);
}
return null;
}
}, false, true);
return null;
}
}, AuthenticationUtil.getSystemUserName());
}
}
/**
* See init - eg. registers "do nothing" copy behaviour for "cm:thumbnailModification" aspect
*
* @return Returns {@link DoNothingCopyBehaviourCallback}
*/
public CopyBehaviourCallback getCopyCallback(QName classRef, CopyDetails copyDetails)
{
return DoNothingCopyBehaviourCallback.getInstance();
}
}