blob: 7895751ecf6e475af9dd6328be75cd93e111d71c [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 Hunt23f9c7b2017-07-10 20:00:30 -070064import org.onosproject.ui.lion.LionBundle;
Simon Huntb8042032017-06-13 15:03:23 -070065import org.onosproject.ui.lion.LionUtils;
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080066import org.slf4j.Logger;
67import org.slf4j.LoggerFactory;
68
Simon Hunt1169c952017-06-05 11:20:11 -070069import java.math.BigInteger;
70import java.security.SecureRandom;
Thomas Vachuska0af26912016-03-21 21:37:30 -070071import java.util.LinkedHashMap;
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080072import java.util.List;
Simon Huntd6d3ad32017-06-21 15:27:06 -070073import java.util.Locale;
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080074import java.util.Map;
Thomas Vachuska9ed335b2015-04-14 12:07:47 -070075import java.util.Set;
Madan Jampanibf8ee802016-05-04 14:07:36 -070076import java.util.concurrent.ExecutorService;
77import java.util.concurrent.Executors;
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080078
79import static com.google.common.collect.ImmutableList.of;
80import static java.util.stream.Collectors.toSet;
Heedo Kang4a47a302016-02-29 17:40:23 +090081import static org.onosproject.security.AppGuard.checkPermission;
82import static org.onosproject.security.AppPermission.Type.UI_READ;
83import static org.onosproject.security.AppPermission.Type.UI_WRITE;
Thomas Vachuska0af26912016-03-21 21:37:30 -070084import static org.onosproject.ui.UiView.Category.NETWORK;
85import static org.onosproject.ui.UiView.Category.PLATFORM;
Simon Huntd8754652017-06-21 11:45:22 -070086import static org.onosproject.ui.impl.lion.BundleStitcher.generateBundles;
Heedo Kang4a47a302016-02-29 17:40:23 +090087
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080088/**
89 * Manages the user interface extensions.
90 */
91@Component(immediate = true)
92@Service
Simon Hunt3678c2a2016-03-28 14:48:07 -070093public class UiExtensionManager
Simon Hunt1169c952017-06-05 11:20:11 -070094 implements UiExtensionService, UiPreferencesService, SpriteService,
95 UiTokenService {
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080096
Thomas Vachuskafa74dd72016-03-20 19:11:12 -070097 private static final ClassLoader CL = UiExtensionManager.class.getClassLoader();
Simon Hunt3678c2a2016-03-28 14:48:07 -070098
Madan Jampani7b93ceb2016-05-04 09:58:40 -070099 private static final String ONOS_USER_PREFERENCES = "onos-ui-user-preferences";
Simon Hunt1169c952017-06-05 11:20:11 -0700100 private static final String ONOS_SESSION_TOKENS = "onos-ui-session-tokens";
Simon Hunte05cae42015-07-23 17:35:24 -0700101 private static final String CORE = "core";
Thomas Vachuskafa74dd72016-03-20 19:11:12 -0700102 private static final String GUI_ADDED = "guiAdded";
103 private static final String GUI_REMOVED = "guiRemoved";
Thomas Vachuska0af26912016-03-21 21:37:30 -0700104 private static final String UPDATE_PREFS = "updatePrefs";
Simon Hunt3678c2a2016-03-28 14:48:07 -0700105 private static final String SLASH = "/";
106
107 private static final int IDX_USER = 0;
108 private static final int IDX_KEY = 1;
Simon Hunte05cae42015-07-23 17:35:24 -0700109
Simon Hunt7379a3d2017-06-20 16:50:39 -0700110 private static final String LION_BASE = "/org/onosproject/ui/lion";
111
112 private static final String[] LION_TAGS = {
Simon Huntcffb2862017-06-23 12:02:43 -0700113 // framework component localization
114 "core.fw.Mast",
115 "core.fw.Nav",
116 "core.fw.QuickHelp",
117
118 // view component localization
119 "core.view.App",
Simon Hunt7379a3d2017-06-20 16:50:39 -0700120 "core.view.Cluster",
121
Simon Huntcffb2862017-06-23 12:02:43 -0700122 // TODO: More to come...
Simon Hunt7379a3d2017-06-20 16:50:39 -0700123 };
124
125
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800126 private final Logger log = LoggerFactory.getLogger(getClass());
127
Simon Huntd6d3ad32017-06-21 15:27:06 -0700128 // First thing to do is to set the locale (before creating core extension).
129 private final Locale runtimeLocale = LionUtils.setupRuntimeLocale();
130
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800131 // List of all extensions
132 private final List<UiExtension> extensions = Lists.newArrayList();
133
134 // Map of views to extensions
135 private final Map<String, UiExtension> views = Maps.newHashMap();
136
137 // Core views & core extension
Thomas Vachuska3553b302015-03-07 14:49:43 -0800138 private final UiExtension core = createCoreExtension();
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800139
Thomas Vachuska51f540f2015-05-27 17:26:57 -0700140 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
141 protected MastershipService mastershipService;
Thomas Vachuska3553b302015-03-07 14:49:43 -0800142
Thomas Vachuska0af26912016-03-21 21:37:30 -0700143 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
144 protected StorageService storageService;
145
146 // User preferences
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700147 private ConsistentMap<String, ObjectNode> prefsConsistentMap;
148 private Map<String, ObjectNode> prefs;
149 private final MapEventListener<String, ObjectNode> prefsListener =
Thomas Vachuska0af26912016-03-21 21:37:30 -0700150 new InternalPrefsListener();
151
Simon Hunt1169c952017-06-05 11:20:11 -0700152 // Session tokens
153 private ConsistentMap<UiSessionToken, String> tokensConsistentMap;
154 private Map<UiSessionToken, String> tokens;
155 private final SessionTokenGenerator tokenGen =
156 new SessionTokenGenerator();
157
Thomas Vachuska0af26912016-03-21 21:37:30 -0700158 private final ObjectMapper mapper = new ObjectMapper();
159
Madan Jampanibf8ee802016-05-04 14:07:36 -0700160 private final ExecutorService eventHandlingExecutor =
Simon Huntd5b96732016-07-08 13:22:27 -0700161 Executors.newSingleThreadExecutor(
162 Tools.groupedThreads("onos/ui-ext-manager", "event-handler", log));
Madan Jampanibf8ee802016-05-04 14:07:36 -0700163
Simon Hunt23f9c7b2017-07-10 20:00:30 -0700164 private LionBundle navLion;
165
166
167 private String lionNavText(String id) {
168 return navLion.getValue("nav_item_" + id);
169 }
170
171 private UiView mkView(UiView.Category cat, String id, String iconId) {
172 return new UiView(cat, id, lionNavText(id), iconId);
173 }
174
Simon Huntc54cd1b2015-05-11 13:43:44 -0700175 private UiExtension createCoreExtension() {
Simon Hunt23f9c7b2017-07-10 20:00:30 -0700176 List<LionBundle> lionBundles = generateBundles(LION_BASE, LION_TAGS);
177
178 navLion = lionBundles.stream()
179 .filter(f -> f.id().equals("core.fw.Nav")).findFirst().get();
180
Simon Hunt20e16792015-04-24 14:29:39 -0700181 List<UiView> coreViews = of(
Simon Hunt23f9c7b2017-07-10 20:00:30 -0700182 mkView(PLATFORM, "app", "nav_apps"),
183 mkView(PLATFORM, "settings", "nav_settings"),
184 mkView(PLATFORM, "cluster", "nav_cluster"),
185 mkView(PLATFORM, "processor", "nav_processors"),
186 mkView(PLATFORM, "partition", "nav_partitions"),
187
188 mkView(NETWORK, "topo", "nav_topo"),
189 mkView(NETWORK, "topo2", "nav_topo2"),
190 mkView(NETWORK, "device", "nav_devs"),
191
Simon Hunt20e16792015-04-24 14:29:39 -0700192 new UiViewHidden("flow"),
Bri Prebilic Coleac829e42015-05-05 13:42:06 -0700193 new UiViewHidden("port"),
Bri Prebilic Coleff3dc672015-05-06 12:59:38 -0700194 new UiViewHidden("group"),
Jian Li1f544732015-12-30 23:36:37 -0800195 new UiViewHidden("meter"),
Simon Hunt23f9c7b2017-07-10 20:00:30 -0700196
197 mkView(NETWORK, "link", "nav_links"),
198 mkView(NETWORK, "host", "nav_hosts"),
199 mkView(NETWORK, "intent", "nav_intents"),
200 mkView(NETWORK, "tunnel", "nav_tunnels")
Simon Hunt20e16792015-04-24 14:29:39 -0700201 );
Simon Hunt1002cd82015-04-23 14:44:03 -0700202
Simon Hunt4c7edd32015-03-11 10:42:53 -0700203 UiMessageHandlerFactory messageHandlerFactory =
204 () -> ImmutableList.of(
Thomas Vachuska0af26912016-03-21 21:37:30 -0700205 new UserPreferencesMessageHandler(),
Thomas Vachuskae586b792015-03-26 13:59:38 -0700206 new TopologyViewMessageHandler(),
Simon Huntd5b96732016-07-08 13:22:27 -0700207 new Topo2ViewMessageHandler(),
Simon Hunte6f64612017-04-28 00:01:48 -0700208 new Topo2TrafficMessageHandler(),
Thomas Vachuska26be4f32016-03-31 01:10:27 -0700209 new MapSelectorMessageHandler(),
Thomas Vachuskae586b792015-03-26 13:59:38 -0700210 new DeviceViewMessageHandler(),
Thomas Vachuska583bc632015-04-14 10:10:57 -0700211 new LinkViewMessageHandler(),
Thomas Vachuska0fa583c2015-03-30 23:07:41 -0700212 new HostViewMessageHandler(),
Bri Prebilic Colecdc188d2015-04-24 16:40:11 -0700213 new FlowViewMessageHandler(),
Bri Prebilic Coleac829e42015-05-05 13:42:06 -0700214 new PortViewMessageHandler(),
Bri Prebilic Coleff3dc672015-05-06 12:59:38 -0700215 new GroupViewMessageHandler(),
Jian Li1f544732015-12-30 23:36:37 -0800216 new MeterViewMessageHandler(),
Bri Prebilic Cole384e8dc2015-04-13 15:51:14 -0700217 new IntentViewMessageHandler(),
Thomas Vachuska583bc632015-04-14 10:10:57 -0700218 new ApplicationViewMessageHandler(),
Thomas Vachuskaaa8b0eb2015-05-22 09:54:15 -0700219 new SettingsViewMessageHandler(),
chengfanb466a7e2015-08-21 09:59:29 -0500220 new ClusterViewMessageHandler(),
Thomas Vachuska3ece3732015-09-22 23:58:50 -0700221 new ProcessorViewMessageHandler(),
chengfanc5a99dc2017-01-08 19:28:29 +0800222 new TunnelViewMessageHandler(),
223 new PartitionViewMessageHandler()
Simon Hunt4c7edd32015-03-11 10:42:53 -0700224 );
Simon Hunt1002cd82015-04-23 14:44:03 -0700225
Simon Hunte05cae42015-07-23 17:35:24 -0700226 UiTopoOverlayFactory topoOverlayFactory =
227 () -> ImmutableList.of(
Andrea Campanella732ea832017-02-06 09:25:59 -0800228 new TrafficOverlay(),
229 new ProtectedIntentOverlay()
Simon Hunte05cae42015-07-23 17:35:24 -0700230 );
231
Simon Hunt6cc86452017-04-27 17:46:22 -0700232 UiTopo2OverlayFactory topo2OverlayFactory =
233 () -> ImmutableList.of(
234 new Traffic2Overlay()
235 );
236
Steven Burrows3a9a6442016-05-05 15:31:16 +0100237 UiTopoMapFactory topoMapFactory =
238 () -> ImmutableList.of(
239 new UiTopoMap("australia", "Australia", "*australia", 1.0),
240 new UiTopoMap("americas", "North, Central and South America", "*americas", 0.7),
241 new UiTopoMap("n_america", "North America", "*n_america", 0.9),
242 new UiTopoMap("s_america", "South America", "*s_america", 0.9),
Thomas Vachuskacc0b7d62016-07-12 14:03:11 -0700243 new UiTopoMap("usa", "United States", "*continental_us", 1.3),
Steven Burrows3a9a6442016-05-05 15:31:16 +0100244 new UiTopoMap("bayareaGEO", "Bay Area, California", "*bayarea", 1.0),
Thomas Vachuskacc0b7d62016-07-12 14:03:11 -0700245 new UiTopoMap("europe", "Europe", "*europe", 10.0),
Steven Burrows3a9a6442016-05-05 15:31:16 +0100246 new UiTopoMap("italy", "Italy", "*italy", 0.8),
Thomas Vachuskacc0b7d62016-07-12 14:03:11 -0700247 new UiTopoMap("uk", "United Kingdom and Ireland", "*uk", 2.0),
Steven Burrows3a9a6442016-05-05 15:31:16 +0100248 new UiTopoMap("japan", "Japan", "*japan", 0.8),
249 new UiTopoMap("s_korea", "South Korea", "*s_korea", 0.75),
250 new UiTopoMap("taiwan", "Taiwan", "*taiwan", 0.7),
251 new UiTopoMap("africa", "Africa", "*africa", 0.7),
252 new UiTopoMap("oceania", "Oceania", "*oceania", 0.7),
253 new UiTopoMap("asia", "Asia", "*asia", 0.7)
254 );
255
Simon Hunte05cae42015-07-23 17:35:24 -0700256 return new UiExtension.Builder(CL, coreViews)
Simon Hunt23f9c7b2017-07-10 20:00:30 -0700257 .lionBundles(lionBundles)
Simon Hunte05cae42015-07-23 17:35:24 -0700258 .messageHandlerFactory(messageHandlerFactory)
259 .topoOverlayFactory(topoOverlayFactory)
Simon Hunt6cc86452017-04-27 17:46:22 -0700260 .topo2OverlayFactory(topo2OverlayFactory)
Steven Burrows3a9a6442016-05-05 15:31:16 +0100261 .topoMapFactory(topoMapFactory)
Simon Hunte05cae42015-07-23 17:35:24 -0700262 .resourcePath(CORE)
263 .build();
Thomas Vachuska3553b302015-03-07 14:49:43 -0800264 }
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800265
Simon Hunt7379a3d2017-06-20 16:50:39 -0700266
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800267 @Activate
268 public void activate() {
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700269 Serializer serializer = Serializer.using(KryoNamespaces.API,
Simon Huntd6d3ad32017-06-21 15:27:06 -0700270 ObjectNode.class, ArrayNode.class,
271 JsonNodeFactory.class, LinkedHashMap.class,
272 TextNode.class, BooleanNode.class,
273 LongNode.class, DoubleNode.class, ShortNode.class,
274 IntNode.class, NullNode.class, UiSessionToken.class);
Thomas Vachuska0af26912016-03-21 21:37:30 -0700275
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700276 prefsConsistentMap = storageService.<String, ObjectNode>consistentMapBuilder()
Simon Hunt3678c2a2016-03-28 14:48:07 -0700277 .withName(ONOS_USER_PREFERENCES)
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700278 .withSerializer(serializer)
279 .withRelaxedReadConsistency()
Thomas Vachuska0af26912016-03-21 21:37:30 -0700280 .build();
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700281 prefsConsistentMap.addListener(prefsListener);
282 prefs = prefsConsistentMap.asJavaMap();
Simon Hunt1169c952017-06-05 11:20:11 -0700283
284 tokensConsistentMap = storageService.<UiSessionToken, String>consistentMapBuilder()
285 .withName(ONOS_SESSION_TOKENS)
286 .withSerializer(serializer)
287 .withRelaxedReadConsistency()
288 .build();
289 tokens = tokensConsistentMap.asJavaMap();
290
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800291 register(core);
Simon Huntb8042032017-06-13 15:03:23 -0700292
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800293 log.info("Started");
294 }
295
296 @Deactivate
297 public void deactivate() {
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700298 prefsConsistentMap.removeListener(prefsListener);
Madan Jampanibf8ee802016-05-04 14:07:36 -0700299 eventHandlingExecutor.shutdown();
Thomas Vachuska51f540f2015-05-27 17:26:57 -0700300 UiWebSocketServlet.closeAll();
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800301 unregister(core);
302 log.info("Stopped");
303 }
304
305 @Override
306 public synchronized void register(UiExtension extension) {
Heedo Kang4a47a302016-02-29 17:40:23 +0900307 checkPermission(UI_WRITE);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800308 if (!extensions.contains(extension)) {
309 extensions.add(extension);
310 for (UiView view : extension.views()) {
311 views.put(view.id(), extension);
312 }
Thomas Vachuskafa74dd72016-03-20 19:11:12 -0700313 UiWebSocketServlet.sendToAll(GUI_ADDED, null);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800314 }
315 }
316
317 @Override
318 public synchronized void unregister(UiExtension extension) {
Heedo Kang4a47a302016-02-29 17:40:23 +0900319 checkPermission(UI_WRITE);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800320 extensions.remove(extension);
Simon Huntd6d3ad32017-06-21 15:27:06 -0700321 extension.views().stream()
322 .map(UiView::id).collect(toSet()).forEach(views::remove);
Thomas Vachuskafa74dd72016-03-20 19:11:12 -0700323 UiWebSocketServlet.sendToAll(GUI_REMOVED, null);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800324 }
325
326 @Override
327 public synchronized List<UiExtension> getExtensions() {
Heedo Kang4a47a302016-02-29 17:40:23 +0900328 checkPermission(UI_READ);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800329 return ImmutableList.copyOf(extensions);
330 }
331
332 @Override
333 public synchronized UiExtension getViewExtension(String viewId) {
Heedo Kang4a47a302016-02-29 17:40:23 +0900334 checkPermission(UI_READ);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800335 return views.get(viewId);
336 }
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700337
Thomas Vachuska0af26912016-03-21 21:37:30 -0700338 @Override
Simon Hunt23f9c7b2017-07-10 20:00:30 -0700339 public synchronized LionBundle getNavLionBundle() {
340 return navLion;
341 }
342
343 @Override
Thomas Vachuska0af26912016-03-21 21:37:30 -0700344 public Set<String> getUserNames() {
345 ImmutableSet.Builder<String> builder = ImmutableSet.builder();
346 prefs.keySet().forEach(k -> builder.add(userName(k)));
347 return builder.build();
348 }
349
350 @Override
351 public Map<String, ObjectNode> getPreferences(String userName) {
352 ImmutableMap.Builder<String, ObjectNode> builder = ImmutableMap.builder();
353 prefs.entrySet().stream()
Simon Hunt3678c2a2016-03-28 14:48:07 -0700354 .filter(e -> e.getKey().startsWith(userName + SLASH))
Thomas Vachuska0af26912016-03-21 21:37:30 -0700355 .forEach(e -> builder.put(keyName(e.getKey()), e.getValue()));
356 return builder.build();
357 }
358
359 @Override
360 public void setPreference(String userName, String preference, ObjectNode value) {
361 prefs.put(key(userName, preference), value);
362 }
363
Simon Huntc54cd1b2015-05-11 13:43:44 -0700364 // =====================================================================
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700365 // Provisional tracking of sprite definitions
Simon Huntc54cd1b2015-05-11 13:43:44 -0700366
367 private final Map<String, JsonNode> sprites = Maps.newHashMap();
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700368
369 @Override
370 public Set<String> getNames() {
371 return ImmutableSet.copyOf(sprites.keySet());
372 }
373
374 @Override
375 public void put(String name, JsonNode spriteData) {
Simon Huntfd8c7d72015-04-14 17:53:37 -0700376 log.info("Registered sprite definition [{}]", name);
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700377 sprites.put(name, spriteData);
378 }
379
380 @Override
381 public JsonNode get(String name) {
382 return sprites.get(name);
383 }
384
Thomas Vachuska0af26912016-03-21 21:37:30 -0700385 private String key(String userName, String keyName) {
Simon Hunt3678c2a2016-03-28 14:48:07 -0700386 return userName + SLASH + keyName;
Thomas Vachuska0af26912016-03-21 21:37:30 -0700387 }
388
Simon Hunt3678c2a2016-03-28 14:48:07 -0700389
Thomas Vachuska0af26912016-03-21 21:37:30 -0700390 private String userName(String key) {
Simon Hunt3678c2a2016-03-28 14:48:07 -0700391 return key.split(SLASH)[IDX_USER];
Thomas Vachuska0af26912016-03-21 21:37:30 -0700392 }
393
394 private String keyName(String key) {
Simon Hunt3678c2a2016-03-28 14:48:07 -0700395 return key.split(SLASH)[IDX_KEY];
Thomas Vachuska0af26912016-03-21 21:37:30 -0700396 }
397
Simon Hunt1169c952017-06-05 11:20:11 -0700398
399 // =====================================================================
400 // UiTokenService
401
402 @Override
403 public UiSessionToken issueToken(String username) {
404 UiSessionToken token = new UiSessionToken(tokenGen.nextSessionId());
405 tokens.put(token, username);
406 log.debug("UiSessionToken issued: {}", token);
407 return token;
408 }
409
410 @Override
411 public void revokeToken(UiSessionToken token) {
412 if (token != null) {
413 tokens.remove(token);
414 log.debug("UiSessionToken revoked: {}", token);
415 }
416 }
417
418 @Override
419 public boolean isTokenValid(UiSessionToken token) {
420 return token != null && tokens.containsKey(token);
421 }
422
423 private final class SessionTokenGenerator {
424 private final SecureRandom random = new SecureRandom();
425
426 /*
427 This works by choosing 130 bits from a cryptographically secure
428 random bit generator, and encoding them in base-32.
429
430 128 bits is considered to be cryptographically strong, but each
431 digit in a base 32 number can encode 5 bits, so 128 is rounded up
432 to the next multiple of 5.
433
434 This encoding is compact and efficient, with 5 random bits per
435 character. Compare this to a random UUID, which only has 3.4 bits
436 per character in standard layout, and only 122 random bits in total.
437
438 Note that SecureRandom objects are expensive to initialize, so
439 we'll want to keep it around and re-use it.
440 */
441
442 private String nextSessionId() {
443 return new BigInteger(130, random).toString(32);
444 }
445 }
446
Thomas Vachuska0af26912016-03-21 21:37:30 -0700447 // Auxiliary listener to preference map events.
448 private class InternalPrefsListener
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700449 implements MapEventListener<String, ObjectNode> {
Thomas Vachuska0af26912016-03-21 21:37:30 -0700450 @Override
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700451 public void event(MapEvent<String, ObjectNode> event) {
Madan Jampanibf8ee802016-05-04 14:07:36 -0700452 eventHandlingExecutor.execute(() -> {
453 String userName = userName(event.key());
454 if (event.type() == MapEvent.Type.INSERT || event.type() == MapEvent.Type.UPDATE) {
455 UiWebSocketServlet.sendToUser(userName, UPDATE_PREFS, jsonPrefs());
456 }
457 });
Thomas Vachuska0af26912016-03-21 21:37:30 -0700458 }
459
460 private ObjectNode jsonPrefs() {
461 ObjectNode json = mapper.createObjectNode();
Simon Hunt1169c952017-06-05 11:20:11 -0700462 prefs.forEach((key, value) -> json.set(keyName(key), value));
Thomas Vachuska0af26912016-03-21 21:37:30 -0700463 return json;
464 }
465 }
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800466}