Dave Ward 840440764b Merged V4.0-BUG-FIX to HEAD
34321: Make CIFS virtual circuit per session limit configurable, return correct status code when virtual circuit limit is reached.
   Required by Terminal Server clients as they can use a single session for all connections. ALF-13156
   34322: Services part of fix for ALF-13057 Nodes marked with rma:ghosted aspect (or other configurable aspects/types) should not be renditioned.
   34323: Added configuration property for CIFS virtual circuits per session, required by Terminal Server clients. ALF-13156.
   34332: Fix for ALF-8405
   34336: Merged BRANCHES/V4.0 to BRANCHES/DEV/V4.0-BUG-FIX:
      34335: Fixing a failing test as part of ALF-13057
   34338: NodeDAO: re-parent "lost & found" orphan child nodes (see ALF-12358 & ALF-13066 / SYS-301)
   - test fix (follow-on to r34279)
   34341: NodeDAO: re-parent "lost & found" orphan child nodes (see ALF-12358 & ALF-13066 / SYS-301)
   - ano test fix (once more with feeling)
   34351: Merged V4.0 to V4.0-BUG-FIX
      34335: (RECORD ONLY) Fixing a failing test as part of ALF-13057
      34350: Merged V3.4 to V4.0
         34327: ALF-13030 ALF-13041 Transformer Server needs filename and to make its transformer preferred over OpenOffice and JOD
            Relates to ALF-10976 and ALF-10412 set of changes
            - TransformerOptions.sourceNodeRef now populated
            - Set average transformer time via global properties
            - TransformerDebug of active transformers added
         34331: ALF-13030 ALF-13041 Transformer Server needs filename and to make its transformer preferred over OpenOffice and JOD
            Relates to ALF-10976 and ALF-10412 set of changes
            - Fix test failures
         34346: ALF-13030 ALF-13041 Transformer Server needs filename and to make its transformer preferred over OpenOffice and JOD
            Relates to ALF-10976 and ALF-10412 set of changes
            - sourceNodeRef was not available to the first child transformer of a ComplexContentTransformer
   34358: Merged V4.0 to V4.0-BUG-FIX (RECORD ONLY)
      34324: Merged BRANCHES/DEV/V4.0-BUG-FIX to BRANCHES/V4.0:
         34322: Services part of fix for ALF-13057 Nodes marked with rma:ghosted aspect (or other configurable aspects/types) should not be renditioned.
   34360: Merged V3.4-BUG-FIX to V4.0-BUG-FIX (RECORD ONLY)
      34349: Merged V3.4 to V3.4-BUG-FIX
         34327: ALF-13030 ALF-13041 Transformer Server needs filename and to make its transformer preferred over OpenOffice and JOD
            Relates to ALF-10976 and ALF-10412 set of changes
            - TransformerOptions.sourceNodeRef now populated
            - Set average transformer time via global properties
            - TransformerDebug of active transformers added
         34331: ALF-13030 ALF-13041 Transformer Server needs filename and to make its transformer preferred over OpenOffice and JOD
            Relates to ALF-10976 and ALF-10412 set of changes
            - Fix test failures
         34346: ALF-13030 ALF-13041 Transformer Server needs filename and to make its transformer preferred over OpenOffice and JOD
            Relates to ALF-10976 and ALF-10412 set of changes
            - sourceNodeRef was not available to the first child transformer of a ComplexContentTransformer
         34348: Merged PATCHES/V3.4.7 to V3.4
            34347: ALF-12949: Merged V4.0-BUG-FIX to PATCHES/V3.4.7
               33959: Merged BRANCHES/DEV/V3.4-BUG-FIX to BRANCHES/DEV/V4.0-BUG-FIX
                  33950: Fix for ALF-12787:
                         - Surf Memory usage optimizations and improvements to ModelObject and derived classes.
                         - Reduced avg memory usage for a ModelObject in the Surf persister cache from 4.0K to 1.5K.
                         - Related refactoring to support removal of in-memory XML DOM previously used to maintain ModelObject state. Now XML DOM only created as needed for set() operations and thrown away.
                         - Improvements to Surf Persister cache strategy to support MRU style concurrent cache, now uses a LinkedConcurrentHashMap to provide a concurrent cache that also has a configurable maximum capacity
                         - Added various improved spring config for persister caches and default sizes.
               Merged BRANCHES/DEV/V3.4-BUG-FIX to BRANCHES/DEV/V4.0-BUG-FIX
                  33956: Latest SpringSurf libs: Fixed issue with overly aggressive caching of missing page View objects.
               34108: Merged BRANCHES/DEV/V3.4-BUG-FIX to BRANCHES/DEV/V4.0-BUG-FIX
                  34107: Added missing jar lib to wcmquickstart and webeditor dependencies
   34363: Merged V4.0 to V4.0-BUG-FIX
      34362: Merged V3.4 to V4.0 (RECORD ONLY)
         34282: ALF-13059: Windows 7 specific: It's impossible to add documents to DWS
         - Fix by Alex Malinovsky
         34348: Merged PATCHES/V3.4.7 to V3.4
            34347: ALF-12949: Merged V4.0-BUG-FIX to PATCHES/V3.4.7
               33959: Merged BRANCHES/DEV/V3.4-BUG-FIX to BRANCHES/DEV/V4.0-BUG-FIX
                  33950: Fix for ALF-12787:
                         - Surf Memory usage optimizations and improvements to ModelObject and derived classes.
                         - Reduced avg memory usage for a ModelObject in the Surf persister cache from 4.0K to 1.5K.
                         - Related refactoring to support removal of in-memory XML DOM previously used to maintain ModelObject state. Now XML DOM only created as needed for set() operations and thrown away.
                         - Improvements to Surf Persister cache strategy to support MRU style concurrent cache, now uses a LinkedConcurrentHashMap to provide a concurrent cache that also has a configurable maximum capacity
                         - Added various improved spring config for persister caches and default sizes.
               Merged BRANCHES/DEV/V3.4-BUG-FIX to BRANCHES/DEV/V4.0-BUG-FIX
                  33956: Latest SpringSurf libs: Fixed issue with overly aggressive caching of missing page View objects.
               34108: Merged BRANCHES/DEV/V3.4-BUG-FIX to BRANCHES/DEV/V4.0-BUG-FIX
                  34107: Added missing jar lib to wcmquickstart and webeditor dependencies


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@34364 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
2012-03-02 15:00:35 +00:00

551 lines
21 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.rendition;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.alfresco.model.ContentModel;
import org.alfresco.model.RenditionModel;
import org.alfresco.repo.action.executer.ActionExecuter;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.action.ActionDefinition;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.rendition.CompositeRenditionDefinition;
import org.alfresco.service.cmr.rendition.RenderCallback;
import org.alfresco.service.cmr.rendition.RenderingEngineDefinition;
import org.alfresco.service.cmr.rendition.RenditionDefinition;
import org.alfresco.service.cmr.rendition.RenditionPreventedException;
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.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.util.GUID;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/*
* @author Nick Smith
* @author Neil McErlean
* @since 3.3
*/
public class RenditionServiceImpl implements RenditionService, RenditionDefinitionPersister
{
private static final Log log = LogFactory.getLog(RenditionServiceImpl.class);
private ActionService actionService;
private ContentService contentService;
private DictionaryService dictionaryService;
private NodeService nodeService;
private RenditionDefinitionPersisterImpl renditionDefinitionPersister;
/**
* @since 4.0.1
*/
private RenditionPreventionRegistry renditionPreventionRegistry;
/**
* Injects the RenditionDefinitionPersister bean.
* @param renditionDefinitionPersister
*/
public void setRenditionDefinitionPersister(RenditionDefinitionPersisterImpl renditionDefinitionPersister)
{
this.renditionDefinitionPersister = renditionDefinitionPersister;
}
/**
* @since 4.0.1
*/
public void setRenditionPreventionRegistry(RenditionPreventionRegistry registry)
{
this.renditionPreventionRegistry = registry;
}
/**
* Injects the ServiceRegistry bean.
* @param serviceRegistry
*/
public void setServiceRegistry(ServiceRegistry serviceRegistry)
{
this.contentService = serviceRegistry.getContentService();
this.nodeService = serviceRegistry.getNodeService();
}
/**
* Injects the ActionService bean.
* @param actionService
*/
public void setActionService(ActionService actionService)
{
this.actionService = actionService;
}
/**
* Injects the DictionaryService bean.
* @param dictionaryService
*/
public void setDictionaryService(DictionaryService dictionaryService)
{
this.dictionaryService = dictionaryService;
}
/*
* (non-Javadoc)
* @see org.alfresco.service.cmr.rendition.RenditionService#getRenderingEngineDefinition(java.lang.String)
*/
public RenderingEngineDefinition getRenderingEngineDefinition(String name)
{
ActionDefinition actionDefinition = actionService.getActionDefinition(name);
if (actionDefinition instanceof RenderingEngineDefinition)
{
return (RenderingEngineDefinition) actionDefinition;
}
else
return null;
}
/*
* (non-Javadoc)
* @see org.alfresco.service.cmr.rendition.RenditionService#getRenderingEngineDefinitions()
*/
public List<RenderingEngineDefinition> getRenderingEngineDefinitions()
{
List<RenderingEngineDefinition> results = new ArrayList<RenderingEngineDefinition>();
List<ActionDefinition> actionDefs = actionService.getActionDefinitions();
for (ActionDefinition actionDef : actionDefs)
{
if (actionDef instanceof RenderingEngineDefinition)
{
RenderingEngineDefinition renderingDef = (RenderingEngineDefinition) actionDef;
results.add(renderingDef);
}
}
return results;
}
/*
* (non-Javadoc)
* @see
* org.alfresco.service.cmr.rendition.RenditionService#createRenditionDefinition
* (org.alfresco.service.namespace.QName, java.lang.String)
*/
public RenditionDefinition createRenditionDefinition(QName renditionDefinitionName, String renderingEngineName)
{
if (log.isDebugEnabled())
{
StringBuilder msg = new StringBuilder();
msg.append("Creating rendition definition ")
.append(renditionDefinitionName)
.append(" ")
.append(renderingEngineName);
log.debug(msg.toString());
}
return new RenditionDefinitionImpl(GUID.generate(), renditionDefinitionName, renderingEngineName);
}
/*
* (non-Javadoc)
* @see org.alfresco.service.cmr.rendition.RenditionService#createCompositeRenditionDefinition(org.alfresco.service.namespace.QName)
*/
public CompositeRenditionDefinition createCompositeRenditionDefinition(QName renditionName)
{
if (log.isDebugEnabled())
{
StringBuilder msg = new StringBuilder();
msg.append("Creating composite rendition definition ")
.append(renditionName);
log.debug(msg.toString());
}
return new CompositeRenditionDefinitionImpl(GUID.generate(), renditionName);
}
/*
* (non-Javadoc)
* @see org.alfresco.service.cmr.rendition.RenditionService#render(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.rendition.RenditionDefinition)
*/
public ChildAssociationRef render(NodeRef sourceNode, RenditionDefinition definition)
{
checkSourceNodeForPreventionClass(sourceNode);
ChildAssociationRef result = executeRenditionAction(sourceNode, definition, false);
if (log.isDebugEnabled())
{
log.debug("Produced rendition " + result);
}
return result;
}
public void render(NodeRef sourceNode, RenditionDefinition definition,
RenderCallback callback)
{
checkSourceNodeForPreventionClass(sourceNode);
// The asynchronous render can't return a ChildAssociationRef as it is created
// asynchronously after this method returns.
definition.setCallback(callback);
executeRenditionAction(sourceNode, definition, true);
return;
}
/*
* (non-Javadoc)
* @see org.alfresco.service.cmr.rendition.RenditionService#render(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName)
*/
public ChildAssociationRef render(NodeRef sourceNode, final QName renditionDefinitionQName)
{
checkSourceNodeForPreventionClass(sourceNode);
RenditionDefinition rendDefn = AuthenticationUtil.runAs(
new AuthenticationUtil.RunAsWork<RenditionDefinition>()
{
public RenditionDefinition doWork() throws Exception
{
return loadRenditionDefinition(renditionDefinitionQName);
}
}, AuthenticationUtil.getSystemUserName());
if (rendDefn == null)
{
throw new RenditionServiceException("Rendition Definition " + renditionDefinitionQName + " was not found.");
}
return this.render(sourceNode, rendDefn);
}
/*
* (non-Javadoc)
* @see org.alfresco.service.cmr.rendition.RenditionService#render(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName, org.alfresco.service.cmr.rendition.RenderCallback)
*/
public void render(NodeRef sourceNode, final QName renditionDefinitionQName, RenderCallback callback)
{
checkSourceNodeForPreventionClass(sourceNode);
RenditionDefinition rendDefn = AuthenticationUtil.runAs(
new AuthenticationUtil.RunAsWork<RenditionDefinition>()
{
public RenditionDefinition doWork() throws Exception
{
return loadRenditionDefinition(renditionDefinitionQName);
}
}, AuthenticationUtil.getSystemUserName());
if (rendDefn == null)
{
throw new RenditionServiceException("Rendition Definition " + renditionDefinitionQName + " was not found.");
}
this.render(sourceNode, rendDefn, callback);
}
/**
* This method checks whether the specified source node is of a content class which has been registered for rendition prevention.
*
* @param sourceNode the node to check.
* @throws RenditionPreventedException if the source node is configured for rendition prevention.
* @since 4.0.1
* @see RenditionPreventionRegistry
*/
private void checkSourceNodeForPreventionClass(NodeRef sourceNode)
{
// A node's content class is its type and all its aspects.
// We'll not check the source node for null and leave that to the rendering action.
if (sourceNode != null && nodeService.exists(sourceNode))
{
Set<QName> nodeContentClasses = nodeService.getAspects(sourceNode);
nodeContentClasses.add(nodeService.getType(sourceNode));
for (QName contentClass : nodeContentClasses)
{
if (renditionPreventionRegistry.isContentClassRegistered(contentClass))
{
StringBuilder msg = new StringBuilder();
msg.append("Node ").append(sourceNode)
.append(" cannot be renditioned as it is of class ").append(contentClass);
if (log.isDebugEnabled())
{
log.debug(msg.toString());
}
throw new RenditionPreventedException(msg.toString());
}
}
}
}
/**
* This method delegates the execution of the specified RenditionDefinition
* to the {@link ActionService action service}.
*
* @param sourceNode the source node which is to be rendered.
* @param definition the rendition definition to be used.
* @param asynchronous <code>true</code> for asynchronous execution,
* <code>false</code> for synchronous.
* @return the ChildAssociationRef whose child is the rendition node.
*/
private ChildAssociationRef executeRenditionAction(NodeRef sourceNode,
RenditionDefinition definition, boolean asynchronous)
{
if (log.isDebugEnabled())
{
StringBuilder msg = new StringBuilder();
if (asynchronous)
{
msg.append("Asynchronously");
}
else
{
msg.append("Synchronously");
}
msg.append(" rendering node ").append(sourceNode)
.append(" with ").append(definition.getRenditionName());
log.debug(msg.toString());
}
final boolean checkConditions = true;
actionService.executeAction(definition, sourceNode, checkConditions, asynchronous);
ChildAssociationRef result = (ChildAssociationRef)definition.getParameterValue(ActionExecuter.PARAM_RESULT);
return result;
}
/*
* (non-Javadoc)
* @see
* org.alfresco.service.cmr.rendition.RenditionService#saveRenditionDefinition
* (org.alfresco.service.cmr.rendition.RenditionDefinition)
*/
public void saveRenditionDefinition(RenditionDefinition renderingAction)
{
this.renditionDefinitionPersister.saveRenditionDefinition(renderingAction);
}
/*
* @see
* org.alfresco.service.cmr.rendition.RenditionService#loadRenderingAction
* (org.alfresco.service.namespace.QName)
*/
public RenditionDefinition loadRenditionDefinition(QName renditionDefinitionName)
{
return this.renditionDefinitionPersister.loadRenditionDefinition(renditionDefinitionName);
}
/*
* (non-Javadoc)
* @see org.alfresco.service.cmr.rendition.RenditionService#loadRenditionDefinitions()
*/
public List<RenditionDefinition> loadRenditionDefinitions()
{
return this.renditionDefinitionPersister.loadRenditionDefinitions();
}
/*
* (non-Javadoc)
* @see
* org.alfresco.service.cmr.rendition.RenditionService#loadRenderingActions
* (java.lang.String)
*/
public List<RenditionDefinition> loadRenditionDefinitions(String renditionEngineName)
{
return this.renditionDefinitionPersister.loadRenditionDefinitions(renditionEngineName);
}
/*
* (non-Javadoc)
* @see
* org.alfresco.service.cmr.rendition.RenditionService#getRenditions(org
* .alfresco.service.cmr.repository.NodeRef)
*/
public List<ChildAssociationRef> getRenditions(NodeRef node)
{
List<ChildAssociationRef> result = Collections.emptyList();
// Check that the node has the renditioned aspect applied
if (nodeService.hasAspect(node, RenditionModel.ASPECT_RENDITIONED) == true)
{
// Get all the renditions that match the given rendition name
result = nodeService.getChildAssocs(node, RenditionModel.ASSOC_RENDITION, RegexQNamePattern.MATCH_ALL);
result = removeArchivedRenditionsFrom(result);
}
return result;
}
private List<ChildAssociationRef> removeArchivedRenditionsFrom(List<ChildAssociationRef> renditionAssocs)
{
// This is a workaround for a bug in the NodeService (no JIRA number yet) whereby a call to
// nodeService.getChildAssocs can return all children, including children in the archive store.
List<ChildAssociationRef> result = new ArrayList<ChildAssociationRef>();
for (ChildAssociationRef chAssRef : renditionAssocs)
{
// If the rendition has *not* been deleted, then it should remain in the result list.
if (chAssRef.getChildRef().getStoreRef().equals(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE) == false)
{
result.add(chAssRef);
}
}
return result;
}
/*
* (non-Javadoc)
* @see
* org.alfresco.service.cmr.rendition.RenditionService#getRenditions(org
* .alfresco.service.cmr.repository.NodeRef, java.lang.String)
*/
public List<ChildAssociationRef> getRenditions(NodeRef node, String mimeTypePrefix)
{
List<ChildAssociationRef> allRenditions = this.getRenditions(node);
List<ChildAssociationRef> filteredResults = new ArrayList<ChildAssociationRef>();
for (ChildAssociationRef chAssRef : allRenditions)
{
NodeRef renditionNode = chAssRef.getChildRef();
QName contentProperty = ContentModel.PROP_CONTENT;
Serializable contentPropertyName = nodeService.getProperty(renditionNode,
ContentModel.PROP_CONTENT_PROPERTY_NAME);
if (contentPropertyName != null)
{
contentProperty = (QName) contentPropertyName;
}
ContentReader reader = contentService.getReader(renditionNode, contentProperty);
if (reader != null && reader.exists())
{
String readerMimeType = reader.getMimetype();
if (readerMimeType.startsWith(mimeTypePrefix))
{
filteredResults.add(chAssRef);
}
}
}
filteredResults = removeArchivedRenditionsFrom(filteredResults);
return filteredResults;
}
/*
* (non-Javadoc)
* @see org.alfresco.service.cmr.rendition.RenditionService#getRenditionByName(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName)
*/
public ChildAssociationRef getRenditionByName(NodeRef node, QName renditionName)
{
List<ChildAssociationRef> renditions = Collections.emptyList();
// Check that the node has the renditioned aspect applied
if (nodeService.hasAspect(node, RenditionModel.ASPECT_RENDITIONED) == true)
{
// Get all the renditions that match the given rendition name -
// there should only be 1 (or 0)
renditions = this.nodeService.getChildAssocs(node, RenditionModel.ASSOC_RENDITION, renditionName);
renditions = this.removeArchivedRenditionsFrom(renditions);
}
if (renditions.isEmpty())
{
return null;
}
else
{
if (renditions.size() > 1 && log.isDebugEnabled())
{
log.debug("Unexpectedly found " + renditions.size() + " renditions of name " + renditionName + " on node " + node);
}
return renditions.get(0);
}
}
/*
* (non-Javadoc)
* @see org.alfresco.service.cmr.rendition.RenditionService#isRendition(org.alfresco.service.cmr.repository.NodeRef)
*/
public boolean isRendition(NodeRef node)
{
final QName aspectToCheckFor = RenditionModel.ASPECT_RENDITION;
Set<QName> existingAspects = nodeService.getAspects(node);
for (QName nextAspect : existingAspects)
{
if (nextAspect.equals(aspectToCheckFor) || dictionaryService.isSubClass(nextAspect, aspectToCheckFor))
{
return true;
}
}
return false;
}
/*
* (non-Javadoc)
* @see org.alfresco.service.cmr.rendition.RenditionService#getSourceNode(org.alfresco.service.cmr.repository.NodeRef)
*/
public ChildAssociationRef getSourceNode(NodeRef renditionNode)
{
// In normal circumstances only a node which is itself a rendition can have
// a source node - as linked by the rn:rendition association.
//
// However there are some circumstances where a node which is not
// technically a rendition can still have a source. One such example is the case
// of thumbnail nodes created in a pre-3.3 Alfresco which have not been patched
// to have the correct rendition aspect applied.
// This will also occur *during* execution of the webscript patch and so the
// decision was made not to throw an exception or log a warning if such a
// situation is encountered.
// A rendition node should have 1 and only 1 source node.
List<ChildAssociationRef> parents = nodeService.getParentAssocs(renditionNode,
RenditionModel.ASSOC_RENDITION, RegexQNamePattern.MATCH_ALL);
if (parents.size() > 1)
{
StringBuilder msg = new StringBuilder();
msg.append("NodeRef ")
.append(renditionNode)
.append(" unexpectedly has ")
.append(parents.size())
.append(" rendition parents.");
if (log.isWarnEnabled())
{
log.warn(msg.toString());
}
throw new RenditionServiceException(msg.toString());
}
else
{
return parents.isEmpty() ? null : parents.get(0);
}
}
}