mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-06-30 18:15:39 +00:00
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@32129 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
362 lines
14 KiB
Java
362 lines
14 KiB
Java
/*
|
|
* Copyright (C) 2005-2011 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.rendition;
|
|
|
|
import java.io.Serializable;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
import org.alfresco.model.RenditionModel;
|
|
import org.alfresco.repo.copy.CopyBehaviourCallback;
|
|
import org.alfresco.repo.copy.CopyDetails;
|
|
import org.alfresco.repo.copy.CopyServicePolicies;
|
|
import org.alfresco.repo.copy.DefaultCopyBehaviourCallback;
|
|
import org.alfresco.repo.node.NodeServicePolicies;
|
|
import org.alfresco.repo.policy.Behaviour;
|
|
import org.alfresco.repo.policy.JavaBehaviour;
|
|
import org.alfresco.repo.policy.PolicyComponent;
|
|
import org.alfresco.repo.rendition.executer.AbstractRenderingEngine;
|
|
import org.alfresco.repo.rendition.executer.DeleteRenditionActionExecuter;
|
|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
|
import org.alfresco.service.cmr.action.Action;
|
|
import org.alfresco.service.cmr.action.ActionService;
|
|
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
|
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
|
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
|
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.repository.ChildAssociationRef;
|
|
import org.alfresco.service.cmr.repository.NodeRef;
|
|
import org.alfresco.service.cmr.repository.NodeService;
|
|
import org.alfresco.service.namespace.NamespaceService;
|
|
import org.alfresco.service.namespace.QName;
|
|
import org.alfresco.util.EqualsHelper;
|
|
import org.alfresco.util.Pair;
|
|
import org.apache.commons.logging.Log;
|
|
import org.apache.commons.logging.LogFactory;
|
|
|
|
/**
|
|
* Renditioned aspect behaviour bean.
|
|
* When any node with the renditioned aspect has a property updated, then all
|
|
* associated renditions are eligible for re-rendering.
|
|
* Each rendition (as identified by the name in its rn:rendition association) will
|
|
* be loaded and if the renditionDefinition exists, the rendition will be updated
|
|
* asynchronously, subject to the defined update policy.
|
|
*
|
|
* @author Neil McErlean
|
|
* @author Roy Wetherall
|
|
*/
|
|
public class RenditionedAspect implements NodeServicePolicies.OnUpdatePropertiesPolicy,
|
|
CopyServicePolicies.OnCopyNodePolicy
|
|
{
|
|
/** logger */
|
|
private static final Log logger = LogFactory.getLog(RenditionedAspect.class);
|
|
|
|
/** Services */
|
|
private ActionService actionService;
|
|
private DictionaryService dictionaryService;
|
|
private NodeService nodeService;
|
|
private PolicyComponent policyComponent;
|
|
private RenditionService renditionService;
|
|
|
|
/**
|
|
* Set the policy component
|
|
*
|
|
* @param policyComponent policy component
|
|
*/
|
|
public void setPolicyComponent(PolicyComponent policyComponent)
|
|
{
|
|
this.policyComponent = policyComponent;
|
|
}
|
|
|
|
/**
|
|
* @since 3.4.2
|
|
*/
|
|
public void setActionService(ActionService actionService)
|
|
{
|
|
this.actionService = actionService;
|
|
}
|
|
|
|
/**
|
|
* Set the node service
|
|
*
|
|
* @param nodeService node service
|
|
*/
|
|
public void setNodeService(NodeService nodeService)
|
|
{
|
|
this.nodeService = nodeService;
|
|
}
|
|
|
|
/**
|
|
* Set the rendition service
|
|
*
|
|
* @param renditionService rendition service
|
|
*/
|
|
public void setRenditionService(RenditionService renditionService)
|
|
{
|
|
this.renditionService = renditionService;
|
|
}
|
|
|
|
/**
|
|
* Set the dictionary service
|
|
*
|
|
* @param dictionaryService dictionary service
|
|
*/
|
|
public void setDictionaryService(DictionaryService dictionaryService)
|
|
{
|
|
this.dictionaryService = dictionaryService;
|
|
}
|
|
|
|
/**
|
|
* Initialise method
|
|
*/
|
|
public void init()
|
|
{
|
|
this.policyComponent.bindClassBehaviour(
|
|
QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"),
|
|
RenditionModel.ASPECT_RENDITIONED,
|
|
new JavaBehaviour(this, "onUpdateProperties", Behaviour.NotificationFrequency.EVERY_EVENT));
|
|
this.policyComponent.bindClassBehaviour(
|
|
QName.createQName(NamespaceService.ALFRESCO_URI, "getCopyCallback"),
|
|
RenditionModel.ASPECT_RENDITIONED,
|
|
new JavaBehaviour(this, "getCopyCallback"));
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.repo.node.NodeServicePolicies.OnUpdatePropertiesPolicy#onUpdateProperties(org.alfresco.service.cmr.repository.NodeRef, java.util.Map, java.util.Map)
|
|
*/
|
|
public void onUpdateProperties(
|
|
NodeRef nodeRef,
|
|
Map<QName, Serializable> before,
|
|
Map<QName, Serializable> after)
|
|
{
|
|
if (this.nodeService.exists(nodeRef))
|
|
{
|
|
// Find the changed properties
|
|
List<QName> changedProperties = getChangedProperties(before, after);
|
|
|
|
// There may be a different policy for different rendition kinds.
|
|
List<ChildAssociationRef> renditions = this.renditionService.getRenditions(nodeRef);
|
|
for (ChildAssociationRef chAssRef : renditions)
|
|
{
|
|
final QName renditionAssocName = chAssRef.getQName();
|
|
|
|
// Rendition Definitions are persisted underneath the Data Dictionary for which Group ALL
|
|
// has Consumer access by default. However, we cannot assume that that access level applies for all deployments. See ALF-7334.
|
|
RenditionDefinition rendDefn = AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<RenditionDefinition>()
|
|
{
|
|
@Override
|
|
public RenditionDefinition doWork() throws Exception
|
|
{
|
|
return renditionService.loadRenditionDefinition(renditionAssocName);
|
|
}
|
|
}, AuthenticationUtil.getSystemUserName());
|
|
|
|
if (rendDefn == null)
|
|
{
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
StringBuilder msg = new StringBuilder();
|
|
msg.append("Cannot update rendition ")
|
|
.append(renditionAssocName)
|
|
.append(" on node ").append(nodeRef)
|
|
.append(" as the renditionDefinition could not be loaded.");
|
|
logger.debug(msg.toString());
|
|
}
|
|
continue;
|
|
}
|
|
Serializable updateRenditionsPolicy = rendDefn.getParameterValue(AbstractRenderingEngine.PARAM_UPDATE_RENDITIONS_ON_ANY_PROPERTY_CHANGE);
|
|
boolean updateRenditionsAlways = updateRenditionsPolicy == null ? false : (Boolean)updateRenditionsPolicy;
|
|
|
|
boolean renditionUpdateRequired = false;
|
|
|
|
for (QName qname : changedProperties)
|
|
{
|
|
try
|
|
{
|
|
PropertyDefinition propertyDef = dictionaryService.getProperty(qname);
|
|
if (propertyDef == null)
|
|
{
|
|
// the property is not recognised
|
|
continue;
|
|
}
|
|
else if (!propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))
|
|
{
|
|
// not a content type
|
|
if (updateRenditionsAlways)
|
|
{
|
|
renditionUpdateRequired = true;
|
|
}
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
// it is a content property. We always update renditions for changes to content.
|
|
renditionUpdateRequired = true;
|
|
}
|
|
} catch (ClassCastException ccx)
|
|
{
|
|
// the property does not confirm to the model
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (renditionUpdateRequired)
|
|
{
|
|
this.queueUpdate(nodeRef, rendDefn, chAssRef);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private List<QName> getChangedProperties(Map<QName, Serializable> before, Map<QName, Serializable> after)
|
|
{
|
|
List<QName> results = new ArrayList<QName>();
|
|
for (QName propQName : before.keySet())
|
|
{
|
|
if (after.keySet().contains(propQName) == false)
|
|
{
|
|
// property was deleted
|
|
results.add(propQName);
|
|
}
|
|
else
|
|
{
|
|
Serializable beforeValue = before.get(propQName);
|
|
Serializable afterValue = after.get(propQName);
|
|
if (EqualsHelper.nullSafeEquals(beforeValue, afterValue) == false)
|
|
{
|
|
// Property was changed
|
|
results.add(propQName);
|
|
}
|
|
}
|
|
}
|
|
for (QName propQName : after.keySet())
|
|
{
|
|
if (before.containsKey(propQName) == false)
|
|
{
|
|
// property was added
|
|
results.add(propQName);
|
|
}
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
/**
|
|
* Queue the update to happen asynchronously
|
|
*
|
|
* @param sourceNodeRef node reference
|
|
*/
|
|
private void queueUpdate(final NodeRef sourceNodeRef, final RenditionDefinition rendDefn,
|
|
final ChildAssociationRef renditionAssoc)
|
|
{
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
StringBuilder msg = new StringBuilder();
|
|
msg.append("Queueing rendition update for node ").append(sourceNodeRef).append(": ").append(rendDefn.getRenditionName());
|
|
logger.debug(msg.toString());
|
|
}
|
|
|
|
if (rendDefn != null)
|
|
{
|
|
Action deleteRendition = actionService.createAction(DeleteRenditionActionExecuter.NAME);
|
|
deleteRendition.setParameterValue(DeleteRenditionActionExecuter.PARAM_RENDITION_DEFINITION_NAME, rendDefn.getRenditionName());
|
|
rendDefn.setCompensatingAction(deleteRendition);
|
|
|
|
renditionService.render(sourceNodeRef, rendDefn, new RenderCallback()
|
|
{
|
|
public void handleFailedRendition(Throwable t)
|
|
{
|
|
// In the event of a failed re-rendition, we will delete the rendition node
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
StringBuilder msg = new StringBuilder();
|
|
msg.append("Re-rendering of node ")
|
|
.append(sourceNodeRef)
|
|
.append(" with renditionDefinition ")
|
|
.append(rendDefn.getRenditionName())
|
|
.append(" failed. Deleting defunct rendition. ")
|
|
.append("The following exception is shown for informational purposes only ")
|
|
.append("and does not affect operation of the system.");
|
|
logger.debug(msg.toString(), t);
|
|
}
|
|
}
|
|
|
|
public void handleSuccessfulRendition(ChildAssociationRef primaryParentOfNewRendition)
|
|
{
|
|
// Intentionally empty
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return Returns {@link RenditionedAspectCopyBehaviourCallback}
|
|
*/
|
|
public CopyBehaviourCallback getCopyCallback(QName classRef, CopyDetails copyDetails)
|
|
{
|
|
return RenditionedAspectCopyBehaviourCallback.INSTANCE;
|
|
}
|
|
|
|
/**
|
|
* Behaviour for the {@link RenditionModel#ASPECT_RENDITIONED <b>rn:renditioned</b>} aspect.
|
|
*
|
|
* @author Derek Hulley
|
|
* @author Neil McErlean
|
|
* @since 3.2
|
|
*/
|
|
private static class RenditionedAspectCopyBehaviourCallback extends DefaultCopyBehaviourCallback
|
|
{
|
|
private static final CopyBehaviourCallback INSTANCE = new RenditionedAspectCopyBehaviourCallback();
|
|
|
|
|
|
|
|
/**
|
|
* We do not copy the {@link RenditionModel#ASPECT_RENDITIONED rn:renditioned} aspect.
|
|
*/
|
|
@Override
|
|
public boolean getMustCopy(QName classQName, CopyDetails copyDetails)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public Pair<AssocCopySourceAction, AssocCopyTargetAction> getAssociationCopyAction(
|
|
QName classQName,
|
|
CopyDetails copyDetails,
|
|
CopyAssociationDetails assocCopyDetails)
|
|
{
|
|
return new Pair<AssocCopySourceAction, AssocCopyTargetAction>(
|
|
AssocCopySourceAction.IGNORE,
|
|
AssocCopyTargetAction.USE_COPIED_OTHERWISE_ORIGINAL_TARGET);
|
|
}
|
|
|
|
@Override
|
|
public ChildAssocCopyAction getChildAssociationCopyAction(
|
|
QName classQName,
|
|
CopyDetails copyDetails,
|
|
CopyChildAssociationDetails childAssocCopyDetails)
|
|
{
|
|
return ChildAssocCopyAction.IGNORE;
|
|
}
|
|
}
|
|
}
|