Britt Park c012545bfa Callback mechanism for notifying CIFS when Stores are
created or purged, and when versions are created or purged.


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@4541 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
2006-12-07 01:02:40 +00:00

343 lines
10 KiB
Java

/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
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);
}
}
}