Merge commit 'c46f0ff'
[simantics/platform.git] / bundles / org.simantics.db.procore / src / fi / vtt / simantics / procore / internal / VirtualGraphServerSupportImpl.java
1 package fi.vtt.simantics.procore.internal;\r
2 \r
3 import gnu.trove.set.hash.TIntHashSet;\r
4 \r
5 import java.io.File;\r
6 import java.io.FileInputStream;\r
7 import java.io.FileOutputStream;\r
8 import java.io.FilenameFilter;\r
9 import java.io.IOException;\r
10 import java.io.InputStream;\r
11 import java.io.ObjectInputStream;\r
12 import java.io.ObjectOutputStream;\r
13 import java.io.OutputStream;\r
14 import java.util.ArrayList;\r
15 import java.util.Collection;\r
16 import java.util.HashMap;\r
17 import java.util.Map;\r
18 import java.util.concurrent.CopyOnWriteArrayList;\r
19 import java.util.concurrent.atomic.AtomicInteger;\r
20 \r
21 import org.eclipse.core.runtime.IStatus;\r
22 import org.eclipse.core.runtime.Status;\r
23 import org.simantics.db.ReadGraph;\r
24 import org.simantics.db.Resource;\r
25 import org.simantics.db.Statement;\r
26 import org.simantics.db.VirtualGraph;\r
27 import org.simantics.db.VirtualGraph.Persistency;\r
28 import org.simantics.db.WriteOnlyGraph;\r
29 import org.simantics.db.common.utils.NameUtils;\r
30 import org.simantics.db.exception.DatabaseException;\r
31 import org.simantics.db.impl.ClusterI;\r
32 import org.simantics.db.impl.ResourceImpl;\r
33 import org.simantics.db.impl.TransientGraph;\r
34 import org.simantics.db.impl.graph.ReadGraphImpl;\r
35 import org.simantics.db.impl.support.VirtualGraphServerSupport;\r
36 import org.simantics.db.request.Read;\r
37 import org.simantics.db.service.SerialisationSupport;\r
38 import org.simantics.db.service.ServerInformation;\r
39 import org.simantics.db.service.TransferableGraphSupport;\r
40 import org.simantics.db.service.VirtualGraphSupport;\r
41 import org.simantics.db.service.XSupport;\r
42 import org.simantics.layer0.Layer0;\r
43 import org.simantics.utils.FileUtils;\r
44 \r
45 public class VirtualGraphServerSupportImpl implements VirtualGraphSupport, VirtualGraphServerSupport {\r
46 \r
47         final private static boolean DEBUG = false;\r
48         final private SessionImplSocket session;\r
49 \r
50         final public File virtualGraphStoragePath;\r
51         public String dbString = null;\r
52 \r
53         public TIntHashSet virtuals = new TIntHashSet();\r
54 \r
55         final public CopyOnWriteArrayList<TransientGraph>  providers          = new CopyOnWriteArrayList<TransientGraph>();\r
56         final private CopyOnWriteArrayList<TransientGraph> workspaceProviders = new CopyOnWriteArrayList<TransientGraph>();\r
57         final private CopyOnWriteArrayList<TransientGraph> memoryProviders    = new CopyOnWriteArrayList<TransientGraph>();\r
58 \r
59         public AtomicInteger virtualId;\r
60         private boolean hasVirtuals = false;\r
61 \r
62         public VirtualGraphServerSupportImpl(SessionImplSocket session, File path) {\r
63                 this.session = session;\r
64                 this.virtualGraphStoragePath = path;\r
65         }\r
66 \r
67     void connect(String dbString) throws Exception {\r
68         virtualId = new AtomicInteger(-2);\r
69         this.dbString = dbString;\r
70 \r
71         XSupport support = session.getService(XSupport.class);\r
72         if (support.rolledback()) {\r
73             for (File file : virtualGraphStoragePath.listFiles()) {\r
74                 if (!file.delete()) {\r
75                     throw new IOException("Could not delete file " + file.getAbsolutePath());\r
76                 }\r
77             }\r
78         }\r
79 \r
80                 File file = new File(virtualGraphStoragePath, "virtualGraphs." + dbString + ".dat");\r
81                 \r
82                 //      System.out.println("scanning " + file.getAbsolutePath());\r
83 \r
84                 if(file.exists()) {\r
85                         try {\r
86                                 InputStream stream = new FileInputStream(file);\r
87                                 final ObjectInputStream os = new ObjectInputStream(stream);\r
88                                 virtualId = new AtomicInteger(os.readInt());\r
89                                 //                      System.out.println("virtualId=" + virtualId.get());\r
90                                 os.close();\r
91                                 stream.close();\r
92 \r
93                                 hasVirtuals = true;\r
94 \r
95                                 String databaseId = session.getService(ServerInformation.class).getDatabaseId();\r
96                                 String matcher = ".W." + databaseId + ".vg.";\r
97                                 \r
98                                 // Load existing workspace persistent graphs\r
99                                 for(File virtualGraph : virtualGraphStoragePath.listFiles(new FilenameFilter() {\r
100                                         @Override\r
101                                         public boolean accept(File dir, String name) {\r
102                                             boolean matches = name.contains(matcher);\r
103                                                 return matches;\r
104                                         }\r
105                                 })) {\r
106                                         String name = virtualGraph.getName();\r
107                                         String[] parts = name.split("\\x2E", 2);\r
108                                         getWorkspacePersistent(parts[0]);\r
109                                 }\r
110 \r
111                         } catch (IOException e) {\r
112                                 e.printStackTrace();\r
113                         }\r
114                 } else {\r
115                         if (DEBUG)\r
116                                 System.out.println("No stored virtual graphs.");\r
117                 }\r
118 \r
119         }\r
120 \r
121         public void saveVirtualGraphState(SessionImplSocket session) {\r
122 \r
123                 if(!hasVirtuals) return;\r
124 \r
125                 try {\r
126 \r
127                         String databaseId = session.getService(ServerInformation.class).getDatabaseId();\r
128                         String serverId = session.getService(ServerInformation.class).getServerId();\r
129                         File file = new File(virtualGraphStoragePath, "virtualGraphs." + databaseId + "." + serverId + ".dat");\r
130 \r
131                         OutputStream stream = new FileOutputStream(file);\r
132                         final ObjectOutputStream os = new ObjectOutputStream(stream);\r
133                         os.writeInt(virtualId.get());\r
134                         os.flush();\r
135                         stream.close();\r
136 \r
137                 } catch (IOException e) {\r
138                         e.printStackTrace();\r
139                 }\r
140 \r
141         }\r
142 \r
143         public void disposeVirtualGraphs() {\r
144 \r
145                 if(!hasVirtuals) return;\r
146 \r
147                 saveVirtualGraphState(session);\r
148                 for(TransientGraph graph : workspaceProviders) graph.dispose();\r
149 \r
150         }\r
151 \r
152         public void saveVirtualGraphs() {\r
153 \r
154                 if(!hasVirtuals) return;\r
155 \r
156                 saveVirtualGraphState(session);\r
157                 for(TransientGraph graph : workspaceProviders) graph.save();\r
158 \r
159         }\r
160 \r
161         @Override\r
162         public void saveAll() {\r
163                 saveVirtualGraphs();\r
164         }\r
165 \r
166         @Override\r
167         public VirtualGraph getMemoryPersistent(String identifier) {\r
168 \r
169                 if(identifier == null) throw new IllegalArgumentException("Argument cannot be null!");\r
170 \r
171                 for(TransientGraph graph : memoryProviders) {\r
172                         if(identifier.equals(graph.getIdentifier())) return graph;\r
173                 }\r
174 \r
175                 String databaseId =  session.getService(ServerInformation.class).getDatabaseId();\r
176                 VirtualGraphServerSupport vgss = session.getService(VirtualGraphServerSupport.class);\r
177 \r
178         TransientGraph result = TransientGraph.memoryPersistent(new SerialisationSupportImpl(session), vgss, session.resourceSupport, session, databaseId, identifier);\r
179         memoryProviders.add(result);\r
180         providers.add(result);\r
181         return result;\r
182         }\r
183 \r
184         private TransientGraph createWorkspacePersistentInternal(String identifier) {\r
185 \r
186             String databaseId = session.getService(ServerInformation.class).getDatabaseId();\r
187             VirtualGraphServerSupport vgss = session.getService(VirtualGraphServerSupport.class);\r
188 \r
189             try {\r
190                 return TransientGraph.workspacePersistent(new SerialisationSupportImpl(session), vgss, session.resourceSupport, session, databaseId, identifier);\r
191             } catch (Exception e) {\r
192                 Activator.log(new Status(IStatus.WARNING, Activator.PLUGIN_ID, "Failed to restore contents of previous virtual graph with identifier '" + identifier + "'. Resetting its contents to empty. See exception for problem details.", e));\r
193             return TransientGraph.memoryPersistent(new SerialisationSupportImpl(session), vgss, session.resourceSupport, session, databaseId, identifier);\r
194             }\r
195 \r
196         }\r
197         \r
198         @Override\r
199         public VirtualGraph getWorkspacePersistent(String identifier) {\r
200 \r
201                 if(identifier == null) throw new IllegalArgumentException("Argument cannot be null!");\r
202 \r
203                 for(TransientGraph graph : workspaceProviders) {\r
204                         if(identifier.equals(graph.getIdentifier())) return graph;\r
205                 }\r
206 \r
207                 TransientGraph result = createWorkspacePersistentInternal(identifier);\r
208                 \r
209         workspaceProviders.add(result);\r
210         providers.add(result);\r
211         hasVirtuals = true;\r
212         return result;\r
213 \r
214         }\r
215 \r
216         @Override\r
217         public boolean discard(VirtualGraph provider) {\r
218                 if (!(provider instanceof TransientGraph))\r
219                         return false;\r
220                 if (!providers.remove(provider))\r
221                         return false;\r
222 \r
223                 TransientGraph tg = (TransientGraph) provider;\r
224 \r
225                 if (workspaceProviders.remove(provider)) {\r
226                         // TODO: remove possibly existing data from disk\r
227                         tg.dispose();\r
228                 } else if (memoryProviders.remove(provider)) {\r
229                         tg.dispose();\r
230                 }\r
231                 return true;\r
232         }\r
233 \r
234         public Resource getPersistentResource(WriteOnlyGraph graph, Resource resource, Map<Resource, Resource> creation) throws DatabaseException {\r
235                 if(resource.isPersistent()) return resource;\r
236                 else {\r
237                         Resource result = creation.get(resource);\r
238                         if(result == null) {\r
239                                 result = graph.newResource();\r
240                                 creation.put(resource, result);\r
241                         }\r
242                         return result;\r
243                 }\r
244         }\r
245         \r
246         @Override\r
247         public boolean integrate(WriteOnlyGraph graph, VirtualGraph provider) throws DatabaseException {\r
248 \r
249                 if (!(provider instanceof TransientGraph))\r
250                         return false;\r
251                 if (!providers.remove(provider))\r
252                         return false;\r
253 \r
254                 workspaceProviders.remove(provider);\r
255                 memoryProviders.remove(provider);\r
256                 \r
257                 TransferableGraphSupport tgSupport = graph.getService(TransferableGraphSupport.class);\r
258                 TransientGraph tg = (TransientGraph) provider;\r
259                 \r
260                 Map<Resource, Resource> creation = new HashMap<Resource, Resource>();\r
261                 for(Statement stm : tg.listStatements()) {\r
262                         Resource subject = getPersistentResource(graph, stm.getSubject(), creation);\r
263                         Resource predicate = getPersistentResource(graph, stm.getPredicate(), creation);\r
264                         Resource object = getPersistentResource(graph, stm.getObject(), creation);\r
265                         graph.claim(subject, predicate, null, object);\r
266                 }\r
267                 for(Resource r : tg.listValues()) {\r
268                         byte[] value = tg.getValue(((ResourceImpl)r).id);\r
269                         tgSupport.setValue(graph, getPersistentResource(graph, r, creation), null, value);\r
270                 }\r
271                 discard(provider);\r
272                 return true;\r
273                 \r
274         }\r
275 \r
276         @Override\r
277         public Collection<TransientGraph> getVirtualGraphs(int subject) {\r
278                 if(subject < 0 || virtuals.contains(subject)) return providers;\r
279                 else return null; \r
280         }\r
281 \r
282         @Override\r
283         public void removeVirtual(int id) {\r
284                 virtuals.remove(id);\r
285         }\r
286 \r
287         @Override\r
288         public void addVirtual(int id) {\r
289                 assert(id > 0);\r
290                 //              System.err.println("addVirtual " + id);\r
291                 virtuals.add(id);\r
292                 ClusterI cluster = session.clusterTable.getClusterByResourceKey(id);\r
293                 cluster.markVirtual();\r
294         }\r
295 \r
296         @Override\r
297         public int createVirtual() {\r
298                 return virtualId.decrementAndGet();\r
299         }\r
300 \r
301         @Override\r
302         public File storagePath() {\r
303                 return virtualGraphStoragePath;\r
304         }\r
305 \r
306         @Override\r
307         public Collection<Statement> listStatements(VirtualGraph graph_) {\r
308                 TransientGraph graph = (TransientGraph)graph_;\r
309                 return graph.listStatements();\r
310         }\r
311 \r
312         @Override\r
313         public Collection<Resource> listValues(VirtualGraph graph_) {\r
314                 TransientGraph graph = (TransientGraph)graph_;\r
315                 return graph.listValues();\r
316         }\r
317 \r
318         @Override\r
319         public Collection<VirtualGraph> listGraphs() {\r
320                 ArrayList<VirtualGraph> result = new ArrayList<VirtualGraph>();\r
321                 result.addAll(memoryProviders);\r
322                 result.addAll(workspaceProviders);\r
323                 return result;\r
324         }\r
325 \r
326         public String report(final File file) {\r
327 \r
328                 session.asyncRequest(new Read<String>() {\r
329 \r
330                         @Override\r
331                         public String perform(ReadGraph graph) throws DatabaseException {\r
332 \r
333                                 SerialisationSupport ss = session.getService(SerialisationSupport.class);\r
334                                 StringBuilder b = new StringBuilder();\r
335                                 try {\r
336                                         for(VirtualGraph vg : listGraphs()) {\r
337                                                 TransientGraph tg = (TransientGraph)vg;\r
338                                                 if(Persistency.MEMORY == tg.getPersistency()) b.append("Memory persistent virtual graph '" + tg.getIdentifier() + "'\n");\r
339                                                 if(Persistency.WORKSPACE == tg.getPersistency()) b.append("Workspace persistent virtual graph '" + tg.getIdentifier() + "'\n");\r
340                                                 for(Statement stm : listStatements(tg)) {\r
341                                                         int s = ss.getTransientId(stm.getSubject());\r
342                                                         int p = ss.getTransientId(stm.getPredicate());\r
343                                                         int o = ss.getTransientId(stm.getObject());\r
344                                                         String sName = NameUtils.getSafeName(graph, stm.getSubject());\r
345                                                         String pName = NameUtils.getSafeName(graph, stm.getPredicate());\r
346                                                         String oName = NameUtils.getSafeName(graph, stm.getObject());\r
347                                                         b.append(" S '" + sName + "' '" + pName + "' '" + oName + "' " + s + " " + p + " " + o + "\n");\r
348                                                 }\r
349                                                 for(Resource r : listValues(tg)) {\r
350                                                         String sName = NameUtils.getSafeName(graph, r);\r
351                                                         Object value = graph.getPossibleValue(r);\r
352                                                         b.append(" V '" + sName + "' '" + value + "'\n");\r
353                                                 }\r
354                                         }\r
355                                         FileUtils.writeFile(file, b.toString().getBytes());\r
356                                 } catch (IOException e) {\r
357                                         e.printStackTrace();\r
358                                         return "ERROR";\r
359                                 } catch (DatabaseException e) {\r
360                                         e.printStackTrace();\r
361                                         return "ERROR";\r
362                                 }\r
363                                 return "OK";\r
364 \r
365                         }\r
366 \r
367                 });\r
368                 return "OK";\r
369 \r
370         }\r
371 \r
372         @Override\r
373         public VirtualGraph getGraph(ReadGraph graph, Resource subject, Resource predicate, Resource object) throws DatabaseException {\r
374                 ReadGraphImpl impl = (ReadGraphImpl)graph;\r
375                 return impl.processor.getProvider(subject, predicate, object);\r
376         }\r
377 \r
378         @Override\r
379         public VirtualGraph getGraph(ReadGraph graph, Resource subject, Resource predicate) throws DatabaseException {\r
380                 ReadGraphImpl impl = (ReadGraphImpl)graph;\r
381                 return impl.processor.getProvider(subject, predicate);\r
382         }\r
383 \r
384         @Override\r
385         public VirtualGraph getGraph(ReadGraph graph, Resource subject) throws DatabaseException {\r
386                 Layer0 L0 = Layer0.getInstance(graph);\r
387                 if(graph.hasStatement(subject, L0.InstanceOf)) {\r
388                         return getGraph(graph, subject, L0.InstanceOf);\r
389                 } else if (graph.hasStatement(subject, L0.Inherits)) {\r
390                         return getGraph(graph, subject, L0.Inherits);\r
391                 } else if (graph.hasStatement(subject, L0.SubrelationOf)) {\r
392                         return getGraph(graph, subject, L0.SubrelationOf);\r
393                 } else {\r
394                         throw new DatabaseException("Resource is invalid, should have a statement with either L0.InstanceOf, L0.Inherits or L0.SubrelationOf " + subject);\r
395                 }\r
396         }\r
397         \r
398 }\r