1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.db.indexing;
15 import java.io.FileFilter;
16 import java.io.IOException;
17 import java.nio.file.Files;
18 import java.nio.file.Path;
19 import java.util.ArrayList;
21 import org.simantics.db.Resource;
22 import org.simantics.db.Session;
23 import org.simantics.db.WriteGraph;
24 import org.simantics.db.common.request.IndexRoot;
25 import org.simantics.db.common.request.WriteRequest;
26 import org.simantics.db.common.utils.Logger;
27 import org.simantics.db.exception.DatabaseException;
28 import org.simantics.db.indexing.internal.IndexChangedWriter;
29 import org.simantics.db.layer0.adapter.GenericRelationIndex;
30 import org.simantics.db.layer0.genericrelation.IndexedRelations;
31 import org.simantics.db.layer0.internal.SimanticsInternal;
32 import org.simantics.db.service.ServerInformation;
33 import org.simantics.utils.FileUtils;
36 * A facade for Simantics graph database index management facilities.
38 * @author Tuukka Lehtonen
40 public final class DatabaseIndexing {
42 private static final boolean DEBUG = IndexPolicy.TRACE_INDEX_MANAGEMENT;
44 public static File getIndexBaseLocation() {
45 return Activator.getDefault().getIndexBaseFile();
46 // Activator activator = Activator.getDefault();
47 // Bundle b = Platform.getBundle(Activator.BUNDLE_ID);
48 // IPath state = Platform.getStateLocation(b);
49 // File path = state.append("index").toFile();
53 public static File getIndexLocation(Session session, Resource relation, Resource input) {
55 throw new NullPointerException("null session");
57 throw new NullPointerException("null relation");
59 throw new NullPointerException("null input");
61 String dir = session.getService(ServerInformation.class).getDatabaseId()
62 + "." + relation.getResourceId()
63 + "." + input.getResourceId();
65 return new File(getIndexBaseLocation(), dir);
68 private static File getAllDirtyFile() {
69 return new File(getIndexBaseLocation(), ".dirty");
72 private static File getChangedFile(File indexPath) {
73 return new File(indexPath, ".changed");
76 public static void markAllDirty() throws IOException {
77 File indexBase = getIndexBaseLocation();
78 if (!indexBase.exists() || !indexBase.isDirectory())
81 System.out.println("Marking all indexes dirty");
82 File allDirtyFile = getAllDirtyFile();
83 if (allDirtyFile.createNewFile()) {
84 FileUtils.syncFile(allDirtyFile);
88 public static void clearAllDirty() throws IOException {
90 System.out.println("Clearing dirty state of all indexes");
92 File indexBase = getIndexBaseLocation();
93 if (!indexBase.exists() || !indexBase.isDirectory())
96 forEachIndexPath(new Procedure<File, IOException>() {
98 public void execute(File indexPath) throws IOException {
99 getChangedFile(indexPath).delete();
103 getAllDirtyFile().delete();
107 * Internal to indexing, invoked by {@link IndexedRelationsImpl} which
108 * doesn't want to throw these exceptions forward. Just log it.
112 static void markIndexChanged(Session session, File indexPath) {
114 System.out.println("Marking index dirty: " + indexPath);
116 File changedFile = getChangedFile(indexPath);
117 // Mark change only once per DB session.
118 if (getIndexChangedWriter(session).markDirty(changedFile)) {
119 if (indexPath.mkdirs()) {
120 if (changedFile.createNewFile()) {
121 FileUtils.syncFile(changedFile);
125 } catch (IOException e) {
126 Logger.defaultLogError(e);
130 private static IndexChangedWriter getIndexChangedWriter(Session session) {
131 IndexChangedWriter writer = session.peekService(IndexChangedWriter.class);
132 if (writer == null) {
133 synchronized (IndexChangedWriter.class) {
135 session.registerService(IndexChangedWriter.class, writer = new IndexChangedWriter());
141 public static void deleteAllIndexes() throws IOException {
142 File indexBase = DatabaseIndexing.getIndexBaseLocation();
144 ArrayList<String> filter = new ArrayList<>(2);
145 filter.add(getAllDirtyFile().getAbsolutePath());
146 filter.add(indexBase.getAbsolutePath());
148 FileUtils.deleteAllWithFilter(indexBase, filter);
149 FileUtils.deleteAll(indexBase);
152 public static void deleteIndex(final Resource relation, final Resource modelPart) throws DatabaseException {
154 SimanticsInternal.getSession().syncRequest(new WriteRequest() {
157 public void perform(WriteGraph graph) throws DatabaseException {
158 deleteIndex(graph, relation, modelPart);
165 public static void deleteIndex(WriteGraph graph, final Resource relation, final Resource modelPart) throws DatabaseException {
167 Resource model = graph.syncRequest(new IndexRoot(modelPart));
168 GenericRelationIndex index = graph.adapt(relation, GenericRelationIndex.class);
169 IndexedRelations ir = graph.getService(IndexedRelations.class);
170 // Deletes index files
171 ir.reset(null, graph, relation, model);
172 // Notifies DB listeners
173 index.reset(graph, model);
177 public static void deleteIndex(File indexPath) throws IOException {
179 System.out.println("Deleting index " + indexPath);
181 ArrayList<String> filter = new ArrayList<>(2);
182 filter.add(getChangedFile(indexPath).getAbsolutePath());
183 filter.add(indexPath.getAbsolutePath());
185 FileUtils.deleteAllWithFilter(indexPath, filter);
186 FileUtils.deleteAll(indexPath);
189 public static void validateIndexes() throws IOException {
190 File indexBase = getIndexBaseLocation();
192 System.out.println("Validating indexes at " + indexBase);
193 if (!indexBase.exists())
195 if (!indexBase.isDirectory()) {
196 // Make sure that index-base is a valid directory
198 System.out.println(indexBase + " is not a directory! Removing it.");
199 Path base = indexBase.toPath();
200 FileUtils.emptyDirectory(base);
201 Files.createDirectories(base);
204 File allDirtyFile = getAllDirtyFile();
205 if (allDirtyFile.isFile()) {
207 System.out.println("All indexes marked dirty, removing them.");
210 forEachIndexPath(new Procedure<File, IOException>() {
212 public void execute(File indexPath) throws IOException {
213 File changed = getChangedFile(indexPath);
214 if (changed.isFile()) {
216 System.out.println("Index is dirty, removing: " + indexPath);
217 deleteIndex(indexPath);
224 interface Procedure<T, E extends Throwable> {
225 void execute(T t) throws E;
228 private static <E extends Throwable> void forEachIndexPath(Procedure<File, E> callback) throws E {
229 for (File indexPath : getIndexBaseLocation().listFiles(new FileFilter() {
231 public boolean accept(File pathname) {
232 return pathname.isDirectory();
235 callback.execute(indexPath);