/*
* This file is part of the HyperGraphDB source distribution. This is copyrighted
* software. For permitted uses, licensing options and redistribution, please see
* the LicensingInformation file at the root level of the distribution.
*
* Copyright (c) 2005-2010 Kobrix Software, Inc. All rights reserved.
*/
package org.hypergraphdb.maintenance;
import java.util.ArrayList;
import java.util.List;
import org.hypergraphdb.HGHandle;
import org.hypergraphdb.HGIndex;
import org.hypergraphdb.HGPersistentHandle;
import org.hypergraphdb.HGRandomAccessResult;
import org.hypergraphdb.HGRandomAccessResult.GotoResult;
import org.hypergraphdb.HyperGraph;
import org.hypergraphdb.HGQuery.hg;
import org.hypergraphdb.indexing.HGIndexer;
import org.hypergraphdb.util.HGUtils;
/**
*
* <p>
* The <code>MaintenanceOperation</code> will create index entries for a newly
* added <code>HGIndexer</code>.
* </p>
*
* @author Borislav Iordanov
*
*/
@SuppressWarnings("unchecked")
public class ApplyNewIndexer implements MaintenanceOperation
{
private HGHandle hindexer;
private List<HGHandle> typesAdded = new ArrayList<HGHandle>();
private HGPersistentHandle lastProcessed = null;
private int batchSize = 100;
private void cleanupAfterFailure(HyperGraph graph, HGIndexer indexer, MaintenanceException ex)
{
try
{
graph.getIndexManager().unregister(indexer); // this will delete all existing entries
graph.getIndexManager().register(indexer);
}
catch (Throwable t)
{
ex.setFatal(true);
}
}
private void indexAtomsTypedWith(HyperGraph graph,
HGIndex<?,?> idx,
HGIndexer indexer,
HGHandle typeHandle) throws MaintenanceException
{
HGRandomAccessResult<HGPersistentHandle> rs = null;
while (true)
{
HGPersistentHandle txLastProcessed = lastProcessed;
graph.getTransactionManager().beginTransaction();
try
{
rs = (HGRandomAccessResult<HGPersistentHandle>)(HGRandomAccessResult<?>)
graph.find(hg.type(typeHandle));
if (txLastProcessed == null)
{
if (!rs.hasNext())
{
rs.close();
graph.getTransactionManager().endTransaction(false);
return;
}
else rs.next();
}
else
{
GotoResult gt = rs.goTo(txLastProcessed, false);
if (gt == GotoResult.nothing) // last processed was actually last element in result set
{
rs.close();
graph.getTransactionManager().endTransaction(false);
return;
}
else if (gt == GotoResult.found)
{
if (!rs.hasNext())
{
rs.close();
graph.getTransactionManager().endTransaction(false);
return;
}
else
rs.next();
} // else we are already positioned after the last processed, which is not present for god know why?
}
for (int i = 0; i < batchSize; i++)
{
Object atom = graph.get(rs.current());
indexer.index(graph, rs.current(), atom, idx);
txLastProcessed = rs.current();
if (!rs.hasNext())
break;
else
rs.next();
}
rs.close();
rs = null;
graph.update(this);
graph.getTransactionManager().endTransaction(true);
lastProcessed = txLastProcessed;
}
catch (Throwable t)
{
Throwable cause = HGUtils.getRootCause(t);
if (graph.getStore().getTransactionFactory().canRetryAfter(cause))
continue;
try { graph.getTransactionManager().endTransaction(false); }
catch (Throwable tt) { tt.printStackTrace(System.err); }
MaintenanceException mex = new MaintenanceException(
false,
"While creating populating index for indexer : " + indexer,
t);
cleanupAfterFailure(graph, indexer, mex);
throw mex;
}
finally
{
HGUtils.closeNoException(rs);
}
}
}
public ApplyNewIndexer()
{
}
public ApplyNewIndexer(HGHandle hIndexer)
{
this.hindexer = hIndexer;
}
public void execute(HyperGraph graph) throws MaintenanceException
{
HGIndexer indexer = graph.get(hindexer);
if (indexer == null)
return;
HGIndex<?,?> idx = graph.getIndexManager().getIndex(indexer);
if (idx == null)
throw new MaintenanceException(false,"Indexer " + indexer + " with handle " + hindexer +
" present in graph, but no actual index has been created.");
for (HGHandle currentType : hg.typePlus(indexer.getType()).getSubTypes(graph))
{
if (typesAdded.contains(currentType)) // are we resuming from a previous interruption?
{
// if the type has been completed processed, skip it
if (!typesAdded.get(typesAdded.size()-1).equals(currentType))
continue;
// otherwise, we are resuming the processing of 'currentType' and
// the lastProcessed variable should contain the handle of the last
// atom of that type that was added to the index
}
else
{
typesAdded.add(currentType);
lastProcessed = null;
}
indexAtomsTypedWith(graph, idx, indexer, currentType);
}
}
public HGHandle getHindexer()
{
return hindexer;
}
public void setHindexer(HGHandle indexer)
{
hindexer = indexer;
}
public HGPersistentHandle getLastProcessed()
{
return lastProcessed;
}
public void setLastProcessed(HGPersistentHandle lastProcessed)
{
this.lastProcessed = lastProcessed;
}
public int getBatchSize()
{
return batchSize;
}
public void setBatchSize(int batchSize)
{
this.batchSize = batchSize;
}
}