1 package org.simantics.district.route.internal;
3 import java.io.Closeable;
4 import java.util.ArrayList;
5 import java.util.Collections;
7 import java.util.concurrent.CompletableFuture;
9 import org.eclipse.core.runtime.ListenerList;
10 import org.simantics.Simantics;
11 import org.simantics.db.ReadGraph;
12 import org.simantics.db.Resource;
13 import org.simantics.db.Session;
14 import org.simantics.db.exception.DatabaseException;
15 import org.simantics.db.layer0.request.PossibleActiveModel;
16 import org.simantics.db.management.ISessionContext;
17 import org.simantics.db.management.ISessionContextChangedListener;
18 import org.simantics.db.management.SessionContextChangedEvent;
19 import org.simantics.db.procedure.SyncListener;
20 import org.simantics.district.route.Route;
21 import org.simantics.district.route.RouteEvent;
22 import org.simantics.district.route.RouteService;
23 import org.simantics.district.route.RouteServiceListener;
24 import org.simantics.district.route.Router;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
29 * @author Tuukka Lehtonen
31 public class RouteServiceImpl implements RouteService, ISessionContextChangedListener, Closeable {
33 private final Logger LOGGER = LoggerFactory.getLogger(RouteServiceImpl.class);
35 private ListenerList<RouteServiceListener> listeners = new ListenerList<>();
36 private List<Route> routes = new ArrayList<>();
37 private List<Route> unmodifiableRoutes = Collections.unmodifiableList(routes);
38 private List<Router> routers = new ArrayList<>();
39 private List<Router> unmodifiableRouters = Collections.unmodifiableList(routers);
41 private class StoreListener implements SyncListener<Resource> {
42 private boolean disposed = false;
45 public void execute(ReadGraph graph, Resource activeModel) {
47 if (activeModel != null) {
48 resetRoutes(RoutePersistence.findRoutes(graph, activeModel));
50 resetRoutes(Collections.emptyList());
52 fireEvent(RouteEvent.TYPE_ROUTE_SOURCE_CHANGED, this);
53 } catch (DatabaseException e) {
54 LOGGER.error("Failed to read routes from model {}", activeModel, e);
59 public void exception(ReadGraph graph, Throwable t) {
60 LOGGER.error("Failed to listen to current route service storage", t);
63 public void dispose() {
68 public boolean isDisposed() {
73 private StoreListener storeListener;
75 private synchronized void listenToActiveModels(Session s) {
76 StoreListener sl = storeListener;
81 new PossibleActiveModel(Simantics.getProjectResource()),
82 storeListener = new StoreListener());
84 resetRoutes(Collections.emptyList());
88 void resetRoutes(List<RouteImpl> newRoutes) {
90 newRoutes.forEach(r -> r.routeService(this));
91 routes.addAll(newRoutes);
95 public void sessionContextChanged(SessionContextChangedEvent event) {
96 ISessionContext ctx = event.getNewValue();
97 Session s = ctx != null ? ctx.getSession() : null;
98 listenToActiveModels(s);
101 public RouteServiceImpl() {
102 Session s = Simantics.peekSession();
103 Simantics.getSessionContextProvider().addContextChangedListener(this);
105 listenToActiveModels(s);
109 public void close() {
110 Simantics.getSessionContextProvider().removeContextChangedListener(this);
114 public void addListener(RouteServiceListener l) {
119 public void removeListener(RouteServiceListener l) {
124 public Route createRoute(String name, Object backendModelEntity) {
125 Route r = new RouteImpl(name).modelEntity((Resource) backendModelEntity).routeService(this);
126 fireEvent(RouteEvent.TYPE_ROUTE_CREATED, r);
131 public void registerRoute(Route route) {
133 fireEvent(RouteEvent.TYPE_ROUTE_REGISTERED, route);
137 public CompletableFuture<Route> persistRoute(Route route) {
138 fireEvent(RouteEvent.TYPE_ROUTE_PERSISTING, route);
139 CompletableFuture<Route> future = new CompletableFuture<>();
140 new RoutePersistenceJob((RouteImpl) route, RouteEvent.TYPE_ROUTE_PERSISTING, future).schedule();
141 future.thenAccept(r -> fireEvent(RouteEvent.TYPE_ROUTE_PERSISTED, r));
146 public CompletableFuture<Route> discardRoute(Route route) {
147 fireEvent(RouteEvent.TYPE_ROUTE_DISCARDING, route);
148 CompletableFuture<Route> result = new CompletableFuture<>();
149 RouteImpl ri = (RouteImpl) route;
150 result.thenAccept(r -> {
151 routes.remove(route);
152 fireEvent(RouteEvent.TYPE_ROUTE_DISCARDED, route);
154 if (ri.backend() != null) {
155 new RoutePersistenceJob(ri, RouteEvent.TYPE_ROUTE_DISCARDING, result).schedule();
157 result.complete(route);
163 public List<Route> listRoutes() {
164 return unmodifiableRoutes;
168 public void registerRouter(Router router) {
170 fireEvent(RouteEvent.TYPE_ROUTER_REGISTERED, router);
174 public void unregisterRouter(Router router) {
176 fireEvent(RouteEvent.TYPE_ROUTER_UNREGISTERED, router);
179 public List<Router> routers() {
180 return unmodifiableRouters;
183 void fireEvent(int type, Object obj) {
184 RouteEvent e = new RouteEvent(this, type, obj);
185 LOGGER.info("firing route event {}", e);
186 listeners.forEach(l -> {
189 } catch (Exception | LinkageError | AssertionError ex) {
190 LOGGER.error("Failed to invoke RouteListener {}", l, ex);