NodeStructureRequest and NodeValueRequest fire ExternalRead twice
[simantics/platform.git] / bundles / org.simantics.db.layer0 / src / org / simantics / db / layer0 / variable / NodeValueRequest.java
1 package org.simantics.db.layer0.variable;
2
3 import org.simantics.databoard.binding.Binding;
4 import org.simantics.databoard.binding.error.BindingException;
5 import org.simantics.databoard.binding.mutable.Variant;
6 import org.simantics.databoard.util.ObjectUtils;
7 import org.simantics.db.ReadGraph;
8 import org.simantics.db.common.request.ParametrizedPrimitiveRead;
9 import org.simantics.db.exception.DatabaseException;
10 import org.simantics.db.procedure.Listener;
11 import org.simantics.simulator.variable.NodeManager;
12 import org.simantics.simulator.variable.exceptions.NodeIsNotValidAnymoreException;
13 import org.simantics.simulator.variable.exceptions.NodeManagerException;
14 import org.simantics.utils.datastructures.Pair;
15 import org.slf4j.Logger;
16 import org.slf4j.LoggerFactory;
17
18 @SuppressWarnings("rawtypes")
19 class NodeValueRequest extends ParametrizedPrimitiveRead<Pair<VariableNode,Binding>, Variant> implements VariableNodeReadRunnable {
20
21     private static final Logger LOGGER = LoggerFactory.getLogger(NodeValueRequest.class);
22
23     private Listener<Variant> listener = null;
24     private Variant value = Variables.PENDING_NODE_VALUE;
25     private boolean wasRun = false;
26
27     static class Probe implements Runnable {
28
29         private Pair<VariableNode,Binding> parameter;
30         public Variant result;
31
32         public Probe(Pair<VariableNode,Binding> parameter) {
33             this.parameter = parameter;
34         }
35
36         @SuppressWarnings("unchecked")
37         @Override
38         public void run() {
39             try {
40                 result = NodeValueRequest.get(parameter);
41                 parameter.first.support.valueCache.put(parameter.first.node, result, 1000000000L);
42             } catch (NodeManagerException e) {
43                 e.printStackTrace();
44             } catch (BindingException e) {
45                 e.printStackTrace();
46             }
47         }
48
49     }
50
51     public NodeValueRequest(VariableNode node) {
52         super(Pair.<VariableNode, Binding>make(node, null));
53     }
54
55     public NodeValueRequest(VariableNode node, Binding binding) {
56         super(Pair.<VariableNode, Binding>make(node, binding));
57     }
58
59     @SuppressWarnings("unchecked")
60     @Override
61     public void register(ReadGraph graph, final Listener<Variant> procedure) {
62
63         VariableNode node = parameter.first;
64
65         if(procedure.isDisposed()) {
66
67             // We are not listening
68             Variant result = (Variant)node.support.valueCache.get(node.node);
69
70             if(result != null) {
71                 // Return cached value immediately
72                 procedure.execute(result);
73             } else {
74
75 //              
76 //            listener = procedure;
77 //            
78 //            if(graph.getSynchronous()) {
79 //                try {
80 //                    parameter.support.manager.getRealm().syncExec(this);
81 //                } catch (InterruptedException e) {
82 //                    if (!wasRun) procedure.exception(e);
83 //                } catch (Throwable e) {
84 //                    if (!wasRun) procedure.exception(e);
85 //                }
86 //            } else {
87 //                parameter.support.manager.getRealm().asyncExec(this);
88 //            
89 //                if(value == Variables.PENDING_NODE_VALUE) {
90 //                    procedure.execute(Variables.PENDING_NODE_VALUE);
91 //                }
92 //            }
93 //            return;
94
95                 NodeValueRequest.Probe probe = new Probe(parameter);
96                 node.support.manager.getRealm().asyncExec(probe);
97                 if(probe.result != null) {
98                     procedure.execute(probe.result);
99                 } else {
100                     procedure.execute(Variables.PENDING_NODE_VALUE);
101                 }
102
103             }
104
105             return;            
106         }
107
108         // We need to listen
109         listener = procedure;
110         // Register listening
111         node.support.manager.addNodeListener(node.node, this);
112         synchronized(this) {
113             if(!wasRun) {
114                 Variant result = (Variant)node.support.valueCache.get(node.node);
115                 if(result != null) {
116                     procedure.execute(result);
117                 } else {
118                     procedure.execute(Variables.PENDING_NODE_VALUE);
119                 }
120             }
121         }
122
123 //        if(listener != null) {
124 //            throw new UnsupportedOperationException();
125 //        }
126 //        listener = procedure;
127 //        if(graph.getSynchronous()) {
128 //            try {
129 //                parameter.support.manager.getRealm().syncExec(new VariableNodeReadRunnable() {
130 //                    @Override
131 //                    public void run() {
132 //                        parameter.support.manager.addNodeListener(parameter.node, NodeValueRequest.this);
133 //                    }
134 //                    @Override
135 //                    public String toString() {
136 //                        return "NodeValueRequest.register.sync.addNodeListener @ " + System.identityHashCode(NodeValueRequest.this);
137 //                    }
138 //                });
139 //                
140 //                if (!wasRun) procedure.exception(new InternalException("No invocation of listener from node manager " + parameter.support.manager.getClass().getName()));
141 //            } catch (InterruptedException e) {
142 //                if (!wasRun) procedure.exception(e);
143 //            } catch (Throwable e) {
144 //                if (!wasRun) procedure.exception(e);
145 //            }
146 //        } else {
147 //            parameter.support.manager.addNodeListener(parameter.node, this);
148 //            if(value == Variables.PENDING_NODE_VALUE) procedure.execute(Variables.PENDING_NODE_VALUE);
149 //        }
150
151     }
152
153     static class NodeListener implements VariableNodeReadRunnable {
154
155         private VariableNode node;
156         private NodeValueRequest request;
157
158         public NodeListener(VariableNode node, NodeValueRequest request) {
159             this.node = node;
160             this.request = request;
161         }
162
163         @SuppressWarnings("unchecked")
164         @Override
165         public void run() {
166             node.support.manager.addNodeListener(node.node, request);
167         }
168
169     }
170
171
172     @SuppressWarnings("unchecked")
173     @Override
174     public void unregistered() {
175         VariableNode node = parameter.first;
176         node.support.manager.removeNodeListener(node.node, this);
177         node.support.valueCache.removeListening(node.node);
178         listener = null;
179     }
180
181     @SuppressWarnings("unchecked")
182     public static Variant get(Pair<VariableNode,Binding> parameter) throws NodeManagerException, BindingException {
183
184         VariableNode node = parameter.first;
185         Binding binding = parameter.second;
186
187         if (binding != null) {
188             Object raw = node.support.manager.getValue(node.node, binding);
189             if(raw == null)
190                 return null;
191             else if(NodeManager.PENDING_NODE_VALUE == raw)
192                 return NodeManager.PENDING_NODE_VALUE;
193             else return new Variant(binding, raw);
194         } else {
195             return node.support.manager.getValue(node.node);
196         }
197
198     }
199
200     @SuppressWarnings("unchecked")
201     @Override
202     public synchronized void run() {
203
204         VariableNode node = parameter.first;
205
206         try {
207             Variant newValue = get(parameter);
208             if (wasRun && ObjectUtils.objectEquals(value, newValue)) {
209                 //System.out.println("CACHE VALUE MATCH (" + newValue + ") for " + node.node);
210                 return;
211             }
212             value = newValue;
213             node.support.valueCache.put(node.node, value);
214         } catch (Throwable e) {
215             // Must catch everything to prevent DB client from getting stuck.
216             if(!(e instanceof NodeIsNotValidAnymoreException))
217                 LOGGER.error("Error while computing node value", e);
218             // Invoke the exception method of the listener
219             Listener<Variant> listener = this.listener;
220             if (listener != null) {
221                 listener.exception(new DatabaseException("External data access error", e));
222                 wasRun = true;
223             }
224             return;
225         }
226         // Must always invoke an existing listener, regardless of earlier errors.
227         Listener<Variant> listener = this.listener;
228         if (listener != null) {
229             //System.out.println("LISTENER " + listener + " invoked with value " + value);
230             listener.execute(value);
231             wasRun = true;
232         }
233     }
234
235     @Override
236     public String toString() {
237         return "NodeValueRequest.run " + parameter.first.node + " " + parameter.first.support.manager + " " + System.identityHashCode(this);
238     }
239
240 }