]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.indexing/src/org/simantics/db/indexing/DatabaseIndexing.java
Worked around Windows FS problems in IndexedRelationsSearcherBase
[simantics/platform.git] / bundles / org.simantics.db.indexing / src / org / simantics / db / indexing / DatabaseIndexing.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
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
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.db.indexing;
13
14 import java.io.File;
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;
20
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;
34
35 /**
36  * A facade for Simantics graph database index management facilities.
37  * 
38  * @author Tuukka Lehtonen
39  */
40 public final class DatabaseIndexing {
41
42     private static final boolean DEBUG = IndexPolicy.TRACE_INDEX_MANAGEMENT;
43
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();
50 //        return path;
51     }
52
53     public static File getIndexLocation(Session session, Resource relation, Resource input) {
54         if (session == null)
55             throw new NullPointerException("null session");
56         if (relation == null)
57             throw new NullPointerException("null relation");
58         if (input == null)
59             throw new NullPointerException("null input");
60
61         String dir = session.getService(ServerInformation.class).getDatabaseId()
62         + "." + relation.getResourceId()
63         + "." + input.getResourceId();
64
65         return new File(getIndexBaseLocation(), dir);
66     }
67
68     private static File getAllDirtyFile() {
69         return new File(getIndexBaseLocation(), ".dirty");
70     }
71
72     private static File getChangedFile(File indexPath) {
73         return new File(indexPath, ".changed");
74     }
75
76     public static void markAllDirty() throws IOException {
77         File indexBase = getIndexBaseLocation();
78         if (!indexBase.exists() || !indexBase.isDirectory())
79             return;
80         if (DEBUG)
81             System.out.println("Marking all indexes dirty");
82         File allDirtyFile = getAllDirtyFile();
83         if (allDirtyFile.createNewFile()) {
84             FileUtils.syncFile(allDirtyFile);
85         }
86     }
87
88     public static void clearAllDirty() throws IOException {
89         if (DEBUG)
90             System.out.println("Clearing dirty state of all indexes");
91
92         File indexBase = getIndexBaseLocation();
93         if (!indexBase.exists() || !indexBase.isDirectory())
94             return;
95
96         forEachIndexPath(new Procedure<File, IOException>() {
97             @Override
98             public void execute(File indexPath) throws IOException {
99                 getChangedFile(indexPath).delete();
100             }
101         });
102
103         getAllDirtyFile().delete();
104     }
105     
106     /**
107      * Internal to indexing, invoked by {@link IndexedRelationsImpl} which
108      * doesn't want to throw these exceptions forward. Just log it.
109      * 
110      * @param indexPath
111      */
112     static void markIndexChanged(Session session, File indexPath) {
113         if (DEBUG)
114             System.out.println("Marking index dirty: " + indexPath);
115         try {
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);
122                     }
123                 }
124             }
125         } catch (IOException e) {
126             Logger.defaultLogError(e);
127         }
128     }
129
130     private static IndexChangedWriter getIndexChangedWriter(Session session) {
131         IndexChangedWriter writer = session.peekService(IndexChangedWriter.class);
132         if (writer == null) {
133             synchronized (IndexChangedWriter.class) {
134                 if (writer == null)
135                     session.registerService(IndexChangedWriter.class, writer = new IndexChangedWriter());
136             }
137         }
138         return writer;
139     }
140
141     public static void deleteAllIndexes() throws IOException {
142         File indexBase = DatabaseIndexing.getIndexBaseLocation();
143
144         ArrayList<String> filter = new ArrayList<>(2);
145         filter.add(getAllDirtyFile().getAbsolutePath());
146         filter.add(indexBase.getAbsolutePath());
147
148         FileUtils.deleteAllWithFilter(indexBase, filter);
149         FileUtils.deleteAll(indexBase);
150     }
151
152     public static void deleteIndex(final Resource relation, final Resource modelPart) throws DatabaseException {
153
154         SimanticsInternal.getSession().syncRequest(new WriteRequest() {
155
156                         @Override
157                         public void perform(WriteGraph graph) throws DatabaseException {
158                                 deleteIndex(graph, relation, modelPart);
159                         }
160                 
161         });
162
163     }
164
165     public static void deleteIndex(WriteGraph graph, final Resource relation, final Resource modelPart) throws DatabaseException {
166         
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);
174         
175     }
176     
177     public static void deleteIndex(File indexPath) throws IOException {
178         if (DEBUG)
179             System.out.println("Deleting index " + indexPath);
180
181         ArrayList<String> filter = new ArrayList<>(2);
182         filter.add(getChangedFile(indexPath).getAbsolutePath());
183         filter.add(indexPath.getAbsolutePath());
184
185         FileUtils.deleteAllWithFilter(indexPath, filter);
186         FileUtils.deleteAll(indexPath);
187     }
188
189     public static void validateIndexes() throws IOException {
190         File indexBase = getIndexBaseLocation();
191         if (DEBUG)
192             System.out.println("Validating indexes at " + indexBase);
193         if (!indexBase.exists())
194             return;
195         if (!indexBase.isDirectory()) {
196             // Make sure that index-base is a valid directory
197             if (DEBUG)
198                 System.out.println(indexBase + " is not a directory! Removing it.");
199             Path base = indexBase.toPath();
200             FileUtils.emptyDirectory(base);
201             Files.createDirectories(base);
202             return;
203         }
204         File allDirtyFile = getAllDirtyFile();
205         if (allDirtyFile.isFile()) {
206             if (DEBUG)
207                 System.out.println("All indexes marked dirty, removing them.");
208             deleteAllIndexes();
209         } else {
210             forEachIndexPath(new Procedure<File, IOException>() {
211                 @Override
212                 public void execute(File indexPath) throws IOException {
213                     File changed = getChangedFile(indexPath);
214                     if (changed.isFile()) {
215                         if (DEBUG)
216                             System.out.println("Index is dirty, removing: " + indexPath);
217                         deleteIndex(indexPath);
218                     }
219                 }
220             });
221         }
222     }
223
224     interface Procedure<T, E extends Throwable> {
225         void execute(T t) throws E;
226     }
227
228     private static <E extends Throwable> void forEachIndexPath(Procedure<File, E> callback) throws E {
229         for (File indexPath : getIndexBaseLocation().listFiles(new FileFilter() {
230             @Override
231             public boolean accept(File pathname) {
232                 return pathname.isDirectory();
233             }
234         })) {
235             callback.execute(indexPath);
236         }
237     }
238
239 }