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