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();
                                String thumbnailName = (String) nodeService.getProperty(thumbnailNodeRef, ContentModel.PROP_NAME);
                                //behaviourFilter.disableBehaviour(sourceNodeRef, ContentModel.ASPECT_VERSIONABLE);
                                // 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());
        }
    }
}