]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.indexing/src/org/simantics/db/indexing/DatabaseIndexing.java
f8a19504c8dc0165f38ddb34f27b7b31fe8a38ed
[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.IOException;
15 import java.nio.file.Files;
16 import java.nio.file.Path;
17 import java.util.ArrayList;
18 import java.util.Iterator;
19 import java.util.function.Consumer;
20 import java.util.stream.Stream;
21
22 import org.simantics.db.Resource;
23 import org.simantics.db.Session;
24 import org.simantics.db.WriteGraph;
25 import org.simantics.db.common.request.IndexRoot;
26 import org.simantics.db.common.request.WriteRequest;
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 import org.slf4j.LoggerFactory;
35
36 /**
37  * A facade for Simantics graph database index management facilities.
38  * 
39  * @author Tuukka Lehtonen
40  */
41 public final class DatabaseIndexing {
42
43     private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(DatabaseIndexing.class);
44
45     public static Path getIndexBaseLocation() {
46         return Activator.getDefault().getIndexBaseFile();
47     }
48
49     public static Path getIndexLocation(Session session, Resource relation, Resource input) {
50         if (session == null)
51             throw new NullPointerException("null session");
52         if (relation == null)
53             throw new NullPointerException("null relation");
54         if (input == null)
55             throw new NullPointerException("null input");
56
57         String dir = session.getService(ServerInformation.class).getDatabaseId()
58         + "." + relation.getResourceId()
59         + "." + input.getResourceId();
60
61         return getIndexBaseLocation().resolve(dir);
62     }
63
64     private static Path getAllDirtyFile() {
65         return getIndexBaseLocation().resolve(".dirty");
66     }
67
68     private static Path getChangedFile(Path indexPath) {
69         return indexPath.resolve(".changed");
70     }
71
72     public static void markAllDirty() throws IOException {
73         Path indexBase = getIndexBaseLocation();
74         if (!Files.exists(indexBase) || !Files.isDirectory(indexBase))
75             return;
76         if (LOGGER.isDebugEnabled())
77             LOGGER.debug("Marking all indexes dirty");
78         Path allDirtyFile = getAllDirtyFile();
79         if (!Files.exists(allDirtyFile)) {
80             Files.createFile(allDirtyFile);
81             FileUtils.sync(allDirtyFile);
82         }
83     }
84
85     public static void clearAllDirty() throws IOException {
86         if (LOGGER.isDebugEnabled())
87             LOGGER.debug("Clearing dirty state of all indexes");
88
89         Path indexBase = getIndexBaseLocation();
90         if (!Files.exists(indexBase) || !Files.isDirectory(indexBase))
91             return;
92
93         forEachIndexPath(indexPath -> {
94             Path p = getChangedFile(indexPath);
95             try {
96                 FileUtils.delete(p);
97             } catch (IOException e) {
98                 LOGGER.error("Could not delete {}", p.toAbsolutePath(), e);
99             }
100         });
101
102         FileUtils.delete(getAllDirtyFile());
103     }
104     
105     /**
106      * Internal to indexing, invoked by {@link IndexedRelationsImpl} which
107      * doesn't want to throw these exceptions forward. Just log it.
108      * 
109      * @param indexPath
110      */
111     static void markIndexChanged(Session session, Path indexPath) {
112         if (LOGGER.isDebugEnabled())
113             LOGGER.debug("Marking index dirty: " + indexPath);
114         Path changedFile = getChangedFile(indexPath);
115         try {
116             
117             // Mark change only once per DB session.
118             if (getIndexChangedWriter(session).markDirty(changedFile)) {
119                 Files.createDirectories(indexPath);
120                 Files.createFile(changedFile);
121                 FileUtils.sync(changedFile);
122             }
123         } catch (IOException e) {
124             LOGGER.error("Could not mark index changed for indexPath={} and changedFile={}", indexPath.toAbsolutePath(), changedFile.toAbsolutePath());
125         }
126     }
127
128     private static IndexChangedWriter getIndexChangedWriter(Session session) {
129         IndexChangedWriter writer = session.peekService(IndexChangedWriter.class);
130         if (writer == null) {
131             synchronized (IndexChangedWriter.class) {
132                 if (writer == null)
133                     session.registerService(IndexChangedWriter.class, writer = new IndexChangedWriter());
134             }
135         }
136         return writer;
137     }
138
139     public static void deleteAllIndexes() throws IOException {
140         Path indexBase = DatabaseIndexing.getIndexBaseLocation();
141
142         ArrayList<Path> filter = new ArrayList<>(2);
143         filter.add(getAllDirtyFile());
144         filter.add(indexBase);
145
146         FileUtils.deleteWithFilter(indexBase, path -> !filter.contains(path));
147         FileUtils.delete(indexBase);
148     }
149
150     public static void deleteIndex(final Resource relation, final Resource modelPart) throws DatabaseException {
151
152         SimanticsInternal.getSession().syncRequest(new WriteRequest() {
153
154                         @Override
155                         public void perform(WriteGraph graph) throws DatabaseException {
156                                 deleteIndex(graph, relation, modelPart);
157                         }
158                 
159         });
160
161     }
162
163     public static void deleteIndex(WriteGraph graph, final Resource relation, final Resource modelPart) throws DatabaseException {
164         
165         Resource model = graph.syncRequest(new IndexRoot(modelPart));
166         GenericRelationIndex index = graph.adapt(relation, GenericRelationIndex.class);
167         IndexedRelations ir = graph.getService(IndexedRelations.class);
168         // Deletes index files
169         ir.reset(null, graph, relation, model);
170         // Notifies DB listeners
171         index.reset(graph, model);
172         
173     }
174     
175     public static void deleteIndex(Path indexPath) throws IOException {
176         if (LOGGER.isDebugEnabled())
177             LOGGER.debug("Deleting index " + indexPath.toAbsolutePath());
178
179         ArrayList<Path> filter = new ArrayList<>(2);
180         filter.add(getChangedFile(indexPath));
181         filter.add(indexPath);
182
183         FileUtils.deleteWithFilter(indexPath, path -> !filter.contains(path));
184         FileUtils.delete(indexPath);
185     }
186
187     public static void validateIndexes() throws IOException {
188         Path indexBase = getIndexBaseLocation();
189         if (LOGGER.isDebugEnabled())
190             LOGGER.debug("Validating indexes at " + indexBase);
191         if (!Files.exists(indexBase))
192             return;
193         if (!Files.isDirectory(indexBase)) {
194             // Make sure that index-base is a valid directory
195             if (LOGGER.isDebugEnabled())
196                 LOGGER.debug(indexBase + " is not a directory! Removing it.");
197             FileUtils.delete(indexBase);
198             Files.createDirectories(indexBase);
199             return;
200         }
201         Path allDirtyFile = getAllDirtyFile();
202         if (Files.isRegularFile(allDirtyFile)) {
203             if (LOGGER.isDebugEnabled())
204                 LOGGER.debug("All indexes marked dirty, removing them.");
205             deleteAllIndexes();
206         } else {
207             forEachIndexPath(indexPath -> {
208                 Path changed = getChangedFile(indexPath);
209                 if (Files.isRegularFile(changed)) {
210                     if (LOGGER.isDebugEnabled())
211                         LOGGER.debug("Index is dirty, removing: " + indexPath);
212                     try {
213                         deleteIndex(indexPath);
214                     } catch (IOException e) {
215                         LOGGER.error("Could not delete index {}", indexPath.toAbsolutePath(), e);
216                     }
217                 }
218             });
219         }
220     }
221
222     private static void forEachIndexPath(Consumer<Path> callback) throws IOException {
223         try (Stream<Path> paths = Files.walk(getIndexBaseLocation(), 1).filter(Files::isDirectory)) {
224             Iterator<Path> iter = paths.iterator();
225             while (iter.hasNext()) {
226                 Path p = iter.next();
227                 callback.accept(p);
228             }
229         }
230     }
231
232 }