package org.simantics.db.layer0.variable; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.binding.error.BindingException; import org.simantics.databoard.binding.mutable.Variant; import org.simantics.databoard.util.ObjectUtils; import org.simantics.db.ReadGraph; import org.simantics.db.common.request.ParametrizedPrimitiveRead; import org.simantics.db.common.utils.Logger; import org.simantics.db.exception.DatabaseException; import org.simantics.db.procedure.Listener; import org.simantics.simulator.variable.exceptions.NodeIsNotValidAnymoreException; import org.simantics.simulator.variable.exceptions.NodeManagerException; import org.simantics.utils.datastructures.Pair; @SuppressWarnings("rawtypes") class NodeValueRequest extends ParametrizedPrimitiveRead, Variant> implements VariableNodeReadRunnable { private Listener listener = null; private Variant value = Variables.PENDING_NODE_VALUE; private boolean wasRun = false; static class Probe implements Runnable { private Pair parameter; public Variant result; public Probe(Pair parameter) { this.parameter = parameter; } @SuppressWarnings("unchecked") @Override public void run() { try { result = NodeValueRequest.get(parameter); parameter.first.support.valueCache.put(parameter.first.node, result, 1000000000L); } catch (NodeManagerException e) { e.printStackTrace(); } catch (BindingException e) { e.printStackTrace(); } } } public NodeValueRequest(VariableNode node) { super(Pair.make(node, null)); } public NodeValueRequest(VariableNode node, Binding binding) { super(Pair.make(node, binding)); } @SuppressWarnings("unchecked") @Override public void register(ReadGraph graph, final Listener procedure) { VariableNode node = parameter.first; if(procedure.isDisposed()) { // We are not listening Variant result = (Variant)node.support.valueCache.get(node.node); if(result != null) { // Return cached value immediately procedure.execute(result); } else { // // listener = procedure; // // if(graph.getSynchronous()) { // try { // parameter.support.manager.getRealm().syncExec(this); // } catch (InterruptedException e) { // if (!wasRun) procedure.exception(e); // } catch (Throwable e) { // if (!wasRun) procedure.exception(e); // } // } else { // parameter.support.manager.getRealm().asyncExec(this); // // if(value == Variables.PENDING_NODE_VALUE) { // procedure.execute(Variables.PENDING_NODE_VALUE); // } // } // return; NodeValueRequest.Probe probe = new Probe(parameter); node.support.manager.getRealm().asyncExec(probe); if(probe.result != null) { procedure.execute(probe.result); } else { procedure.execute(Variables.PENDING_NODE_VALUE); } } return; } // We need to listen listener = procedure; // Register listening node.support.manager.addNodeListener(node.node, this); synchronized(this) { if(wasRun) { procedure.execute(value); } else { Variant result = (Variant)node.support.valueCache.get(node.node); if(result != null) { procedure.execute(result); } else { procedure.execute(Variables.PENDING_NODE_VALUE); } } } // if(listener != null) { // throw new UnsupportedOperationException(); // } // listener = procedure; // if(graph.getSynchronous()) { // try { // parameter.support.manager.getRealm().syncExec(new VariableNodeReadRunnable() { // @Override // public void run() { // parameter.support.manager.addNodeListener(parameter.node, NodeValueRequest.this); // } // @Override // public String toString() { // return "NodeValueRequest.register.sync.addNodeListener @ " + System.identityHashCode(NodeValueRequest.this); // } // }); // // if (!wasRun) procedure.exception(new InternalException("No invocation of listener from node manager " + parameter.support.manager.getClass().getName())); // } catch (InterruptedException e) { // if (!wasRun) procedure.exception(e); // } catch (Throwable e) { // if (!wasRun) procedure.exception(e); // } // } else { // parameter.support.manager.addNodeListener(parameter.node, this); // if(value == Variables.PENDING_NODE_VALUE) procedure.execute(Variables.PENDING_NODE_VALUE); // } } static class NodeListener implements VariableNodeReadRunnable { private VariableNode node; private NodeValueRequest request; public NodeListener(VariableNode node, NodeValueRequest request) { this.node = node; this.request = request; } @SuppressWarnings("unchecked") @Override public void run() { node.support.manager.addNodeListener(node.node, request); } } @SuppressWarnings("unchecked") @Override public void unregistered() { VariableNode node = parameter.first; node.support.manager.removeNodeListener(node.node, this); node.support.valueCache.removeListening(node.node); listener = null; } @SuppressWarnings("unchecked") public static Variant get(Pair parameter) throws NodeManagerException, BindingException { VariableNode node = parameter.first; Binding binding = parameter.second; if (binding != null) { Object raw = node.support.manager.getValue(node.node, binding); if(raw == null) return null; else return new Variant(binding, raw); } else { return node.support.manager.getValue(node.node); } } @SuppressWarnings("unchecked") @Override public synchronized void run() { VariableNode node = parameter.first; try { Variant newValue = get(parameter); if (wasRun && ObjectUtils.objectEquals(value, newValue)) { //System.out.println("CACHE VALUE MATCH (" + newValue + ") for " + node.node); return; } value = newValue; node.support.valueCache.put(node.node, value); } catch (Throwable e) { // Must catch everything to prevent DB client from getting stuck. if(!(e instanceof NodeIsNotValidAnymoreException)) Logger.defaultLogError(e); // Invoke the exception method of the listener Listener listener = this.listener; if (listener != null) listener.exception(new DatabaseException("External data access error", e)); wasRun = true; return; } // Must always invoke an existing listener, regardless of earlier errors. Listener listener = this.listener; if (listener != null) { //System.out.println("LISTENER " + listener + " invoked with value " + value); listener.execute(value); } wasRun = true; } @Override public String toString() { return "NodeValueRequest.run " + parameter.first.node + " " + parameter.first.support.manager + " " + System.identityHashCode(this); } }