/* * 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; } }