blob: 32d142712a7c0cc56f068fd4073812ee18186f7f [file] [log] [blame]
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -08003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.ui.impl;
17
Thomas Vachuska9ed335b2015-04-14 12:07:47 -070018import com.fasterxml.jackson.databind.JsonNode;
Thomas Vachuska0af26912016-03-21 21:37:30 -070019import com.fasterxml.jackson.databind.ObjectMapper;
20import com.fasterxml.jackson.databind.node.ArrayNode;
21import com.fasterxml.jackson.databind.node.BooleanNode;
22import com.fasterxml.jackson.databind.node.DoubleNode;
23import com.fasterxml.jackson.databind.node.IntNode;
24import com.fasterxml.jackson.databind.node.JsonNodeFactory;
25import com.fasterxml.jackson.databind.node.LongNode;
26import com.fasterxml.jackson.databind.node.NullNode;
27import com.fasterxml.jackson.databind.node.ObjectNode;
28import com.fasterxml.jackson.databind.node.ShortNode;
29import com.fasterxml.jackson.databind.node.TextNode;
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080030import com.google.common.collect.ImmutableList;
Thomas Vachuska0af26912016-03-21 21:37:30 -070031import com.google.common.collect.ImmutableMap;
Thomas Vachuska9ed335b2015-04-14 12:07:47 -070032import com.google.common.collect.ImmutableSet;
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080033import com.google.common.collect.Lists;
34import com.google.common.collect.Maps;
35import org.apache.felix.scr.annotations.Activate;
36import org.apache.felix.scr.annotations.Component;
37import org.apache.felix.scr.annotations.Deactivate;
Thomas Vachuska51f540f2015-05-27 17:26:57 -070038import org.apache.felix.scr.annotations.Reference;
39import org.apache.felix.scr.annotations.ReferenceCardinality;
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080040import org.apache.felix.scr.annotations.Service;
Madan Jampanibf8ee802016-05-04 14:07:36 -070041import org.onlab.util.Tools;
Thomas Vachuska51f540f2015-05-27 17:26:57 -070042import org.onosproject.mastership.MastershipService;
Thomas Vachuska0af26912016-03-21 21:37:30 -070043import org.onosproject.store.serializers.KryoNamespaces;
Madan Jampani7b93ceb2016-05-04 09:58:40 -070044import org.onosproject.store.service.ConsistentMap;
45import org.onosproject.store.service.MapEvent;
46import org.onosproject.store.service.MapEventListener;
47import org.onosproject.store.service.Serializer;
Thomas Vachuska0af26912016-03-21 21:37:30 -070048import org.onosproject.store.service.StorageService;
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080049import org.onosproject.ui.UiExtension;
50import org.onosproject.ui.UiExtensionService;
Thomas Vachuska3553b302015-03-07 14:49:43 -080051import org.onosproject.ui.UiMessageHandlerFactory;
Thomas Vachuska0af26912016-03-21 21:37:30 -070052import org.onosproject.ui.UiPreferencesService;
Simon Hunt1169c952017-06-05 11:20:11 -070053import org.onosproject.ui.UiSessionToken;
54import org.onosproject.ui.UiTokenService;
Simon Hunt6cc86452017-04-27 17:46:22 -070055import org.onosproject.ui.UiTopo2OverlayFactory;
Simon Huntd5b96732016-07-08 13:22:27 -070056import org.onosproject.ui.UiTopoMap;
Steven Burrows3a9a6442016-05-05 15:31:16 +010057import org.onosproject.ui.UiTopoMapFactory;
Simon Huntd5b96732016-07-08 13:22:27 -070058import org.onosproject.ui.UiTopoOverlayFactory;
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080059import org.onosproject.ui.UiView;
Simon Hunt1002cd82015-04-23 14:44:03 -070060import org.onosproject.ui.UiViewHidden;
Simon Hunte6f64612017-04-28 00:01:48 -070061import org.onosproject.ui.impl.topo.Topo2TrafficMessageHandler;
Simon Huntd5b96732016-07-08 13:22:27 -070062import org.onosproject.ui.impl.topo.Topo2ViewMessageHandler;
Simon Hunt6cc86452017-04-27 17:46:22 -070063import org.onosproject.ui.impl.topo.Traffic2Overlay;
Simon Huntb8042032017-06-13 15:03:23 -070064import org.onosproject.ui.lion.LionUtils;
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080065import org.slf4j.Logger;
66import org.slf4j.LoggerFactory;
67
Simon Hunt1169c952017-06-05 11:20:11 -070068import java.math.BigInteger;
69import java.security.SecureRandom;
Thomas Vachuska0af26912016-03-21 21:37:30 -070070import java.util.LinkedHashMap;
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080071import java.util.List;
72import java.util.Map;
Thomas Vachuska9ed335b2015-04-14 12:07:47 -070073import java.util.Set;
Madan Jampanibf8ee802016-05-04 14:07:36 -070074import java.util.concurrent.ExecutorService;
75import java.util.concurrent.Executors;
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080076
77import static com.google.common.collect.ImmutableList.of;
78import static java.util.stream.Collectors.toSet;
Heedo Kang4a47a302016-02-29 17:40:23 +090079import static org.onosproject.security.AppGuard.checkPermission;
80import static org.onosproject.security.AppPermission.Type.UI_READ;
81import static org.onosproject.security.AppPermission.Type.UI_WRITE;
Thomas Vachuska0af26912016-03-21 21:37:30 -070082import static org.onosproject.ui.UiView.Category.NETWORK;
83import static org.onosproject.ui.UiView.Category.PLATFORM;
Heedo Kang4a47a302016-02-29 17:40:23 +090084
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080085/**
86 * Manages the user interface extensions.
87 */
88@Component(immediate = true)
89@Service
Simon Hunt3678c2a2016-03-28 14:48:07 -070090public class UiExtensionManager
Simon Hunt1169c952017-06-05 11:20:11 -070091 implements UiExtensionService, UiPreferencesService, SpriteService,
92 UiTokenService {
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080093
Thomas Vachuskafa74dd72016-03-20 19:11:12 -070094 private static final ClassLoader CL = UiExtensionManager.class.getClassLoader();
Simon Hunt3678c2a2016-03-28 14:48:07 -070095
Madan Jampani7b93ceb2016-05-04 09:58:40 -070096 private static final String ONOS_USER_PREFERENCES = "onos-ui-user-preferences";
Simon Hunt1169c952017-06-05 11:20:11 -070097 private static final String ONOS_SESSION_TOKENS = "onos-ui-session-tokens";
Simon Hunte05cae42015-07-23 17:35:24 -070098 private static final String CORE = "core";
Thomas Vachuskafa74dd72016-03-20 19:11:12 -070099 private static final String GUI_ADDED = "guiAdded";
100 private static final String GUI_REMOVED = "guiRemoved";
Thomas Vachuska0af26912016-03-21 21:37:30 -0700101 private static final String UPDATE_PREFS = "updatePrefs";
Simon Hunt3678c2a2016-03-28 14:48:07 -0700102 private static final String SLASH = "/";
103
104 private static final int IDX_USER = 0;
105 private static final int IDX_KEY = 1;
Simon Hunte05cae42015-07-23 17:35:24 -0700106
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800107 private final Logger log = LoggerFactory.getLogger(getClass());
108
109 // List of all extensions
110 private final List<UiExtension> extensions = Lists.newArrayList();
111
112 // Map of views to extensions
113 private final Map<String, UiExtension> views = Maps.newHashMap();
114
115 // Core views & core extension
Thomas Vachuska3553b302015-03-07 14:49:43 -0800116 private final UiExtension core = createCoreExtension();
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800117
Thomas Vachuska51f540f2015-05-27 17:26:57 -0700118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 protected MastershipService mastershipService;
Thomas Vachuska3553b302015-03-07 14:49:43 -0800120
Thomas Vachuska0af26912016-03-21 21:37:30 -0700121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
122 protected StorageService storageService;
123
124 // User preferences
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700125 private ConsistentMap<String, ObjectNode> prefsConsistentMap;
126 private Map<String, ObjectNode> prefs;
127 private final MapEventListener<String, ObjectNode> prefsListener =
Thomas Vachuska0af26912016-03-21 21:37:30 -0700128 new InternalPrefsListener();
129
Simon Hunt1169c952017-06-05 11:20:11 -0700130 // Session tokens
131 private ConsistentMap<UiSessionToken, String> tokensConsistentMap;
132 private Map<UiSessionToken, String> tokens;
133 private final SessionTokenGenerator tokenGen =
134 new SessionTokenGenerator();
135
Thomas Vachuska0af26912016-03-21 21:37:30 -0700136 private final ObjectMapper mapper = new ObjectMapper();
137
Madan Jampanibf8ee802016-05-04 14:07:36 -0700138 private final ExecutorService eventHandlingExecutor =
Simon Huntd5b96732016-07-08 13:22:27 -0700139 Executors.newSingleThreadExecutor(
140 Tools.groupedThreads("onos/ui-ext-manager", "event-handler", log));
Madan Jampanibf8ee802016-05-04 14:07:36 -0700141
Thomas Vachuska3553b302015-03-07 14:49:43 -0800142 // Creates core UI extension
Simon Huntc54cd1b2015-05-11 13:43:44 -0700143 private UiExtension createCoreExtension() {
Simon Hunt20e16792015-04-24 14:29:39 -0700144 List<UiView> coreViews = of(
145 new UiView(PLATFORM, "app", "Applications", "nav_apps"),
Thomas Vachuskaaa8b0eb2015-05-22 09:54:15 -0700146 new UiView(PLATFORM, "settings", "Settings", "nav_settings"),
Simon Hunt20e16792015-04-24 14:29:39 -0700147 new UiView(PLATFORM, "cluster", "Cluster Nodes", "nav_cluster"),
Thomas Vachuska3ece3732015-09-22 23:58:50 -0700148 new UiView(PLATFORM, "processor", "Packet Processors", "nav_processors"),
chengfanc5a99dc2017-01-08 19:28:29 +0800149 new UiView(PLATFORM, "partition", "Partitions", "nav_partitions"),
Simon Hunt20e16792015-04-24 14:29:39 -0700150 new UiView(NETWORK, "topo", "Topology", "nav_topo"),
Simon Hunt47f8fb72017-04-11 13:49:31 -0700151 new UiView(NETWORK, "topo2", "Topology 2", "nav_topo2"),
Simon Hunt20e16792015-04-24 14:29:39 -0700152 new UiView(NETWORK, "device", "Devices", "nav_devs"),
153 new UiViewHidden("flow"),
Bri Prebilic Coleac829e42015-05-05 13:42:06 -0700154 new UiViewHidden("port"),
Bri Prebilic Coleff3dc672015-05-06 12:59:38 -0700155 new UiViewHidden("group"),
Jian Li1f544732015-12-30 23:36:37 -0800156 new UiViewHidden("meter"),
Simon Hunt20e16792015-04-24 14:29:39 -0700157 new UiView(NETWORK, "link", "Links", "nav_links"),
158 new UiView(NETWORK, "host", "Hosts", "nav_hosts"),
chengfanb466a7e2015-08-21 09:59:29 -0500159 new UiView(NETWORK, "intent", "Intents", "nav_intents"),
Simon Hunt986b92f2016-06-03 15:46:59 -0700160 new UiView(NETWORK, "tunnel", "Tunnels", "nav_tunnels")
Simon Hunt20e16792015-04-24 14:29:39 -0700161 );
Simon Hunt1002cd82015-04-23 14:44:03 -0700162
Simon Hunt4c7edd32015-03-11 10:42:53 -0700163 UiMessageHandlerFactory messageHandlerFactory =
164 () -> ImmutableList.of(
Thomas Vachuska0af26912016-03-21 21:37:30 -0700165 new UserPreferencesMessageHandler(),
Thomas Vachuskae586b792015-03-26 13:59:38 -0700166 new TopologyViewMessageHandler(),
Simon Huntd5b96732016-07-08 13:22:27 -0700167 new Topo2ViewMessageHandler(),
Simon Hunte6f64612017-04-28 00:01:48 -0700168 new Topo2TrafficMessageHandler(),
Thomas Vachuska26be4f32016-03-31 01:10:27 -0700169 new MapSelectorMessageHandler(),
Thomas Vachuskae586b792015-03-26 13:59:38 -0700170 new DeviceViewMessageHandler(),
Thomas Vachuska583bc632015-04-14 10:10:57 -0700171 new LinkViewMessageHandler(),
Thomas Vachuska0fa583c2015-03-30 23:07:41 -0700172 new HostViewMessageHandler(),
Bri Prebilic Colecdc188d2015-04-24 16:40:11 -0700173 new FlowViewMessageHandler(),
Bri Prebilic Coleac829e42015-05-05 13:42:06 -0700174 new PortViewMessageHandler(),
Bri Prebilic Coleff3dc672015-05-06 12:59:38 -0700175 new GroupViewMessageHandler(),
Jian Li1f544732015-12-30 23:36:37 -0800176 new MeterViewMessageHandler(),
Bri Prebilic Cole384e8dc2015-04-13 15:51:14 -0700177 new IntentViewMessageHandler(),
Thomas Vachuska583bc632015-04-14 10:10:57 -0700178 new ApplicationViewMessageHandler(),
Thomas Vachuskaaa8b0eb2015-05-22 09:54:15 -0700179 new SettingsViewMessageHandler(),
chengfanb466a7e2015-08-21 09:59:29 -0500180 new ClusterViewMessageHandler(),
Thomas Vachuska3ece3732015-09-22 23:58:50 -0700181 new ProcessorViewMessageHandler(),
chengfanc5a99dc2017-01-08 19:28:29 +0800182 new TunnelViewMessageHandler(),
183 new PartitionViewMessageHandler()
Simon Hunt4c7edd32015-03-11 10:42:53 -0700184 );
Simon Hunt1002cd82015-04-23 14:44:03 -0700185
Simon Hunte05cae42015-07-23 17:35:24 -0700186 UiTopoOverlayFactory topoOverlayFactory =
187 () -> ImmutableList.of(
Andrea Campanella732ea832017-02-06 09:25:59 -0800188 new TrafficOverlay(),
189 new ProtectedIntentOverlay()
Simon Hunte05cae42015-07-23 17:35:24 -0700190 );
191
Simon Hunt6cc86452017-04-27 17:46:22 -0700192 UiTopo2OverlayFactory topo2OverlayFactory =
193 () -> ImmutableList.of(
194 new Traffic2Overlay()
195 );
196
Steven Burrows3a9a6442016-05-05 15:31:16 +0100197 UiTopoMapFactory topoMapFactory =
198 () -> ImmutableList.of(
199 new UiTopoMap("australia", "Australia", "*australia", 1.0),
200 new UiTopoMap("americas", "North, Central and South America", "*americas", 0.7),
201 new UiTopoMap("n_america", "North America", "*n_america", 0.9),
202 new UiTopoMap("s_america", "South America", "*s_america", 0.9),
Thomas Vachuskacc0b7d62016-07-12 14:03:11 -0700203 new UiTopoMap("usa", "United States", "*continental_us", 1.3),
Steven Burrows3a9a6442016-05-05 15:31:16 +0100204 new UiTopoMap("bayareaGEO", "Bay Area, California", "*bayarea", 1.0),
Thomas Vachuskacc0b7d62016-07-12 14:03:11 -0700205 new UiTopoMap("europe", "Europe", "*europe", 10.0),
Steven Burrows3a9a6442016-05-05 15:31:16 +0100206 new UiTopoMap("italy", "Italy", "*italy", 0.8),
Thomas Vachuskacc0b7d62016-07-12 14:03:11 -0700207 new UiTopoMap("uk", "United Kingdom and Ireland", "*uk", 2.0),
Steven Burrows3a9a6442016-05-05 15:31:16 +0100208 new UiTopoMap("japan", "Japan", "*japan", 0.8),
209 new UiTopoMap("s_korea", "South Korea", "*s_korea", 0.75),
210 new UiTopoMap("taiwan", "Taiwan", "*taiwan", 0.7),
211 new UiTopoMap("africa", "Africa", "*africa", 0.7),
212 new UiTopoMap("oceania", "Oceania", "*oceania", 0.7),
213 new UiTopoMap("asia", "Asia", "*asia", 0.7)
214 );
215
Simon Hunte05cae42015-07-23 17:35:24 -0700216 return new UiExtension.Builder(CL, coreViews)
217 .messageHandlerFactory(messageHandlerFactory)
218 .topoOverlayFactory(topoOverlayFactory)
Simon Hunt6cc86452017-04-27 17:46:22 -0700219 .topo2OverlayFactory(topo2OverlayFactory)
Steven Burrows3a9a6442016-05-05 15:31:16 +0100220 .topoMapFactory(topoMapFactory)
Simon Hunte05cae42015-07-23 17:35:24 -0700221 .resourcePath(CORE)
222 .build();
Thomas Vachuska3553b302015-03-07 14:49:43 -0800223 }
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800224
225 @Activate
226 public void activate() {
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700227 Serializer serializer = Serializer.using(KryoNamespaces.API,
Simon Hunt6cc86452017-04-27 17:46:22 -0700228 ObjectNode.class, ArrayNode.class,
229 JsonNodeFactory.class, LinkedHashMap.class,
230 TextNode.class, BooleanNode.class,
231 LongNode.class, DoubleNode.class, ShortNode.class,
Simon Hunt1169c952017-06-05 11:20:11 -0700232 IntNode.class, NullNode.class, UiSessionToken.class);
Thomas Vachuska0af26912016-03-21 21:37:30 -0700233
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700234 prefsConsistentMap = storageService.<String, ObjectNode>consistentMapBuilder()
Simon Hunt3678c2a2016-03-28 14:48:07 -0700235 .withName(ONOS_USER_PREFERENCES)
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700236 .withSerializer(serializer)
237 .withRelaxedReadConsistency()
Thomas Vachuska0af26912016-03-21 21:37:30 -0700238 .build();
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700239 prefsConsistentMap.addListener(prefsListener);
240 prefs = prefsConsistentMap.asJavaMap();
Simon Hunt1169c952017-06-05 11:20:11 -0700241
242 tokensConsistentMap = storageService.<UiSessionToken, String>consistentMapBuilder()
243 .withName(ONOS_SESSION_TOKENS)
244 .withSerializer(serializer)
245 .withRelaxedReadConsistency()
246 .build();
247 tokens = tokensConsistentMap.asJavaMap();
248
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800249 register(core);
Simon Huntb8042032017-06-13 15:03:23 -0700250 LionUtils.setupRuntimeLocale();
251
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800252 log.info("Started");
253 }
254
255 @Deactivate
256 public void deactivate() {
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700257 prefsConsistentMap.removeListener(prefsListener);
Madan Jampanibf8ee802016-05-04 14:07:36 -0700258 eventHandlingExecutor.shutdown();
Thomas Vachuska51f540f2015-05-27 17:26:57 -0700259 UiWebSocketServlet.closeAll();
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800260 unregister(core);
261 log.info("Stopped");
262 }
263
264 @Override
265 public synchronized void register(UiExtension extension) {
Heedo Kang4a47a302016-02-29 17:40:23 +0900266 checkPermission(UI_WRITE);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800267 if (!extensions.contains(extension)) {
268 extensions.add(extension);
269 for (UiView view : extension.views()) {
270 views.put(view.id(), extension);
271 }
Thomas Vachuskafa74dd72016-03-20 19:11:12 -0700272 UiWebSocketServlet.sendToAll(GUI_ADDED, null);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800273 }
274 }
275
276 @Override
277 public synchronized void unregister(UiExtension extension) {
Heedo Kang4a47a302016-02-29 17:40:23 +0900278 checkPermission(UI_WRITE);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800279 extensions.remove(extension);
Thomas Vachuskafa74dd72016-03-20 19:11:12 -0700280 extension.views().stream().map(UiView::id).collect(toSet()).forEach(views::remove);
281 UiWebSocketServlet.sendToAll(GUI_REMOVED, null);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800282 }
283
284 @Override
285 public synchronized List<UiExtension> getExtensions() {
Heedo Kang4a47a302016-02-29 17:40:23 +0900286 checkPermission(UI_READ);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800287 return ImmutableList.copyOf(extensions);
288 }
289
290 @Override
291 public synchronized UiExtension getViewExtension(String viewId) {
Heedo Kang4a47a302016-02-29 17:40:23 +0900292 checkPermission(UI_READ);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800293 return views.get(viewId);
294 }
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700295
Thomas Vachuska0af26912016-03-21 21:37:30 -0700296 @Override
297 public Set<String> getUserNames() {
298 ImmutableSet.Builder<String> builder = ImmutableSet.builder();
299 prefs.keySet().forEach(k -> builder.add(userName(k)));
300 return builder.build();
301 }
302
303 @Override
304 public Map<String, ObjectNode> getPreferences(String userName) {
305 ImmutableMap.Builder<String, ObjectNode> builder = ImmutableMap.builder();
306 prefs.entrySet().stream()
Simon Hunt3678c2a2016-03-28 14:48:07 -0700307 .filter(e -> e.getKey().startsWith(userName + SLASH))
Thomas Vachuska0af26912016-03-21 21:37:30 -0700308 .forEach(e -> builder.put(keyName(e.getKey()), e.getValue()));
309 return builder.build();
310 }
311
312 @Override
313 public void setPreference(String userName, String preference, ObjectNode value) {
314 prefs.put(key(userName, preference), value);
315 }
316
Simon Huntc54cd1b2015-05-11 13:43:44 -0700317 // =====================================================================
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700318 // Provisional tracking of sprite definitions
Simon Huntc54cd1b2015-05-11 13:43:44 -0700319
320 private final Map<String, JsonNode> sprites = Maps.newHashMap();
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700321
322 @Override
323 public Set<String> getNames() {
324 return ImmutableSet.copyOf(sprites.keySet());
325 }
326
327 @Override
328 public void put(String name, JsonNode spriteData) {
Simon Huntfd8c7d72015-04-14 17:53:37 -0700329 log.info("Registered sprite definition [{}]", name);
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700330 sprites.put(name, spriteData);
331 }
332
333 @Override
334 public JsonNode get(String name) {
335 return sprites.get(name);
336 }
337
Thomas Vachuska0af26912016-03-21 21:37:30 -0700338 private String key(String userName, String keyName) {
Simon Hunt3678c2a2016-03-28 14:48:07 -0700339 return userName + SLASH + keyName;
Thomas Vachuska0af26912016-03-21 21:37:30 -0700340 }
341
Simon Hunt3678c2a2016-03-28 14:48:07 -0700342
Thomas Vachuska0af26912016-03-21 21:37:30 -0700343 private String userName(String key) {
Simon Hunt3678c2a2016-03-28 14:48:07 -0700344 return key.split(SLASH)[IDX_USER];
Thomas Vachuska0af26912016-03-21 21:37:30 -0700345 }
346
347 private String keyName(String key) {
Simon Hunt3678c2a2016-03-28 14:48:07 -0700348 return key.split(SLASH)[IDX_KEY];
Thomas Vachuska0af26912016-03-21 21:37:30 -0700349 }
350
Simon Hunt1169c952017-06-05 11:20:11 -0700351
352 // =====================================================================
353 // UiTokenService
354
355 @Override
356 public UiSessionToken issueToken(String username) {
357 UiSessionToken token = new UiSessionToken(tokenGen.nextSessionId());
358 tokens.put(token, username);
359 log.debug("UiSessionToken issued: {}", token);
360 return token;
361 }
362
363 @Override
364 public void revokeToken(UiSessionToken token) {
365 if (token != null) {
366 tokens.remove(token);
367 log.debug("UiSessionToken revoked: {}", token);
368 }
369 }
370
371 @Override
372 public boolean isTokenValid(UiSessionToken token) {
373 return token != null && tokens.containsKey(token);
374 }
375
376 private final class SessionTokenGenerator {
377 private final SecureRandom random = new SecureRandom();
378
379 /*
380 This works by choosing 130 bits from a cryptographically secure
381 random bit generator, and encoding them in base-32.
382
383 128 bits is considered to be cryptographically strong, but each
384 digit in a base 32 number can encode 5 bits, so 128 is rounded up
385 to the next multiple of 5.
386
387 This encoding is compact and efficient, with 5 random bits per
388 character. Compare this to a random UUID, which only has 3.4 bits
389 per character in standard layout, and only 122 random bits in total.
390
391 Note that SecureRandom objects are expensive to initialize, so
392 we'll want to keep it around and re-use it.
393 */
394
395 private String nextSessionId() {
396 return new BigInteger(130, random).toString(32);
397 }
398 }
399
Thomas Vachuska0af26912016-03-21 21:37:30 -0700400 // Auxiliary listener to preference map events.
401 private class InternalPrefsListener
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700402 implements MapEventListener<String, ObjectNode> {
Thomas Vachuska0af26912016-03-21 21:37:30 -0700403 @Override
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700404 public void event(MapEvent<String, ObjectNode> event) {
Madan Jampanibf8ee802016-05-04 14:07:36 -0700405 eventHandlingExecutor.execute(() -> {
406 String userName = userName(event.key());
407 if (event.type() == MapEvent.Type.INSERT || event.type() == MapEvent.Type.UPDATE) {
408 UiWebSocketServlet.sendToUser(userName, UPDATE_PREFS, jsonPrefs());
409 }
410 });
Thomas Vachuska0af26912016-03-21 21:37:30 -0700411 }
412
413 private ObjectNode jsonPrefs() {
414 ObjectNode json = mapper.createObjectNode();
Simon Hunt1169c952017-06-05 11:20:11 -0700415 prefs.forEach((key, value) -> json.set(keyName(key), value));
Thomas Vachuska0af26912016-03-21 21:37:30 -0700416 return json;
417 }
418 }
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800419}