package fi.vtt.simantics.procore.internal; import gnu.trove.set.hash.TIntHashSet; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicInteger; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.Statement; import org.simantics.db.VirtualGraph; import org.simantics.db.VirtualGraph.Persistency; import org.simantics.db.WriteOnlyGraph; import org.simantics.db.common.utils.NameUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.db.impl.ClusterI; import org.simantics.db.impl.ResourceImpl; import org.simantics.db.impl.TransientGraph; import org.simantics.db.impl.graph.ReadGraphImpl; import org.simantics.db.impl.support.VirtualGraphServerSupport; import org.simantics.db.request.Read; import org.simantics.db.service.SerialisationSupport; import org.simantics.db.service.ServerInformation; import org.simantics.db.service.TransferableGraphSupport; import org.simantics.db.service.VirtualGraphSupport; import org.simantics.db.service.XSupport; import org.simantics.layer0.Layer0; import org.simantics.utils.FileUtils; public class VirtualGraphServerSupportImpl implements VirtualGraphSupport, VirtualGraphServerSupport { final private static boolean DEBUG = false; final private SessionImplSocket session; final public File virtualGraphStoragePath; public String dbString = null; public TIntHashSet virtuals = new TIntHashSet(); final public CopyOnWriteArrayList providers = new CopyOnWriteArrayList(); final private CopyOnWriteArrayList workspaceProviders = new CopyOnWriteArrayList(); final private CopyOnWriteArrayList memoryProviders = new CopyOnWriteArrayList(); public AtomicInteger virtualId; private boolean hasVirtuals = false; public VirtualGraphServerSupportImpl(SessionImplSocket session, File path) { this.session = session; this.virtualGraphStoragePath = path; } void connect(String dbString) throws Exception { virtualId = new AtomicInteger(-2); this.dbString = dbString; XSupport support = session.getService(XSupport.class); if (support.rolledback()) { for (File file : virtualGraphStoragePath.listFiles()) { if (!file.delete()) { throw new IOException("Could not delete file " + file.getAbsolutePath()); } } } File file = new File(virtualGraphStoragePath, "virtualGraphs." + dbString + ".dat"); // System.out.println("scanning " + file.getAbsolutePath()); if(file.exists()) { try { InputStream stream = new FileInputStream(file); final ObjectInputStream os = new ObjectInputStream(stream); virtualId = new AtomicInteger(os.readInt()); // System.out.println("virtualId=" + virtualId.get()); os.close(); stream.close(); hasVirtuals = true; String databaseId = session.getService(ServerInformation.class).getDatabaseId(); String matcher = ".W." + databaseId + ".vg."; // Load existing workspace persistent graphs for(File virtualGraph : virtualGraphStoragePath.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { boolean matches = name.contains(matcher); return matches; } })) { String name = virtualGraph.getName(); String[] parts = name.split("\\x2E", 2); getWorkspacePersistent(parts[0]); } } catch (IOException e) { e.printStackTrace(); } } else { if (DEBUG) System.out.println("No stored virtual graphs."); } } public void saveVirtualGraphState(SessionImplSocket session) { if(!hasVirtuals) return; try { String databaseId = session.getService(ServerInformation.class).getDatabaseId(); String serverId = session.getService(ServerInformation.class).getServerId(); File file = new File(virtualGraphStoragePath, "virtualGraphs." + databaseId + "." + serverId + ".dat"); OutputStream stream = new FileOutputStream(file); final ObjectOutputStream os = new ObjectOutputStream(stream); os.writeInt(virtualId.get()); os.flush(); stream.close(); } catch (IOException e) { e.printStackTrace(); } } public void disposeVirtualGraphs() { if(!hasVirtuals) return; saveVirtualGraphState(session); for(TransientGraph graph : workspaceProviders) graph.dispose(); } public void saveVirtualGraphs() { if(!hasVirtuals) return; saveVirtualGraphState(session); for(TransientGraph graph : workspaceProviders) graph.save(); } @Override public void saveAll() { saveVirtualGraphs(); } @Override public VirtualGraph getMemoryPersistent(String identifier) { if(identifier == null) throw new IllegalArgumentException("Argument cannot be null!"); for(TransientGraph graph : memoryProviders) { if(identifier.equals(graph.getIdentifier())) return graph; } String databaseId = session.getService(ServerInformation.class).getDatabaseId(); VirtualGraphServerSupport vgss = session.getService(VirtualGraphServerSupport.class); TransientGraph result = TransientGraph.memoryPersistent(new SerialisationSupportImpl(session), vgss, session.resourceSupport, session, databaseId, identifier); memoryProviders.add(result); providers.add(result); return result; } private TransientGraph createWorkspacePersistentInternal(String identifier) { String databaseId = session.getService(ServerInformation.class).getDatabaseId(); VirtualGraphServerSupport vgss = session.getService(VirtualGraphServerSupport.class); try { return TransientGraph.workspacePersistent(new SerialisationSupportImpl(session), vgss, session.resourceSupport, session, databaseId, identifier); } catch (Exception e) { 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)); return TransientGraph.memoryPersistent(new SerialisationSupportImpl(session), vgss, session.resourceSupport, session, databaseId, identifier); } } @Override public VirtualGraph getWorkspacePersistent(String identifier) { if(identifier == null) throw new IllegalArgumentException("Argument cannot be null!"); for(TransientGraph graph : workspaceProviders) { if(identifier.equals(graph.getIdentifier())) return graph; } TransientGraph result = createWorkspacePersistentInternal(identifier); workspaceProviders.add(result); providers.add(result); hasVirtuals = true; return result; } @Override public boolean discard(VirtualGraph provider) { if (!(provider instanceof TransientGraph)) return false; if (!providers.remove(provider)) return false; TransientGraph tg = (TransientGraph) provider; if (workspaceProviders.remove(provider)) { // TODO: remove possibly existing data from disk tg.dispose(); } else if (memoryProviders.remove(provider)) { tg.dispose(); } return true; } public Resource getPersistentResource(WriteOnlyGraph graph, Resource resource, Map creation) throws DatabaseException { if(resource.isPersistent()) return resource; else { Resource result = creation.get(resource); if(result == null) { result = graph.newResource(); creation.put(resource, result); } return result; } } @Override public boolean integrate(WriteOnlyGraph graph, VirtualGraph provider) throws DatabaseException { if (!(provider instanceof TransientGraph)) return false; if (!providers.remove(provider)) return false; workspaceProviders.remove(provider); memoryProviders.remove(provider); TransferableGraphSupport tgSupport = graph.getService(TransferableGraphSupport.class); TransientGraph tg = (TransientGraph) provider; Map creation = new HashMap(); for(Statement stm : tg.listStatements()) { Resource subject = getPersistentResource(graph, stm.getSubject(), creation); Resource predicate = getPersistentResource(graph, stm.getPredicate(), creation); Resource object = getPersistentResource(graph, stm.getObject(), creation); graph.claim(subject, predicate, null, object); } for(Resource r : tg.listValues()) { byte[] value = tg.getValue(((ResourceImpl)r).id); tgSupport.setValue(graph, getPersistentResource(graph, r, creation), null, value); } discard(provider); return true; } @Override public Collection getVirtualGraphs(int subject) { if(subject < 0 || virtuals.contains(subject)) return providers; else return null; } @Override public void removeVirtual(int id) { virtuals.remove(id); } @Override public void addVirtual(int id) { assert(id > 0); // System.err.println("addVirtual " + id); virtuals.add(id); ClusterI cluster = session.clusterTable.getClusterByResourceKey(id); cluster.markVirtual(); } @Override public int createVirtual() { return virtualId.decrementAndGet(); } @Override public File storagePath() { return virtualGraphStoragePath; } @Override public Collection listStatements(VirtualGraph graph_) { TransientGraph graph = (TransientGraph)graph_; return graph.listStatements(); } @Override public Collection listValues(VirtualGraph graph_) { TransientGraph graph = (TransientGraph)graph_; return graph.listValues(); } @Override public Collection listGraphs() { ArrayList result = new ArrayList(); result.addAll(memoryProviders); result.addAll(workspaceProviders); return result; } public String report(final File file) { session.asyncRequest(new Read() { @Override public String perform(ReadGraph graph) throws DatabaseException { SerialisationSupport ss = session.getService(SerialisationSupport.class); StringBuilder b = new StringBuilder(); try { for(VirtualGraph vg : listGraphs()) { TransientGraph tg = (TransientGraph)vg; if(Persistency.MEMORY == tg.getPersistency()) b.append("Memory persistent virtual graph '" + tg.getIdentifier() + "'\n"); if(Persistency.WORKSPACE == tg.getPersistency()) b.append("Workspace persistent virtual graph '" + tg.getIdentifier() + "'\n"); for(Statement stm : listStatements(tg)) { int s = ss.getTransientId(stm.getSubject()); int p = ss.getTransientId(stm.getPredicate()); int o = ss.getTransientId(stm.getObject()); String sName = NameUtils.getSafeName(graph, stm.getSubject()); String pName = NameUtils.getSafeName(graph, stm.getPredicate()); String oName = NameUtils.getSafeName(graph, stm.getObject()); b.append(" S '" + sName + "' '" + pName + "' '" + oName + "' " + s + " " + p + " " + o + "\n"); } for(Resource r : listValues(tg)) { String sName = NameUtils.getSafeName(graph, r); Object value = graph.getPossibleValue(r); b.append(" V '" + sName + "' '" + value + "'\n"); } } FileUtils.writeFile(file, b.toString().getBytes()); } catch (IOException e) { e.printStackTrace(); return "ERROR"; } catch (DatabaseException e) { e.printStackTrace(); return "ERROR"; } return "OK"; } }); return "OK"; } @Override public VirtualGraph getGraph(ReadGraph graph, Resource subject, Resource predicate, Resource object) throws DatabaseException { ReadGraphImpl impl = (ReadGraphImpl)graph; return impl.processor.getProvider(subject, predicate, object); } @Override public VirtualGraph getGraph(ReadGraph graph, Resource subject, Resource predicate) throws DatabaseException { ReadGraphImpl impl = (ReadGraphImpl)graph; return impl.processor.getProvider(subject, predicate); } @Override public VirtualGraph getGraph(ReadGraph graph, Resource subject) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); if(graph.hasStatement(subject, L0.InstanceOf)) { return getGraph(graph, subject, L0.InstanceOf); } else if (graph.hasStatement(subject, L0.Inherits)) { return getGraph(graph, subject, L0.Inherits); } else if (graph.hasStatement(subject, L0.SubrelationOf)) { return getGraph(graph, subject, L0.SubrelationOf); } else { throw new DatabaseException("Resource is invalid, should have a statement with either L0.InstanceOf, L0.Inherits or L0.SubrelationOf " + subject); } } }