blob: 7909498e642b29fae167088f50a22a10b0013708 [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;
Simon Huntd6d3ad32017-06-21 15:27:06 -070072import java.util.Locale;
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080073import java.util.Map;
Thomas Vachuska9ed335b2015-04-14 12:07:47 -070074import java.util.Set;
Madan Jampanibf8ee802016-05-04 14:07:36 -070075import java.util.concurrent.ExecutorService;
76import java.util.concurrent.Executors;
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080077
78import static com.google.common.collect.ImmutableList.of;
79import static java.util.stream.Collectors.toSet;
Heedo Kang4a47a302016-02-29 17:40:23 +090080import static org.onosproject.security.AppGuard.checkPermission;
81import static org.onosproject.security.AppPermission.Type.UI_READ;
82import static org.onosproject.security.AppPermission.Type.UI_WRITE;
Thomas Vachuska0af26912016-03-21 21:37:30 -070083import static org.onosproject.ui.UiView.Category.NETWORK;
84import static org.onosproject.ui.UiView.Category.PLATFORM;
Simon Huntd8754652017-06-21 11:45:22 -070085import static org.onosproject.ui.impl.lion.BundleStitcher.generateBundles;
Heedo Kang4a47a302016-02-29 17:40:23 +090086
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080087/**
88 * Manages the user interface extensions.
89 */
90@Component(immediate = true)
91@Service
Simon Hunt3678c2a2016-03-28 14:48:07 -070092public class UiExtensionManager
Simon Hunt1169c952017-06-05 11:20:11 -070093 implements UiExtensionService, UiPreferencesService, SpriteService,
94 UiTokenService {
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080095
Thomas Vachuskafa74dd72016-03-20 19:11:12 -070096 private static final ClassLoader CL = UiExtensionManager.class.getClassLoader();
Simon Hunt3678c2a2016-03-28 14:48:07 -070097
Madan Jampani7b93ceb2016-05-04 09:58:40 -070098 private static final String ONOS_USER_PREFERENCES = "onos-ui-user-preferences";
Simon Hunt1169c952017-06-05 11:20:11 -070099 private static final String ONOS_SESSION_TOKENS = "onos-ui-session-tokens";
Simon Hunte05cae42015-07-23 17:35:24 -0700100 private static final String CORE = "core";
Thomas Vachuskafa74dd72016-03-20 19:11:12 -0700101 private static final String GUI_ADDED = "guiAdded";
102 private static final String GUI_REMOVED = "guiRemoved";
Thomas Vachuska0af26912016-03-21 21:37:30 -0700103 private static final String UPDATE_PREFS = "updatePrefs";
Simon Hunt3678c2a2016-03-28 14:48:07 -0700104 private static final String SLASH = "/";
105
106 private static final int IDX_USER = 0;
107 private static final int IDX_KEY = 1;
Simon Hunte05cae42015-07-23 17:35:24 -0700108
Simon Hunt7379a3d2017-06-20 16:50:39 -0700109 private static final String LION_BASE = "/org/onosproject/ui/lion";
110
111 private static final String[] LION_TAGS = {
Simon Huntcffb2862017-06-23 12:02:43 -0700112 // framework component localization
113 "core.fw.Mast",
114 "core.fw.Nav",
115 "core.fw.QuickHelp",
116
117 // view component localization
118 "core.view.App",
Simon Hunt7379a3d2017-06-20 16:50:39 -0700119 "core.view.Cluster",
120
Simon Huntcffb2862017-06-23 12:02:43 -0700121 // TODO: More to come...
Simon Hunt7379a3d2017-06-20 16:50:39 -0700122 };
123
124
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800125 private final Logger log = LoggerFactory.getLogger(getClass());
126
Simon Huntd6d3ad32017-06-21 15:27:06 -0700127 // First thing to do is to set the locale (before creating core extension).
128 private final Locale runtimeLocale = LionUtils.setupRuntimeLocale();
129
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800130 // List of all extensions
131 private final List<UiExtension> extensions = Lists.newArrayList();
132
133 // Map of views to extensions
134 private final Map<String, UiExtension> views = Maps.newHashMap();
135
136 // Core views & core extension
Thomas Vachuska3553b302015-03-07 14:49:43 -0800137 private final UiExtension core = createCoreExtension();
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800138
Thomas Vachuska51f540f2015-05-27 17:26:57 -0700139 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
140 protected MastershipService mastershipService;
Thomas Vachuska3553b302015-03-07 14:49:43 -0800141
Thomas Vachuska0af26912016-03-21 21:37:30 -0700142 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
143 protected StorageService storageService;
144
145 // User preferences
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700146 private ConsistentMap<String, ObjectNode> prefsConsistentMap;
147 private Map<String, ObjectNode> prefs;
148 private final MapEventListener<String, ObjectNode> prefsListener =
Thomas Vachuska0af26912016-03-21 21:37:30 -0700149 new InternalPrefsListener();
150
Simon Hunt1169c952017-06-05 11:20:11 -0700151 // Session tokens
152 private ConsistentMap<UiSessionToken, String> tokensConsistentMap;
153 private Map<UiSessionToken, String> tokens;
154 private final SessionTokenGenerator tokenGen =
155 new SessionTokenGenerator();
156
Thomas Vachuska0af26912016-03-21 21:37:30 -0700157 private final ObjectMapper mapper = new ObjectMapper();
158
Madan Jampanibf8ee802016-05-04 14:07:36 -0700159 private final ExecutorService eventHandlingExecutor =
Simon Huntd5b96732016-07-08 13:22:27 -0700160 Executors.newSingleThreadExecutor(
161 Tools.groupedThreads("onos/ui-ext-manager", "event-handler", log));
Madan Jampanibf8ee802016-05-04 14:07:36 -0700162
Thomas Vachuska3553b302015-03-07 14:49:43 -0800163 // Creates core UI extension
Simon Huntc54cd1b2015-05-11 13:43:44 -0700164 private UiExtension createCoreExtension() {
Simon Hunt20e16792015-04-24 14:29:39 -0700165 List<UiView> coreViews = of(
166 new UiView(PLATFORM, "app", "Applications", "nav_apps"),
Thomas Vachuskaaa8b0eb2015-05-22 09:54:15 -0700167 new UiView(PLATFORM, "settings", "Settings", "nav_settings"),
Simon Hunt20e16792015-04-24 14:29:39 -0700168 new UiView(PLATFORM, "cluster", "Cluster Nodes", "nav_cluster"),
Thomas Vachuska3ece3732015-09-22 23:58:50 -0700169 new UiView(PLATFORM, "processor", "Packet Processors", "nav_processors"),
chengfanc5a99dc2017-01-08 19:28:29 +0800170 new UiView(PLATFORM, "partition", "Partitions", "nav_partitions"),
Simon Hunt20e16792015-04-24 14:29:39 -0700171 new UiView(NETWORK, "topo", "Topology", "nav_topo"),
Simon Hunt47f8fb72017-04-11 13:49:31 -0700172 new UiView(NETWORK, "topo2", "Topology 2", "nav_topo2"),
Simon Hunt20e16792015-04-24 14:29:39 -0700173 new UiView(NETWORK, "device", "Devices", "nav_devs"),
174 new UiViewHidden("flow"),
Bri Prebilic Coleac829e42015-05-05 13:42:06 -0700175 new UiViewHidden("port"),
Bri Prebilic Coleff3dc672015-05-06 12:59:38 -0700176 new UiViewHidden("group"),
Jian Li1f544732015-12-30 23:36:37 -0800177 new UiViewHidden("meter"),
Simon Hunt20e16792015-04-24 14:29:39 -0700178 new UiView(NETWORK, "link", "Links", "nav_links"),
179 new UiView(NETWORK, "host", "Hosts", "nav_hosts"),
chengfanb466a7e2015-08-21 09:59:29 -0500180 new UiView(NETWORK, "intent", "Intents", "nav_intents"),
Simon Hunt986b92f2016-06-03 15:46:59 -0700181 new UiView(NETWORK, "tunnel", "Tunnels", "nav_tunnels")
Simon Hunt20e16792015-04-24 14:29:39 -0700182 );
Simon Hunt1002cd82015-04-23 14:44:03 -0700183
Simon Hunt4c7edd32015-03-11 10:42:53 -0700184 UiMessageHandlerFactory messageHandlerFactory =
185 () -> ImmutableList.of(
Thomas Vachuska0af26912016-03-21 21:37:30 -0700186 new UserPreferencesMessageHandler(),
Thomas Vachuskae586b792015-03-26 13:59:38 -0700187 new TopologyViewMessageHandler(),
Simon Huntd5b96732016-07-08 13:22:27 -0700188 new Topo2ViewMessageHandler(),
Simon Hunte6f64612017-04-28 00:01:48 -0700189 new Topo2TrafficMessageHandler(),
Thomas Vachuska26be4f32016-03-31 01:10:27 -0700190 new MapSelectorMessageHandler(),
Thomas Vachuskae586b792015-03-26 13:59:38 -0700191 new DeviceViewMessageHandler(),
Thomas Vachuska583bc632015-04-14 10:10:57 -0700192 new LinkViewMessageHandler(),
Thomas Vachuska0fa583c2015-03-30 23:07:41 -0700193 new HostViewMessageHandler(),
Bri Prebilic Colecdc188d2015-04-24 16:40:11 -0700194 new FlowViewMessageHandler(),
Bri Prebilic Coleac829e42015-05-05 13:42:06 -0700195 new PortViewMessageHandler(),
Bri Prebilic Coleff3dc672015-05-06 12:59:38 -0700196 new GroupViewMessageHandler(),
Jian Li1f544732015-12-30 23:36:37 -0800197 new MeterViewMessageHandler(),
Bri Prebilic Cole384e8dc2015-04-13 15:51:14 -0700198 new IntentViewMessageHandler(),
Thomas Vachuska583bc632015-04-14 10:10:57 -0700199 new ApplicationViewMessageHandler(),
Thomas Vachuskaaa8b0eb2015-05-22 09:54:15 -0700200 new SettingsViewMessageHandler(),
chengfanb466a7e2015-08-21 09:59:29 -0500201 new ClusterViewMessageHandler(),
Thomas Vachuska3ece3732015-09-22 23:58:50 -0700202 new ProcessorViewMessageHandler(),
chengfanc5a99dc2017-01-08 19:28:29 +0800203 new TunnelViewMessageHandler(),
204 new PartitionViewMessageHandler()
Simon Hunt4c7edd32015-03-11 10:42:53 -0700205 );
Simon Hunt1002cd82015-04-23 14:44:03 -0700206
Simon Hunte05cae42015-07-23 17:35:24 -0700207 UiTopoOverlayFactory topoOverlayFactory =
208 () -> ImmutableList.of(
Andrea Campanella732ea832017-02-06 09:25:59 -0800209 new TrafficOverlay(),
210 new ProtectedIntentOverlay()
Simon Hunte05cae42015-07-23 17:35:24 -0700211 );
212
Simon Hunt6cc86452017-04-27 17:46:22 -0700213 UiTopo2OverlayFactory topo2OverlayFactory =
214 () -> ImmutableList.of(
215 new Traffic2Overlay()
216 );
217
Steven Burrows3a9a6442016-05-05 15:31:16 +0100218 UiTopoMapFactory topoMapFactory =
219 () -> ImmutableList.of(
220 new UiTopoMap("australia", "Australia", "*australia", 1.0),
221 new UiTopoMap("americas", "North, Central and South America", "*americas", 0.7),
222 new UiTopoMap("n_america", "North America", "*n_america", 0.9),
223 new UiTopoMap("s_america", "South America", "*s_america", 0.9),
Thomas Vachuskacc0b7d62016-07-12 14:03:11 -0700224 new UiTopoMap("usa", "United States", "*continental_us", 1.3),
Steven Burrows3a9a6442016-05-05 15:31:16 +0100225 new UiTopoMap("bayareaGEO", "Bay Area, California", "*bayarea", 1.0),
Thomas Vachuskacc0b7d62016-07-12 14:03:11 -0700226 new UiTopoMap("europe", "Europe", "*europe", 10.0),
Steven Burrows3a9a6442016-05-05 15:31:16 +0100227 new UiTopoMap("italy", "Italy", "*italy", 0.8),
Thomas Vachuskacc0b7d62016-07-12 14:03:11 -0700228 new UiTopoMap("uk", "United Kingdom and Ireland", "*uk", 2.0),
Steven Burrows3a9a6442016-05-05 15:31:16 +0100229 new UiTopoMap("japan", "Japan", "*japan", 0.8),
230 new UiTopoMap("s_korea", "South Korea", "*s_korea", 0.75),
231 new UiTopoMap("taiwan", "Taiwan", "*taiwan", 0.7),
232 new UiTopoMap("africa", "Africa", "*africa", 0.7),
233 new UiTopoMap("oceania", "Oceania", "*oceania", 0.7),
234 new UiTopoMap("asia", "Asia", "*asia", 0.7)
235 );
236
Simon Hunte05cae42015-07-23 17:35:24 -0700237 return new UiExtension.Builder(CL, coreViews)
Simon Huntd8754652017-06-21 11:45:22 -0700238 .lionBundles(generateBundles(LION_BASE, LION_TAGS))
Simon Hunte05cae42015-07-23 17:35:24 -0700239 .messageHandlerFactory(messageHandlerFactory)
240 .topoOverlayFactory(topoOverlayFactory)
Simon Hunt6cc86452017-04-27 17:46:22 -0700241 .topo2OverlayFactory(topo2OverlayFactory)
Steven Burrows3a9a6442016-05-05 15:31:16 +0100242 .topoMapFactory(topoMapFactory)
Simon Hunte05cae42015-07-23 17:35:24 -0700243 .resourcePath(CORE)
244 .build();
Thomas Vachuska3553b302015-03-07 14:49:43 -0800245 }
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800246
Simon Hunt7379a3d2017-06-20 16:50:39 -0700247
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800248 @Activate
249 public void activate() {
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700250 Serializer serializer = Serializer.using(KryoNamespaces.API,
Simon Huntd6d3ad32017-06-21 15:27:06 -0700251 ObjectNode.class, ArrayNode.class,
252 JsonNodeFactory.class, LinkedHashMap.class,
253 TextNode.class, BooleanNode.class,
254 LongNode.class, DoubleNode.class, ShortNode.class,
255 IntNode.class, NullNode.class, UiSessionToken.class);
Thomas Vachuska0af26912016-03-21 21:37:30 -0700256
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700257 prefsConsistentMap = storageService.<String, ObjectNode>consistentMapBuilder()
Simon Hunt3678c2a2016-03-28 14:48:07 -0700258 .withName(ONOS_USER_PREFERENCES)
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700259 .withSerializer(serializer)
260 .withRelaxedReadConsistency()
Thomas Vachuska0af26912016-03-21 21:37:30 -0700261 .build();
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700262 prefsConsistentMap.addListener(prefsListener);
263 prefs = prefsConsistentMap.asJavaMap();
Simon Hunt1169c952017-06-05 11:20:11 -0700264
265 tokensConsistentMap = storageService.<UiSessionToken, String>consistentMapBuilder()
266 .withName(ONOS_SESSION_TOKENS)
267 .withSerializer(serializer)
268 .withRelaxedReadConsistency()
269 .build();
270 tokens = tokensConsistentMap.asJavaMap();
271
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800272 register(core);
Simon Huntb8042032017-06-13 15:03:23 -0700273
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800274 log.info("Started");
275 }
276
277 @Deactivate
278 public void deactivate() {
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700279 prefsConsistentMap.removeListener(prefsListener);
Madan Jampanibf8ee802016-05-04 14:07:36 -0700280 eventHandlingExecutor.shutdown();
Thomas Vachuska51f540f2015-05-27 17:26:57 -0700281 UiWebSocketServlet.closeAll();
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800282 unregister(core);
283 log.info("Stopped");
284 }
285
286 @Override
287 public synchronized void register(UiExtension extension) {
Heedo Kang4a47a302016-02-29 17:40:23 +0900288 checkPermission(UI_WRITE);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800289 if (!extensions.contains(extension)) {
290 extensions.add(extension);
291 for (UiView view : extension.views()) {
292 views.put(view.id(), extension);
293 }
Thomas Vachuskafa74dd72016-03-20 19:11:12 -0700294 UiWebSocketServlet.sendToAll(GUI_ADDED, null);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800295 }
296 }
297
298 @Override
299 public synchronized void unregister(UiExtension extension) {
Heedo Kang4a47a302016-02-29 17:40:23 +0900300 checkPermission(UI_WRITE);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800301 extensions.remove(extension);
Simon Huntd6d3ad32017-06-21 15:27:06 -0700302 extension.views().stream()
303 .map(UiView::id).collect(toSet()).forEach(views::remove);
Thomas Vachuskafa74dd72016-03-20 19:11:12 -0700304 UiWebSocketServlet.sendToAll(GUI_REMOVED, null);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800305 }
306
307 @Override
308 public synchronized List<UiExtension> getExtensions() {
Heedo Kang4a47a302016-02-29 17:40:23 +0900309 checkPermission(UI_READ);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800310 return ImmutableList.copyOf(extensions);
311 }
312
313 @Override
314 public synchronized UiExtension getViewExtension(String viewId) {
Heedo Kang4a47a302016-02-29 17:40:23 +0900315 checkPermission(UI_READ);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800316 return views.get(viewId);
317 }
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700318
Thomas Vachuska0af26912016-03-21 21:37:30 -0700319 @Override
320 public Set<String> getUserNames() {
321 ImmutableSet.Builder<String> builder = ImmutableSet.builder();
322 prefs.keySet().forEach(k -> builder.add(userName(k)));
323 return builder.build();
324 }
325
326 @Override
327 public Map<String, ObjectNode> getPreferences(String userName) {
328 ImmutableMap.Builder<String, ObjectNode> builder = ImmutableMap.builder();
329 prefs.entrySet().stream()
Simon Hunt3678c2a2016-03-28 14:48:07 -0700330 .filter(e -> e.getKey().startsWith(userName + SLASH))
Thomas Vachuska0af26912016-03-21 21:37:30 -0700331 .forEach(e -> builder.put(keyName(e.getKey()), e.getValue()));
332 return builder.build();
333 }
334
335 @Override
336 public void setPreference(String userName, String preference, ObjectNode value) {
337 prefs.put(key(userName, preference), value);
338 }
339
Simon Huntc54cd1b2015-05-11 13:43:44 -0700340 // =====================================================================
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700341 // Provisional tracking of sprite definitions
Simon Huntc54cd1b2015-05-11 13:43:44 -0700342
343 private final Map<String, JsonNode> sprites = Maps.newHashMap();
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700344
345 @Override
346 public Set<String> getNames() {
347 return ImmutableSet.copyOf(sprites.keySet());
348 }
349
350 @Override
351 public void put(String name, JsonNode spriteData) {
Simon Huntfd8c7d72015-04-14 17:53:37 -0700352 log.info("Registered sprite definition [{}]", name);
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700353 sprites.put(name, spriteData);
354 }
355
356 @Override
357 public JsonNode get(String name) {
358 return sprites.get(name);
359 }
360
Thomas Vachuska0af26912016-03-21 21:37:30 -0700361 private String key(String userName, String keyName) {
Simon Hunt3678c2a2016-03-28 14:48:07 -0700362 return userName + SLASH + keyName;
Thomas Vachuska0af26912016-03-21 21:37:30 -0700363 }
364
Simon Hunt3678c2a2016-03-28 14:48:07 -0700365
Thomas Vachuska0af26912016-03-21 21:37:30 -0700366 private String userName(String key) {
Simon Hunt3678c2a2016-03-28 14:48:07 -0700367 return key.split(SLASH)[IDX_USER];
Thomas Vachuska0af26912016-03-21 21:37:30 -0700368 }
369
370 private String keyName(String key) {
Simon Hunt3678c2a2016-03-28 14:48:07 -0700371 return key.split(SLASH)[IDX_KEY];
Thomas Vachuska0af26912016-03-21 21:37:30 -0700372 }
373
Simon Hunt1169c952017-06-05 11:20:11 -0700374
375 // =====================================================================
376 // UiTokenService
377
378 @Override
379 public UiSessionToken issueToken(String username) {
380 UiSessionToken token = new UiSessionToken(tokenGen.nextSessionId());
381 tokens.put(token, username);
382 log.debug("UiSessionToken issued: {}", token);
383 return token;
384 }
385
386 @Override
387 public void revokeToken(UiSessionToken token) {
388 if (token != null) {
389 tokens.remove(token);
390 log.debug("UiSessionToken revoked: {}", token);
391 }
392 }
393
394 @Override
395 public boolean isTokenValid(UiSessionToken token) {
396 return token != null && tokens.containsKey(token);
397 }
398
399 private final class SessionTokenGenerator {
400 private final SecureRandom random = new SecureRandom();
401
402 /*
403 This works by choosing 130 bits from a cryptographically secure
404 random bit generator, and encoding them in base-32.
405
406 128 bits is considered to be cryptographically strong, but each
407 digit in a base 32 number can encode 5 bits, so 128 is rounded up
408 to the next multiple of 5.
409
410 This encoding is compact and efficient, with 5 random bits per
411 character. Compare this to a random UUID, which only has 3.4 bits
412 per character in standard layout, and only 122 random bits in total.
413
414 Note that SecureRandom objects are expensive to initialize, so
415 we'll want to keep it around and re-use it.
416 */
417
418 private String nextSessionId() {
419 return new BigInteger(130, random).toString(32);
420 }
421 }
422
Thomas Vachuska0af26912016-03-21 21:37:30 -0700423 // Auxiliary listener to preference map events.
424 private class InternalPrefsListener
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700425 implements MapEventListener<String, ObjectNode> {
Thomas Vachuska0af26912016-03-21 21:37:30 -0700426 @Override
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700427 public void event(MapEvent<String, ObjectNode> event) {
Madan Jampanibf8ee802016-05-04 14:07:36 -0700428 eventHandlingExecutor.execute(() -> {
429 String userName = userName(event.key());
430 if (event.type() == MapEvent.Type.INSERT || event.type() == MapEvent.Type.UPDATE) {
431 UiWebSocketServlet.sendToUser(userName, UPDATE_PREFS, jsonPrefs());
432 }
433 });
Thomas Vachuska0af26912016-03-21 21:37:30 -0700434 }
435
436 private ObjectNode jsonPrefs() {
437 ObjectNode json = mapper.createObjectNode();
Simon Hunt1169c952017-06-05 11:20:11 -0700438 prefs.forEach((key, value) -> json.set(keyName(key), value));
Thomas Vachuska0af26912016-03-21 21:37:30 -0700439 return json;
440 }
441 }
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800442}