]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.diagram/src/org/simantics/diagram/runtime/RuntimeDiagramManager.java
bfc439101a81593c895d29da41a6183c0bbfec5c
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / runtime / RuntimeDiagramManager.java
1 /*******************************************************************************
2  * Copyright (c) 2010, 2013 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *     Semantum Oy - issue #4384
12  *******************************************************************************/
13 package org.simantics.diagram.runtime;
14
15 import java.util.concurrent.atomic.AtomicReference;
16
17 import org.eclipse.ui.IEditorInput;
18 import org.simantics.databoard.Bindings;
19 import org.simantics.db.AsyncReadGraph;
20 import org.simantics.db.RequestProcessor;
21 import org.simantics.db.Resource;
22 import org.simantics.db.Session;
23 import org.simantics.db.VirtualGraph;
24 import org.simantics.db.WriteGraph;
25 import org.simantics.db.common.procedure.adapter.ListenerSupport;
26 import org.simantics.db.common.request.WriteRequest;
27 import org.simantics.db.common.request.WriteResultRequest;
28 import org.simantics.db.exception.DatabaseException;
29 import org.simantics.db.layer0.util.RemoverUtil;
30 import org.simantics.db.layer0.variable.RVI;
31 import org.simantics.db.procedure.AsyncListener;
32 import org.simantics.diagram.stubs.DiagramResource;
33 import org.simantics.layer0.Layer0;
34 import org.simantics.operation.Layer0X;
35 import org.simantics.ui.workbench.IResourceEditorInput2;
36 import org.simantics.utils.ui.ErrorLogger;
37
38 /**
39  * A helper class for managing the life-cycle of a diagram runtime realization.
40  * 
41  * <p>
42  * The diagram runtime resource is a virtual (transient) graph database resource
43  * that specifies the active runtime properties of the diagram as follows:
44  * 
45  * <pre>
46  *    RUNTIME RESOURCE
47  *    |---[DIAGRAM.RuntimeDiagram.HasConfiguration]---> :DIAGRAM.Diagram (directed link, no inverse relation)
48  *    |---[DIAGRAM.RuntimeDiagram.HasRuntimeProfile]---> :DIAGRAM.Profile (directed link, no inverse relation)
49  *    |---[DIAGRAM.HasModelURI]------------ "Model URI" : L0.String
50  *    |---[DIAGRAM.HasVariable]------------ "Variable URI" : L0.String
51  *    `---[DIAGRAM.HasRVI]----------------- "RVI" : L0.String
52  * </pre>
53  * 
54  * The runtime resource itself is not attached anywhere in the graph, it only
55  * contains a directed link back to its diagram.
56  * 
57  * <p>
58  * For example diagram profiles require the runtime diagram to have the
59  * DIAGRAM.HasVariable property. This in turn requires that the model has a
60  * proper {@link Layer0X#HasBaseRealization} relation in order to be resolved
61  * (see {@link RuntimeVariable}).
62  * 
63  * <p>
64  * To get started using this class, see
65  * {@link #track(Session, Resource, IEditorInput, ListenerSupport)} and
66  * {@link #create(Session, Resource, String, String)}.
67  * 
68  * <p>
69  * Instances of this class are not thread-safe.
70  * 
71  * @author Tuukka Lehtonen
72  */
73 public class RuntimeDiagramManager {
74
75     private boolean                   disposed;
76
77     private Session                   session;
78     private IEditorInput              editorInput;
79     private ListenerSupport           support;
80
81     private AtomicReference<Resource> runtimeDiagram = new AtomicReference<Resource>();
82
83     /**
84      * Constructs a new RuntimeDiagramManager, creates a new runtime diagram
85      * based on the specified editor input if it is an
86      * {@link IResourceEditorInput2} and starts tracking (listening) to changes
87      * in editor input which specifies the ModelURI and RVI needed for the
88      * runtime diagram. The tracking aspect comes to play in that editor inputs
89      * are basically allowed to change and therefore this manager tries to
90      * listen to changes in both the Model URI and RVI. If the input changes,
91      * the manager will automatically update the properties of the runtime
92      * diagram to match the input.
93      * 
94      * <p>
95      * Invoking this method is equal to calling:
96      * 
97      * <pre>
98      * RuntimeDiagramManager manager = new RuntimeDiagramManager(session);
99      * manager.track(diagram, editorInput, listenerSupport);
100      * </pre>
101      * 
102      * @param session
103      * @param diagram
104      * @param editorInput
105      * @param listenerSupport
106      * @return
107      * @throws DatabaseException
108      */
109     public static RuntimeDiagramManager track(
110             Session session,
111             final Resource diagram,
112             IEditorInput editorInput,
113             ListenerSupport listenerSupport)
114     throws DatabaseException
115     {
116         RuntimeDiagramManager manager = new RuntimeDiagramManager(session);
117         manager.track(diagram, editorInput, listenerSupport);
118         return manager;
119     }
120
121     /**
122      * Just as {@link #track(Session, Resource, IEditorInput, ListenerSupport)}
123      * this method will construct a RuntimeDiagramManager and create a runtime
124      * diagram resource, but contrary to
125      * {@link #track(Session, Resource, IEditorInput, ListenerSupport)} it will
126      * not listen to changes nor update the runtime diagram properties.
127      * 
128      * <p>
129      * Invoking this method is equal to calling:
130      * <pre>
131      * RuntimeDiagramManager manager = new RuntimeDiagramManager(session);
132      * manager.createRuntimeDiagram(diagram, modelURI, RVI);
133      * </pre>
134      * 
135      * @param session
136      * @param diagram
137      * @param modelURI
138      * @param RVI
139      * @return
140      * @throws DatabaseException
141      * 
142      * @see {@link #track(Session, Resource, IEditorInput, ListenerSupport)}
143      */
144     public static RuntimeDiagramManager create(
145             Session session,
146             Resource diagram,
147             String modelURI,
148             String RVI)
149     throws DatabaseException
150     {
151         RuntimeDiagramManager manager = new RuntimeDiagramManager(session);
152         manager.createRuntimeDiagram(diagram, modelURI, RVI);
153         return manager;
154     }
155
156     public RuntimeDiagramManager(Session session) {
157         this.session = session;
158     }
159
160     public Resource getRuntimeDiagram() {
161         return runtimeDiagram.get();
162     }
163
164     public void dispose() {
165         synchronized (this) {
166             assertNotDisposed();
167             disposed = true;
168         }
169
170         destroy();
171
172         this.session = null;
173         this.editorInput = null;
174         this.support = null;
175     }
176
177     private void assertNotDisposed() {
178         if (disposed)
179             throw new IllegalStateException(this + " is disposed");
180     }
181
182     private static IResourceEditorInput2 getResourceInput(Object input) {
183         if (input instanceof IResourceEditorInput2)
184             return (IResourceEditorInput2) input;
185         return null;
186     }
187
188     private IResourceEditorInput2 getResourceInput() {
189         return getResourceInput(editorInput);
190     }
191
192     /**
193      * Does nothing if already tracking a diagram runtime resource.
194      * 
195      * @param diagram
196      * @param editorInput
197      * @param listenerSupport
198      * @return the current tracked diagram runtime resource
199      * @throws DatabaseException
200      * 
201      * @see {@link #track(Session, Resource, IEditorInput, ListenerSupport)}
202      */
203     public Resource track(final Resource diagram, IEditorInput editorInput, ListenerSupport listenerSupport) throws DatabaseException {
204         Resource runtime = runtimeDiagram.get();
205         if (runtime != null)
206             return runtime;
207
208         if (editorInput == null)
209             throw new NullPointerException("null editorInput");
210         if (listenerSupport == null)
211             throw new NullPointerException("null listenerSupport");
212
213         final IResourceEditorInput2 input = getResourceInput(editorInput);
214         if (input == null)
215             return null;
216
217         this.editorInput = editorInput;
218         this.support = listenerSupport;
219
220         runtime = session.syncRequest(new WriteResultRequest<Resource>(session.getService(VirtualGraph.class)) {
221             @Override
222             public Resource perform(WriteGraph graph) throws DatabaseException {
223                 RuntimeDiagramDesc variable = graph.syncRequest(new RuntimeVariableForInput(input));
224                 if (variable == null)
225                     return null;
226
227                 final Resource runtime = createRuntimeDiagram(graph, diagram, variable);
228                 listenRequest(graph, diagram);
229                 return runtime;
230             }
231         });
232
233         runtimeDiagram.set(runtime);
234         return runtime;
235     }
236
237     /**
238      * @param diagram
239      * @param modelURI
240      * @param RVI
241      * @return
242      * @throws DatabaseException
243      * 
244      * @see {@link #create(Session, Resource, String, String)
245      */
246     public Resource createRuntimeDiagram(final Resource diagram, final String modelURI, final String RVI) throws DatabaseException {
247         Resource runtime = runtimeDiagram.get();
248         if (runtime != null)
249             return runtime;
250
251         runtime = session.syncRequest(new WriteResultRequest<Resource>(session.getService(VirtualGraph.class)) {
252             @Override
253             public Resource perform(WriteGraph graph) throws DatabaseException {
254                 Resource model = graph.getPossibleResource(modelURI);
255                 return createRuntimeDiagram(graph, diagram, model, RVI);
256             }
257         });
258
259         runtimeDiagram.set(runtime);
260         return runtime;
261     }
262
263     /**
264      * @param graph
265      * @param diagram
266      * @param modelURI
267      * @param RVI
268      * @return
269      * @throws DatabaseException
270      */
271     public Resource createRuntimeDiagram(WriteGraph graph, final Resource diagram, final Resource model, final String rvis) throws DatabaseException {
272         RVI rvi = rvis != null ? RVI.fromResourceFormat(graph, rvis) : null;
273         RuntimeDiagramDesc desc = graph.syncRequest(new RuntimeVariable(model, rvi, diagram));
274         if (desc == null)
275             return null;
276         return createRuntimeDiagram(graph, diagram, desc);
277     }
278
279     private void listenRequest(RequestProcessor processor, final Resource diagram) {
280         processor.asyncRequest(new RuntimeVariableForInput(getResourceInput()), new AsyncListener<RuntimeDiagramDesc>() {
281
282             @Override
283             public void exception(AsyncReadGraph graph, Throwable throwable) {
284                 ListenerSupport s = support;
285                 if (s != null)
286                     s.exception(throwable);
287             }
288
289             @Override
290             public void execute(AsyncReadGraph graph, final RuntimeDiagramDesc desc) {
291                 if (desc == null)
292                     return;
293
294                 Session session = graph.getSession();
295                 session.asyncRequest(new WriteRequest(session.getService(VirtualGraph.class)) {
296                     @Override
297                     public void perform(WriteGraph graph) throws DatabaseException {
298                         Resource runtime = getRuntimeDiagram();
299                         if (runtime != null)
300                             writeConfig(graph, runtime, diagram, desc);
301                     }
302                 }, e -> {
303                     ListenerSupport s = support;
304                     if (e != null && s != null)
305                         s.exception(e);
306                 });
307             }
308
309             @Override
310             public boolean isDisposed() {
311                 if(disposed) return true;
312                 else return support != null && support.isDisposed();
313             }
314         });
315     }
316
317     private Resource createRuntimeDiagram(WriteGraph graph, Resource diagram, RuntimeDiagramDesc desc) throws DatabaseException {
318
319         Layer0 L0 = Layer0.getInstance(graph);
320         final DiagramResource DIA = DiagramResource.getInstance(graph);
321
322         // Create the new runtime diagram instance!
323         final Resource runtime = graph.newResource();
324         graph.claim(runtime, L0.InstanceOf, null, DIA.RuntimeDiagram);
325         graph.claim(runtime, DIA.RuntimeDiagram_HasConfiguration, null, diagram);
326
327         writeConfig(graph, runtime, diagram, desc);
328
329         return runtime;
330     }
331
332     private void writeConfig(WriteGraph graph, final Resource runtime, final Resource diagram, RuntimeDiagramDesc desc) throws DatabaseException {
333         final DiagramResource DIA = DiagramResource.getInstance(graph);
334         if (desc.getVariableURI() != null)
335             graph.claimLiteral(runtime, DIA.RuntimeDiagram_HasVariable, desc.getVariableURI(), Bindings.STRING);
336         if (desc.getRVI() != null)
337             graph.claimLiteral(runtime, DIA.RuntimeDiagram_HasRVI, desc.getRVI(), Bindings.STRING);
338         if (desc.getModelURI() != null)
339             graph.claimLiteral(runtime, DIA.RuntimeDiagram_HasModelURI, desc.getModelURI(), Bindings.STRING);
340         if (desc.getActiveProfileURI() != null) {
341             Resource activeProfile = graph.getPossibleResource(desc.getActiveProfileURI());
342             if (activeProfile != null) {
343                 graph.deny(runtime, DIA.RuntimeDiagram_HasRuntimeProfile);
344                 graph.claim(runtime, DIA.RuntimeDiagram_HasRuntimeProfile, null, activeProfile);
345             }
346         }
347     }
348
349     private void destroy() {
350         final Resource rd = runtimeDiagram.getAndSet(null);
351         if (rd != null) {
352             try {
353                 session.syncRequest(new WriteRequest(session.getService(VirtualGraph.class)) {
354                     @Override
355                     public void perform(WriteGraph graph) throws DatabaseException {
356                         RemoverUtil.remove(graph, rd);
357                     }
358                 });
359             } catch (DatabaseException e) {
360                 ListenerSupport s = support;
361                 if (s != null)
362                     s.exception(e);
363                 else
364                     ErrorLogger.defaultLogError(e);
365             }
366         }
367     }
368
369 }