mirror of
				https://github.com/Alfresco/alfresco-community-repo.git
				synced 2025-10-29 15:21:53 +00:00 
			
		
		
		
	125515 slanglois: MNT-16155 Update source headers - add new Copyrights for Java and JSP source files + automatic check in the build git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.1.N/root@125606 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
		
			
				
	
	
		
			1126 lines
		
	
	
		
			45 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			1126 lines
		
	
	
		
			45 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
/*
 | 
						|
 * #%L
 | 
						|
 * Alfresco Repository
 | 
						|
 * %%
 | 
						|
 * Copyright (C) 2005 - 2016 Alfresco Software Limited
 | 
						|
 * %%
 | 
						|
 * This file is part of the Alfresco software. 
 | 
						|
 * If the software was purchased under a paid Alfresco license, the terms of 
 | 
						|
 * the paid license agreement will prevail.  Otherwise, the software is 
 | 
						|
 * provided under the following open source license terms:
 | 
						|
 * 
 | 
						|
 * 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/>.
 | 
						|
 * #L%
 | 
						|
 */
 | 
						|
package org.alfresco.repo.rendition.executer;
 | 
						|
 | 
						|
import static org.alfresco.service.cmr.rendition.RenditionService.PARAM_DESTINATION_PATH_TEMPLATE;
 | 
						|
import static org.alfresco.service.cmr.rendition.RenditionService.PARAM_IS_COMPONENT_RENDITION;
 | 
						|
import static org.alfresco.service.cmr.rendition.RenditionService.PARAM_ORPHAN_EXISTING_RENDITION;
 | 
						|
import static org.alfresco.service.cmr.rendition.RenditionService.PARAM_RENDITION_NODETYPE;
 | 
						|
 | 
						|
import java.io.Serializable;
 | 
						|
import java.util.ArrayList;
 | 
						|
import java.util.Collection;
 | 
						|
import java.util.HashMap;
 | 
						|
import java.util.List;
 | 
						|
import java.util.Map;
 | 
						|
 | 
						|
import org.alfresco.error.AlfrescoRuntimeException;
 | 
						|
import org.alfresco.model.ContentModel;
 | 
						|
import org.alfresco.model.RenditionModel;
 | 
						|
import org.alfresco.repo.action.ParameterDefinitionImpl;
 | 
						|
import org.alfresco.repo.action.executer.ActionExecuterAbstractBase;
 | 
						|
import org.alfresco.repo.content.MimetypeMap;
 | 
						|
import org.alfresco.repo.content.transform.UnimportantTransformException;
 | 
						|
import org.alfresco.repo.nodelocator.NodeLocator;
 | 
						|
import org.alfresco.repo.nodelocator.SelfNodeLocator;
 | 
						|
import org.alfresco.repo.policy.BehaviourFilter;
 | 
						|
import org.alfresco.repo.rendition.RenderingEngineDefinitionImpl;
 | 
						|
import org.alfresco.repo.rendition.RenditionDefinitionImpl;
 | 
						|
import org.alfresco.repo.rendition.RenditionLocation;
 | 
						|
import org.alfresco.repo.rendition.RenditionLocationResolver;
 | 
						|
import org.alfresco.repo.rendition.RenditionNodeManager;
 | 
						|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
 | 
						|
import org.alfresco.service.cmr.action.Action;
 | 
						|
import org.alfresco.service.cmr.action.ActionDefinition;
 | 
						|
import org.alfresco.service.cmr.action.ActionServiceException;
 | 
						|
import org.alfresco.service.cmr.action.ActionTrackingService;
 | 
						|
import org.alfresco.service.cmr.action.ExecutionSummary;
 | 
						|
import org.alfresco.service.cmr.action.ParameterDefinition;
 | 
						|
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
 | 
						|
import org.alfresco.service.cmr.rendition.RenderCallback;
 | 
						|
import org.alfresco.service.cmr.rendition.RenditionDefinition;
 | 
						|
import org.alfresco.service.cmr.rendition.RenditionService;
 | 
						|
import org.alfresco.service.cmr.rendition.RenditionServiceException;
 | 
						|
import org.alfresco.service.cmr.repository.ChildAssociationRef;
 | 
						|
import org.alfresco.service.cmr.repository.ContentReader;
 | 
						|
import org.alfresco.service.cmr.repository.ContentService;
 | 
						|
import org.alfresco.service.cmr.repository.ContentWriter;
 | 
						|
import org.alfresco.service.cmr.repository.NodeRef;
 | 
						|
import org.alfresco.service.cmr.repository.NodeService;
 | 
						|
import org.alfresco.service.cmr.repository.SerializedTransformationOptionsAccessor;
 | 
						|
import org.alfresco.service.namespace.NamespaceException;
 | 
						|
import org.alfresco.service.namespace.NamespaceService;
 | 
						|
import org.alfresco.service.namespace.QName;
 | 
						|
import org.alfresco.util.GUID;
 | 
						|
import org.apache.commons.logging.Log;
 | 
						|
import org.apache.commons.logging.LogFactory;
 | 
						|
import org.springframework.extensions.surf.util.I18NUtil;
 | 
						|
 | 
						|
import com.sun.star.lang.NullPointerException;
 | 
						|
 | 
						|
/**
 | 
						|
 * This class adds some new behaviour to the standard ActionExecuterAbstractBase
 | 
						|
 * in order to support the RenditionService.
 | 
						|
 * 
 | 
						|
 * @author Neil McErlean
 | 
						|
 * @author Nick Smith
 | 
						|
 * @since 3.3
 | 
						|
 */
 | 
						|
public abstract class AbstractRenderingEngine extends ActionExecuterAbstractBase
 | 
						|
{
 | 
						|
 | 
						|
    /** Logger */
 | 
						|
    private static Log logger = LogFactory.getLog(AbstractRenderingEngine.class);
 | 
						|
 | 
						|
    protected static final String CONTENT_READER_NOT_FOUND_MESSAGE = "Cannot find Content Reader for document. Operation can't be performed";
 | 
						|
    protected static final String DEFAULT_RUN_AS_NAME = AuthenticationUtil.getSystemUserName();
 | 
						|
 | 
						|
    // A word on the default* fields below:
 | 
						|
    //
 | 
						|
    // RenditionExecuters can be executed with or without two optional
 | 
						|
    // parameters: "rendition node type"
 | 
						|
    // and a "rendition content property" parameter.
 | 
						|
    // These parameters can be specified on a per-action basis.
 | 
						|
    // If no value is specified, then the default is used.
 | 
						|
    // That default can be injected via Spring.
 | 
						|
    // If no default is injected via spring, then there is a "default default"
 | 
						|
    // for the two params
 | 
						|
 | 
						|
    /**
 | 
						|
     * This is the default default node type for renditions - used if no value
 | 
						|
     * is injected from spring.
 | 
						|
     */
 | 
						|
    private static final QName DEFAULT_DEFAULT_RENDITION_NODE_TYPE = ContentModel.TYPE_CONTENT;
 | 
						|
 | 
						|
    /**
 | 
						|
     * This is the default default property used to specify where rendition
 | 
						|
     * content is stored - used if no value is injected from spring.
 | 
						|
     */
 | 
						|
    private static final QName DEFAULT_DEFAULT_RENDITION_CONTENT_PROP = ContentModel.PROP_CONTENT;
 | 
						|
 | 
						|
    private static final String DEFAULT_MIMETYPE = MimetypeMap.MIMETYPE_TEXT_PLAIN;
 | 
						|
    private static final String DEFAULT_ENCODING = "UTF-8";
 | 
						|
 | 
						|
    /**
 | 
						|
     * This is the default node type that is used when creating rendition
 | 
						|
     * objects.
 | 
						|
     */
 | 
						|
    private QName defaultRenditionNodeType = DEFAULT_DEFAULT_RENDITION_NODE_TYPE;
 | 
						|
 | 
						|
    /**
 | 
						|
     * This is the default property that is used to store rendition objects'
 | 
						|
     * content.
 | 
						|
     */
 | 
						|
    private QName defaultRenditionContentProp = DEFAULT_DEFAULT_RENDITION_CONTENT_PROP;
 | 
						|
 | 
						|
    /**
 | 
						|
     * This is the default content property.
 | 
						|
     */
 | 
						|
    private static final QName DEFAULT_CONTENT_PROPERTY = ContentModel.TYPE_CONTENT;
 | 
						|
 | 
						|
    /* Injected Services */
 | 
						|
    protected ContentService contentService;
 | 
						|
    protected MimetypeMap mimetypeMap;
 | 
						|
    protected ActionTrackingService actionTrackingService;
 | 
						|
 | 
						|
    /* Parameter names common to all Rendering Actions */
 | 
						|
    /**
 | 
						|
     * This optional {@link String} parameter specifies the location of a
 | 
						|
     * classpath resource which can be used as a placeholder while a rendition
 | 
						|
     * is being generated. For example, this might be a simple icon to indicate
 | 
						|
     * a rendition is not yet available. This is intended to be used in
 | 
						|
     * conjunction with asynchronous generation of renditions.
 | 
						|
     */
 | 
						|
    public static final String PARAM_PLACEHOLDER_RESOURCE_PATH = "placeHolderResourcePath";
 | 
						|
 | 
						|
    /**
 | 
						|
     * This optional {@link QName} parameter specifies which property the
 | 
						|
     * Rendering Engine uses to read content from the source node in order to
 | 
						|
     * create a rendition. By default this property will be cm:content.
 | 
						|
     */
 | 
						|
    public static final String PARAM_SOURCE_CONTENT_PROPERTY = "sourceContentProperty";
 | 
						|
 | 
						|
    /**
 | 
						|
     * This optional {@link QName} parameter specifies which property the
 | 
						|
     * Rendering Engine uses to write content to the rendition node. By default
 | 
						|
     * the property used is cm:content.
 | 
						|
     */
 | 
						|
    public static final String PARAM_TARGET_CONTENT_PROPERTY = "targetContentProperty";
 | 
						|
 | 
						|
    /**
 | 
						|
     * This optional {@link Boolean} flag property specifies whether a rendition
 | 
						|
     * should be updated automatically if the source node changes. If set to
 | 
						|
     * <code>true</code> then the rendition will be re-rendered any time any
 | 
						|
     * property changes occur on the source node. This parameter defaults to
 | 
						|
     * <code>false</code>.
 | 
						|
     */
 | 
						|
    public static final String PARAM_UPDATE_RENDITIONS_ON_ANY_PROPERTY_CHANGE = "update-renditions-on-any-property-change";
 | 
						|
 | 
						|
    /**
 | 
						|
     * This optional {@link String} parameter specifies what user permissions
 | 
						|
     * are used when creating a rendition. By default the system user is used.
 | 
						|
     */
 | 
						|
    public static final String PARAM_RUN_AS = "runAs";
 | 
						|
 | 
						|
    // mime-type is not a common parameter on all Rendering Actions, but it is
 | 
						|
    // common to many and is used in some common handling code in this class.
 | 
						|
    /**
 | 
						|
     * This optional {@link String} parameter specifies the mime type of the
 | 
						|
     * rendition content. This defaults to the mime type of the source node
 | 
						|
     * content.
 | 
						|
     */
 | 
						|
    public static final String PARAM_MIME_TYPE = "mime-type";
 | 
						|
 | 
						|
    /**
 | 
						|
     * This optional {@link String} paramter specifies the encoding used to
 | 
						|
     * create the rendition content. The derfault encoding is UTF-8.
 | 
						|
     */
 | 
						|
    public static final String PARAM_ENCODING = "encoding";
 | 
						|
 | 
						|
    /**
 | 
						|
     * Default {@link NodeLocator} simply returns the source node.
 | 
						|
     */
 | 
						|
    private final static NodeLocator defaultNodeLocator = new SelfNodeLocator();
 | 
						|
    
 | 
						|
    /*
 | 
						|
     * Injected beans
 | 
						|
     */
 | 
						|
    private RenditionLocationResolver renditionLocationResolver;
 | 
						|
    protected NodeService nodeService;
 | 
						|
    private RenditionService renditionService;
 | 
						|
    private BehaviourFilter behaviourFilter;
 | 
						|
    
 | 
						|
    private final NodeLocator temporaryParentNodeLocator;
 | 
						|
    private final QName temporaryRenditionLinkType;
 | 
						|
 | 
						|
    /**
 | 
						|
     * Injects the nodeService bean.
 | 
						|
     * 
 | 
						|
     * @param nodeService
 | 
						|
     *            the nodeService.
 | 
						|
     */
 | 
						|
    public void setNodeService(NodeService nodeService)
 | 
						|
    {
 | 
						|
        this.nodeService = nodeService;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Injects the renditionService bean.
 | 
						|
     * 
 | 
						|
     * @param renditionService RenditionService
 | 
						|
     */
 | 
						|
    public void setRenditionService(RenditionService renditionService)
 | 
						|
    {
 | 
						|
        this.renditionService = renditionService;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @param behaviourFilter  policy behaviour filter 
 | 
						|
     */
 | 
						|
    public void setBehaviourFilter(BehaviourFilter behaviourFilter)
 | 
						|
    {
 | 
						|
        this.behaviourFilter = behaviourFilter;
 | 
						|
    }
 | 
						|
 | 
						|
    public void setRenditionLocationResolver(RenditionLocationResolver renditionLocationResolver)
 | 
						|
    {
 | 
						|
        this.renditionLocationResolver = renditionLocationResolver;
 | 
						|
    }
 | 
						|
    
 | 
						|
    public AbstractRenderingEngine(NodeLocator temporaryParentNodeLocator, QName temporaryRenditionLinkType)
 | 
						|
    {
 | 
						|
        this.publicAction = false;
 | 
						|
        this.temporaryParentNodeLocator = temporaryParentNodeLocator != null ? temporaryParentNodeLocator
 | 
						|
                    : defaultNodeLocator;
 | 
						|
        this.temporaryRenditionLinkType = temporaryRenditionLinkType != null ? temporaryRenditionLinkType
 | 
						|
                    : RenditionModel.ASSOC_RENDITION;
 | 
						|
    }
 | 
						|
 | 
						|
    public AbstractRenderingEngine()
 | 
						|
    {
 | 
						|
        this(null, null);
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    
 | 
						|
    /**
 | 
						|
     * Sets the default rendition-node type.
 | 
						|
     * 
 | 
						|
     * @param type String
 | 
						|
     */
 | 
						|
    public void setDefaultRenditionNodeType(String type)
 | 
						|
    {
 | 
						|
        QName qname;
 | 
						|
        try
 | 
						|
        {
 | 
						|
            qname = QName.createQName(type);
 | 
						|
        }
 | 
						|
        catch (NamespaceException nx)
 | 
						|
        {
 | 
						|
            if (logger.isErrorEnabled())
 | 
						|
            {
 | 
						|
                logger.error("Error when setting default rendition node type: ", nx);
 | 
						|
            }
 | 
						|
            throw nx;
 | 
						|
        }
 | 
						|
 | 
						|
        if (logger.isInfoEnabled())
 | 
						|
        {
 | 
						|
            logger.info("Using default rendition node type: " + qname);
 | 
						|
        }
 | 
						|
        this.defaultRenditionNodeType = qname;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * This method returns the type of the default rendition node type.
 | 
						|
     * 
 | 
						|
     * @return the QName representing the type of the default rendition node
 | 
						|
     *         type.
 | 
						|
     */
 | 
						|
    protected QName getDefaultRenditionNodeType()
 | 
						|
    {
 | 
						|
        return defaultRenditionNodeType;
 | 
						|
    }
 | 
						|
 | 
						|
    protected String getTargetMimeType(RenderingContext context)
 | 
						|
    {
 | 
						|
        return context.getParamWithDefault(PARAM_MIME_TYPE, DEFAULT_MIMETYPE);
 | 
						|
    }
 | 
						|
 | 
						|
    protected String getTargetEncoding(RenderingContext context)
 | 
						|
    {
 | 
						|
        return context.getParamWithDefault(PARAM_ENCODING, DEFAULT_ENCODING);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Sets the default rendition content property.
 | 
						|
     * 
 | 
						|
     * @param prop String
 | 
						|
     */
 | 
						|
    public void setDefaultRenditionContentProp(String prop)
 | 
						|
    {
 | 
						|
        QName qname;
 | 
						|
        try
 | 
						|
        {
 | 
						|
            qname = QName.createQName(prop);
 | 
						|
        }
 | 
						|
        catch (NamespaceException nx)
 | 
						|
        {
 | 
						|
            if (logger.isErrorEnabled())
 | 
						|
            {
 | 
						|
                logger.error("Error when setting default rendition content property: ", nx);
 | 
						|
            }
 | 
						|
            throw nx;
 | 
						|
        }
 | 
						|
 | 
						|
        if (logger.isInfoEnabled())
 | 
						|
        {
 | 
						|
            logger.info("Using default rendition content property: " + qname);
 | 
						|
        }
 | 
						|
        this.defaultRenditionContentProp = qname;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * This method returns the QName of the property that defines the location
 | 
						|
     * of the rendition content. An example would be cm:content.
 | 
						|
     * 
 | 
						|
     * @return the QName the property defining the location of the rendition
 | 
						|
     *         content.
 | 
						|
     */
 | 
						|
    protected QName getDefaultRenditionContentProp()
 | 
						|
    {
 | 
						|
        return defaultRenditionContentProp;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Set the content service
 | 
						|
     * 
 | 
						|
     * @param contentService the content service
 | 
						|
     */
 | 
						|
    public void setContentService(ContentService contentService)
 | 
						|
    {
 | 
						|
        this.contentService = contentService;
 | 
						|
    }
 | 
						|
 | 
						|
    public void setMimetypeMap(MimetypeMap mimetypeMap)
 | 
						|
    {
 | 
						|
        this.mimetypeMap = mimetypeMap;
 | 
						|
    }
 | 
						|
 | 
						|
    public void setActionTrackingService(ActionTrackingService actionTrackingService)
 | 
						|
    {
 | 
						|
        this.actionTrackingService = actionTrackingService;
 | 
						|
    }
 | 
						|
 | 
						|
    @Override
 | 
						|
    protected ActionDefinition createActionDefinition(String definitionName)
 | 
						|
    {
 | 
						|
        return new RenderingEngineDefinitionImpl(definitionName);
 | 
						|
    }
 | 
						|
 | 
						|
    @Override
 | 
						|
    protected void executeImpl(final Action action, final NodeRef sourceNode)
 | 
						|
    {
 | 
						|
        executeImpl( (RenditionDefinition)action, sourceNode );
 | 
						|
    }
 | 
						|
        
 | 
						|
    protected void executeImpl(final RenditionDefinition renditionDef, final NodeRef sourceNode)
 | 
						|
    {
 | 
						|
        // Don't render the nodes without content.
 | 
						|
        // MNT-10178
 | 
						|
        if (!nodeService.exists(sourceNode))
 | 
						|
        {
 | 
						|
            if (logger.isDebugEnabled() == true)
 | 
						|
            {
 | 
						|
                logger.debug("Rendition has not been created, because the node no longer exists.  (sourceNode=" + sourceNode + ")");
 | 
						|
            }
 | 
						|
            notifyCallbackOfException(renditionDef, new UnimportantTransformException("Rendition was cancelled, because the node no longer exists."));
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        else if (nodeService.getProperty(sourceNode, ContentModel.PROP_CONTENT) == null)
 | 
						|
        {
 | 
						|
            if (logger.isDebugEnabled() == true)
 | 
						|
            {
 | 
						|
                logger.debug("Rendition has not been created, because the node has no content to render.  (sourceNode=" + sourceNode + ")");
 | 
						|
            }
 | 
						|
            notifyCallbackOfException(renditionDef, new UnimportantTransformException("Rendition was cancelled, because the node has no content to render."));
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        if (logger.isDebugEnabled())
 | 
						|
        {
 | 
						|
            StringBuilder msg = new StringBuilder();
 | 
						|
            msg.append("Rendering node ").append(sourceNode).append(" with rendition definition ").append(
 | 
						|
                    renditionDef.getRenditionName());
 | 
						|
            msg.append("\n").append("  parameters:").append("\n");
 | 
						|
            if (renditionDef.getParameterValues().isEmpty() == false)
 | 
						|
            {
 | 
						|
                for (String paramKey : renditionDef.getParameterValues().keySet())
 | 
						|
                {
 | 
						|
                    msg.append("    ").append(paramKey).append("=").append(renditionDef.getParameterValue(paramKey)).append("\n");
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                msg.append("    [None]");
 | 
						|
            }
 | 
						|
            logger.debug(msg.toString());
 | 
						|
        }
 | 
						|
 | 
						|
        Serializable runAsParam = renditionDef.getParameterValue(AbstractRenderingEngine.PARAM_RUN_AS);
 | 
						|
        String runAsName = runAsParam == null ? DEFAULT_RUN_AS_NAME : (String) runAsParam;
 | 
						|
 | 
						|
        // Renditions should all be created by system by default.
 | 
						|
        // When renditions are created by a user and are to be created under a
 | 
						|
        // node
 | 
						|
        // other than the source node, it is possible that the user will not
 | 
						|
        // have
 | 
						|
        // permissions to create content under that node.
 | 
						|
        // For that reason, we execute all rendition actions as system
 | 
						|
        // by default.
 | 
						|
        AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Void>()
 | 
						|
        {
 | 
						|
            @Override
 | 
						|
            public Void doWork() throws Exception
 | 
						|
            {
 | 
						|
                ChildAssociationRef result = null;
 | 
						|
                try
 | 
						|
                {
 | 
						|
                    // Check whether this rendition is a component of a larger CompositeRendition
 | 
						|
                    boolean isComponentRendition = isComponentRendition(renditionDef);
 | 
						|
                    if (isComponentRendition == false)
 | 
						|
                    {
 | 
						|
                        // Request that the rendition is initially created
 | 
						|
                        //  as a child of the source node
 | 
						|
                        setTemporaryRenditionProperties(sourceNode, renditionDef);
 | 
						|
                    }
 | 
						|
 | 
						|
                    // Have the concrete implementation do the actual rendition
 | 
						|
                    executeRenditionImpl(renditionDef, sourceNode);
 | 
						|
 | 
						|
                    //
 | 
						|
                    if (isComponentRendition == false)
 | 
						|
                    {
 | 
						|
                        // Add renditioned aspect to the source node
 | 
						|
                        tagSourceNodeAsRenditioned(renditionDef, sourceNode);
 | 
						|
 | 
						|
                        // Currently the rendition is on a temporary node, which may
 | 
						|
                        //  have the wrong name on it, and for path based renditions is
 | 
						|
                        //  in the wrong place
 | 
						|
                        // So, have the correct node created, and switch everything to use it
 | 
						|
                        switchToFinalRenditionNode(renditionDef, sourceNode);
 | 
						|
                    }
 | 
						|
 | 
						|
                    // Grab a link to the rendition node - it's been saved as a parameter for us
 | 
						|
                    // (Wait until now to fetch in case it was moved)
 | 
						|
                    result = (ChildAssociationRef)renditionDef.getParameterValue(PARAM_RESULT);
 | 
						|
                } catch (Throwable t)
 | 
						|
                {
 | 
						|
                    notifyCallbackOfException(renditionDef, t);
 | 
						|
                    throwWrappedException(t);
 | 
						|
                }
 | 
						|
                if (result != null)
 | 
						|
                {
 | 
						|
                    notifyCallbackOfResult(renditionDef, result);
 | 
						|
                }
 | 
						|
                return null;
 | 
						|
            }
 | 
						|
        }, runAsName);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Is this a standalone rendition, or is it a sub-component of
 | 
						|
     *  a composite rendition?
 | 
						|
     * This is false for standalone renditions, AND ALSO false for
 | 
						|
     *  the main part of a composite rendition.
 | 
						|
     * This only returns true if we're currently processing a
 | 
						|
     *  component of a composite rendition.
 | 
						|
     * @param action Action
 | 
						|
     * @return boolean
 | 
						|
     */
 | 
						|
    private boolean isComponentRendition(Action action) {
 | 
						|
        Serializable s = action.getParameterValue(PARAM_IS_COMPONENT_RENDITION);
 | 
						|
        boolean result = s == null ? false : (Boolean)s;
 | 
						|
        return result;
 | 
						|
    }
 | 
						|
    
 | 
						|
    protected void executeRenditionImpl(Action action, NodeRef sourceNode)
 | 
						|
    {
 | 
						|
        if (logger.isDebugEnabled())
 | 
						|
        {
 | 
						|
            StringBuilder msg = new StringBuilder();
 | 
						|
            msg.append("Executing rendering engine; name:")
 | 
						|
               .append(this.name).append(", class:")
 | 
						|
               .append(this.getClass().getName());
 | 
						|
            logger.debug(msg.toString());
 | 
						|
        }
 | 
						|
        
 | 
						|
        checkParameterValues(action);
 | 
						|
        RenditionDefinition renditionDefinition = checkActionIsRenditionDefinition(action);
 | 
						|
        checkSourceNodeExists(sourceNode);
 | 
						|
        
 | 
						|
        QName targetContentProp = getRenditionContentProperty(renditionDefinition);
 | 
						|
 | 
						|
        RenderingContext context = new RenderingContext(sourceNode,
 | 
						|
                    renditionDefinition,
 | 
						|
                    targetContentProp);
 | 
						|
        render(context);
 | 
						|
        // This is a workaround for the fact that actions don't have return
 | 
						|
        // values.
 | 
						|
        action.getParameterValues().put(PARAM_RESULT, context.getChildAssociationRef());
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * This method can be overridden by subclasses to provide checking of parameter
 | 
						|
     * values.
 | 
						|
     * If a parameter value is illegal or inappropriate, an exception
 | 
						|
     * should be thrown.
 | 
						|
     */
 | 
						|
    protected void checkParameterValues(Action action)
 | 
						|
    {
 | 
						|
        // Intentionally empty
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @param renditionDefinition RenditionDefinition
 | 
						|
     * @return QName
 | 
						|
     */
 | 
						|
    protected QName getRenditionContentProperty(RenditionDefinition renditionDefinition)
 | 
						|
    {
 | 
						|
        return getParamWithDefault(PARAM_TARGET_CONTENT_PROPERTY, defaultRenditionContentProp, renditionDefinition);
 | 
						|
    }
 | 
						|
 | 
						|
    protected abstract void render(RenderingContext context);
 | 
						|
 | 
						|
    /**
 | 
						|
     * @param actionedUponNodeRef NodeRef
 | 
						|
     */
 | 
						|
    protected void checkSourceNodeExists(NodeRef actionedUponNodeRef)
 | 
						|
    {
 | 
						|
        if (nodeService.exists(actionedUponNodeRef) == false)
 | 
						|
        {
 | 
						|
            String msg = "Cannot execute action as node does not exist: " + actionedUponNodeRef;
 | 
						|
            logger.warn(msg);
 | 
						|
            throw new RenditionServiceException(msg);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @param action Action
 | 
						|
     */
 | 
						|
    protected RenditionDefinition checkActionIsRenditionDefinition(Action action)
 | 
						|
    {
 | 
						|
        if (action instanceof RenditionDefinition)
 | 
						|
        {
 | 
						|
            return (RenditionDefinition)action;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            return new RenditionDefinitionImpl(action);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * If no rendition node type is specified, then the default is used
 | 
						|
     * 
 | 
						|
     * @param renditionDefinition RenditionDefinition
 | 
						|
     * @return QName
 | 
						|
     */
 | 
						|
    private QName getRenditionNodeType(RenditionDefinition renditionDefinition)
 | 
						|
    {
 | 
						|
        return getParamWithDefault(PARAM_RENDITION_NODETYPE, defaultRenditionNodeType, renditionDefinition);
 | 
						|
    }
 | 
						|
 | 
						|
    @Override
 | 
						|
    final protected void addParameterDefinitions(List<ParameterDefinition> paramList)
 | 
						|
    {
 | 
						|
        paramList.addAll(getParameterDefinitions());
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * This method gets the parameter definition display label from the properties file.
 | 
						|
     * It looks first for a property whose key has a fixed rendition service-specific
 | 
						|
     * prefix and if that gets null, it then delegates to the standard bean name-based
 | 
						|
     * approach.
 | 
						|
     * 
 | 
						|
     * @param paramName  the name of the parameter
 | 
						|
     * @return           the display label of the parameter
 | 
						|
     */
 | 
						|
    @Override
 | 
						|
    protected String getParamDisplayLabel(String paramName)
 | 
						|
    {
 | 
						|
        // First we try to get the message using a common prefix for all rendering engines.
 | 
						|
        final String commonPropertiesPrefix = "baseRenderingAction";
 | 
						|
        String message = I18NUtil.getMessage(commonPropertiesPrefix + "." + paramName + "." + DISPLAY_LABEL);
 | 
						|
        
 | 
						|
        // And if that doesn't work we delegate to the standard bean name-based approach.
 | 
						|
        if (message == null)
 | 
						|
        {
 | 
						|
            message = super.getParamDisplayLabel(paramName);
 | 
						|
        }
 | 
						|
        return message;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Supplies the list of parameters required by this rendering engine.
 | 
						|
     * 
 | 
						|
     * @return Collection<ParameterDefinition>
 | 
						|
     */
 | 
						|
    protected Collection<ParameterDefinition> getParameterDefinitions()
 | 
						|
    {
 | 
						|
        List<ParameterDefinition> paramList = new ArrayList<ParameterDefinition>();
 | 
						|
        
 | 
						|
        paramList.add(new ParameterDefinitionImpl(RenditionDefinitionImpl.RENDITION_DEFINITION_NAME, DataTypeDefinition.QNAME, true,
 | 
						|
                getParamDisplayLabel(RenditionDefinitionImpl.RENDITION_DEFINITION_NAME)));
 | 
						|
 | 
						|
        paramList.add(new ParameterDefinitionImpl(PARAM_RUN_AS, DataTypeDefinition.TEXT, false,
 | 
						|
                getParamDisplayLabel(PARAM_RUN_AS)));
 | 
						|
 | 
						|
        paramList.add(new ParameterDefinitionImpl(PARAM_UPDATE_RENDITIONS_ON_ANY_PROPERTY_CHANGE, DataTypeDefinition.BOOLEAN, false,
 | 
						|
                getParamDisplayLabel(PARAM_UPDATE_RENDITIONS_ON_ANY_PROPERTY_CHANGE)));
 | 
						|
 | 
						|
        paramList.add(new ParameterDefinitionImpl(PARAM_RENDITION_NODETYPE, DataTypeDefinition.QNAME, false,
 | 
						|
                    getParamDisplayLabel(PARAM_RENDITION_NODETYPE)));
 | 
						|
 | 
						|
        paramList.add(new ParameterDefinitionImpl(PARAM_PLACEHOLDER_RESOURCE_PATH, DataTypeDefinition.TEXT, false,
 | 
						|
                    getParamDisplayLabel(PARAM_PLACEHOLDER_RESOURCE_PATH)));
 | 
						|
 | 
						|
        paramList.add(new ParameterDefinitionImpl(PARAM_SOURCE_CONTENT_PROPERTY, DataTypeDefinition.QNAME, false,
 | 
						|
                    getParamDisplayLabel(PARAM_SOURCE_CONTENT_PROPERTY)));
 | 
						|
 | 
						|
        paramList.add(new ParameterDefinitionImpl(PARAM_TARGET_CONTENT_PROPERTY, DataTypeDefinition.QNAME, false,
 | 
						|
                    getParamDisplayLabel(PARAM_TARGET_CONTENT_PROPERTY)));
 | 
						|
 | 
						|
        paramList.add(new ParameterDefinitionImpl(PARAM_DESTINATION_PATH_TEMPLATE, DataTypeDefinition.TEXT, false,
 | 
						|
                    getParamDisplayLabel(PARAM_DESTINATION_PATH_TEMPLATE)));
 | 
						|
 | 
						|
        paramList.add(new ParameterDefinitionImpl(PARAM_ORPHAN_EXISTING_RENDITION, DataTypeDefinition.BOOLEAN, false,
 | 
						|
                getParamDisplayLabel(PARAM_ORPHAN_EXISTING_RENDITION)));
 | 
						|
 | 
						|
        paramList.add(new ParameterDefinitionImpl(PARAM_RESULT, DataTypeDefinition.CHILD_ASSOC_REF, false,
 | 
						|
                getParamDisplayLabel(PARAM_RESULT)));
 | 
						|
 | 
						|
        paramList.add(new ParameterDefinitionImpl(PARAM_IS_COMPONENT_RENDITION, DataTypeDefinition.BOOLEAN, false,
 | 
						|
                getParamDisplayLabel(PARAM_IS_COMPONENT_RENDITION)));
 | 
						|
        return paramList;
 | 
						|
    }
 | 
						|
 | 
						|
    private ChildAssociationRef createRenditionNodeAssoc(NodeRef sourceNode, RenditionDefinition renditionDefinition)
 | 
						|
    {
 | 
						|
        QName renditionName = renditionDefinition.getRenditionName();
 | 
						|
 | 
						|
        // The ThumbnailService puts a cm:name property on its thumbnail nodes.
 | 
						|
        Map<QName, Serializable> nodeProps = new HashMap<QName, Serializable>();
 | 
						|
        nodeProps.put(ContentModel.PROP_NAME, renditionName.getLocalName());
 | 
						|
        nodeProps.put(ContentModel.PROP_CONTENT_PROPERTY_NAME, getRenditionContentProp(renditionDefinition));
 | 
						|
        QName assocName = QName.createQName(NamespaceService.RENDITION_MODEL_1_0_URI, GUID.generate());
 | 
						|
        NodeRef parentNode = renditionDefinition.getRenditionParent();
 | 
						|
        QName assocType = renditionDefinition.getRenditionAssociationType();
 | 
						|
        QName nodeType = getRenditionNodeType(renditionDefinition);
 | 
						|
        
 | 
						|
        // Ensure that the creation of rendition children does not cause updates
 | 
						|
        // to the modified, modifier properties on the source node
 | 
						|
        behaviourFilter.disableBehaviour(parentNode, ContentModel.ASPECT_AUDITABLE);
 | 
						|
        ChildAssociationRef childAssoc = null;
 | 
						|
        try
 | 
						|
        {
 | 
						|
            childAssoc = nodeService.createNode(parentNode, assocType, assocName, nodeType, nodeProps);
 | 
						|
            if (logger.isDebugEnabled())
 | 
						|
            {
 | 
						|
                logger.debug("Created node " + childAssoc + " as child of " + parentNode + " with assoc-type " + assocType);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        finally
 | 
						|
        {
 | 
						|
            behaviourFilter.enableBehaviour(parentNode, ContentModel.ASPECT_AUDITABLE);
 | 
						|
        }
 | 
						|
        return childAssoc;
 | 
						|
    }
 | 
						|
 | 
						|
    private Serializable getRenditionContentProp(RenditionDefinition renditionDefinition)
 | 
						|
    {
 | 
						|
        return getParamWithDefault(PARAM_TARGET_CONTENT_PROPERTY, getDefaultRenditionContentProp(), renditionDefinition);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Gets the value for the named parameter. Checks the type of the parameter
 | 
						|
     * is correct and throws a {@link RenditionServiceException} if it isn't.
 | 
						|
     * Returns <code>null</code> if the parameter value is <code>null</code>
 | 
						|
     * 
 | 
						|
     * @param paramName the name of the parameter being checked.
 | 
						|
     * @param clazz the expected {@link Class} of the parameter value.
 | 
						|
     * @param definition the {@link RenditionDefinition} containing the
 | 
						|
     *            parameters.
 | 
						|
     * @return the parameter value or <code>null</code>.
 | 
						|
     */
 | 
						|
    @SuppressWarnings("unchecked")
 | 
						|
    public static <T> T getCheckedParam(String paramName, Class<T> clazz, RenditionDefinition definition)
 | 
						|
    {
 | 
						|
        Serializable value = definition.getParameterValue(paramName);
 | 
						|
        if (value == null)
 | 
						|
            return null;
 | 
						|
        else
 | 
						|
        {
 | 
						|
            if(clazz == null)
 | 
						|
                throw new RenditionServiceException("The class must not be null!", new NullPointerException());
 | 
						|
            Class<? extends Serializable> valueClass = value.getClass();
 | 
						|
            if ( !clazz.isAssignableFrom(valueClass))
 | 
						|
            {
 | 
						|
                throw new RenditionServiceException("The parameter: " + paramName + " must be of type: "
 | 
						|
                            + clazz.getName() + "but was of type: " + valueClass.getName());
 | 
						|
            }
 | 
						|
            else
 | 
						|
                return (T) value;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Gets the value for the named parameter. Checks the type of the parameter
 | 
						|
     * is the same as the type of <code>defaultValue</code> and throws a
 | 
						|
     * {@link RenditionServiceException} if it isn't. Returns
 | 
						|
     * <code>defaultValue</code> if the parameter value is <code>null</code>
 | 
						|
     *
 | 
						|
     * @param paramName String
 | 
						|
     * @param defaultValue T
 | 
						|
     * @param definition RenditionDefinition
 | 
						|
     * @param <T> T
 | 
						|
     * @return T
 | 
						|
     */
 | 
						|
    @SuppressWarnings("unchecked")
 | 
						|
    public static <T> T getParamWithDefault(String paramName, T defaultValue, RenditionDefinition definition)
 | 
						|
    {
 | 
						|
        if(defaultValue == null)
 | 
						|
            throw new RenditionServiceException("The defaultValue cannot be null!", new NullPointerException());
 | 
						|
        Class<? extends T> clazz = (Class<? extends T>) defaultValue.getClass();
 | 
						|
        T result = getCheckedParam(paramName, clazz, definition);
 | 
						|
        if (result == null)
 | 
						|
            result = defaultValue;
 | 
						|
        return result;
 | 
						|
    }
 | 
						|
 | 
						|
    protected class RenderingContext implements SerializedTransformationOptionsAccessor
 | 
						|
    {
 | 
						|
        private final NodeRef sourceNode;
 | 
						|
        private final RenditionDefinition definition;
 | 
						|
        private final QName renditionContentProperty;
 | 
						|
        
 | 
						|
        private ChildAssociationRef caNodeRef;
 | 
						|
 | 
						|
        /**
 | 
						|
         * @param sourceNode NodeRef
 | 
						|
         * @param definition RenditionDefinition
 | 
						|
         * @param renditionContentProperty QName
 | 
						|
         */
 | 
						|
        public RenderingContext(NodeRef sourceNode,//
 | 
						|
                    RenditionDefinition definition,//
 | 
						|
                    QName renditionContentProperty)
 | 
						|
        {
 | 
						|
            this.sourceNode = sourceNode;
 | 
						|
            this.definition = definition;
 | 
						|
            this.renditionContentProperty = renditionContentProperty;
 | 
						|
        }
 | 
						|
 | 
						|
        /**
 | 
						|
         * @return the sourceNode
 | 
						|
         */
 | 
						|
        public NodeRef getSourceNode()
 | 
						|
        {
 | 
						|
            return this.sourceNode;
 | 
						|
        }
 | 
						|
        
 | 
						|
        /**
 | 
						|
         * Lazily instantiation of the ChildAssociationRef
 | 
						|
         * @return ChildAssociationRef
 | 
						|
         */
 | 
						|
        public synchronized ChildAssociationRef getChildAssociationRef()
 | 
						|
        {
 | 
						|
            if (this.caNodeRef == null)
 | 
						|
            {
 | 
						|
                this.caNodeRef = createRenditionNodeAssoc(sourceNode, definition);
 | 
						|
            }
 | 
						|
            return this.caNodeRef;
 | 
						|
        }
 | 
						|
 | 
						|
        /**
 | 
						|
         * @return the destinationNode
 | 
						|
         */
 | 
						|
        public NodeRef getDestinationNode()
 | 
						|
        {
 | 
						|
            return getChildAssociationRef().getChildRef();
 | 
						|
        }
 | 
						|
 | 
						|
        /**
 | 
						|
         * @return the definition
 | 
						|
         */
 | 
						|
        public RenditionDefinition getDefinition()
 | 
						|
        {
 | 
						|
            return this.definition;
 | 
						|
        }
 | 
						|
 | 
						|
        public <T> T getCheckedParam(String paramName, Class<T> clazz)
 | 
						|
        {
 | 
						|
            return AbstractRenderingEngine.getCheckedParam(paramName, clazz, definition);
 | 
						|
        }
 | 
						|
        
 | 
						|
        public <T> T getParamWithDefault(String paramName, T defaultValue)
 | 
						|
        {
 | 
						|
            return AbstractRenderingEngine.getParamWithDefault(paramName, defaultValue, definition);
 | 
						|
        }
 | 
						|
 | 
						|
        public ContentReader makeContentReader()
 | 
						|
        {
 | 
						|
            QName srcContentProp = getParamWithDefault(PARAM_SOURCE_CONTENT_PROPERTY, DEFAULT_CONTENT_PROPERTY);
 | 
						|
            ContentReader contentReader = contentService.getReader(sourceNode, srcContentProp);
 | 
						|
            if (contentReader == null || !contentReader.exists())
 | 
						|
            {
 | 
						|
                throw new UnimportantTransformException(CONTENT_READER_NOT_FOUND_MESSAGE);
 | 
						|
            }
 | 
						|
            return contentReader;
 | 
						|
        }
 | 
						|
 | 
						|
        public ContentWriter makeContentWriter()
 | 
						|
        {
 | 
						|
            ContentWriter contentWriter = contentService.getWriter(getDestinationNode(), renditionContentProperty, true);
 | 
						|
            String mimetype = getTargetMimeType(this);
 | 
						|
            contentWriter.setMimetype(mimetype);
 | 
						|
            String encoding = getTargetEncoding(this);
 | 
						|
            contentWriter.setEncoding(encoding);
 | 
						|
            return contentWriter;
 | 
						|
        }
 | 
						|
 | 
						|
        public int getIntegerParam(String key, int defaultValue)
 | 
						|
        {
 | 
						|
            Serializable serializable = definition.getParameterValue(key);
 | 
						|
            if (serializable == null)
 | 
						|
                return defaultValue;
 | 
						|
            else
 | 
						|
            {
 | 
						|
                Number number = (Number) serializable;
 | 
						|
                return number.intValue();
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    
 | 
						|
    
 | 
						|
    protected void tagSourceNodeAsRenditioned(final RenditionDefinition renditionDef, final NodeRef actionedUponNodeRef)
 | 
						|
    {
 | 
						|
        // Adds the 'Renditioned' aspect to the source node if it
 | 
						|
        // doesn't already have it.
 | 
						|
        if (!nodeService.hasAspect(actionedUponNodeRef, RenditionModel.ASPECT_RENDITIONED))
 | 
						|
        {
 | 
						|
            if (logger.isDebugEnabled())
 | 
						|
            {
 | 
						|
                logger.debug("Applying " + RenditionModel.ASPECT_RENDITIONED + " to " + actionedUponNodeRef);
 | 
						|
            }
 | 
						|
            // Ensure we do not update the 'modifier' due to rendition addition
 | 
						|
            behaviourFilter.disableBehaviour(actionedUponNodeRef, ContentModel.ASPECT_AUDITABLE);
 | 
						|
            try
 | 
						|
            {
 | 
						|
                nodeService.addAspect(actionedUponNodeRef, RenditionModel.ASPECT_RENDITIONED, null);
 | 
						|
            }
 | 
						|
            finally
 | 
						|
            {
 | 
						|
                behaviourFilter.enableBehaviour(actionedUponNodeRef, ContentModel.ASPECT_AUDITABLE);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    protected void switchToFinalRenditionNode(final RenditionDefinition renditionDef, final NodeRef actionedUponNodeRef)
 | 
						|
    {
 | 
						|
        ChildAssociationRef tempRendAssoc = (ChildAssociationRef)renditionDef.getParameterValue(PARAM_RESULT);
 | 
						|
        if (logger.isDebugEnabled())
 | 
						|
        {
 | 
						|
            logger.debug("Switching temporary rendition: " + tempRendAssoc);
 | 
						|
        }
 | 
						|
        ChildAssociationRef result = createOrUpdateRendition(actionedUponNodeRef, tempRendAssoc, renditionDef);
 | 
						|
        renditionDef.setParameterValue(PARAM_RESULT, result);
 | 
						|
    }
 | 
						|
 | 
						|
    protected void notifyCallbackOfException(RenditionDefinition renditionDefinition, Throwable t)
 | 
						|
    {
 | 
						|
        // Rendition has failed. If there is a callback, it needs to be notified
 | 
						|
        if (renditionDefinition != null)
 | 
						|
        {
 | 
						|
            RenderCallback callback = renditionDefinition.getCallback();
 | 
						|
            if (callback != null)
 | 
						|
            {
 | 
						|
                callback.handleFailedRendition(t);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    protected void throwWrappedException(Throwable t)
 | 
						|
    {
 | 
						|
        // and rethrow Exception
 | 
						|
        if (t instanceof AlfrescoRuntimeException)
 | 
						|
        {
 | 
						|
            throw (AlfrescoRuntimeException) t;
 | 
						|
        } else
 | 
						|
        {
 | 
						|
            throw new RenditionServiceException(t.getMessage(), t);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    protected void notifyCallbackOfResult(RenditionDefinition renditionDefinition, ChildAssociationRef result)
 | 
						|
    {
 | 
						|
        // Rendition was successful. Notify the callback object.
 | 
						|
        if (renditionDefinition != null)
 | 
						|
        {
 | 
						|
            RenderCallback callback = renditionDefinition.getCallback();
 | 
						|
            if (callback != null)
 | 
						|
            {
 | 
						|
                callback.handleSuccessfulRendition(result);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * This method sets the temporary rendition parent node and the rendition assocType on the
 | 
						|
     * rendition definition.
 | 
						|
     * 
 | 
						|
     * @param sourceNode NodeRef
 | 
						|
     * @param definition the rendition definition.
 | 
						|
     */
 | 
						|
    private void setTemporaryRenditionProperties(NodeRef sourceNode, RenditionDefinition definition)
 | 
						|
    {
 | 
						|
        // Set the parent and assoc type for the temporary rendition to be
 | 
						|
        // created.
 | 
						|
        NodeRef parent = temporaryParentNodeLocator.getNode(sourceNode, definition.getParameterValues());
 | 
						|
        definition.setRenditionParent(parent);
 | 
						|
        definition.setRenditionAssociationType(temporaryRenditionLinkType);
 | 
						|
        
 | 
						|
        if (logger.isDebugEnabled())
 | 
						|
        {
 | 
						|
            StringBuilder msg = new StringBuilder();
 | 
						|
            msg.append("Temporary rendition will have parent=").append(parent)
 | 
						|
               .append(" and assoc-type=").append(temporaryRenditionLinkType);
 | 
						|
            logger.debug(msg.toString());
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * 
 | 
						|
     * @param sourceNode The node that has been rendered
 | 
						|
     * @param tempRendition The relationship between the node and its rendition
 | 
						|
     * @param renditionDefinition The definition of the rendition that has just been performed.
 | 
						|
     *                            In the case of a composite rendition, this parameter refers
 | 
						|
     *                            to that CompositeRendition and not to any of its component renditions.
 | 
						|
     * @return ChildAssociationRef
 | 
						|
     */
 | 
						|
    private ChildAssociationRef createOrUpdateRendition(NodeRef sourceNode, ChildAssociationRef tempRendition,
 | 
						|
                RenditionDefinition renditionDefinition)
 | 
						|
    {
 | 
						|
        NodeRef tempRenditionNode = tempRendition.getChildRef();
 | 
						|
        RenditionLocation renditionLocation = resolveRenditionLocation(sourceNode, renditionDefinition, tempRenditionNode);
 | 
						|
 | 
						|
        QName renditionQName = renditionDefinition.getRenditionName();
 | 
						|
 | 
						|
        RenditionNodeManager renditionNodeManager = new RenditionNodeManager(sourceNode, tempRenditionNode,
 | 
						|
                renditionLocation, renditionDefinition, nodeService, renditionService, behaviourFilter);
 | 
						|
        ChildAssociationRef renditionNode = renditionNodeManager.findOrCreateRenditionNode();
 | 
						|
 | 
						|
        // Set the name property on the rendition if it has not already been
 | 
						|
        // set.
 | 
						|
        String renditionName = getRenditionName(tempRenditionNode, renditionLocation, renditionDefinition);
 | 
						|
        nodeService.setProperty(renditionNode.getChildRef(), ContentModel.PROP_NAME, renditionName); // to manager
 | 
						|
 | 
						|
        // Add temporary aspect for temporary rendition node
 | 
						|
        // When this node id deleted, will not appear in user's trashcan
 | 
						|
        nodeService.addAspect(tempRendition.getChildRef(), ContentModel.ASPECT_TEMPORARY, null);
 | 
						|
        // Delete the temporary rendition.
 | 
						|
        nodeService.removeChildAssociation(tempRendition);
 | 
						|
        if (logger.isDebugEnabled())
 | 
						|
        {
 | 
						|
            logger.debug("Removed temporary child-association " + tempRendition);
 | 
						|
        }
 | 
						|
 | 
						|
        // Handle the rendition aspects
 | 
						|
        manageRenditionAspects(sourceNode, renditionNode);
 | 
						|
        
 | 
						|
        // Verify that everything has gone to plan, and nothing got lost on the way!
 | 
						|
        ChildAssociationRef renditionAssoc = renditionService.getRenditionByName(sourceNode, renditionQName);
 | 
						|
        if (renditionAssoc == null)
 | 
						|
        {
 | 
						|
            String msg = "A rendition of name: " + renditionQName + " should have been created for source node: "
 | 
						|
                        + sourceNode;
 | 
						|
            throw new RenditionServiceException(msg);
 | 
						|
        }
 | 
						|
        // Return the link between the source and the new, final rendition
 | 
						|
        return renditionAssoc;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * This method manages the <code>rn:rendition</code> aspects on the rendition node. It applies the
 | 
						|
     * correct rendition aspect based on the rendition node's location and removes any out-of-date rendition
 | 
						|
     * aspect.
 | 
						|
     */
 | 
						|
    private void manageRenditionAspects(NodeRef sourceNode, ChildAssociationRef renditionParentAssoc)
 | 
						|
    {
 | 
						|
        NodeRef renditionNode = renditionParentAssoc.getChildRef();
 | 
						|
        NodeRef primaryParent = renditionParentAssoc.getParentRef();
 | 
						|
 | 
						|
        // If the rendition is located directly underneath its own source node
 | 
						|
        if (primaryParent.equals(sourceNode))
 | 
						|
        {
 | 
						|
            // It should be a 'hidden' rendition.
 | 
						|
            // Ensure we do not update the 'modifier' due to rendition addition
 | 
						|
            behaviourFilter.disableBehaviour(renditionNode, ContentModel.ASPECT_AUDITABLE);
 | 
						|
            try
 | 
						|
            {
 | 
						|
                nodeService.addAspect(renditionNode, RenditionModel.ASPECT_HIDDEN_RENDITION, null);
 | 
						|
                nodeService.removeAspect(renditionNode, RenditionModel.ASPECT_VISIBLE_RENDITION);
 | 
						|
            }
 | 
						|
            finally
 | 
						|
            {
 | 
						|
                behaviourFilter.enableBehaviour(renditionNode, ContentModel.ASPECT_AUDITABLE);
 | 
						|
            }
 | 
						|
            // We remove the other aspect to cover the potential case where a
 | 
						|
            // rendition
 | 
						|
            // has been updated in a different location.
 | 
						|
        } else
 | 
						|
        {
 | 
						|
            // Renditions stored underneath any node other than their source are
 | 
						|
            // 'visible'.
 | 
						|
            behaviourFilter.disableBehaviour(renditionNode, ContentModel.ASPECT_AUDITABLE);
 | 
						|
            try
 | 
						|
            {
 | 
						|
                nodeService.addAspect(renditionNode, RenditionModel.ASPECT_VISIBLE_RENDITION, null);
 | 
						|
                nodeService.removeAspect(renditionNode, RenditionModel.ASPECT_HIDDEN_RENDITION);
 | 
						|
            }
 | 
						|
            finally
 | 
						|
            {
 | 
						|
                behaviourFilter.enableBehaviour(renditionNode, ContentModel.ASPECT_AUDITABLE);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * This method calculates the name for a rendition node. The following approaches are attempted in
 | 
						|
     * the order given below.
 | 
						|
     * <ol>
 | 
						|
     *    <li>If a name is defined in the {@link RenditionLocation} then that is used.</li>
 | 
						|
     *    <li>If the temporary rendition has a <code>cm:name</code> value, then that is used.</li>
 | 
						|
     *    <li>Otherwise use the rendition definition's rendition name.</li>
 | 
						|
     * </ol>
 | 
						|
     * 
 | 
						|
     * @param tempRenditionNode the temporary rendition node.
 | 
						|
     * @param location a RenditionLocation struct.
 | 
						|
     * @param renditionDefinition the rendition definition.
 | 
						|
     * @return the name for the rendition.
 | 
						|
     */
 | 
						|
    private String getRenditionName(NodeRef tempRenditionNode, RenditionLocation location,
 | 
						|
                RenditionDefinition renditionDefinition)
 | 
						|
    {
 | 
						|
        // If a location name is set then use it.
 | 
						|
        String locName = location.getChildName();
 | 
						|
        if (locName != null && locName.length() > 0)
 | 
						|
        {
 | 
						|
            return locName;
 | 
						|
        }
 | 
						|
        // Else if the temporary rendition specifies a name property use that.
 | 
						|
        Serializable tempName = nodeService.getProperty(tempRenditionNode, ContentModel.PROP_NAME);
 | 
						|
        if (tempName != null)
 | 
						|
        {
 | 
						|
            return (String) tempName;
 | 
						|
        }
 | 
						|
        // Otherwise use the rendition definition local name.
 | 
						|
        return renditionDefinition.getRenditionName().getLocalName();
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Given a rendition definition, a source node and a temporary rendition node, this method uses a
 | 
						|
     * {@link RenditionLocationResolver} to calculate the {@link RenditionLocation} of the rendition.
 | 
						|
     */
 | 
						|
    protected RenditionLocation resolveRenditionLocation(NodeRef sourceNode, RenditionDefinition definition,
 | 
						|
                NodeRef tempRendition)
 | 
						|
    {
 | 
						|
        return renditionLocationResolver.getRenditionLocation(sourceNode, definition, tempRendition);
 | 
						|
    }
 | 
						|
    
 | 
						|
    /**
 | 
						|
     * Gets the <code>ExecutionSummary</code> for the given <code>renderingContext</code>
 | 
						|
     * from the {@link ActionTrackingService}.
 | 
						|
     * <p>
 | 
						|
     * Note that multiple summaries of the same action instance are not currently supported.
 | 
						|
     * @param renderingContext      the rendering context
 | 
						|
     * @return                      the found summary or null
 | 
						|
     */
 | 
						|
    protected ExecutionSummary getExecutionSummary(RenderingContext renderingContext)
 | 
						|
    {
 | 
						|
        List<ExecutionSummary> executionSummaries = actionTrackingService.getExecutingActions(renderingContext.getDefinition());
 | 
						|
        if (executionSummaries == null || executionSummaries.size() == 0)
 | 
						|
        {
 | 
						|
            return null;
 | 
						|
        }
 | 
						|
        if (executionSummaries.size() > 1)
 | 
						|
        {
 | 
						|
            throw new ActionServiceException("getExecutionSummary not supported for " +
 | 
						|
                    "multiple instances of the same action");
 | 
						|
        }
 | 
						|
        return executionSummaries.iterator().next();
 | 
						|
    }
 | 
						|
}
 |