/*
* 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 .
*/
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.TreeSet;
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;
private boolean cacheAncestors =true;
@Override
public boolean isEnabled()
{
return enabled;
}
@Override
public void setEnabled(boolean enabled)
{
this.enabled = enabled;
}
/**
* @param cacheAncestors the cacheAncestors to set
*/
public void setCacheAncestors(boolean cacheAncestors)
{
this.cacheAncestors = cacheAncestors;
}
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 getAclChangeSets(Long minAclChangeSetId, Long fromCommitTime, Long maxAclChangeSetId, Long toCommitTime, int maxResults)
{
if(enabled)
{
List changesets = solrDAO.getAclChangeSets(minAclChangeSetId, fromCommitTime, maxAclChangeSetId, toCommitTime, maxResults);
return changesets;
}
else
{
return Collections.emptyList();
}
}
@Override
public List getAcls(List aclChangeSetIds, Long minAclId, int maxResults)
{
if(enabled)
{
List acls = solrDAO.getAcls(aclChangeSetIds, minAclId, maxResults);
return acls;
}
else
{
return Collections.emptyList();
}
}
@Override
public List getAclsReaders(List aclIds)
{
if(enabled)
{
// We don't want the caches to lie and we may not be part of the cluster
aclDAO.setCheckAclConsistency();
/*
* This is an N+1 query that should, in theory, make use of cached ACL readers data.
*/
Map aclChangeSetTenant = new HashMap(aclIds.size());
List aclsReaders = new ArrayList(aclIds.size() * 10);
for (Long aclId : aclIds)
{
Set 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.emptyList();
}
}
private String getTenant(long aclId, long aclChangeSetId)
{
String tenantDomain = getAclTenant(aclId);
if (tenantDomain == null)
{
List aclChangeSetIds = new ArrayList(1);
aclChangeSetIds.add(aclChangeSetId);
List 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 nodeIds = aclDAO.getADMNodesByAcl(aclId, 1);
if (nodeIds.size() == 0)
{
return null;
}
nodeDAO.setCheckNodeConsistency();
Pair nodePair = nodeDAO.getNodePair(nodeIds.get(0));
if (nodePair == null)
{
return null;
}
return tenantService.getDomain(nodePair.getSecond().getStoreRef().getIdentifier());
}
@Override
public List getTransactions(Long minTxnId, Long fromCommitTime, Long maxTxnId, Long toCommitTime, int maxResults)
{
if(enabled)
{
List txns = solrDAO.getTransactions(minTxnId, fromCommitTime, maxTxnId, toCommitTime, maxResults);
return txns;
}
else
{
return Collections.emptyList();
}
}
/**
* {@inheritDoc}
*/
public void getNodes(NodeParameters nodeParameters, NodeQueryCallback callback)
{
if(enabled)
{
List 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, Iterator
{
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 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> paths;
List categoryParents;
CategoryPaths( Collection> paths, List categoryParents)
{
this.paths = paths;
this.categoryParents = categoryParents;
}
/**
* @return the paths
*/
public Collection> getPaths()
{
return paths;
}
/**
* @return the categoryParents
*/
public List getCategoryParents()
{
return categoryParents;
}
}
private CategoryPaths getCategoryPaths(NodeRef nodeRef, Set aspects, Map properties)
{
ArrayList> categoryPaths = new ArrayList>();
ArrayList categoryParents = new ArrayList();
nodeDAO.setCheckNodeConsistency();
for (QName classRef : aspects)
{
AspectDefinition aspDef = dictionaryService.getAspect(classRef);
if (!isCategorised(aspDef))
{
continue;
}
LinkedList> aspectPaths = new LinkedList>();
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 pair = nodeDAO.getNodePair(catRef);
if(pair != null)
{
for (Path path : nodeDAO.getPaths(pair, false))
{
aspectPaths.add(new Pair(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 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 preCacheNodes(NodeMetaDataParameters nodeMetaDataParameters)
{
int maxResults = nodeMetaDataParameters.getMaxResults();
boolean isLimitSet = (maxResults != 0 && maxResults != Integer.MAX_VALUE);
List nodeIds = null;
Iterable iterable = null;
List 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(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-evaluate ancestors so we can bulk load them
List ancestors;
if(cacheAncestors)
{
ancestors = cacheAncestors(nodeIds);
}
else
{
ancestors = nodeIds;
}
// Ensure that we get fresh node references
nodeDAO.setCheckNodeConsistency();
// bulk load nodes and their ancestors
nodeDAO.cacheNodesById(ancestors);
return nodeIds;
}
/**
* Does a 'breadth first' search of ancestors, caching as it goes
* @param nodeIds initial list of nodes to visit
* @return all visited nodes, in no particular order
*/
private List cacheAncestors(List nodeIds)
{
final LinkedList toVisit = new LinkedList(nodeIds);
Set visited = new TreeSet();
Long nodeId;
nodeDAO.cacheNodesById(toVisit);
Long lastCached = toVisit.peekLast();
while ((nodeId = toVisit.pollFirst()) != null)
{
if (visited.add(nodeId))
{
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 childAssocPair,
Pair parentNodePair, Pair childNodePair)
{
toVisit.add(parentNodePair.getFirst());
return true;
}
@Override
public void done()
{
}
});
}
if (nodeId == lastCached && !toVisit.isEmpty())
{
nodeDAO.cacheNodesById(toVisit);
lastCached = toVisit.peekLast();
}
}
return new ArrayList(visited);
}
protected Map getProperties(Long nodeId)
{
Map props = null;
// ALF-10641
// Residual properties are un-indexed -> break serlialisation
nodeDAO.setCheckNodeConsistency();
Map sourceProps = nodeDAO.getNodeProperties(nodeId);
props = new HashMap((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;
}
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 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 props = null;
Set 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.emptyMap());
}
if(includeAspects || includePaths || includeParentAssociations)
{
aspects = new HashSet();
Set 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>(), new ArrayList());
if(includePaths || includeParentAssociations)
{
if(props == null)
{
props = getProperties(nodeId);
}
categoryPaths = getCategoryPaths(status.getNodeRef(), aspects, props);
}
if(includePaths)
{
if(props == null)
{
props = getProperties(nodeId);
}
List directPaths = nodeDAO.getPaths(new Pair(nodeId, status.getNodeRef()), false);
Collection> paths = new ArrayList>(directPaths.size() + categoryPaths.getPaths().size());
for (Path path : directPaths)
{
paths.add(new Pair(path.getBaseNamePath(tenantService), null));
}
for(Pair catPair : categoryPaths.getPaths())
{
paths.add(new Pair(catPair.getFirst().getBaseNamePath(tenantService), catPair.getSecond()));
}
nodeMetaData.setPaths(paths);
}
nodeMetaData.setTenantDomain(tenantService.getDomain(nodeRef.getStoreRef().getIdentifier()));
if(includeChildAssociations)
{
final List childAssocs = new ArrayList(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 childAssocPair, Pair parentNodePair,
Pair childNodePair)
{
childAssocs.add(tenantService.getBaseName(childAssocPair.getSecond(), true));
return true;
}
@Override
public void done()
{
}
});
nodeMetaData.setChildAssocs(childAssocs);
}
if(includeChildIds)
{
final List childIds = new ArrayList(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 childAssocPair, Pair parentNodePair,
Pair childNodePair)
{
childIds.add(childNodePair.getFirst());
return true;
}
@Override
public void done()
{
}
});
nodeMetaData.setChildIds(childIds);
}
if(includeParentAssociations)
{
final List parentAssocs = new ArrayList(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 childAssocPair,
Pair parentNodePair, Pair 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> sourceAssocs = nodeDAO.getSourceNodeAssocs(nodeId);
// Collection> 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 getModelDiffs(Map models)
{
if(false == enabled)
{
return Collections.emptyList();
}
List diffs = new ArrayList();
// get all models the repository knows about and add each to a list with its checksum
Collection 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);
}
}