1 /*******************************************************************************
2 * Copyright (c) 2010, 2013 Association for Decentralized Information Management
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
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 * Semantum Oy - issue #4384
12 *******************************************************************************/
13 package org.simantics.diagram.runtime;
15 import java.util.concurrent.atomic.AtomicReference;
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.datastructures.Callback;
37 import org.simantics.utils.ui.ErrorLogger;
40 * A helper class for managing the life-cycle of a diagram runtime realization.
43 * The diagram runtime resource is a virtual (transient) graph database resource
44 * that specifies the active runtime properties of the diagram as follows:
48 * |---[DIAGRAM.RuntimeDiagram.HasConfiguration]---> :DIAGRAM.Diagram (directed link, no inverse relation)
49 * |---[DIAGRAM.RuntimeDiagram.HasRuntimeProfile]---> :DIAGRAM.Profile (directed link, no inverse relation)
50 * |---[DIAGRAM.HasModelURI]------------ "Model URI" : L0.String
51 * |---[DIAGRAM.HasVariable]------------ "Variable URI" : L0.String
52 * `---[DIAGRAM.HasRVI]----------------- "RVI" : L0.String
55 * The runtime resource itself is not attached anywhere in the graph, it only
56 * contains a directed link back to its diagram.
59 * For example diagram profiles require the runtime diagram to have the
60 * DIAGRAM.HasVariable property. This in turn requires that the model has a
61 * proper {@link Layer0X#HasBaseRealization} relation in order to be resolved
62 * (see {@link RuntimeVariable}).
65 * To get started using this class, see
66 * {@link #track(Session, Resource, IEditorInput, ListenerSupport)} and
67 * {@link #create(Session, Resource, String, String)}.
70 * Instances of this class are not thread-safe.
72 * @author Tuukka Lehtonen
74 public class RuntimeDiagramManager {
76 private boolean disposed;
78 private Session session;
79 private IEditorInput editorInput;
80 private ListenerSupport support;
82 private AtomicReference<Resource> runtimeDiagram = new AtomicReference<Resource>();
85 * Constructs a new RuntimeDiagramManager, creates a new runtime diagram
86 * based on the specified editor input if it is an
87 * {@link IResourceEditorInput2} and starts tracking (listening) to changes
88 * in editor input which specifies the ModelURI and RVI needed for the
89 * runtime diagram. The tracking aspect comes to play in that editor inputs
90 * are basically allowed to change and therefore this manager tries to
91 * listen to changes in both the Model URI and RVI. If the input changes,
92 * the manager will automatically update the properties of the runtime
93 * diagram to match the input.
96 * Invoking this method is equal to calling:
99 * RuntimeDiagramManager manager = new RuntimeDiagramManager(session);
100 * manager.track(diagram, editorInput, listenerSupport);
106 * @param listenerSupport
108 * @throws DatabaseException
110 public static RuntimeDiagramManager track(
112 final Resource diagram,
113 IEditorInput editorInput,
114 ListenerSupport listenerSupport)
115 throws DatabaseException
117 RuntimeDiagramManager manager = new RuntimeDiagramManager(session);
118 manager.track(diagram, editorInput, listenerSupport);
123 * Just as {@link #track(Session, Resource, IEditorInput, ListenerSupport)}
124 * this method will construct a RuntimeDiagramManager and create a runtime
125 * diagram resource, but contrary to
126 * {@link #track(Session, Resource, IEditorInput, ListenerSupport)} it will
127 * not listen to changes nor update the runtime diagram properties.
130 * Invoking this method is equal to calling:
132 * RuntimeDiagramManager manager = new RuntimeDiagramManager(session);
133 * manager.createRuntimeDiagram(diagram, modelURI, RVI);
141 * @throws DatabaseException
143 * @see {@link #track(Session, Resource, IEditorInput, ListenerSupport)}
145 public static RuntimeDiagramManager create(
150 throws DatabaseException
152 RuntimeDiagramManager manager = new RuntimeDiagramManager(session);
153 manager.createRuntimeDiagram(diagram, modelURI, RVI);
157 public RuntimeDiagramManager(Session session) {
158 this.session = session;
161 public Resource getRuntimeDiagram() {
162 return runtimeDiagram.get();
165 public void dispose() {
166 synchronized (this) {
174 this.editorInput = null;
178 private void assertNotDisposed() {
180 throw new IllegalStateException(this + " is disposed");
183 private static IResourceEditorInput2 getResourceInput(Object input) {
184 if (input instanceof IResourceEditorInput2)
185 return (IResourceEditorInput2) input;
189 private IResourceEditorInput2 getResourceInput() {
190 return getResourceInput(editorInput);
194 * Does nothing if already tracking a diagram runtime resource.
198 * @param listenerSupport
199 * @return the current tracked diagram runtime resource
200 * @throws DatabaseException
202 * @see {@link #track(Session, Resource, IEditorInput, ListenerSupport)}
204 public Resource track(final Resource diagram, IEditorInput editorInput, ListenerSupport listenerSupport) throws DatabaseException {
205 Resource runtime = runtimeDiagram.get();
209 if (editorInput == null)
210 throw new NullPointerException("null editorInput");
211 if (listenerSupport == null)
212 throw new NullPointerException("null listenerSupport");
214 final IResourceEditorInput2 input = getResourceInput(editorInput);
218 this.editorInput = editorInput;
219 this.support = listenerSupport;
221 runtime = session.syncRequest(new WriteResultRequest<Resource>(session.getService(VirtualGraph.class)) {
223 public Resource perform(WriteGraph graph) throws DatabaseException {
224 RuntimeDiagramDesc variable = graph.syncRequest(new RuntimeVariableForInput(input));
225 if (variable == null)
228 final Resource runtime = createRuntimeDiagram(graph, diagram, variable);
229 listenRequest(graph, diagram);
234 runtimeDiagram.set(runtime);
243 * @throws DatabaseException
245 * @see {@link #create(Session, Resource, String, String)
247 public Resource createRuntimeDiagram(final Resource diagram, final String modelURI, final String RVI) throws DatabaseException {
248 Resource runtime = runtimeDiagram.get();
252 runtime = session.syncRequest(new WriteResultRequest<Resource>(session.getService(VirtualGraph.class)) {
254 public Resource perform(WriteGraph graph) throws DatabaseException {
255 Resource model = graph.getPossibleResource(modelURI);
256 return createRuntimeDiagram(graph, diagram, model, RVI);
260 runtimeDiagram.set(runtime);
270 * @throws DatabaseException
272 public Resource createRuntimeDiagram(WriteGraph graph, final Resource diagram, final Resource model, final String rvis) throws DatabaseException {
273 RVI rvi = rvis != null ? RVI.fromResourceFormat(graph, rvis) : null;
274 RuntimeDiagramDesc desc = graph.syncRequest(new RuntimeVariable(model, rvi, diagram));
277 return createRuntimeDiagram(graph, diagram, desc);
280 private void listenRequest(RequestProcessor processor, final Resource diagram) {
281 processor.asyncRequest(new RuntimeVariableForInput(getResourceInput()), new AsyncListener<RuntimeDiagramDesc>() {
284 public void exception(AsyncReadGraph graph, Throwable throwable) {
285 ListenerSupport s = support;
287 s.exception(throwable);
291 public void execute(AsyncReadGraph graph, final RuntimeDiagramDesc desc) {
295 Session session = graph.getSession();
296 session.asyncRequest(new WriteRequest(session.getService(VirtualGraph.class)) {
298 public void perform(WriteGraph graph) throws DatabaseException {
299 Resource runtime = getRuntimeDiagram();
301 writeConfig(graph, runtime, diagram, desc);
303 }, new Callback<DatabaseException>() {
305 public void run(DatabaseException e) {
306 ListenerSupport s = support;
307 if (e != null && s != null)
314 public boolean isDisposed() {
315 if(disposed) return true;
316 else return support != null && support.isDisposed();
321 private Resource createRuntimeDiagram(WriteGraph graph, Resource diagram, RuntimeDiagramDesc desc) throws DatabaseException {
323 Layer0 L0 = Layer0.getInstance(graph);
324 final DiagramResource DIA = DiagramResource.getInstance(graph);
326 // Create the new runtime diagram instance!
327 final Resource runtime = graph.newResource();
328 graph.claim(runtime, L0.InstanceOf, null, DIA.RuntimeDiagram);
329 graph.claim(runtime, DIA.RuntimeDiagram_HasConfiguration, null, diagram);
331 writeConfig(graph, runtime, diagram, desc);
336 private void writeConfig(WriteGraph graph, final Resource runtime, final Resource diagram, RuntimeDiagramDesc desc) throws DatabaseException {
337 final DiagramResource DIA = DiagramResource.getInstance(graph);
338 if (desc.getVariableURI() != null)
339 graph.claimLiteral(runtime, DIA.RuntimeDiagram_HasVariable, desc.getVariableURI(), Bindings.STRING);
340 if (desc.getRVI() != null)
341 graph.claimLiteral(runtime, DIA.RuntimeDiagram_HasRVI, desc.getRVI(), Bindings.STRING);
342 if (desc.getModelURI() != null)
343 graph.claimLiteral(runtime, DIA.RuntimeDiagram_HasModelURI, desc.getModelURI(), Bindings.STRING);
344 if (desc.getActiveProfileURI() != null) {
345 Resource activeProfile = graph.getPossibleResource(desc.getActiveProfileURI());
346 if (activeProfile != null) {
347 graph.deny(runtime, DIA.RuntimeDiagram_HasRuntimeProfile);
348 graph.claim(runtime, DIA.RuntimeDiagram_HasRuntimeProfile, null, activeProfile);
353 private void destroy() {
354 final Resource rd = runtimeDiagram.getAndSet(null);
357 session.syncRequest(new WriteRequest(session.getService(VirtualGraph.class)) {
359 public void perform(WriteGraph graph) throws DatabaseException {
360 RemoverUtil.remove(graph, rd);
363 } catch (DatabaseException e) {
364 ListenerSupport s = support;
368 ErrorLogger.defaultLogError(e);