/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.db.indexing; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.util.ArrayList; import org.simantics.db.Resource; import org.simantics.db.Session; import org.simantics.db.WriteGraph; import org.simantics.db.common.request.IndexRoot; import org.simantics.db.common.request.WriteRequest; import org.simantics.db.common.utils.Logger; import org.simantics.db.exception.DatabaseException; import org.simantics.db.indexing.internal.IndexChangedWriter; import org.simantics.db.layer0.adapter.GenericRelationIndex; import org.simantics.db.layer0.genericrelation.IndexedRelations; import org.simantics.db.layer0.internal.SimanticsInternal; import org.simantics.db.service.ServerInformation; import org.simantics.utils.FileUtils; /** * A facade for Simantics graph database index management facilities. * * @author Tuukka Lehtonen */ public final class DatabaseIndexing { private static final boolean DEBUG = IndexPolicy.TRACE_INDEX_MANAGEMENT; public static File getIndexBaseLocation() { return Activator.getDefault().getIndexBaseFile(); // Activator activator = Activator.getDefault(); // Bundle b = Platform.getBundle(Activator.BUNDLE_ID); // IPath state = Platform.getStateLocation(b); // File path = state.append("index").toFile(); // return path; } public static File getIndexLocation(Session session, Resource relation, Resource input) { if (session == null) throw new NullPointerException("null session"); if (relation == null) throw new NullPointerException("null relation"); if (input == null) throw new NullPointerException("null input"); String dir = session.getService(ServerInformation.class).getDatabaseId() + "." + relation.getResourceId() + "." + input.getResourceId(); return new File(getIndexBaseLocation(), dir); } private static File getAllDirtyFile() { return new File(getIndexBaseLocation(), ".dirty"); } private static File getChangedFile(File indexPath) { return new File(indexPath, ".changed"); } public static void markAllDirty() throws IOException { File indexBase = getIndexBaseLocation(); if (!indexBase.exists() || !indexBase.isDirectory()) return; if (DEBUG) System.out.println("Marking all indexes dirty"); File allDirtyFile = getAllDirtyFile(); if (allDirtyFile.createNewFile()) { FileUtils.syncFile(allDirtyFile); } } public static void clearAllDirty() throws IOException { if (DEBUG) System.out.println("Clearing dirty state of all indexes"); File indexBase = getIndexBaseLocation(); if (!indexBase.exists() || !indexBase.isDirectory()) return; forEachIndexPath(new Procedure() { @Override public void execute(File indexPath) throws IOException { getChangedFile(indexPath).delete(); } }); getAllDirtyFile().delete(); } /** * Internal to indexing, invoked by {@link IndexedRelationsImpl} which * doesn't want to throw these exceptions forward. Just log it. * * @param indexPath */ static void markIndexChanged(Session session, File indexPath) { if (DEBUG) System.out.println("Marking index dirty: " + indexPath); try { File changedFile = getChangedFile(indexPath); // Mark change only once per DB session. if (getIndexChangedWriter(session).markDirty(changedFile)) { if (indexPath.mkdirs()) { if (changedFile.createNewFile()) { FileUtils.syncFile(changedFile); } } } } catch (IOException e) { Logger.defaultLogError(e); } } private static IndexChangedWriter getIndexChangedWriter(Session session) { IndexChangedWriter writer = session.peekService(IndexChangedWriter.class); if (writer == null) { synchronized (IndexChangedWriter.class) { if (writer == null) session.registerService(IndexChangedWriter.class, writer = new IndexChangedWriter()); } } return writer; } public static void deleteAllIndexes() throws IOException { File indexBase = DatabaseIndexing.getIndexBaseLocation(); ArrayList filter = new ArrayList<>(2); filter.add(getAllDirtyFile().getAbsolutePath()); filter.add(indexBase.getAbsolutePath()); FileUtils.deleteAllWithFilter(indexBase, filter); FileUtils.deleteAll(indexBase); } public static void deleteIndex(final Resource relation, final Resource modelPart) throws DatabaseException { SimanticsInternal.getSession().syncRequest(new WriteRequest() { @Override public void perform(WriteGraph graph) throws DatabaseException { deleteIndex(graph, relation, modelPart); } }); } public static void deleteIndex(WriteGraph graph, final Resource relation, final Resource modelPart) throws DatabaseException { Resource model = graph.syncRequest(new IndexRoot(modelPart)); GenericRelationIndex index = graph.adapt(relation, GenericRelationIndex.class); IndexedRelations ir = graph.getService(IndexedRelations.class); // Deletes index files ir.reset(null, graph, relation, model); // Notifies DB listeners index.reset(graph, model); } public static void deleteIndex(File indexPath) throws IOException { if (DEBUG) System.out.println("Deleting index " + indexPath); ArrayList filter = new ArrayList<>(2); filter.add(getChangedFile(indexPath).getAbsolutePath()); filter.add(indexPath.getAbsolutePath()); FileUtils.deleteAllWithFilter(indexPath, filter); FileUtils.deleteAll(indexPath); } public static void validateIndexes() throws IOException { File indexBase = getIndexBaseLocation(); if (DEBUG) System.out.println("Validating indexes at " + indexBase); if (!indexBase.exists()) return; if (!indexBase.isDirectory()) { // Make sure that index-base is a valid directory if (DEBUG) System.out.println(indexBase + " is not a directory! Removing it."); FileUtils.deleteAll(indexBase); indexBase.mkdirs(); return; } File allDirtyFile = getAllDirtyFile(); if (allDirtyFile.isFile()) { if (DEBUG) System.out.println("All indexes marked dirty, removing them."); deleteAllIndexes(); } else { forEachIndexPath(new Procedure() { @Override public void execute(File indexPath) throws IOException { File changed = getChangedFile(indexPath); if (changed.isFile()) { if (DEBUG) System.out.println("Index is dirty, removing: " + indexPath); deleteIndex(indexPath); } } }); } } interface Procedure { void execute(T t) throws E; } private static void forEachIndexPath(Procedure callback) throws E { for (File indexPath : getIndexBaseLocation().listFiles(new FileFilter() { @Override public boolean accept(File pathname) { return pathname.isDirectory(); } })) { callback.execute(indexPath); } } }