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