]> gerrit.simantics Code Review - simantics/district.git/blob - org.simantics.district.route/src/org/simantics/district/route/internal/RouteServiceImpl.java
e37a803b6fa76b96cb84cd8c48200b19d5b4f48b
[simantics/district.git] / org.simantics.district.route / src / org / simantics / district / route / internal / RouteServiceImpl.java
1 package org.simantics.district.route.internal;
2
3 import java.io.Closeable;
4 import java.util.ArrayList;
5 import java.util.Collections;
6 import java.util.List;
7 import java.util.concurrent.CompletableFuture;
8
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;
27
28 /**
29  * @author Tuukka Lehtonen
30  */
31 public class RouteServiceImpl implements RouteService, ISessionContextChangedListener, Closeable {
32
33     private final Logger LOGGER = LoggerFactory.getLogger(RouteServiceImpl.class); 
34
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);
40
41     private class StoreListener implements SyncListener<Resource> {
42         private boolean disposed = false;
43
44         @Override
45         public void execute(ReadGraph graph, Resource activeModel) {
46             try {
47                 if (activeModel != null) {
48                     resetRoutes(RoutePersistence.findRoutes(graph, activeModel));
49                 } else {
50                     resetRoutes(Collections.emptyList());
51                 }
52                 fireEvent(RouteEvent.TYPE_ROUTE_SOURCE_CHANGED, this);
53             } catch (DatabaseException e) {
54                 LOGGER.error("Failed to read routes from model {}", activeModel, e);
55             }
56         }
57
58         @Override
59         public void exception(ReadGraph graph, Throwable t) {
60             LOGGER.error("Failed to listen to current route service storage", t);
61         }
62
63         public void dispose() {
64             disposed = true;
65         }
66
67         @Override
68         public boolean isDisposed() {
69             return disposed;
70         }
71     }
72
73     private StoreListener storeListener;
74
75     private synchronized void listenToActiveModels(Session s) {
76         StoreListener sl =  storeListener;
77         if (sl != null)
78             sl.dispose();
79         if (s != null) {
80             s.asyncRequest(
81                     new PossibleActiveModel(Simantics.getProjectResource()),
82                     storeListener = new StoreListener());
83         } else {
84             resetRoutes(Collections.emptyList());
85         }
86     }
87
88     void resetRoutes(List<RouteImpl> newRoutes) {
89         routes.clear();
90         newRoutes.forEach(r -> r.routeService(this));
91         routes.addAll(newRoutes);
92     }
93
94     @Override
95     public void sessionContextChanged(SessionContextChangedEvent event) {
96         ISessionContext ctx = event.getNewValue();
97         Session s = ctx != null ? ctx.getSession() : null;
98         listenToActiveModels(s);
99     }
100
101     public RouteServiceImpl() {
102         Session s = Simantics.peekSession();
103         Simantics.getSessionContextProvider().addContextChangedListener(this);
104         if (s != null)
105             listenToActiveModels(s);
106     }
107
108     @Override
109     public void close() {
110         Simantics.getSessionContextProvider().removeContextChangedListener(this);
111     }
112
113     @Override
114     public void addListener(RouteServiceListener l) {
115         listeners.add(l);
116     }
117
118     @Override
119     public void removeListener(RouteServiceListener l) {
120         listeners.remove(l);
121     }
122
123     @Override
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);
127         return r;
128     }
129
130     @Override
131     public void registerRoute(Route route) {
132         routes.add(route);
133         fireEvent(RouteEvent.TYPE_ROUTE_REGISTERED, route);
134     }
135
136     @Override
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));
142         return future;
143     }
144
145     @Override
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);
153         });
154         if (ri.backend() != null) {
155             new RoutePersistenceJob(ri, RouteEvent.TYPE_ROUTE_DISCARDING, result).schedule();
156         } else {
157             result.complete(route);
158         }
159         return result;
160     }
161
162     @Override
163     public List<Route> listRoutes() {
164         return unmodifiableRoutes;
165     }
166
167     @Override
168     public void registerRouter(Router router) {
169         routers.add(router);
170         fireEvent(RouteEvent.TYPE_ROUTER_REGISTERED, router);
171     }
172
173     @Override
174     public void unregisterRouter(Router router) {
175         routers.add(router);
176         fireEvent(RouteEvent.TYPE_ROUTER_UNREGISTERED, router);
177     }
178
179     public List<Router> routers() {
180         return unmodifiableRouters;
181     }
182
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 -> {
187             try {
188                 l.handleEvent(e);
189             } catch (Exception | LinkageError | AssertionError ex) {
190                 LOGGER.error("Failed to invoke RouteListener {}", l, ex);
191             }
192         });
193     }
194
195 }