Files
alfresco-community-repo/source/java/org/alfresco/repo/virtual/bundle/VirtualNodeServiceExtension.java
Alan Davis 84841e063f Merged 5.2.N (5.2.1) to HEAD (5.2)
125788 rmunteanu: Merged 5.1.N (5.1.2) to 5.2.N (5.2.1)
      125606 rmunteanu: Merged 5.1.1 (5.1.1) to 5.1.N (5.1.2)
         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/HEAD/root@127810 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
2016-06-03 17:08:06 +00:00

1528 lines
61 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.virtual.bundle;
import java.io.IOException;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.download.DownloadModel;
import org.alfresco.repo.node.archive.NodeArchiveService;
import org.alfresco.repo.node.db.traitextender.NodeServiceExtension;
import org.alfresco.repo.node.db.traitextender.NodeServiceTrait;
import org.alfresco.repo.virtual.ActualEnvironment;
import org.alfresco.repo.virtual.ActualEnvironmentException;
import org.alfresco.repo.virtual.VirtualContentModel;
import org.alfresco.repo.virtual.VirtualizationException;
import org.alfresco.repo.virtual.config.NodeRefExpression;
import org.alfresco.repo.virtual.ref.GetActualNodeRefMethod;
import org.alfresco.repo.virtual.ref.GetAspectsMethod;
import org.alfresco.repo.virtual.ref.GetParentReferenceMethod;
import org.alfresco.repo.virtual.ref.NodeProtocol;
import org.alfresco.repo.virtual.ref.Protocols;
import org.alfresco.repo.virtual.ref.Reference;
import org.alfresco.repo.virtual.store.VirtualStore;
import org.alfresco.repo.virtual.template.FilingData;
import org.alfresco.service.cmr.dictionary.InvalidAspectException;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.InvalidChildAssociationRefException;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.InvalidStoreRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeRef.Status;
import org.alfresco.service.cmr.repository.Path;
import org.alfresco.service.cmr.repository.StoreExistsException;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.QNamePattern;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class VirtualNodeServiceExtension extends VirtualSpringBeanExtension<NodeServiceExtension, NodeServiceTrait>
implements NodeServiceExtension
{
private static Log logger = LogFactory.getLog(VirtualNodeServiceExtension.class);
private VirtualStore smartStore;
private ActualEnvironment environment;
private NodeRefExpression downloadAssociationsFolder;
public VirtualNodeServiceExtension()
{
super(NodeServiceTrait.class);
}
public void setEnvironment(ActualEnvironment environment)
{
this.environment = environment;
}
public void setSmartStore(VirtualStore smartStore)
{
this.smartStore = smartStore;
}
@Override
public boolean hasAspect(NodeRef nodeRef, QName aspectQName)
{
if (Reference.isReference(nodeRef))
{
boolean isNodeProtocol = Reference.fromNodeRef(nodeRef).getProtocol().equals(Protocols.NODE.protocol);
if (VirtualContentModel.ASPECT_VIRTUAL.equals(aspectQName) || ContentModel.ASPECT_TITLED.equals(aspectQName))
{
return !isNodeProtocol;
}
else if (VirtualContentModel.ASPECT_VIRTUAL_DOCUMENT.equals(aspectQName))
{
return isNodeProtocol;
}
else
{
Reference reference = Reference.fromNodeRef(nodeRef);
NodeRef actualNodeRef = reference.execute(new GetActualNodeRefMethod(environment));
return getTrait().hasAspect(actualNodeRef,
aspectQName);
}
}
else
{
return getTrait().hasAspect(nodeRef,
aspectQName);
}
}
@Override
public QName getType(NodeRef nodeRef)
{
if (Reference.isReference(nodeRef))
{
QName type = smartStore.getType(Reference.fromNodeRef(nodeRef));
return type;
}
else
{
return getTrait().getType(nodeRef);
}
}
private Map<QName, Serializable> getVirtualProperties(Reference reference)
{
return smartStore.getProperties(reference);
}
@Override
public Map<QName, Serializable> getProperties(NodeRef nodeRef)
{
if (Reference.isReference(nodeRef))
{
return getVirtualProperties(Reference.fromNodeRef(nodeRef));
}
else
{
return getTrait().getProperties(nodeRef);
}
}
@Override
public Serializable getProperty(NodeRef nodeRef, QName qname)
{
if (Reference.isReference(nodeRef))
{
return getVirtualProperties(Reference.fromNodeRef(nodeRef)).get(qname);
}
else
{
return getTrait().getProperty(nodeRef,
qname);
}
}
@Override
public Set<QName> getAspects(NodeRef nodeRef)
{
NodeServiceTrait theTrait = getTrait();
if (Reference.isReference(nodeRef))
{
Reference vRef = Reference.fromNodeRef(nodeRef);
GetAspectsMethod method = new GetAspectsMethod(theTrait,
environment);
return vRef.execute(method);
}
else
{
return theTrait.getAspects(nodeRef);
}
}
@Override
public Path getPath(NodeRef nodeRef)
{
if (Reference.isReference(nodeRef))
{
return Reference.fromNodeRef(nodeRef).execute(new GetPathMethod(smartStore,
environment));
}
else
{
return getTrait().getPath(nodeRef);
}
}
@Override
public List<Path> getPaths(NodeRef nodeRef, boolean primaryOnly)
{
if (Reference.isReference(nodeRef))
{
Reference reference = Reference.fromNodeRef(nodeRef);
NodeRef actualNodeRef = reference.execute(new GetActualNodeRefMethod(environment));
return getTrait().getPaths(actualNodeRef,
primaryOnly);
}
else
{
return getTrait().getPaths(nodeRef,
primaryOnly);
}
}
@Override
public boolean exists(NodeRef nodeRef)
{
if (Reference.isReference(nodeRef))
{
// For now references last forever (i.e. there is no expiration
// mechanism )
return true;
}
else
{
return getTrait().exists(nodeRef);
}
}
@Override
public ChildAssociationRef createNode(NodeRef parentRef, QName assocTypeQName, QName assocQName,
QName nodeTypeQName)
{
if (Reference.isReference(parentRef))
{
return createNode(parentRef,
assocTypeQName,
assocQName,
nodeTypeQName,
Collections.<QName, Serializable> emptyMap());
}
else
{
QName materialAssocQName = materializeAssocQName(assocQName);
return getTrait().createNode(parentRef,
assocTypeQName,
materialAssocQName,
nodeTypeQName);
}
}
@Override
public ChildAssociationRef createNode(NodeRef parentRef, QName assocTypeQName, QName assocQName,
QName nodeTypeQName, Map<QName, Serializable> properties)
{
NodeServiceTrait theTrait = getTrait();
if (Reference.isReference(parentRef) && !isVirtualContextFolder(parentRef,
environment))
{
// CM-533 Suppress options to create folders in a virtual folder
// (repo)
if (environment.isSubClass(nodeTypeQName,
ContentModel.TYPE_FOLDER))
{
throw new VirtualizationException("The creation of folders within virtual folders is disabled.");
}
try
{
Reference parentReference = Reference.fromNodeRef(parentRef);
FilingData filingData = smartStore.createFilingData(parentReference,
assocTypeQName,
assocQName,
nodeTypeQName,
properties);
NodeRef childParentNodeRef = filingData.getFilingNodeRef();
if (childParentNodeRef != null)
{
Map<QName, Serializable> filingDataProperties = filingData.getProperties();
QName changedAssocQName = assocQName;
if (filingDataProperties.containsKey(ContentModel.PROP_NAME))
{
String fileName = (String) filingDataProperties.get(ContentModel.PROP_NAME);
String changedFileName = handleExistingFile(childParentNodeRef,
fileName);
if (!changedFileName.equals(fileName))
{
filingDataProperties.put(ContentModel.PROP_NAME,
changedFileName);
QName filingDataAssocQName = filingData.getAssocQName();
changedAssocQName = QName.createQName(filingDataAssocQName.getNamespaceURI(),
QName.createValidLocalName(changedFileName));
}
}
ChildAssociationRef actualChildAssocRef = theTrait.createNode(childParentNodeRef,
filingData.getAssocTypeQName(),
changedAssocQName == null
? filingData.getAssocQName()
: changedAssocQName,
filingData.getNodeTypeQName(),
filingDataProperties);
Reference nodeProtocolChildRef = NodeProtocol.newReference(actualChildAssocRef.getChildRef(),
parentReference);
QName vChildAssocQName = QName
.createQNameWithValidLocalName(VirtualContentModel.VIRTUAL_CONTENT_MODEL_1_0_URI,
actualChildAssocRef.getQName().getLocalName());
ChildAssociationRef childAssocRef = new ChildAssociationRef(actualChildAssocRef.getTypeQName(),
parentRef,
vChildAssocQName,
nodeProtocolChildRef.toNodeRef());
Set<QName> aspects = filingData.getAspects();
for (QName aspect : aspects)
{
theTrait.addAspect(actualChildAssocRef.getChildRef(),
aspect,
filingDataProperties);
}
return childAssocRef;
}
else
{
throw new InvalidNodeRefException("Can not create node using parent ",
parentRef);
}
}
catch (VirtualizationException e)
{
throw new InvalidNodeRefException("Could not create node in virtual context.",
parentRef,
e);
}
}
else
{
QName materialAssocQName = materializeAssocQName(assocQName);
if (isVirtualContextFolder(parentRef,
environment))
{
parentRef = smartStore.materializeIfPossible(parentRef);
}
return theTrait.createNode(parentRef,
assocTypeQName,
materialAssocQName,
nodeTypeQName,
properties);
}
}
private QName materializeAssocQName(QName assocQName)
{
// Version nodes have too long assocQNames so we try
// to detect references with "material" protocols in order to
// replace the assocQNames with material correspondents.
try
{
String lName = assocQName.getLocalName();
NodeRef nrAssocQName = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE,
lName);
if (Reference.isReference(nrAssocQName))
{
nrAssocQName = smartStore.materializeIfPossible(nrAssocQName);
QName materialAssocQName = QName.createQName(assocQName.getNamespaceURI(),
nrAssocQName.getId());
return materialAssocQName;
}
else
{
return assocQName;
}
}
catch (VirtualizationException e)
{
// We assume it can not be put through the
// isReference-virtualize-materialize.
if (logger.isDebugEnabled())
{
logger.debug("Defaulting on materializeAssocQName due to error.",
e);
}
return assocQName;
}
}
private String handleExistingFile(NodeRef parentNodeRef, String fileName)
{
NodeServiceTrait actualNodeService = getTrait();
NodeRef existingFile = actualNodeService.getChildByName(parentNodeRef,
ContentModel.ASSOC_CONTAINS,
fileName);
if (existingFile != null)
{
int counter = 1;
int dotIndex;
String tmpFilename = "";
final String dot = ".";
final String hyphen = "-";
while (existingFile != null)
{
int beforeCounter = fileName.lastIndexOf(hyphen);
dotIndex = fileName.lastIndexOf(dot);
if (dotIndex == 0)
{
// File didn't have a proper 'name' instead it had just a
// suffix and
// started with a ".", create "1.txt"
tmpFilename = counter + fileName;
}
else if (dotIndex > 0)
{
if (beforeCounter > 0 && beforeCounter < dotIndex)
{
// does file have counter in it's name or it just
// contains -1
String originalFileName = fileName.substring(0,
beforeCounter)
+ fileName.substring(dotIndex);
boolean doesOriginalFileExist = actualNodeService.getChildByName(parentNodeRef,
ContentModel.ASSOC_CONTAINS,
originalFileName) != null;
if (doesOriginalFileExist)
{
String counterStr = fileName.substring(beforeCounter + 1,
dotIndex);
try
{
int parseInt = DefaultTypeConverter.INSTANCE.intValue(counterStr);
counter = parseInt + 1;
fileName = fileName.substring(0,
beforeCounter)
+ fileName.substring(dotIndex);
dotIndex = fileName.lastIndexOf(dot);
}
catch (NumberFormatException ex)
{
// "-" is not before counter
}
}
}
tmpFilename = fileName.substring(0,
dotIndex)
+ hyphen + counter + fileName.substring(dotIndex);
}
else
{
// Filename didn't contain a dot at all, create "filename-1"
tmpFilename = fileName + hyphen + counter;
}
existingFile = actualNodeService.getChildByName(parentNodeRef,
ContentModel.ASSOC_CONTAINS,
tmpFilename);
counter++;
}
fileName = tmpFilename;
}
return fileName;
}
@Override
public List<ChildAssociationRef> getParentAssocs(NodeRef nodeRef)
{
if (Reference.isReference(nodeRef))
{
return getParentAssocs(nodeRef,
RegexQNamePattern.MATCH_ALL,
RegexQNamePattern.MATCH_ALL);
}
else
{
return getTrait().getParentAssocs(nodeRef);
}
}
@Override
public List<ChildAssociationRef> getParentAssocs(NodeRef nodeRef, QNamePattern typeQNamePattern,
QNamePattern qnamePattern)
{
NodeServiceTrait theTrait = getTrait();
if (Reference.isReference(nodeRef))
{
Reference reference = Reference.fromNodeRef(nodeRef);
Reference parent = reference.execute(new GetParentReferenceMethod());
if (parent == null)
{
return theTrait.getParentAssocs(reference.execute(new GetActualNodeRefMethod(environment)),
typeQNamePattern,
qnamePattern);
}
else
{
if (typeQNamePattern.isMatch(ContentModel.ASSOC_CONTAINS))
{
Reference parentsParent = parent.execute(new GetParentReferenceMethod());
NodeRef parentNodeRef = parent.toNodeRef();
if (parentsParent == null)
{
parentNodeRef = parent.execute(new GetActualNodeRefMethod(environment));
}
NodeRef referenceNodeRef = reference.toNodeRef();
Map<QName, Serializable> properties = smartStore.getProperties(reference);
Serializable name = properties.get(ContentModel.PROP_NAME);
QName assocChildName = QName
.createQNameWithValidLocalName(VirtualContentModel.VIRTUAL_CONTENT_MODEL_1_0_URI,
name.toString());
if (qnamePattern.isMatch(assocChildName))
{
ChildAssociationRef assoc = new ChildAssociationRef(ContentModel.ASSOC_CONTAINS,
parentNodeRef,
assocChildName,
referenceNodeRef);
return Arrays.asList(assoc);
}
else
{
return Collections.emptyList();
}
}
else
{
return Collections.emptyList();
}
}
}
else
{
return theTrait.getParentAssocs(nodeRef,
typeQNamePattern,
qnamePattern);
}
}
@Override
public List<ChildAssociationRef> getChildAssocs(NodeRef nodeRef)
{
NodeServiceTrait theTrait = getTrait();
boolean canVirtualize = canVirtualizeAssocNodeRef(nodeRef);
if (canVirtualize)
{
Reference reference = smartStore.virtualize(nodeRef);
List<ChildAssociationRef> virtualAssociations = smartStore.getChildAssocs(reference,
RegexQNamePattern.MATCH_ALL,
RegexQNamePattern.MATCH_ALL,
Integer.MAX_VALUE,
false);
List<ChildAssociationRef> associations = new LinkedList<>(virtualAssociations);
if (smartStore.canMaterialize(reference))
{
NodeRef materialReference = smartStore.materialize(reference);
List<ChildAssociationRef> actualAssociations = theTrait.getChildAssocs(materialReference,
RegexQNamePattern.MATCH_ALL,
RegexQNamePattern.MATCH_ALL,
Integer.MAX_VALUE,
false);
associations.addAll(actualAssociations);
}
return associations;
}
else
{
return theTrait.getChildAssocs(nodeRef);
}
}
@Override
public List<ChildAssociationRef> getChildAssocs(NodeRef nodeRef, QNamePattern typeQNamePattern,
QNamePattern qnamePattern)
{
NodeServiceTrait theTrait = getTrait();
boolean canVirtualize = canVirtualizeAssocNodeRef(nodeRef);
if (canVirtualize)
{
Reference reference = smartStore.virtualize(nodeRef);
List<ChildAssociationRef> virtualAssociations = smartStore.getChildAssocs(reference,
typeQNamePattern,
qnamePattern,
Integer.MAX_VALUE,
false);
List<ChildAssociationRef> associations = new LinkedList<>(virtualAssociations);
if (smartStore.canMaterialize(reference))
{
NodeRef materialReference = smartStore.materialize(reference);
List<ChildAssociationRef> actualAssociations = theTrait.getChildAssocs(materialReference,
typeQNamePattern,
qnamePattern);
associations.addAll(actualAssociations);
}
return associations;
}
else
{
return theTrait.getChildAssocs(nodeRef,
typeQNamePattern,
qnamePattern);
}
}
@Override
public List<ChildAssociationRef> getChildAssocs(NodeRef nodeRef, QNamePattern typeQNamePattern,
QNamePattern qnamePattern, int maxResults, boolean preload)
{
NodeServiceTrait theTrait = getTrait();
boolean canVirtualize = canVirtualizeAssocNodeRef(nodeRef);
if (canVirtualize)
{
Reference reference = smartStore.virtualize(nodeRef);
List<ChildAssociationRef> virtualAssociations = smartStore.getChildAssocs(reference,
typeQNamePattern,
qnamePattern,
maxResults,
preload);
List<ChildAssociationRef> associations = new LinkedList<>(virtualAssociations);
if (associations.size() < maxResults)
{
if (smartStore.canMaterialize(reference))
{
NodeRef materialReference = smartStore.materialize(reference);
List<ChildAssociationRef> actualAssociations = theTrait.getChildAssocs(materialReference,
typeQNamePattern,
qnamePattern,
maxResults - associations
.size(),
preload);
associations.addAll(actualAssociations);
}
}
return associations;
}
else
{
return theTrait.getChildAssocs(nodeRef,
typeQNamePattern,
qnamePattern,
maxResults,
preload);
}
}
private boolean canVirtualizeAssocNodeRef(NodeRef nodeRef)
{
boolean canVirtualize = nodeRef.getId().contains("solr") ? false : smartStore.canVirtualize(nodeRef);
return canVirtualize;
}
@Override
public List<ChildAssociationRef> getChildAssocs(NodeRef nodeRef, QNamePattern typeQNamePattern,
QNamePattern qnamePattern, boolean preload)
{
NodeServiceTrait theTrait = getTrait();
boolean canVirtualize = canVirtualizeAssocNodeRef(nodeRef);
if (canVirtualize)
{
Reference reference = smartStore.virtualize(nodeRef);
List<ChildAssociationRef> virtualAssociations = smartStore.getChildAssocs(reference,
typeQNamePattern,
qnamePattern,
Integer.MAX_VALUE,
preload);
List<ChildAssociationRef> associations = new LinkedList<>(virtualAssociations);
if (smartStore.canMaterialize(reference))
{
NodeRef materialReference = smartStore.materialize(reference);
List<ChildAssociationRef> actualAssociations = theTrait.getChildAssocs(materialReference,
typeQNamePattern,
qnamePattern,
preload);
associations.addAll(actualAssociations);
}
return associations;
}
else
{
return theTrait.getChildAssocs(nodeRef,
typeQNamePattern,
qnamePattern,
preload);
}
}
@Override
public List<ChildAssociationRef> getChildAssocs(NodeRef nodeRef, Set<QName> childNodeTypeQNames)
{
NodeServiceTrait theTrait = getTrait();
boolean canVirtualize = canVirtualizeAssocNodeRef(nodeRef);
if (canVirtualize)
{
Reference reference = smartStore.virtualize(nodeRef);
List<ChildAssociationRef> virtualAssociations = smartStore.getChildAssocs(reference,
childNodeTypeQNames);
List<ChildAssociationRef> associations = new LinkedList<>(virtualAssociations);
if (smartStore.canMaterialize(reference))
{
NodeRef materialReference = smartStore.materialize(reference);
List<ChildAssociationRef> actualAssociations = theTrait.getChildAssocs(materialReference,
childNodeTypeQNames);
associations.addAll(actualAssociations);
}
return associations;
}
else
{
return theTrait.getChildAssocs(nodeRef,
childNodeTypeQNames);
}
}
@Override
public List<ChildAssociationRef> getChildAssocsByPropertyValue(NodeRef nodeRef, QName propertyQName,
Serializable value)
{
NodeServiceTrait theTrait = getTrait();
boolean canVirtualize = canVirtualizeAssocNodeRef(nodeRef);
if (canVirtualize)
{
Reference reference = smartStore.virtualize(nodeRef);
List<ChildAssociationRef> virtualAssociations = smartStore.getChildAssocsByPropertyValue(reference,
propertyQName,
value);
List<ChildAssociationRef> associations = new LinkedList<>(virtualAssociations);
if (smartStore.canMaterialize(reference))
{
NodeRef materialReference = smartStore.materialize(reference);
List<ChildAssociationRef> actualAssociations = theTrait.getChildAssocsByPropertyValue(materialReference,
propertyQName,
value);
associations.addAll(actualAssociations);
}
return associations;
}
else
{
return theTrait.getChildAssocsByPropertyValue(nodeRef,
propertyQName,
value);
}
}
@Override
public NodeRef getChildByName(NodeRef nodeRef, QName assocTypeQName, String childName)
{
// TODO: optimize
if (smartStore.canVirtualize(nodeRef))
{
Reference virtualNode = smartStore.virtualize(nodeRef);
Reference theChild = smartStore.getChildByName(virtualNode,
assocTypeQName,
childName);
if (theChild != null)
{
NodeRef childNodeRef = theChild.toNodeRef();
return childNodeRef;
}
if (smartStore.isVirtual(nodeRef))
{
return null;
}
}
// TODO: add virtualizable enabler
nodeRef = materializeIfPossible(nodeRef);
return getTrait().getChildByName(nodeRef,
assocTypeQName,
childName);
}
@Override
public ChildAssociationRef getPrimaryParent(NodeRef nodeRef)
{
if (Reference.isReference(nodeRef))
{
Reference reference = Reference.fromNodeRef(nodeRef);
Reference parent = reference.execute(new GetParentReferenceMethod());
if (parent == null)
{
return getTrait().getPrimaryParent(reference.execute(new GetActualNodeRefMethod(environment)));
}
else
{
Reference parentsParent = parent.execute(new GetParentReferenceMethod());
NodeRef parentNodeRef = parent.toNodeRef();
if (parentsParent == null)
{
parentNodeRef = parent.execute(new GetActualNodeRefMethod(environment));
}
NodeRef referenceNodeRef = reference.toNodeRef();
Map<QName, Serializable> refProperties = smartStore.getProperties(reference);
Serializable childName = refProperties.get(ContentModel.PROP_NAME);
QName childAssocQName = QName
.createQNameWithValidLocalName(VirtualContentModel.VIRTUAL_CONTENT_MODEL_1_0_URI,
childName.toString());
ChildAssociationRef assoc = new ChildAssociationRef(ContentModel.ASSOC_CONTAINS,
parentNodeRef,
childAssocQName,
referenceNodeRef,
true,
-1);
return assoc;
}
}
else
{
return getTrait().getPrimaryParent(nodeRef);
}
}
public void setDownloadAssociationsFolder(NodeRefExpression downloadAssocaiationsFolder)
{
this.downloadAssociationsFolder = downloadAssocaiationsFolder;
}
private void cleanUpDownloadTargetAssocs(NodeRef sourceNodeRef)
{
NodeRef tempFolderNodeRef = downloadAssociationsFolder.resolve();
if (tempFolderNodeRef == null)
{
return;
}
String tempFileName = sourceNodeRef.getId();
NodeRef tempFileNodeRef = environment.getChildByName(tempFolderNodeRef,
ContentModel.ASSOC_CONTAINS,
tempFileName);
if (tempFileNodeRef == null)
{
return;
}
environment.delete(tempFileNodeRef);
}
private List<AssociationRef> getDownloadTargetAssocs(NodeRef sourceNodeRef)
{
try
{
List<AssociationRef> result = new ArrayList<AssociationRef>();
NodeRef tempFolderNodeRef = downloadAssociationsFolder.resolve();
if (tempFolderNodeRef == null)
{
return result;
}
String tempFileName = sourceNodeRef.getId();
NodeRef tempFileNodeRef = environment.getChildByName(tempFolderNodeRef,
ContentModel.ASSOC_CONTAINS,
tempFileName);
if (tempFileNodeRef == null)
{
return result;
}
List<String> readLines = IOUtils.readLines(environment.openContentStream(tempFileNodeRef),
StandardCharsets.UTF_8);
for (String line : readLines)
{
NodeRef targetRef = new NodeRef(line);
AssociationRef assocRef = new AssociationRef(sourceNodeRef,
DownloadModel.ASSOC_REQUESTED_NODES,
targetRef);
result.add(assocRef);
}
return result;
}
catch (IOException e)
{
throw new VirtualizationException(e);
}
}
private void createDownloadAssociation(NodeRef sourceNodeRef, NodeRef targetRef)
{
NodeRef tempFolderNodeRef = downloadAssociationsFolder.resolve(true);
String tempFileName = sourceNodeRef.getId();
NodeRef tempFileNodeRef = environment.getChildByName(tempFolderNodeRef,
ContentModel.ASSOC_CONTAINS,
tempFileName);
if (tempFileNodeRef == null)
{
FileInfo newTempFileInfo = environment.create(tempFolderNodeRef,
tempFileName,
ContentModel.TYPE_CONTENT);
tempFileNodeRef = newTempFileInfo.getNodeRef();
ContentWriter writer = environment.getWriter(tempFileNodeRef,
ContentModel.PROP_CONTENT,
true);
writer.setMimetype("text/plain");
writer.putContent(targetRef.toString());
}
else
{
ContentWriter writer = environment.getWriter(tempFileNodeRef,
ContentModel.PROP_CONTENT,
true);
try
{
List<String> readLines = IOUtils.readLines(environment.openContentStream(tempFileNodeRef),
StandardCharsets.UTF_8);
String targetRefString = targetRef.toString();
if (!readLines.contains(targetRefString))
{
readLines.add(targetRefString);
}
String text = "";
for (String line : readLines)
{
if (text.isEmpty())
{
text = line;
}
else
{
text = text + IOUtils.LINE_SEPARATOR + line;
}
}
writer.putContent(text);
}
catch (IOException e)
{
throw new ActualEnvironmentException(e);
}
}
}
@Override
public List<AssociationRef> getTargetAssocs(NodeRef sourceRef, QNamePattern qnamePattern)
{
NodeServiceTrait theTrait = getTrait();
List<AssociationRef> targetAssocs = null;
if (Reference.isReference(sourceRef))
{
Reference reference = Reference.fromNodeRef(sourceRef);
if (smartStore.canMaterialize(reference))
{
NodeRef materializedReferece = smartStore.materialize(reference);
targetAssocs = theTrait.getTargetAssocs(materializedReferece,
qnamePattern);
}
else
{
targetAssocs = new LinkedList<>();
}
}
else
{
targetAssocs = theTrait.getTargetAssocs(sourceRef,
qnamePattern);
}
List<AssociationRef> virtualizedIfNeededTargetAssocs = null;
if (Reference.isReference(sourceRef))
{
virtualizedIfNeededTargetAssocs = new LinkedList<>();
Reference sourceReference = Reference.fromNodeRef(sourceRef);
for (AssociationRef associationRef : targetAssocs)
{
NodeRef targetNodeRef = associationRef.getTargetRef();
Reference targetReference = NodeProtocol.newReference(targetNodeRef,
sourceReference
.execute(new GetParentReferenceMethod()));
AssociationRef virtualAssocRef = new AssociationRef(associationRef.getId(),
sourceRef,
associationRef.getTypeQName(),
targetReference.toNodeRef(targetNodeRef
.getStoreRef()));
virtualizedIfNeededTargetAssocs.add(virtualAssocRef);
}
}
else
{
virtualizedIfNeededTargetAssocs = targetAssocs;
if (DownloadModel.TYPE_DOWNLOAD.equals(environment.getType(sourceRef)))
{
if (qnamePattern.isMatch(DownloadModel.ASSOC_REQUESTED_NODES))
{
List<AssociationRef> virtualTargetAssocs = getDownloadTargetAssocs(sourceRef);
virtualizedIfNeededTargetAssocs.addAll(virtualTargetAssocs);
}
}
}
return virtualizedIfNeededTargetAssocs;
}
@Override
public List<AssociationRef> getSourceAssocs(NodeRef targetRef, QNamePattern qnamePattern)
{
NodeServiceTrait theTrait = getTrait();
if (Reference.isReference(targetRef))
{
Reference reference = Reference.fromNodeRef(targetRef);
List<AssociationRef> materialAssocs = new ArrayList<AssociationRef>();
if (smartStore.canMaterialize(reference))
{
List<AssociationRef> sourceAssocs = theTrait.getSourceAssocs(smartStore.materialize(reference),
qnamePattern);
for (AssociationRef associationRef : sourceAssocs)
{
NodeRef sourceRef = associationRef.getSourceRef();
Reference sourceReference = NodeProtocol.newReference(sourceRef,
reference
.execute(new GetParentReferenceMethod()));
AssociationRef virtualAssocRef = new AssociationRef(associationRef.getId(),
sourceReference.toNodeRef(),
associationRef.getTypeQName(),
targetRef);
materialAssocs.add(virtualAssocRef);
}
}
// Download sources are deliberately not virtualized due to
// performance and complexity issues.
// However they could be detected using
// if (qnamePattern.isMatch(DownloadModel.ASSOC_REQUESTED_NODES))
return materialAssocs;
}
else
{
return theTrait.getSourceAssocs(targetRef,
qnamePattern);
}
}
@Override
public ChildAssociationRef moveNode(NodeRef nodeToMoveRef, NodeRef newParentRef, QName assocTypeQName,
QName assocQName)
{
if (Reference.isReference(nodeToMoveRef) || Reference.isReference(newParentRef))
{
throw new UnsupportedOperationException("Unsuported operation for virtual source or destination");
}
else
{
return getTrait().moveNode(nodeToMoveRef,
newParentRef,
assocTypeQName,
assocQName);
}
}
@Override
public void addProperties(NodeRef nodeRef, Map<QName, Serializable> properties)
{
if (Reference.isReference(nodeRef))
{
Reference reference = Reference.fromNodeRef(nodeRef);
NodeRef actualNodeRef = reference.execute(new GetActualNodeRefMethod(null));
getTrait().addProperties(actualNodeRef,
properties);
}
else
{
getTrait().addProperties(nodeRef,
properties);
}
}
@Override
public AssociationRef createAssociation(NodeRef sourceRef, NodeRef targetRef, QName assocTypeQName)
{
if (Reference.isReference(targetRef)
&& getTrait().getType(materializeIfPossible(sourceRef)).equals(DownloadModel.TYPE_DOWNLOAD))
{
// NOTE : this is enables downloads of virtual structures
createDownloadAssociation(sourceRef,
targetRef);
AssociationRef assocRef = new AssociationRef(sourceRef,
assocTypeQName,
targetRef);
return assocRef;
}
else
{
return getTrait().createAssociation(materializeIfPossible(sourceRef),
materializeIfPossible(targetRef),
assocTypeQName);
}
}
private List<NodeRef> materializeIfPossible(Collection<NodeRef> nodeRefs)
{
List<NodeRef> nodeRefList = new LinkedList<>();
for (NodeRef nodeRef : nodeRefs)
{
nodeRefList.add(materializeIfPossible(nodeRef));
}
return nodeRefList;
}
/**
* @deprecated use {@link VirtualStore#materializeIfPossible(NodeRef)}
* instead
*/
private NodeRef materializeIfPossible(NodeRef nodeRef)
{
if (Reference.isReference(nodeRef))
{
Reference ref = Reference.fromNodeRef(nodeRef);
if (smartStore.canMaterialize(ref))
{
return smartStore.materialize(ref);
}
}
return nodeRef;
}
@Override
public List<StoreRef> getStores()
{
return getTrait().getStores();
}
@Override
public StoreRef createStore(String protocol, String identifier) throws StoreExistsException
{
return getTrait().createStore(protocol,
identifier);
}
@Override
public void deleteStore(StoreRef storeRef)
{
getTrait().deleteStore(storeRef);
}
@Override
public boolean exists(StoreRef storeRef)
{
return getTrait().exists(storeRef);
}
@Override
public Status getNodeStatus(NodeRef nodeRef)
{
return getTrait().getNodeStatus(materializeIfPossible(nodeRef));
}
@Override
public NodeRef getNodeRef(Long nodeId)
{
return getTrait().getNodeRef(nodeId);
}
@Override
public NodeRef getRootNode(StoreRef storeRef) throws InvalidStoreRefException
{
return getTrait().getRootNode(storeRef);
}
@Override
public Set<NodeRef> getAllRootNodes(StoreRef storeRef)
{
return getTrait().getAllRootNodes(storeRef);
}
@Override
public void setChildAssociationIndex(ChildAssociationRef childAssocRef, int index)
throws InvalidChildAssociationRefException
{
getTrait().setChildAssociationIndex(childAssocRef,
index);
}
@Override
public void setType(NodeRef nodeRef, QName typeQName) throws InvalidNodeRefException
{
getTrait().setType(materializeIfPossible(nodeRef),
typeQName);
}
@Override
public void addAspect(NodeRef nodeRef, QName aspectTypeQName, Map<QName, Serializable> aspectProperties)
throws InvalidNodeRefException, InvalidAspectException
{
getTrait().addAspect(materializeIfPossible(nodeRef),
aspectTypeQName,
aspectProperties);
}
@Override
public void removeAspect(NodeRef nodeRef, QName aspectTypeQName)
throws InvalidNodeRefException, InvalidAspectException
{
getTrait().removeAspect(materializeIfPossible(nodeRef),
aspectTypeQName);
}
@Override
public void deleteNode(NodeRef nodeRef) throws InvalidNodeRefException
{
NodeServiceTrait theTrait = getTrait();
NodeRef materialNode = smartStore.materializeIfPossible(nodeRef);
boolean isDownload = DownloadModel.TYPE_DOWNLOAD.equals(theTrait.getType(materialNode));
theTrait.deleteNode(materialNode);
if (isDownload)
{
cleanUpDownloadTargetAssocs(nodeRef);
}
}
@Override
public ChildAssociationRef addChild(NodeRef parentRef, NodeRef childRef, QName assocTypeQName, QName qname)
throws InvalidNodeRefException
{
return getTrait().addChild(materializeIfPossible(parentRef),
materializeIfPossible(childRef),
assocTypeQName,
qname);
}
@Override
public List<ChildAssociationRef> addChild(Collection<NodeRef> parentRefs, NodeRef childRef, QName assocTypeQName,
QName qname) throws InvalidNodeRefException
{
return getTrait().addChild(materializeIfPossible(parentRefs),
materializeIfPossible(childRef),
assocTypeQName,
qname);
}
@Override
public void removeChild(NodeRef parentRef, NodeRef childRef) throws InvalidNodeRefException
{
getTrait().removeChild(materializeIfPossible(parentRef),
materializeIfPossible(childRef));
}
@Override
public boolean removeChildAssociation(ChildAssociationRef childAssocRef)
{
NodeServiceTrait theTrait = getTrait();
NodeRef childRef = childAssocRef.getChildRef();
if (Reference.isReference(childRef))
{
List<ChildAssociationRef> assocsToRemove = revertVirtualAssociation(childAssocRef,
theTrait,
childRef);
boolean removed = false;
if (!assocsToRemove.isEmpty())
{
for (ChildAssociationRef assoc : assocsToRemove)
{
removed = removed || theTrait.removeChildAssociation(assoc);
}
}
return removed;
}
else
{
return theTrait.removeChildAssociation(childAssocRef);
}
}
private List<ChildAssociationRef> revertVirtualAssociation(ChildAssociationRef childAssocRef,
NodeServiceTrait theTrait, NodeRef childRef)
{
childRef = smartStore.materialize(Reference.fromNodeRef(childRef));
ChildAssociationRef parent = theTrait.getPrimaryParent(childRef);
final QName assocName = childAssocRef.getQName();
List<ChildAssociationRef> assocsToRemove = theTrait.getChildAssocs(parent.getParentRef(),
childAssocRef.getTypeQName(),
new QNamePattern()
{
@Override
public boolean isMatch(QName qname)
{
return assocName
.getLocalName()
.equals(qname
.getLocalName());
}
});
return assocsToRemove;
}
@Override
public boolean removeSeconaryChildAssociation(ChildAssociationRef childAssocRef)
{
NodeServiceTrait theTrait = getTrait();
NodeRef childRef = childAssocRef.getChildRef();
if (Reference.isReference(childRef))
{
List<ChildAssociationRef> assocsToRemove = revertVirtualAssociation(childAssocRef,
theTrait,
childRef);
boolean removed = false;
if (!assocsToRemove.isEmpty())
{
for (ChildAssociationRef assoc : assocsToRemove)
{
removed = removed || theTrait.removeSeconaryChildAssociation(assoc);
}
}
return removed;
}
else
{
return theTrait.removeSeconaryChildAssociation(childAssocRef);
}
}
@Override
public boolean removeSecondaryChildAssociation(ChildAssociationRef childAssocRef)
{
NodeServiceTrait theTrait = getTrait();
NodeRef childRef = childAssocRef.getChildRef();
if (Reference.isReference(childRef))
{
List<ChildAssociationRef> assocsToRemove = revertVirtualAssociation(childAssocRef,
theTrait,
childRef);
boolean removed = false;
if (!assocsToRemove.isEmpty())
{
for (ChildAssociationRef assoc : assocsToRemove)
{
removed = removed || theTrait.removeSecondaryChildAssociation(assoc);
}
}
return removed;
}
else
{
return theTrait.removeSecondaryChildAssociation(childAssocRef);
}
}
@Override
public Long getNodeAclId(NodeRef nodeRef) throws InvalidNodeRefException
{
return getTrait().getNodeAclId(materializeIfPossible(nodeRef));
}
@Override
public void setProperties(NodeRef nodeRef, Map<QName, Serializable> properties) throws InvalidNodeRefException
{
getTrait().setProperties(materializeIfPossible(nodeRef),
properties);
}
@Override
public void setProperty(NodeRef nodeRef, QName qname, Serializable value) throws InvalidNodeRefException
{
getTrait().setProperty(materializeIfPossible(nodeRef),
qname,
value);
}
@Override
public void removeProperty(NodeRef nodeRef, QName qname) throws InvalidNodeRefException
{
getTrait().removeProperty(materializeIfPossible(nodeRef),
qname);
}
@Override
public List<ChildAssociationRef> getChildrenByName(NodeRef nodeRef, QName assocTypeQName,
Collection<String> childNames)
{
return getTrait().getChildrenByName(materializeIfPossible(nodeRef),
assocTypeQName,
childNames);
}
@Override
public Collection<ChildAssociationRef> getChildAssocsWithoutParentAssocsOfType(NodeRef nodeRef,
QName assocTypeQName)
{
NodeServiceTrait theTrait = getTrait();
boolean canVirtualize = canVirtualizeAssocNodeRef(nodeRef);
if (canVirtualize)
{
Reference reference = smartStore.virtualize(nodeRef);
Collection<ChildAssociationRef> virtualAssociations = smartStore
.getChildAssocsWithoutParentAssocsOfType(reference,
assocTypeQName);
List<ChildAssociationRef> associations = new LinkedList<>(virtualAssociations);
if (smartStore.canMaterialize(reference))
{
NodeRef materialReference = smartStore.materialize(reference);
Collection<ChildAssociationRef> actualAssociations = theTrait
.getChildAssocsWithoutParentAssocsOfType(materialReference,
assocTypeQName);
associations.addAll(actualAssociations);
}
return associations;
}
else
{
return theTrait.getChildAssocsWithoutParentAssocsOfType(nodeRef,
assocTypeQName);
}
}
@Override
public void removeAssociation(NodeRef sourceRef, NodeRef targetRef, QName assocTypeQName)
throws InvalidNodeRefException
{
getTrait().removeAssociation(materializeIfPossible(sourceRef),
materializeIfPossible(targetRef),
assocTypeQName);
}
@Override
public void setAssociations(NodeRef sourceRef, QName assocTypeQName, List<NodeRef> targetRefs)
{
getTrait().setAssociations(materializeIfPossible(sourceRef),
assocTypeQName,
materializeIfPossible(targetRefs));
}
@Override
public AssociationRef getAssoc(Long id)
{
return getTrait().getAssoc(id);
}
@Override
public NodeRef getStoreArchiveNode(StoreRef storeRef)
{
return getTrait().getStoreArchiveNode(storeRef);
}
@Override
public NodeRef restoreNode(NodeRef archivedNodeRef, NodeRef destinationParentNodeRef, QName assocTypeQName,
QName assocQName)
{
return getTrait().restoreNode(materializeIfPossible(archivedNodeRef),
materializeIfPossible(destinationParentNodeRef),
assocTypeQName,
assocQName);
}
@Override
public List<NodeRef> findNodes(FindNodeParameters params)
{
return getTrait().findNodes(params);
}
@Override
public int countChildAssocs(NodeRef nodeRef, boolean isPrimary) throws InvalidNodeRefException
{
return getTrait().countChildAssocs(nodeRef,
isPrimary);
}
@Override
public List<AssociationRef> getTargetAssocsByPropertyValue(NodeRef sourceRef, QNamePattern qnamePattern,
QName propertyQName, Serializable propertyValue)
{
return getTrait().getTargetAssocsByPropertyValue(sourceRef,
qnamePattern,
propertyQName,
propertyValue);
}
}