mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-06-30 18:15:39 +00:00
41597: ALF-1907: Check out rule is active for spaces - Merged in fix from DEV - Changed List of applicable types to a Set - Added quick initial checks against the set before doing the subtype checks - Various typo fixes 41598: Add an additional line of INFO to log the database details 41599: Fix for ALF-8374 - Simple view: incorrect file type icon for *.page and *.eps files 41600: Merge DEV to V4.1-BUG-FIX 41478 : ALF-14078: CLONE - Saving files with apps on Mac OS X Lion in CIFS doesn't invoke rules 41605: Fix for ALF-14653 - Share - Page link created wrongly in wiki. Solution to split wiki page name and title from Alex Bykov. 41608: CIFS cluster saving of text file fails. ALF-14179 The client releases the oplock by closing the file, if a remote oplock break was used then a notification to the remote node was missing in this case. 41616: ALF-13577:modified the delete site REST API to set the status code to 409 if a node is locked. 41624: MT: ALF-14331 - UserUsageTrackingComponent fails if disabled (in MT config) - defensive fix (exact steps do not repeat) - may also fix ALF-15956 41652: more debug 41653: Fix for ALF-15965 - IE9 - Script error occurs when manage aspect for a document 41655: Merged BRANCHES/V4.1 to BRANCHES/DEV/V4.1-BUG-FIX (RECORD ONLY) 41654: Fix for ALF-15965 - hand merged back from rev 41653 41658: ALF-14967: Task operations through task-forms now audited 41664: Reverse-merging change 41616 out of the product due to file formatting irregularities. This checkin was a fix for ALF-13577. A subsequent check-in will add the same fix but without the formatting problems. 41665: ALF-11452: Disabling JBPM now prevents jobexecutor thread from starting and consuming unneeded resources 41671: Reimplementation of fix for ALF-13577. Slight edit on previous fix. It now lets the NodeLockedException out of the Java layer and 'catches' this exception in the JavaScript layer. The API returns a 409 (conflict) if a NodeLockedException makes it to the JavaScript - the previous fix was returning 409 for *any* AlfrescoRuntimeException thrown during a site delete. Also this checkin preserves the whitespace/file layout of the preceding version. 41688: ALF-15867 WikiServiceImplTest failing on SQL Server - Switched WikiService_transaction bean over to using RetryingTransactionInterceptor 41720: ALF-15967: Using START_USER_ID_ instead of "initiator" property to query process instances started by user X to prevent extra joins 41730: Fix for ALF-15995 NodeRef is missing in log on "Problem converting to Freemarker" error 41743: ALF-9779 REGRESSION: FTP - Failed to retrieve directory listing for tenant user 41745: Removed a HomeShareMapper - there were two 41747: Fix for ALF-15811 SOLR query increases DocBitSet inefficiently Part of ALF-14861 SOLR to scale for non-admin users in 100k sites and a subgroup of each of 1000 independent groupings with 1000 subgroups 41748: Fix for ALF-15950 Solr: throws NPE: org.alfresco.solr.SolrLuceneAnalyser.loadAnalyzer - return default analyzer 41752: Fix for ALF-13634 Re-created category won't show up again on a node in Document Library. 41779: ALF-11283 - Got the home share mapper up and working after some refactoring. 41795: Fix for ALF-14361 CMIS object type definition for Document and Folder not spec compliant 41796: ALF-16008: missing column on clean Alfresco and after upgrade from 3.4.10 (703) - Schema-Reference for DB2 referenced a non-existent column. 41802: Fixed ALF-16029: TransactionCleanupTest throws constraint violations - Fallout from ALF-12358; sys:deleted node types have a mandatory property - Before bulk-deleting nodes, we need to delete properties - Use overriding DAo for MySQL. Other DBs can use standard delete with joins - NB: Fixed TransactionalCleanupTest to double-check the purge job 41822: Refactor to both Share Mappers. 41838: BDE-85: Deploy alfresco-mmt with a proper pom.xml containing dependencies Also, refactor Maven deployment to use a macro 41858: ALF-14444 - CloudSync: Ensure unknown properties when synced to Cloud are ignored properly 41876: Fix ALF-16067 - Items with cm:versionable aspect [custom aspect added] throw errors in Share UI 41877: Updated Spring Surf libs (1148) - downgrades module deployment error messages 41878: ALF-16069 - RU: 'Web Deployed' folder is not localized if Alfresco is installed with Russian locale. 41879: Fix non-continuous build, trying to call continuous-init from maven-env-prerequisites 41918: Merged PATCHES/V4.1.1 to V4.1-BUG-FIX 41657: ALF-15965 - IE9 - Script error occurs when manage aspect for a document (correct 4.1.1 branch this time) 41834: Merged DEV to V4.1.1 (4.1.1) 41827: ALF-15983: SPP: Meeting space: all-day event is displayed on the day before specified during creation Removing of time zone has been modified to be sensitive to Outlook meeting events which are not requiring special conversion for the start and end dates 41835: Merged DEV to V4.1.1 (4.1.1) 41833: ALF-16038 : DB2: Upgrade script needed to remove ALFUSER.ACT_HI_ACTINST.OWNER_ The patch was created to remove an unnecessary column in DB2. 41845: Reverse merge: << In attempt to clear 12 new DB2 build errors - don't think it is going to work >> 41835: Merged DEV to V4.1.1 (4.1.1) 41833: ALF-16038 : DB2: Upgrade script needed to remove ALFUSER.ACT_HI_ACTINST.OWNER_ The patch was created to remove an unnecessary column in DB2. Merged V4.1-BUG-FIX (4.1.2) to V4.1.1 (4.1.1) 41337: Merged V3.4-BUG-FIX (3.4.11) to V4.1-BUG-FIX (4.1.2) 41336: TransformerDebug: Use debug rather than trace when there are transformers but they are all unavailable. 41868: Reverse the reverse merge (back to r41835 state used in build #33 given to QA) 41845: Reverse merge: << In attempt to clear 12 new DB2 build errors - don't think it is going to work >> 41835: Merged DEV to V4.1.1 (4.1.1) 41833: ALF-16038 : DB2: Upgrade script needed to remove ALFUSER.ACT_HI_ACTINST.OWNER_ The patch was created to remove an unnecessary column in DB2. Merged V4.1-BUG-FIX (4.1.2) to V4.1.1 (4.1.1) 41337: Merged V3.4-BUG-FIX (3.4.11) to V4.1-BUG-FIX (4.1.2) 41336: TransformerDebug: Use debug rather than trace when there are transformers but they are all unavailable. 41914: Merged DEV to PATCHES/V4.1.1 41909: ALF-16078 : DB2: it's impossible to upgrade Alfresco from 3.4.10 to 4.1.1 (build 33). The remove-column-activiti.sql was marked as optional. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@41919 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
997 lines
34 KiB
Java
997 lines
34 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.solr;
|
|
|
|
import java.io.Serializable;
|
|
import java.io.UnsupportedEncodingException;
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.Collections;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.Iterator;
|
|
import java.util.LinkedList;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import java.util.zip.CRC32;
|
|
|
|
import org.alfresco.error.AlfrescoRuntimeException;
|
|
import org.alfresco.model.ContentModel;
|
|
import org.alfresco.repo.dictionary.DictionaryDAO;
|
|
import org.alfresco.repo.domain.node.Node;
|
|
import org.alfresco.repo.domain.node.NodeDAO;
|
|
import org.alfresco.repo.domain.node.NodeDAO.ChildAssocRefQueryCallback;
|
|
import org.alfresco.repo.domain.permissions.AclDAO;
|
|
import org.alfresco.repo.domain.qname.QNameDAO;
|
|
import org.alfresco.repo.domain.solr.SOLRDAO;
|
|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
|
import org.alfresco.repo.tenant.TenantService;
|
|
import org.alfresco.service.cmr.dictionary.AspectDefinition;
|
|
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
|
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
|
import org.alfresco.service.cmr.dictionary.ModelDefinition;
|
|
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
|
import org.alfresco.service.cmr.dictionary.TypeDefinition;
|
|
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
|
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
|
|
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.datatype.DefaultTypeConverter;
|
|
import org.alfresco.service.cmr.security.OwnableService;
|
|
import org.alfresco.service.cmr.security.PermissionService;
|
|
import org.alfresco.service.namespace.QName;
|
|
import org.alfresco.util.Pair;
|
|
import org.alfresco.util.PropertyCheck;
|
|
|
|
/**
|
|
* Component providing data for SOLR tracking
|
|
*
|
|
* @since 4.0
|
|
*/
|
|
public class SOLRTrackingComponentImpl implements SOLRTrackingComponent
|
|
{
|
|
private NodeDAO nodeDAO;
|
|
private QNameDAO qnameDAO;
|
|
private SOLRDAO solrDAO;
|
|
private DictionaryDAO dictionaryDAO;
|
|
private PermissionService permissionService;
|
|
private AclDAO aclDAO;
|
|
private OwnableService ownableService;
|
|
private TenantService tenantService;
|
|
private DictionaryService dictionaryService;
|
|
private boolean enabled = true;
|
|
|
|
|
|
@Override
|
|
public boolean isEnabled()
|
|
{
|
|
return enabled;
|
|
}
|
|
|
|
@Override
|
|
public void setEnabled(boolean enabled)
|
|
{
|
|
this.enabled = enabled;
|
|
}
|
|
|
|
public void setSolrDAO(SOLRDAO solrDAO)
|
|
{
|
|
this.solrDAO = solrDAO;
|
|
}
|
|
|
|
public void setNodeDAO(NodeDAO nodeDAO)
|
|
{
|
|
this.nodeDAO = nodeDAO;
|
|
}
|
|
|
|
public void setQnameDAO(QNameDAO qnameDAO)
|
|
{
|
|
this.qnameDAO = qnameDAO;
|
|
}
|
|
|
|
public void setPermissionService(PermissionService permissionService)
|
|
{
|
|
this.permissionService = permissionService;
|
|
}
|
|
|
|
public void setOwnableService(OwnableService ownableService)
|
|
{
|
|
this.ownableService = ownableService;
|
|
}
|
|
|
|
public void setTenantService(TenantService tenantService)
|
|
{
|
|
this.tenantService = tenantService;
|
|
}
|
|
|
|
public void setDictionaryService(DictionaryService dictionaryService)
|
|
{
|
|
this.dictionaryService = dictionaryService;
|
|
}
|
|
|
|
public void setAclDAO(AclDAO aclDAO)
|
|
{
|
|
this.aclDAO = aclDAO;
|
|
}
|
|
|
|
public void setDictionaryDAO(DictionaryDAO dictionaryDAO)
|
|
{
|
|
this.dictionaryDAO = dictionaryDAO;
|
|
}
|
|
|
|
/**
|
|
* Initialize
|
|
*/
|
|
public void init()
|
|
{
|
|
PropertyCheck.mandatory(this, "solrDAO", solrDAO);
|
|
PropertyCheck.mandatory(this, "nodeDAO", nodeDAO);
|
|
PropertyCheck.mandatory(this, "qnameDAO", qnameDAO);
|
|
PropertyCheck.mandatory(this, "permissionService", permissionService);
|
|
PropertyCheck.mandatory(this, "ownableService", ownableService);
|
|
PropertyCheck.mandatory(this, "tenantService", tenantService);
|
|
PropertyCheck.mandatory(this, "dictionaryService", dictionaryService);
|
|
PropertyCheck.mandatory(this, "dictionaryDAO", dictionaryDAO);
|
|
PropertyCheck.mandatory(this, "aclDAO", aclDAO);
|
|
}
|
|
|
|
@Override
|
|
public List<AclChangeSet> getAclChangeSets(Long minAclChangeSetId, Long fromCommitTime, Long maxAclChangeSetId, Long toCommitTime, int maxResults)
|
|
{
|
|
if(enabled)
|
|
{
|
|
List<AclChangeSet> changesets = solrDAO.getAclChangeSets(minAclChangeSetId, fromCommitTime, maxAclChangeSetId, toCommitTime, maxResults);
|
|
return changesets;
|
|
}
|
|
else
|
|
{
|
|
return Collections.<AclChangeSet>emptyList();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public List<Acl> getAcls(List<Long> aclChangeSetIds, Long minAclId, int maxResults)
|
|
{
|
|
if(enabled)
|
|
{
|
|
List<Acl> acls = solrDAO.getAcls(aclChangeSetIds, minAclId, maxResults);
|
|
return acls;
|
|
}
|
|
else
|
|
{
|
|
return Collections.<Acl>emptyList();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public List<AclReaders> getAclsReaders(List<Long> aclIds)
|
|
{
|
|
if(enabled)
|
|
{
|
|
/*
|
|
* This is an N+1 query that should, in theory, make use of cached ACL readers data.
|
|
*/
|
|
|
|
Map<Long, String> aclChangeSetTenant = new HashMap<Long, String>(aclIds.size());
|
|
|
|
List<AclReaders> aclsReaders = new ArrayList<AclReaders>(aclIds.size() * 10);
|
|
for (Long aclId : aclIds)
|
|
{
|
|
Set<String> readersSet = permissionService.getReaders(aclId);
|
|
AclReaders readers = new AclReaders();
|
|
readers.setAclId(aclId);
|
|
readers.setReaders(readersSet);
|
|
|
|
Long aclChangeSetId = aclDAO.getAccessControlList(aclId).getProperties().getAclChangeSetId();
|
|
readers.setAclChangeSetId(aclChangeSetId);
|
|
|
|
if (AuthenticationUtil.isMtEnabled())
|
|
{
|
|
// MT - for now, derive the tenant for acl (via acl change set)
|
|
String tenantDomain = aclChangeSetTenant.get(aclChangeSetId);
|
|
if (tenantDomain == null)
|
|
{
|
|
tenantDomain = getTenant(aclId, aclChangeSetId);
|
|
if (tenantDomain == null)
|
|
{
|
|
// skip this acl !
|
|
continue;
|
|
}
|
|
aclChangeSetTenant.put(aclChangeSetId, tenantDomain);
|
|
}
|
|
readers.setTenantDomain(tenantDomain);
|
|
}
|
|
|
|
aclsReaders.add(readers);
|
|
}
|
|
|
|
return aclsReaders;
|
|
}
|
|
else
|
|
{
|
|
return Collections.<AclReaders>emptyList();
|
|
}
|
|
}
|
|
|
|
private String getTenant(long aclId, long aclChangeSetId)
|
|
{
|
|
String tenantDomain = getAclTenant(aclId);
|
|
if (tenantDomain == null)
|
|
{
|
|
List<Long> aclChangeSetIds = new ArrayList<Long>(1);
|
|
aclChangeSetIds.add(aclChangeSetId);
|
|
|
|
List<Acl> acls = solrDAO.getAcls(aclChangeSetIds, null, 1024);
|
|
for (Acl acl : acls)
|
|
{
|
|
tenantDomain = getAclTenant(acl.getId());
|
|
if (tenantDomain != null)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (tenantDomain == null)
|
|
{
|
|
// tenant not found - log warning ?
|
|
tenantDomain = null; // temp - for debug breakpoint only
|
|
}
|
|
}
|
|
return tenantDomain;
|
|
}
|
|
|
|
private String getAclTenant(long aclId)
|
|
{
|
|
List<Long> nodeIds = aclDAO.getADMNodesByAcl(aclId, 1);
|
|
if (nodeIds.size() == 0)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
nodeDAO.setCheckNodeConsistency();
|
|
Pair<Long, NodeRef> nodePair = nodeDAO.getNodePair(nodeIds.get(0));
|
|
if (nodePair == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return tenantService.getDomain(nodePair.getSecond().getStoreRef().getIdentifier());
|
|
}
|
|
|
|
@Override
|
|
public List<Transaction> getTransactions(Long minTxnId, Long fromCommitTime, Long maxTxnId, Long toCommitTime, int maxResults)
|
|
{
|
|
if(enabled)
|
|
{
|
|
List<Transaction> txns = solrDAO.getTransactions(minTxnId, fromCommitTime, maxTxnId, toCommitTime, maxResults);
|
|
return txns;
|
|
}
|
|
else
|
|
{
|
|
return Collections.<Transaction>emptyList();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public void getNodes(NodeParameters nodeParameters, NodeQueryCallback callback)
|
|
{
|
|
if(enabled)
|
|
{
|
|
List<Node> nodes = solrDAO.getNodes(nodeParameters);
|
|
|
|
for (Node node : nodes)
|
|
{
|
|
callback.handleNode(node);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A dumb iterator that iterates over longs in sequence.
|
|
*/
|
|
private static class SequenceIterator implements Iterable<Long>, Iterator<Long>
|
|
{
|
|
private long fromId;
|
|
private long toId;
|
|
private long counter;
|
|
private int maxResults;
|
|
private boolean inUse = false;
|
|
|
|
SequenceIterator(Long fromId, Long toId, int maxResults)
|
|
{
|
|
this.fromId = (fromId == null ? 1 : fromId.longValue());
|
|
this.toId = (toId == null ? Long.MAX_VALUE : toId.longValue());
|
|
this.maxResults = maxResults;
|
|
this.counter = this.fromId;
|
|
}
|
|
|
|
@Override
|
|
public Iterator<Long> iterator()
|
|
{
|
|
if(inUse)
|
|
{
|
|
throw new IllegalStateException("Already in use");
|
|
}
|
|
this.counter = this.fromId;
|
|
this.inUse = true;
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public boolean hasNext()
|
|
{
|
|
return ((counter - this.fromId) < maxResults) && counter <= toId;
|
|
}
|
|
|
|
@Override
|
|
public Long next()
|
|
{
|
|
return counter++;
|
|
}
|
|
|
|
@Override
|
|
public void remove()
|
|
{
|
|
throw new UnsupportedOperationException();
|
|
}
|
|
}
|
|
|
|
private boolean isCategorised(AspectDefinition aspDef)
|
|
{
|
|
if(aspDef == null)
|
|
{
|
|
return false;
|
|
}
|
|
AspectDefinition current = aspDef;
|
|
while (current != null)
|
|
{
|
|
if (current.getName().equals(ContentModel.ASPECT_CLASSIFIABLE))
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
QName parentName = current.getParentName();
|
|
if (parentName == null)
|
|
{
|
|
break;
|
|
}
|
|
current = dictionaryService.getAspect(parentName);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static class CategoryPaths
|
|
{
|
|
Collection<Pair<Path, QName>> paths;
|
|
List<ChildAssociationRef> categoryParents;
|
|
|
|
CategoryPaths( Collection<Pair<Path, QName>> paths, List<ChildAssociationRef> categoryParents)
|
|
{
|
|
this.paths = paths;
|
|
this.categoryParents = categoryParents;
|
|
}
|
|
|
|
/**
|
|
* @return the paths
|
|
*/
|
|
public Collection<Pair<Path, QName>> getPaths()
|
|
{
|
|
return paths;
|
|
}
|
|
|
|
/**
|
|
* @return the categoryParents
|
|
*/
|
|
public List<ChildAssociationRef> getCategoryParents()
|
|
{
|
|
return categoryParents;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
private CategoryPaths getCategoryPaths(NodeRef nodeRef, Set<QName> aspects, Map<QName, Serializable> properties)
|
|
{
|
|
ArrayList<Pair<Path, QName>> categoryPaths = new ArrayList<Pair<Path, QName>>();
|
|
ArrayList<ChildAssociationRef> categoryParents = new ArrayList<ChildAssociationRef>();
|
|
|
|
nodeDAO.setCheckNodeConsistency();
|
|
for (QName classRef : aspects)
|
|
{
|
|
AspectDefinition aspDef = dictionaryService.getAspect(classRef);
|
|
if (!isCategorised(aspDef))
|
|
{
|
|
continue;
|
|
}
|
|
LinkedList<Pair<Path, QName>> aspectPaths = new LinkedList<Pair<Path, QName>>();
|
|
for (PropertyDefinition propDef : aspDef.getProperties().values())
|
|
{
|
|
if (!propDef.getDataType().getName().equals(DataTypeDefinition.CATEGORY))
|
|
{
|
|
// The property is not a category
|
|
continue;
|
|
}
|
|
// Don't try to iterate if the property is null
|
|
Serializable propVal = properties.get(propDef.getName());
|
|
if (propVal == null)
|
|
{
|
|
continue;
|
|
}
|
|
for (NodeRef catRef : DefaultTypeConverter.INSTANCE.getCollection(NodeRef.class, propVal))
|
|
{
|
|
if (catRef == null)
|
|
{
|
|
continue;
|
|
}
|
|
// can be running in context of System user, hence use input nodeRef
|
|
catRef = tenantService.getName(nodeRef, catRef);
|
|
|
|
try
|
|
{
|
|
Pair<Long, NodeRef> pair = nodeDAO.getNodePair(catRef);
|
|
if(pair != null)
|
|
{
|
|
for (Path path : nodeDAO.getPaths(pair, false))
|
|
{
|
|
aspectPaths.add(new Pair<Path, QName>(path, aspDef.getName()));
|
|
}
|
|
}
|
|
}
|
|
catch (InvalidNodeRefException e)
|
|
{
|
|
// If the category does not exists we move on the next
|
|
}
|
|
}
|
|
}
|
|
categoryPaths.addAll(aspectPaths);
|
|
}
|
|
// Add member final element
|
|
for (Pair<Path, QName> pair : categoryPaths)
|
|
{
|
|
if (pair.getFirst().last() instanceof Path.ChildAssocElement)
|
|
{
|
|
Path.ChildAssocElement cae = (Path.ChildAssocElement) pair.getFirst().last();
|
|
ChildAssociationRef assocRef = cae.getRef();
|
|
ChildAssociationRef categoryParentRef = new ChildAssociationRef(assocRef.getTypeQName(), assocRef.getChildRef(), QName.createQName("member"), nodeRef);
|
|
pair.getFirst().append(new Path.ChildAssocElement(categoryParentRef));
|
|
categoryParents.add(categoryParentRef);
|
|
}
|
|
}
|
|
|
|
return new CategoryPaths(categoryPaths, categoryParents);
|
|
}
|
|
|
|
|
|
private List<Long> preCacheNodes(NodeMetaDataParameters nodeMetaDataParameters)
|
|
{
|
|
nodeDAO.setCheckNodeConsistency();
|
|
int maxResults = nodeMetaDataParameters.getMaxResults();
|
|
boolean isLimitSet = (maxResults != 0 && maxResults != Integer.MAX_VALUE);
|
|
|
|
List<Long> nodeIds = null;
|
|
Iterable<Long> iterable = null;
|
|
List<Long> allNodeIds = nodeMetaDataParameters.getNodeIds();
|
|
if(allNodeIds != null)
|
|
{
|
|
int toIndex = (maxResults > allNodeIds.size() ? allNodeIds.size() : maxResults);
|
|
nodeIds = isLimitSet ? allNodeIds.subList(0, toIndex) : nodeMetaDataParameters.getNodeIds();
|
|
iterable = nodeMetaDataParameters.getNodeIds();
|
|
}
|
|
else
|
|
{
|
|
Long fromNodeId = nodeMetaDataParameters.getFromNodeId();
|
|
Long toNodeId = nodeMetaDataParameters.getToNodeId();
|
|
nodeIds = new ArrayList<Long>(isLimitSet ? maxResults : 100); // TODO better default here?
|
|
iterable = new SequenceIterator(fromNodeId, toNodeId, maxResults);
|
|
int counter = 1;
|
|
for(Long nodeId : iterable)
|
|
{
|
|
if(isLimitSet && counter++ > maxResults)
|
|
{
|
|
break;
|
|
}
|
|
nodeIds.add(nodeId);
|
|
}
|
|
}
|
|
// pre-cache nodes
|
|
nodeDAO.cacheNodesById(nodeIds);
|
|
|
|
return nodeIds;
|
|
}
|
|
|
|
protected Map<QName, Serializable> getProperties(Long nodeId)
|
|
{
|
|
Map<QName, Serializable> props = null;
|
|
|
|
// ALF-10641
|
|
// Residual properties are un-indexed -> break serlialisation
|
|
nodeDAO.setCheckNodeConsistency();
|
|
Map<QName, Serializable> sourceProps = nodeDAO.getNodeProperties(nodeId);
|
|
props = new HashMap<QName, Serializable>((int)(sourceProps.size() * 1.3));
|
|
for(QName propertyQName : sourceProps.keySet())
|
|
{
|
|
PropertyDefinition propDef = dictionaryService.getProperty(propertyQName);
|
|
if(propDef != null)
|
|
{
|
|
props.put(propertyQName, sourceProps.get(propertyQName));
|
|
}
|
|
}
|
|
|
|
return props;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public void getNodesMetadata(
|
|
NodeMetaDataParameters nodeMetaDataParameters,
|
|
MetaDataResultsFilter resultFilter,
|
|
NodeMetaDataQueryCallback callback)
|
|
{
|
|
if(false == enabled)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Ensure that we get fresh node references
|
|
nodeDAO.setCheckNodeConsistency();
|
|
|
|
NodeMetaDataQueryRowHandler rowHandler = new NodeMetaDataQueryRowHandler(callback);
|
|
boolean includeType = (resultFilter == null ? true : resultFilter.getIncludeType());
|
|
boolean includeProperties = (resultFilter == null ? true : resultFilter.getIncludeProperties());
|
|
boolean includeAspects = (resultFilter == null ? true : resultFilter.getIncludeAspects());
|
|
boolean includePaths = (resultFilter == null ? true : resultFilter.getIncludePaths());
|
|
boolean includeNodeRef = (resultFilter == null ? true : resultFilter.getIncludeNodeRef());
|
|
boolean includeParentAssociations = (resultFilter == null ? true : resultFilter.getIncludeParentAssociations());
|
|
boolean includeChildAssociations = (resultFilter == null ? true : resultFilter.getIncludeChildAssociations());
|
|
boolean includeOwner = (resultFilter == null ? true : resultFilter.getIncludeOwner());
|
|
boolean includeChildIds = (resultFilter == null ? true : resultFilter.getIncludeChildIds());
|
|
boolean includeTxnId = (resultFilter == null ? true : resultFilter.getIncludeTxnId());
|
|
|
|
List<Long> nodeIds = preCacheNodes(nodeMetaDataParameters);
|
|
|
|
for(Long nodeId : nodeIds)
|
|
{
|
|
Status status = nodeDAO.getNodeIdStatus(nodeId);
|
|
if (status == null)
|
|
{
|
|
// We've been called with the ID of a purged node, probably due to processing a transaction with a
|
|
// cascading delete. Fine to skip and assume it will be processed in a transaction.
|
|
// See org.alfresco.solr.tracker.CoreTracker.updateDescendantAuxDocs(NodeMetaData, boolean, SolrIndexSearcher)
|
|
continue;
|
|
}
|
|
NodeRef nodeRef = status.getNodeRef();
|
|
|
|
NodeMetaData nodeMetaData = new NodeMetaData();
|
|
nodeMetaData.setNodeId(nodeId);
|
|
|
|
if(includeNodeRef)
|
|
{
|
|
nodeMetaData.setNodeRef(tenantService.getBaseName(nodeRef, true));
|
|
}
|
|
|
|
if(includeTxnId)
|
|
{
|
|
nodeMetaData.setTxnId(status.getDbTxnId());
|
|
}
|
|
|
|
if(status.isDeleted())
|
|
{
|
|
rowHandler.processResult(nodeMetaData);
|
|
continue;
|
|
}
|
|
|
|
Map<QName, Serializable> props = null;
|
|
Set<QName> aspects = null;
|
|
|
|
nodeMetaData.setAclId(nodeDAO.getNodeAclId(nodeId));
|
|
|
|
if(includeType)
|
|
{
|
|
QName nodeType = nodeDAO.getNodeType(nodeId);
|
|
TypeDefinition type = dictionaryService.getType(nodeType);
|
|
if(type != null)
|
|
{
|
|
nodeMetaData.setNodeType(nodeType);
|
|
}
|
|
else
|
|
{
|
|
throw new AlfrescoRuntimeException("Nodes with no type are ignored by SOLR");
|
|
}
|
|
}
|
|
|
|
if(includeProperties)
|
|
{
|
|
if(props == null)
|
|
{
|
|
props = getProperties(nodeId);
|
|
}
|
|
nodeMetaData.setProperties(props);
|
|
}
|
|
else
|
|
{
|
|
nodeMetaData.setProperties(Collections.<QName, Serializable>emptyMap());
|
|
}
|
|
|
|
if(includeAspects || includePaths || includeParentAssociations)
|
|
{
|
|
aspects = new HashSet<QName>();
|
|
Set<QName> sourceAspects = nodeDAO.getNodeAspects(nodeId);
|
|
for(QName aspectQName : sourceAspects)
|
|
{
|
|
AspectDefinition aspect = dictionaryService.getAspect(aspectQName);
|
|
if(aspect != null)
|
|
{
|
|
aspects.add(aspectQName);
|
|
}
|
|
}
|
|
}
|
|
nodeMetaData.setAspects(aspects);
|
|
|
|
CategoryPaths categoryPaths = new CategoryPaths(new ArrayList<Pair<Path, QName>>(), new ArrayList<ChildAssociationRef>());
|
|
if(includePaths || includeParentAssociations)
|
|
{
|
|
if(props == null)
|
|
{
|
|
props = getProperties(nodeId);
|
|
}
|
|
categoryPaths = getCategoryPaths(status.getNodeRef(), aspects, props);
|
|
}
|
|
|
|
if(includePaths)
|
|
{
|
|
if(props == null)
|
|
{
|
|
props = getProperties(nodeId);
|
|
}
|
|
|
|
List<Path> directPaths = nodeDAO.getPaths(new Pair<Long, NodeRef>(nodeId, status.getNodeRef()), false);
|
|
|
|
Collection<Pair<Path, QName>> paths = new ArrayList<Pair<Path, QName>>(directPaths.size() + categoryPaths.getPaths().size());
|
|
for (Path path : directPaths)
|
|
{
|
|
paths.add(new Pair<Path, QName>(path.getBaseNamePath(tenantService), null));
|
|
}
|
|
for(Pair<Path, QName> catPair : categoryPaths.getPaths())
|
|
{
|
|
paths.add(new Pair<Path, QName>(catPair.getFirst().getBaseNamePath(tenantService), catPair.getSecond()));
|
|
}
|
|
|
|
nodeMetaData.setPaths(paths);
|
|
}
|
|
|
|
nodeMetaData.setTenantDomain(tenantService.getDomain(nodeRef.getStoreRef().getIdentifier()));
|
|
|
|
if(includeChildAssociations)
|
|
{
|
|
final List<ChildAssociationRef> childAssocs = new ArrayList<ChildAssociationRef>(100);
|
|
nodeDAO.getChildAssocs(nodeId, null, null, null, null, null, new ChildAssocRefQueryCallback()
|
|
{
|
|
@Override
|
|
public boolean preLoadNodes()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean orderResults()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean handle(Pair<Long, ChildAssociationRef> childAssocPair, Pair<Long, NodeRef> parentNodePair,
|
|
Pair<Long, NodeRef> childNodePair)
|
|
{
|
|
childAssocs.add(tenantService.getBaseName(childAssocPair.getSecond(), true));
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public void done()
|
|
{
|
|
}
|
|
});
|
|
nodeMetaData.setChildAssocs(childAssocs);
|
|
}
|
|
|
|
if(includeChildIds)
|
|
{
|
|
final List<Long> childIds = new ArrayList<Long>(100);
|
|
nodeDAO.getChildAssocs(nodeId, null, null, null, null, null, new ChildAssocRefQueryCallback()
|
|
{
|
|
@Override
|
|
public boolean preLoadNodes()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean orderResults()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean handle(Pair<Long, ChildAssociationRef> childAssocPair, Pair<Long, NodeRef> parentNodePair,
|
|
Pair<Long, NodeRef> childNodePair)
|
|
{
|
|
childIds.add(childNodePair.getFirst());
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public void done()
|
|
{
|
|
}
|
|
});
|
|
nodeMetaData.setChildIds(childIds);
|
|
}
|
|
|
|
if(includeParentAssociations)
|
|
{
|
|
final List<ChildAssociationRef> parentAssocs = new ArrayList<ChildAssociationRef>(100);
|
|
nodeDAO.getParentAssocs(nodeId, null, null, null, new ChildAssocRefQueryCallback()
|
|
{
|
|
@Override
|
|
public boolean preLoadNodes()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean orderResults()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean handle(Pair<Long, ChildAssociationRef> childAssocPair,
|
|
Pair<Long, NodeRef> parentNodePair, Pair<Long, NodeRef> childNodePair)
|
|
{
|
|
parentAssocs.add(tenantService.getBaseName(childAssocPair.getSecond(), true));
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public void done()
|
|
{
|
|
}
|
|
});
|
|
for(ChildAssociationRef ref : categoryPaths.getCategoryParents())
|
|
{
|
|
parentAssocs.add(tenantService.getBaseName(ref, true));
|
|
}
|
|
|
|
CRC32 crc = new CRC32();
|
|
for(ChildAssociationRef car : parentAssocs)
|
|
{
|
|
try
|
|
{
|
|
crc.update(car.toString().getBytes("UTF-8"));
|
|
}
|
|
catch (UnsupportedEncodingException e)
|
|
{
|
|
throw new RuntimeException("UTF-8 encoding is not supported");
|
|
}
|
|
}
|
|
nodeMetaData.setParentAssocs(parentAssocs, crc.getValue());
|
|
|
|
// TODO non-child associations
|
|
// Collection<Pair<Long, AssociationRef>> sourceAssocs = nodeDAO.getSourceNodeAssocs(nodeId);
|
|
// Collection<Pair<Long, AssociationRef>> targetAssocs = nodeDAO.getTargetNodeAssocs(nodeId);
|
|
//
|
|
// nodeMetaData.setAssocs();
|
|
}
|
|
|
|
if(includeOwner)
|
|
{
|
|
// cached in OwnableService
|
|
nodeMetaData.setOwner(ownableService.getOwner(status.getNodeRef()));
|
|
}
|
|
|
|
rowHandler.processResult(nodeMetaData);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public AlfrescoModel getModel(QName modelName)
|
|
{
|
|
if(enabled)
|
|
{
|
|
ModelDefinition modelDef = dictionaryService.getModel(modelName);
|
|
return (modelDef != null ? new AlfrescoModel(modelDef) : null);
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public List<AlfrescoModelDiff> getModelDiffs(Map<QName, Long> models)
|
|
{
|
|
if(false == enabled)
|
|
{
|
|
return Collections.<AlfrescoModelDiff>emptyList();
|
|
}
|
|
|
|
List<AlfrescoModelDiff> diffs = new ArrayList<AlfrescoModelDiff>();
|
|
|
|
// get all models the repository knows about and add each to a list with its checksum
|
|
Collection<QName> allModels = dictionaryService.getAllModels();
|
|
|
|
// look for changed and removed models
|
|
for(QName modelName : models.keySet())
|
|
{
|
|
if(allModels.contains(modelName))
|
|
{
|
|
Long checksum = models.get(modelName);
|
|
AlfrescoModel serverModel = getModel(modelName);
|
|
if(serverModel.getChecksum() != checksum.longValue())
|
|
{
|
|
// model has changed, add the changed server model
|
|
diffs.add(new AlfrescoModelDiff(modelName,
|
|
AlfrescoModelDiff.TYPE.CHANGED, checksum, serverModel.getChecksum()));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// model no longer exists, just add it's name
|
|
diffs.add(new AlfrescoModelDiff(modelName,
|
|
AlfrescoModelDiff.TYPE.REMOVED, null, null));
|
|
}
|
|
}
|
|
|
|
// look for new models
|
|
for(QName modelName : allModels)
|
|
{
|
|
if(!models.containsKey(modelName))
|
|
{
|
|
// new model, add the model xml and checksum
|
|
AlfrescoModel model = getModel(modelName);
|
|
diffs.add(new AlfrescoModelDiff(modelName,
|
|
AlfrescoModelDiff.TYPE.NEW, null, model.getChecksum()));
|
|
}
|
|
}
|
|
|
|
// for(AlfrescoModelDiff diff : diffs)
|
|
// {
|
|
// if(diff.getType() != TYPE.REMOVED)
|
|
// {
|
|
// CompiledModel cm = ((DictionaryDAOImpl)dictionaryDAO).getCompiledModel(QName.createQName(diff.getModelName()));
|
|
// File file = TempFileProvider.createTempFile(cm.getM2Model().getChecksum(XMLBindingType.DEFAULT)+ cm.getM2Model().getNamespaces().get(0).getPrefix(), ".xml");
|
|
// FileOutputStream os;
|
|
// try
|
|
// {
|
|
// os = new FileOutputStream(file);
|
|
// cm.getM2Model().toXML(os);
|
|
// os.flush();
|
|
// os.close();
|
|
//
|
|
// }
|
|
// catch (IOException e)
|
|
// {
|
|
// // TODO Auto-generated catch block
|
|
// e.printStackTrace();
|
|
// }
|
|
// }
|
|
//
|
|
// }
|
|
|
|
return diffs;
|
|
}
|
|
|
|
/**
|
|
* Class that passes results from a result entity into the client callback
|
|
*/
|
|
protected class NodeQueryRowHandler
|
|
{
|
|
private final NodeQueryCallback callback;
|
|
private boolean more;
|
|
|
|
private NodeQueryRowHandler(NodeQueryCallback callback)
|
|
{
|
|
this.callback = callback;
|
|
this.more = true;
|
|
}
|
|
|
|
public void processResult(Node row)
|
|
{
|
|
if (!more)
|
|
{
|
|
// No more results required
|
|
return;
|
|
}
|
|
|
|
more = callback.handleNode(row);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Class that passes results from a result entity into the client callback
|
|
*/
|
|
protected class NodeMetaDataQueryRowHandler
|
|
{
|
|
private final NodeMetaDataQueryCallback callback;
|
|
private boolean more;
|
|
|
|
private NodeMetaDataQueryRowHandler(NodeMetaDataQueryCallback callback)
|
|
{
|
|
this.callback = callback;
|
|
this.more = true;
|
|
}
|
|
|
|
public void processResult(NodeMetaData row)
|
|
{
|
|
if (!more)
|
|
{
|
|
// No more results required
|
|
return;
|
|
}
|
|
|
|
more = callback.handleNodeMetaData(row);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Long getMaxTxnCommitTime()
|
|
{
|
|
nodeDAO.setCheckNodeConsistency();
|
|
return nodeDAO.getMaxTxnCommitTime();
|
|
}
|
|
|
|
@Override
|
|
public Long getMaxTxnId()
|
|
{
|
|
long maxCommitTime = System.currentTimeMillis()+1L;
|
|
nodeDAO.setCheckNodeConsistency();
|
|
return nodeDAO.getMaxTxnIdByCommitTime(maxCommitTime);
|
|
}
|
|
|
|
/* (non-Javadoc)
|
|
* @see org.alfresco.repo.solr.SOLRTrackingComponent#getMaxChangeSetCommitTime()
|
|
*/
|
|
@Override
|
|
public Long getMaxChangeSetCommitTime()
|
|
{
|
|
return aclDAO.getMaxChangeSetCommitTime();
|
|
}
|
|
|
|
/* (non-Javadoc)
|
|
* @see org.alfresco.repo.solr.SOLRTrackingComponent#getMaxChangeSetId()
|
|
*/
|
|
@Override
|
|
public Long getMaxChangeSetId()
|
|
{
|
|
long maxCommitTime = System.currentTimeMillis()+1L;
|
|
return aclDAO.getMaxChangeSetIdByCommitTime(maxCommitTime);
|
|
}
|
|
}
|