/******************************************************************************* * 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 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.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"); getAllDirtyFile().createNewFile(); } 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; delete(getAllDirtyFile()); forEachIndexPath(new Procedure() { @Override public void execute(File indexPath) throws IOException { delete(getChangedFile(indexPath)); } }); } /** * Internal to indexing, invoked by {@link IndexedRelationsImpl} which * doesn't want to throw these exceptions forward. Just log it. * * @param indexPath */ static void markIndexChanged(File indexPath) { if (!indexPath.exists()) throw new IllegalArgumentException("index path " + indexPath + " does not exist"); if (!indexPath.isDirectory()) throw new IllegalArgumentException("index path " + indexPath + " is not a directory"); try { if (DEBUG) System.out.println("Marking index dirty: " + indexPath); getChangedFile(indexPath).createNewFile(); } catch (IOException e) { Logger.defaultLogError(e); } } public static void deleteAllIndexes() throws IOException { File indexBase = DatabaseIndexing.getIndexBaseLocation(); delete(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); delete(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."); delete(indexBase); indexBase.mkdirs(); return; } File allDirtyFile = getAllDirtyFile(); if (allDirtyFile.isFile()) { if (DEBUG) System.out.println("All indexes marked dirty, removing them."); delete(allDirtyFile); 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); } } }); } } private static void delete(File fileOrDir) throws IOException { if (fileOrDir.exists()) FileUtils.deleteAll(fileOrDir); } 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); } } }