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.common.procedure.adapter.DisposableListener;
15 import org.simantics.db.common.procedure.adapter.DisposableSyncListener;
16 import org.simantics.db.exception.DatabaseException;
17 import org.simantics.db.layer0.request.PossibleActiveModel;
18 import org.simantics.db.management.ISessionContext;
19 import org.simantics.db.management.ISessionContextChangedListener;
20 import org.simantics.db.management.SessionContextChangedEvent;
21 import org.simantics.district.route.Route;
22 import org.simantics.district.route.RouteEvent;
23 import org.simantics.district.route.RouteService;
24 import org.simantics.district.route.RouteServiceListener;
25 import org.simantics.district.route.Router;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
30 * @author Tuukka Lehtonen
32 public class RouteServiceImpl implements RouteService, ISessionContextChangedListener, Closeable {
34 private final Logger LOGGER = LoggerFactory.getLogger(RouteServiceImpl.class);
36 private ListenerList<RouteServiceListener> listeners = new ListenerList<>();
37 private List<Route> routes = new ArrayList<>();
38 private List<Route> unmodifiableRoutes = Collections.unmodifiableList(routes);
39 private List<Router> routers = new ArrayList<>();
40 private List<Router> unmodifiableRouters = Collections.unmodifiableList(routers);
42 private class StoreRoutesListener extends DisposableListener<List<RouteImpl>> {
44 public void execute(List<RouteImpl> result) {
46 fireEvent(RouteEvent.TYPE_ROUTE_SOURCE_CHANGED, RouteServiceImpl.this);
50 public void exception(Throwable t) {
51 LOGGER.error("Failed to listen to current route store routes", t);
55 private class StoreListener extends DisposableSyncListener<Resource> {
57 public void execute(ReadGraph graph, Resource activeModel) {
58 if (activeModel != null) {
59 StoreRoutesListener srl = storeRoutesListener;
62 Simantics.getSession().asyncRequest(
63 new RoutePersistence.ModelRoutesRequest(activeModel),
64 storeRoutesListener = new StoreRoutesListener());
66 resetRoutes(Collections.emptyList());
67 fireEvent(RouteEvent.TYPE_ROUTE_SOURCE_CHANGED, RouteServiceImpl.this);
72 public void exception(ReadGraph graph, Throwable t) {
73 LOGGER.error("Failed to listen to current route service storage", t);
77 private StoreRoutesListener storeRoutesListener;
78 private StoreListener storeListener;
80 private synchronized void listenToActiveModels(Session s) {
81 StoreListener sl = storeListener;
86 new PossibleActiveModel(Simantics.getProjectResource()),
87 storeListener = new StoreListener());
89 resetRoutes(Collections.emptyList());
93 void resetRoutes(List<RouteImpl> newRoutes) {
95 newRoutes.forEach(r -> r.routeService(this));
96 routes.addAll(newRoutes);
100 public void sessionContextChanged(SessionContextChangedEvent event) {
101 ISessionContext ctx = event.getNewValue();
102 Session s = ctx != null ? ctx.getSession() : null;
103 listenToActiveModels(s);
106 public RouteServiceImpl() {
107 Session s = Simantics.peekSession();
108 Simantics.getSessionContextProvider().addContextChangedListener(this);
110 listenToActiveModels(s);
114 public void close() {
115 Simantics.getSessionContextProvider().removeContextChangedListener(this);
119 public void addListener(RouteServiceListener l) {
124 public void removeListener(RouteServiceListener l) {
129 public Route createRoute(String name, Object backendModelEntity) {
130 Route r = new RouteImpl(name).modelEntity((Resource) backendModelEntity).routeService(this);
131 fireEvent(RouteEvent.TYPE_ROUTE_CREATED, r);
136 public void registerRoute(Route route) {
138 fireEvent(RouteEvent.TYPE_ROUTE_REGISTERED, route);
142 public void refreshRoute(Route route) {
143 fireEvent(RouteEvent.TYPE_ROUTE_MODIFIED, route);
147 public CompletableFuture<Route> persistRoute(Route route) {
148 fireEvent(RouteEvent.TYPE_ROUTE_PERSISTING, route);
149 CompletableFuture<Route> future = new CompletableFuture<>();
150 new RoutePersistenceJob((RouteImpl) route, RouteEvent.TYPE_ROUTE_PERSISTING, future).schedule();
151 future.thenAccept(r -> fireEvent(RouteEvent.TYPE_ROUTE_PERSISTED, r));
156 public CompletableFuture<Route> discardRoute(Route route) {
157 fireEvent(RouteEvent.TYPE_ROUTE_DISCARDING, route);
158 CompletableFuture<Route> result = new CompletableFuture<>();
159 RouteImpl ri = (RouteImpl) route;
160 result.thenAccept(r -> {
161 routes.remove(route);
162 fireEvent(RouteEvent.TYPE_ROUTE_DISCARDED, route);
164 if (ri.backend() != null) {
165 new RoutePersistenceJob(ri, RouteEvent.TYPE_ROUTE_DISCARDING, result).schedule();
167 result.complete(route);
173 public List<Route> listRoutes() {
174 return unmodifiableRoutes;
178 public void registerRouter(Router router) {
180 fireEvent(RouteEvent.TYPE_ROUTER_REGISTERED, router);
184 public void unregisterRouter(Router router) {
186 fireEvent(RouteEvent.TYPE_ROUTER_UNREGISTERED, router);
189 public List<Router> routers() {
190 return unmodifiableRouters;
193 void fireEvent(int type, Object obj) {
194 RouteEvent e = new RouteEvent(this, type, obj);
195 LOGGER.info("firing route event {}", e);
196 listeners.forEach(l -> {
199 } catch (Exception | LinkageError | AssertionError ex) {
200 LOGGER.error("Failed to invoke RouteListener {}", l, ex);
206 public Route readRoute(Object backendRouteObject) {
207 if (!(backendRouteObject instanceof Resource))
211 return Simantics.getSession().syncRequest(new RoutePersistence.RouteRequest((Resource)backendRouteObject));
212 } catch (DatabaseException e) {
213 LOGGER.error("Failed to read district route object for " + backendRouteObject, e);