mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-06-16 17:55:15 +00:00
svn merge svn://svn.alfresco.com:3691/alfresco/BRANCHES/V2.0@5141 svn://svn.alfresco.com:3691/alfresco/BRANCHES/V2.0@51352 . - FLOSS - Some files will need a follow-up -root/projects/repository/source/java/org/alfresco/repo/avm/wf/AVMRemoveWFStoreHandler.java (not yet on HEAD: 5094) -root/projects/repository/source/java/org/alfresco/filesys/server/state/FileStateLockManager.java (not yet on HEAD: 5093) -onContentUpdateRecord (not on HEAD) git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@5167 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
349 lines
11 KiB
Java
349 lines
11 KiB
Java
/*
|
|
* Copyright (C) 2006 Alfresco, Inc.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
|
|
* This program 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 General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
* As a special exception to the terms and conditions of version 2.0 of
|
|
* the GPL, you may redistribute this Program in connection with Free/Libre
|
|
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
|
* FLOSS exception. You should have recieved a copy of the text describing
|
|
* the FLOSS exception, and it is also available here:
|
|
* http://www.alfresco.com/legal/licensing" */
|
|
|
|
package org.alfresco.repo.avm;
|
|
|
|
import java.util.LinkedList;
|
|
import java.util.List;
|
|
|
|
import org.alfresco.repo.domain.DbAccessControlList;
|
|
import org.alfresco.repo.transaction.TransactionUtil;
|
|
import org.alfresco.service.transaction.TransactionService;
|
|
import org.apache.log4j.Logger;
|
|
import org.hibernate.SessionFactory;
|
|
import org.springframework.orm.hibernate3.HibernateTemplate;
|
|
|
|
/**
|
|
* This is the background thread for reaping no longer referenced nodes
|
|
* in the AVM repository. These orphans arise from purge operations.
|
|
* @author britt
|
|
*/
|
|
public class OrphanReaper implements Runnable
|
|
{
|
|
private Logger fgLogger = Logger.getLogger(OrphanReaper.class);
|
|
|
|
/**
|
|
* The Transaction Service
|
|
*/
|
|
private TransactionService fTransactionService;
|
|
|
|
/**
|
|
* The Session Factory
|
|
*/
|
|
private SessionFactory fSessionFactory;
|
|
|
|
/**
|
|
* Inactive base sleep interval.
|
|
*/
|
|
private long fInactiveBaseSleep;
|
|
|
|
/**
|
|
* Active base sleep interval.
|
|
*/
|
|
private long fActiveBaseSleep;
|
|
|
|
/**
|
|
* Batch size.
|
|
*/
|
|
private int fBatchSize;
|
|
|
|
/**
|
|
* Whether we are currently active, ie have
|
|
* work queued up.
|
|
*/
|
|
private boolean fActive;
|
|
|
|
/**
|
|
* Flag for shutting down this.
|
|
*/
|
|
private boolean fDone;
|
|
|
|
/**
|
|
* The thread for this.
|
|
*/
|
|
private Thread fThread;
|
|
|
|
/**
|
|
* The maximum length of the queue.
|
|
*/
|
|
private int fQueueLength;
|
|
|
|
/**
|
|
* The linked list containing ids of nodes that are purgable.
|
|
*/
|
|
private LinkedList<Long> fPurgeQueue;
|
|
|
|
/**
|
|
* Create one with default parameters.
|
|
*/
|
|
public OrphanReaper()
|
|
{
|
|
fInactiveBaseSleep = 30000;
|
|
fActiveBaseSleep = 1000;
|
|
fBatchSize = 50;
|
|
fQueueLength = 1000;
|
|
fActive = false;
|
|
fDone = false;
|
|
}
|
|
|
|
// Setters for configuration.
|
|
|
|
/**
|
|
* Set the Inactive Base Sleep interval.
|
|
* @param interval The interval to set in ms.
|
|
*/
|
|
public void setInactiveBaseSleep(long interval)
|
|
{
|
|
fInactiveBaseSleep = interval;
|
|
}
|
|
|
|
/**
|
|
* Set the active base sleep interval.
|
|
* @param interval The interval to set in ms.
|
|
*/
|
|
public void setActiveBaseSleep(long interval)
|
|
{
|
|
fActiveBaseSleep = interval;
|
|
}
|
|
|
|
/**
|
|
* Set the batch size.
|
|
* @param size The batch size to set.
|
|
*/
|
|
public void setBatchSize(int size)
|
|
{
|
|
fBatchSize = size;
|
|
}
|
|
|
|
/**
|
|
* Set the transaction service.
|
|
* @param transactionService The service.
|
|
*/
|
|
public void setTransactionService(TransactionService transactionService)
|
|
{
|
|
fTransactionService = transactionService;
|
|
}
|
|
|
|
/**
|
|
* Set the hibernate session factory. (For Spring.)
|
|
* @param sessionFactory
|
|
*/
|
|
public void setSessionFactory(SessionFactory sessionFactory)
|
|
{
|
|
fSessionFactory = sessionFactory;
|
|
}
|
|
|
|
/**
|
|
* Set the maximum size of the queue of purgeable nodes.
|
|
* @param queueLength The max length.
|
|
*/
|
|
public void setMaxQueueLength(int queueLength)
|
|
{
|
|
fQueueLength = queueLength;
|
|
}
|
|
|
|
/**
|
|
* Start things up after configuration is complete.
|
|
*/
|
|
public void init()
|
|
{
|
|
fThread = new Thread(this);
|
|
fThread.start();
|
|
}
|
|
|
|
/**
|
|
* Shutdown the reaper. This needs to be called when
|
|
* the application shuts down.
|
|
*/
|
|
public void shutDown()
|
|
{
|
|
synchronized (this)
|
|
{
|
|
fDone = true;
|
|
notify();
|
|
}
|
|
try
|
|
{
|
|
fThread.join();
|
|
}
|
|
catch (InterruptedException ie)
|
|
{
|
|
// Do nothing.
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sit in a loop, periodically querying for orphans. When orphans
|
|
* are found, unhook them in bite sized batches.
|
|
*/
|
|
public void run()
|
|
{
|
|
while (!fDone)
|
|
{
|
|
synchronized (this)
|
|
{
|
|
try
|
|
{
|
|
wait(fActive? fActiveBaseSleep : fInactiveBaseSleep);
|
|
}
|
|
catch (InterruptedException ie)
|
|
{
|
|
// Do nothing.
|
|
}
|
|
doBatch();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This is really for debugging and testing. Allows another thread to
|
|
* mark the orphan reaper busy so that it can monitor for it's being done.
|
|
*/
|
|
public void activate()
|
|
{
|
|
fActive = true;
|
|
}
|
|
|
|
/**
|
|
* See if the reaper is actively reaping.
|
|
* @return Whether this is actively reaping.
|
|
*/
|
|
public boolean isActive()
|
|
{
|
|
return fActive;
|
|
}
|
|
|
|
/**
|
|
* Do a batch of cleanup work.
|
|
*/
|
|
public void doBatch()
|
|
{
|
|
class TxnWork implements TransactionUtil.TransactionWork<Object>
|
|
{
|
|
public Object doWork()
|
|
throws Exception
|
|
{
|
|
if (fPurgeQueue == null)
|
|
{
|
|
List<AVMNode> nodes = AVMDAOs.Instance().fAVMNodeDAO.getOrphans(fQueueLength);
|
|
if (nodes.size() == 0)
|
|
{
|
|
fActive = false;
|
|
return null;
|
|
}
|
|
fPurgeQueue = new LinkedList<Long>();
|
|
for (AVMNode node : nodes)
|
|
{
|
|
fPurgeQueue.add(node.getId());
|
|
}
|
|
}
|
|
fActive = true;
|
|
for (int i = 0; i < fBatchSize; i++)
|
|
{
|
|
if (fPurgeQueue.size() == 0)
|
|
{
|
|
fPurgeQueue = null;
|
|
return null;
|
|
}
|
|
AVMNode node = AVMDAOs.Instance().fAVMNodeDAO.getByID(fPurgeQueue.removeFirst());
|
|
// Save away the ancestor and merged from fields from this node.
|
|
HistoryLink hlink = AVMDAOs.Instance().fHistoryLinkDAO.getByDescendent(node);
|
|
AVMNode ancestor = null;
|
|
if (hlink != null)
|
|
{
|
|
ancestor = hlink.getAncestor();
|
|
AVMDAOs.Instance().fHistoryLinkDAO.delete(hlink);
|
|
}
|
|
MergeLink mlink = AVMDAOs.Instance().fMergeLinkDAO.getByTo(node);
|
|
AVMNode mergedFrom = null;
|
|
if (mlink != null)
|
|
{
|
|
mergedFrom = mlink.getMfrom();
|
|
AVMDAOs.Instance().fMergeLinkDAO.delete(mlink);
|
|
}
|
|
AVMDAOs.Instance().fAVMNodeDAO.flush();
|
|
// Get all the nodes that have this node as ancestor.
|
|
List<HistoryLink> links = AVMDAOs.Instance().fHistoryLinkDAO.getByAncestor(node);
|
|
for (HistoryLink link : links)
|
|
{
|
|
AVMNode desc = link.getDescendent();
|
|
desc.setAncestor(ancestor);
|
|
if (desc.getMergedFrom() == null)
|
|
{
|
|
desc.setMergedFrom(mergedFrom);
|
|
}
|
|
AVMDAOs.Instance().fHistoryLinkDAO.delete(link);
|
|
}
|
|
// Get all the nodes that have this node as mergedFrom
|
|
List<MergeLink> mlinks = AVMDAOs.Instance().fMergeLinkDAO.getByFrom(node);
|
|
for (MergeLink link : mlinks)
|
|
{
|
|
link.getMto().setMergedFrom(ancestor);
|
|
AVMDAOs.Instance().fMergeLinkDAO.delete(link);
|
|
}
|
|
// Get rid of all properties belonging to this node.
|
|
AVMDAOs.Instance().fAVMNodePropertyDAO.deleteAll(node);
|
|
// Get rid of all aspects belonging to this node.
|
|
AVMDAOs.Instance().fAVMAspectNameDAO.delete(node);
|
|
// Get rid of ACL.
|
|
DbAccessControlList acl = node.getAcl();
|
|
node.setAcl(null);
|
|
if (acl != null)
|
|
{
|
|
acl.deleteEntries();
|
|
(new HibernateTemplate(fSessionFactory)).delete(acl);
|
|
}
|
|
// Extra work for directories.
|
|
if (node.getType() == AVMNodeType.PLAIN_DIRECTORY ||
|
|
node.getType() == AVMNodeType.LAYERED_DIRECTORY)
|
|
{
|
|
// First get rid of all child entries for the node.
|
|
AVMDAOs.Instance().fChildEntryDAO.deleteByParent(node);
|
|
}
|
|
// This is not on, since content urls can be shared.
|
|
// else if (node.getType() == AVMNodeType.PLAIN_FILE)
|
|
// {
|
|
// PlainFileNode file = (PlainFileNode)node;
|
|
// String url = file.getContentData(null).getContentUrl();
|
|
// if (url != null)
|
|
// {
|
|
// RawServices.Instance().getContentStore().delete(url);
|
|
// }
|
|
// }
|
|
AVMDAOs.Instance().fAVMNodeDAO.delete(node);
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
try
|
|
{
|
|
TransactionUtil.executeInUserTransaction(fTransactionService,
|
|
new TxnWork());
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
fgLogger.error("Garbage collector error", e);
|
|
}
|
|
}
|
|
}
|