X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.db.indexing%2Fsrc%2Forg%2Fsimantics%2Fdb%2Findexing%2FDatabaseIndexing.java;h=f8a19504c8dc0165f38ddb34f27b7b31fe8a38ed;hp=8b74ebe792eae52e87d2e2b4af35ff276548e2be;hb=3d96e3fa94898d90d6892c081ebed7cb9e773a4a;hpb=969bd23cab98a79ca9101af33334000879fb60c5 diff --git a/bundles/org.simantics.db.indexing/src/org/simantics/db/indexing/DatabaseIndexing.java b/bundles/org.simantics.db.indexing/src/org/simantics/db/indexing/DatabaseIndexing.java index 8b74ebe79..f8a19504c 100644 --- a/bundles/org.simantics.db.indexing/src/org/simantics/db/indexing/DatabaseIndexing.java +++ b/bundles/org.simantics.db.indexing/src/org/simantics/db/indexing/DatabaseIndexing.java @@ -1,210 +1,232 @@ -/******************************************************************************* - * 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); - } - } - -} +/******************************************************************************* + * 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.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.function.Consumer; +import java.util.stream.Stream; + +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.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; +import org.slf4j.LoggerFactory; + +/** + * A facade for Simantics graph database index management facilities. + * + * @author Tuukka Lehtonen + */ +public final class DatabaseIndexing { + + private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(DatabaseIndexing.class); + + public static Path getIndexBaseLocation() { + return Activator.getDefault().getIndexBaseFile(); + } + + public static Path 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 getIndexBaseLocation().resolve(dir); + } + + private static Path getAllDirtyFile() { + return getIndexBaseLocation().resolve(".dirty"); + } + + private static Path getChangedFile(Path indexPath) { + return indexPath.resolve(".changed"); + } + + public static void markAllDirty() throws IOException { + Path indexBase = getIndexBaseLocation(); + if (!Files.exists(indexBase) || !Files.isDirectory(indexBase)) + return; + if (LOGGER.isDebugEnabled()) + LOGGER.debug("Marking all indexes dirty"); + Path allDirtyFile = getAllDirtyFile(); + if (!Files.exists(allDirtyFile)) { + Files.createFile(allDirtyFile); + FileUtils.sync(allDirtyFile); + } + } + + public static void clearAllDirty() throws IOException { + if (LOGGER.isDebugEnabled()) + LOGGER.debug("Clearing dirty state of all indexes"); + + Path indexBase = getIndexBaseLocation(); + if (!Files.exists(indexBase) || !Files.isDirectory(indexBase)) + return; + + forEachIndexPath(indexPath -> { + Path p = getChangedFile(indexPath); + try { + FileUtils.delete(p); + } catch (IOException e) { + LOGGER.error("Could not delete {}", p.toAbsolutePath(), e); + } + }); + + FileUtils.delete(getAllDirtyFile()); + } + + /** + * 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, Path indexPath) { + if (LOGGER.isDebugEnabled()) + LOGGER.debug("Marking index dirty: " + indexPath); + Path changedFile = getChangedFile(indexPath); + try { + + // Mark change only once per DB session. + if (getIndexChangedWriter(session).markDirty(changedFile)) { + Files.createDirectories(indexPath); + Files.createFile(changedFile); + FileUtils.sync(changedFile); + } + } catch (IOException e) { + LOGGER.error("Could not mark index changed for indexPath={} and changedFile={}", indexPath.toAbsolutePath(), changedFile.toAbsolutePath()); + } + } + + 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 { + Path indexBase = DatabaseIndexing.getIndexBaseLocation(); + + ArrayList filter = new ArrayList<>(2); + filter.add(getAllDirtyFile()); + filter.add(indexBase); + + FileUtils.deleteWithFilter(indexBase, path -> !filter.contains(path)); + FileUtils.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(Path indexPath) throws IOException { + if (LOGGER.isDebugEnabled()) + LOGGER.debug("Deleting index " + indexPath.toAbsolutePath()); + + ArrayList filter = new ArrayList<>(2); + filter.add(getChangedFile(indexPath)); + filter.add(indexPath); + + FileUtils.deleteWithFilter(indexPath, path -> !filter.contains(path)); + FileUtils.delete(indexPath); + } + + public static void validateIndexes() throws IOException { + Path indexBase = getIndexBaseLocation(); + if (LOGGER.isDebugEnabled()) + LOGGER.debug("Validating indexes at " + indexBase); + if (!Files.exists(indexBase)) + return; + if (!Files.isDirectory(indexBase)) { + // Make sure that index-base is a valid directory + if (LOGGER.isDebugEnabled()) + LOGGER.debug(indexBase + " is not a directory! Removing it."); + FileUtils.delete(indexBase); + Files.createDirectories(indexBase); + return; + } + Path allDirtyFile = getAllDirtyFile(); + if (Files.isRegularFile(allDirtyFile)) { + if (LOGGER.isDebugEnabled()) + LOGGER.debug("All indexes marked dirty, removing them."); + deleteAllIndexes(); + } else { + forEachIndexPath(indexPath -> { + Path changed = getChangedFile(indexPath); + if (Files.isRegularFile(changed)) { + if (LOGGER.isDebugEnabled()) + LOGGER.debug("Index is dirty, removing: " + indexPath); + try { + deleteIndex(indexPath); + } catch (IOException e) { + LOGGER.error("Could not delete index {}", indexPath.toAbsolutePath(), e); + } + } + }); + } + } + + private static void forEachIndexPath(Consumer callback) throws IOException { + try (Stream paths = Files.walk(getIndexBaseLocation(), 1).filter(Files::isDirectory)) { + Iterator iter = paths.iterator(); + while (iter.hasNext()) { + Path p = iter.next(); + callback.accept(p); + } + } + } + +}