blob: dfc4edfecaf305d66a70ae6112d38797a3f73acd [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;
Simon Huntd8754652017-06-21 11:45:22 -070084import static org.onosproject.ui.impl.lion.BundleStitcher.generateBundles;
Heedo Kang4a47a302016-02-29 17:40:23 +090085
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080086/**
87 * Manages the user interface extensions.
88 */
89@Component(immediate = true)
90@Service
Simon Hunt3678c2a2016-03-28 14:48:07 -070091public class UiExtensionManager
Simon Hunt1169c952017-06-05 11:20:11 -070092 implements UiExtensionService, UiPreferencesService, SpriteService,
93 UiTokenService {
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080094
Thomas Vachuskafa74dd72016-03-20 19:11:12 -070095 private static final ClassLoader CL = UiExtensionManager.class.getClassLoader();
Simon Hunt3678c2a2016-03-28 14:48:07 -070096
Madan Jampani7b93ceb2016-05-04 09:58:40 -070097 private static final String ONOS_USER_PREFERENCES = "onos-ui-user-preferences";
Simon Hunt1169c952017-06-05 11:20:11 -070098 private static final String ONOS_SESSION_TOKENS = "onos-ui-session-tokens";
Simon Hunte05cae42015-07-23 17:35:24 -070099 private static final String CORE = "core";
Thomas Vachuskafa74dd72016-03-20 19:11:12 -0700100 private static final String GUI_ADDED = "guiAdded";
101 private static final String GUI_REMOVED = "guiRemoved";
Thomas Vachuska0af26912016-03-21 21:37:30 -0700102 private static final String UPDATE_PREFS = "updatePrefs";
Simon Hunt3678c2a2016-03-28 14:48:07 -0700103 private static final String SLASH = "/";
104
105 private static final int IDX_USER = 0;
106 private static final int IDX_KEY = 1;
Simon Hunte05cae42015-07-23 17:35:24 -0700107
Simon Hunt7379a3d2017-06-20 16:50:39 -0700108 private static final String LION_BASE = "/org/onosproject/ui/lion";
109
110 private static final String[] LION_TAGS = {
111 "core.view.Cluster",
112
113 // TODO: fill this out, once we have written the other config files
114 };
115
116
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800117 private final Logger log = LoggerFactory.getLogger(getClass());
118
119 // List of all extensions
120 private final List<UiExtension> extensions = Lists.newArrayList();
121
122 // Map of views to extensions
123 private final Map<String, UiExtension> views = Maps.newHashMap();
124
125 // Core views & core extension
Thomas Vachuska3553b302015-03-07 14:49:43 -0800126 private final UiExtension core = createCoreExtension();
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800127
Thomas Vachuska51f540f2015-05-27 17:26:57 -0700128 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
129 protected MastershipService mastershipService;
Thomas Vachuska3553b302015-03-07 14:49:43 -0800130
Thomas Vachuska0af26912016-03-21 21:37:30 -0700131 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
132 protected StorageService storageService;
133
134 // User preferences
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700135 private ConsistentMap<String, ObjectNode> prefsConsistentMap;
136 private Map<String, ObjectNode> prefs;
137 private final MapEventListener<String, ObjectNode> prefsListener =
Thomas Vachuska0af26912016-03-21 21:37:30 -0700138 new InternalPrefsListener();
139
Simon Hunt1169c952017-06-05 11:20:11 -0700140 // Session tokens
141 private ConsistentMap<UiSessionToken, String> tokensConsistentMap;
142 private Map<UiSessionToken, String> tokens;
143 private final SessionTokenGenerator tokenGen =
144 new SessionTokenGenerator();
145
Thomas Vachuska0af26912016-03-21 21:37:30 -0700146 private final ObjectMapper mapper = new ObjectMapper();
147
Madan Jampanibf8ee802016-05-04 14:07:36 -0700148 private final ExecutorService eventHandlingExecutor =
Simon Huntd5b96732016-07-08 13:22:27 -0700149 Executors.newSingleThreadExecutor(
150 Tools.groupedThreads("onos/ui-ext-manager", "event-handler", log));
Madan Jampanibf8ee802016-05-04 14:07:36 -0700151
Thomas Vachuska3553b302015-03-07 14:49:43 -0800152 // Creates core UI extension
Simon Huntc54cd1b2015-05-11 13:43:44 -0700153 private UiExtension createCoreExtension() {
Simon Hunt20e16792015-04-24 14:29:39 -0700154 List<UiView> coreViews = of(
155 new UiView(PLATFORM, "app", "Applications", "nav_apps"),
Thomas Vachuskaaa8b0eb2015-05-22 09:54:15 -0700156 new UiView(PLATFORM, "settings", "Settings", "nav_settings"),
Simon Hunt20e16792015-04-24 14:29:39 -0700157 new UiView(PLATFORM, "cluster", "Cluster Nodes", "nav_cluster"),
Thomas Vachuska3ece3732015-09-22 23:58:50 -0700158 new UiView(PLATFORM, "processor", "Packet Processors", "nav_processors"),
chengfanc5a99dc2017-01-08 19:28:29 +0800159 new UiView(PLATFORM, "partition", "Partitions", "nav_partitions"),
Simon Hunt20e16792015-04-24 14:29:39 -0700160 new UiView(NETWORK, "topo", "Topology", "nav_topo"),
Simon Hunt47f8fb72017-04-11 13:49:31 -0700161 new UiView(NETWORK, "topo2", "Topology 2", "nav_topo2"),
Simon Hunt20e16792015-04-24 14:29:39 -0700162 new UiView(NETWORK, "device", "Devices", "nav_devs"),
163 new UiViewHidden("flow"),
Bri Prebilic Coleac829e42015-05-05 13:42:06 -0700164 new UiViewHidden("port"),
Bri Prebilic Coleff3dc672015-05-06 12:59:38 -0700165 new UiViewHidden("group"),
Jian Li1f544732015-12-30 23:36:37 -0800166 new UiViewHidden("meter"),
Simon Hunt20e16792015-04-24 14:29:39 -0700167 new UiView(NETWORK, "link", "Links", "nav_links"),
168 new UiView(NETWORK, "host", "Hosts", "nav_hosts"),
chengfanb466a7e2015-08-21 09:59:29 -0500169 new UiView(NETWORK, "intent", "Intents", "nav_intents"),
Simon Hunt986b92f2016-06-03 15:46:59 -0700170 new UiView(NETWORK, "tunnel", "Tunnels", "nav_tunnels")
Simon Hunt20e16792015-04-24 14:29:39 -0700171 );
Simon Hunt1002cd82015-04-23 14:44:03 -0700172
Simon Hunt4c7edd32015-03-11 10:42:53 -0700173 UiMessageHandlerFactory messageHandlerFactory =
174 () -> ImmutableList.of(
Thomas Vachuska0af26912016-03-21 21:37:30 -0700175 new UserPreferencesMessageHandler(),
Thomas Vachuskae586b792015-03-26 13:59:38 -0700176 new TopologyViewMessageHandler(),
Simon Huntd5b96732016-07-08 13:22:27 -0700177 new Topo2ViewMessageHandler(),
Simon Hunte6f64612017-04-28 00:01:48 -0700178 new Topo2TrafficMessageHandler(),
Thomas Vachuska26be4f32016-03-31 01:10:27 -0700179 new MapSelectorMessageHandler(),
Thomas Vachuskae586b792015-03-26 13:59:38 -0700180 new DeviceViewMessageHandler(),
Thomas Vachuska583bc632015-04-14 10:10:57 -0700181 new LinkViewMessageHandler(),
Thomas Vachuska0fa583c2015-03-30 23:07:41 -0700182 new HostViewMessageHandler(),
Bri Prebilic Colecdc188d2015-04-24 16:40:11 -0700183 new FlowViewMessageHandler(),
Bri Prebilic Coleac829e42015-05-05 13:42:06 -0700184 new PortViewMessageHandler(),
Bri Prebilic Coleff3dc672015-05-06 12:59:38 -0700185 new GroupViewMessageHandler(),
Jian Li1f544732015-12-30 23:36:37 -0800186 new MeterViewMessageHandler(),
Bri Prebilic Cole384e8dc2015-04-13 15:51:14 -0700187 new IntentViewMessageHandler(),
Thomas Vachuska583bc632015-04-14 10:10:57 -0700188 new ApplicationViewMessageHandler(),
Thomas Vachuskaaa8b0eb2015-05-22 09:54:15 -0700189 new SettingsViewMessageHandler(),
chengfanb466a7e2015-08-21 09:59:29 -0500190 new ClusterViewMessageHandler(),
Thomas Vachuska3ece3732015-09-22 23:58:50 -0700191 new ProcessorViewMessageHandler(),
chengfanc5a99dc2017-01-08 19:28:29 +0800192 new TunnelViewMessageHandler(),
193 new PartitionViewMessageHandler()
Simon Hunt4c7edd32015-03-11 10:42:53 -0700194 );
Simon Hunt1002cd82015-04-23 14:44:03 -0700195
Simon Hunte05cae42015-07-23 17:35:24 -0700196 UiTopoOverlayFactory topoOverlayFactory =
197 () -> ImmutableList.of(
Andrea Campanella732ea832017-02-06 09:25:59 -0800198 new TrafficOverlay(),
199 new ProtectedIntentOverlay()
Simon Hunte05cae42015-07-23 17:35:24 -0700200 );
201
Simon Hunt6cc86452017-04-27 17:46:22 -0700202 UiTopo2OverlayFactory topo2OverlayFactory =
203 () -> ImmutableList.of(
204 new Traffic2Overlay()
205 );
206
Steven Burrows3a9a6442016-05-05 15:31:16 +0100207 UiTopoMapFactory topoMapFactory =
208 () -> ImmutableList.of(
209 new UiTopoMap("australia", "Australia", "*australia", 1.0),
210 new UiTopoMap("americas", "North, Central and South America", "*americas", 0.7),
211 new UiTopoMap("n_america", "North America", "*n_america", 0.9),
212 new UiTopoMap("s_america", "South America", "*s_america", 0.9),
Thomas Vachuskacc0b7d62016-07-12 14:03:11 -0700213 new UiTopoMap("usa", "United States", "*continental_us", 1.3),
Steven Burrows3a9a6442016-05-05 15:31:16 +0100214 new UiTopoMap("bayareaGEO", "Bay Area, California", "*bayarea", 1.0),
Thomas Vachuskacc0b7d62016-07-12 14:03:11 -0700215 new UiTopoMap("europe", "Europe", "*europe", 10.0),
Steven Burrows3a9a6442016-05-05 15:31:16 +0100216 new UiTopoMap("italy", "Italy", "*italy", 0.8),
Thomas Vachuskacc0b7d62016-07-12 14:03:11 -0700217 new UiTopoMap("uk", "United Kingdom and Ireland", "*uk", 2.0),
Steven Burrows3a9a6442016-05-05 15:31:16 +0100218 new UiTopoMap("japan", "Japan", "*japan", 0.8),
219 new UiTopoMap("s_korea", "South Korea", "*s_korea", 0.75),
220 new UiTopoMap("taiwan", "Taiwan", "*taiwan", 0.7),
221 new UiTopoMap("africa", "Africa", "*africa", 0.7),
222 new UiTopoMap("oceania", "Oceania", "*oceania", 0.7),
223 new UiTopoMap("asia", "Asia", "*asia", 0.7)
224 );
225
Simon Hunte05cae42015-07-23 17:35:24 -0700226 return new UiExtension.Builder(CL, coreViews)
Simon Huntd8754652017-06-21 11:45:22 -0700227 .lionBundles(generateBundles(LION_BASE, LION_TAGS))
Simon Hunte05cae42015-07-23 17:35:24 -0700228 .messageHandlerFactory(messageHandlerFactory)
229 .topoOverlayFactory(topoOverlayFactory)
Simon Hunt6cc86452017-04-27 17:46:22 -0700230 .topo2OverlayFactory(topo2OverlayFactory)
Steven Burrows3a9a6442016-05-05 15:31:16 +0100231 .topoMapFactory(topoMapFactory)
Simon Hunte05cae42015-07-23 17:35:24 -0700232 .resourcePath(CORE)
233 .build();
Thomas Vachuska3553b302015-03-07 14:49:43 -0800234 }
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800235
Simon Hunt7379a3d2017-06-20 16:50:39 -0700236
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800237 @Activate
238 public void activate() {
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700239 Serializer serializer = Serializer.using(KryoNamespaces.API,
Simon Hunt7379a3d2017-06-20 16:50:39 -0700240 ObjectNode.class, ArrayNode.class,
241 JsonNodeFactory.class, LinkedHashMap.class,
242 TextNode.class, BooleanNode.class,
243 LongNode.class, DoubleNode.class, ShortNode.class,
244 IntNode.class, NullNode.class, UiSessionToken.class);
Thomas Vachuska0af26912016-03-21 21:37:30 -0700245
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700246 prefsConsistentMap = storageService.<String, ObjectNode>consistentMapBuilder()
Simon Hunt3678c2a2016-03-28 14:48:07 -0700247 .withName(ONOS_USER_PREFERENCES)
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700248 .withSerializer(serializer)
249 .withRelaxedReadConsistency()
Thomas Vachuska0af26912016-03-21 21:37:30 -0700250 .build();
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700251 prefsConsistentMap.addListener(prefsListener);
252 prefs = prefsConsistentMap.asJavaMap();
Simon Hunt1169c952017-06-05 11:20:11 -0700253
254 tokensConsistentMap = storageService.<UiSessionToken, String>consistentMapBuilder()
255 .withName(ONOS_SESSION_TOKENS)
256 .withSerializer(serializer)
257 .withRelaxedReadConsistency()
258 .build();
259 tokens = tokensConsistentMap.asJavaMap();
260
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800261 register(core);
Simon Huntb8042032017-06-13 15:03:23 -0700262 LionUtils.setupRuntimeLocale();
263
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800264 log.info("Started");
265 }
266
267 @Deactivate
268 public void deactivate() {
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700269 prefsConsistentMap.removeListener(prefsListener);
Madan Jampanibf8ee802016-05-04 14:07:36 -0700270 eventHandlingExecutor.shutdown();
Thomas Vachuska51f540f2015-05-27 17:26:57 -0700271 UiWebSocketServlet.closeAll();
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800272 unregister(core);
273 log.info("Stopped");
274 }
275
276 @Override
277 public synchronized void register(UiExtension extension) {
Heedo Kang4a47a302016-02-29 17:40:23 +0900278 checkPermission(UI_WRITE);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800279 if (!extensions.contains(extension)) {
280 extensions.add(extension);
281 for (UiView view : extension.views()) {
282 views.put(view.id(), extension);
283 }
Thomas Vachuskafa74dd72016-03-20 19:11:12 -0700284 UiWebSocketServlet.sendToAll(GUI_ADDED, null);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800285 }
286 }
287
288 @Override
289 public synchronized void unregister(UiExtension extension) {
Heedo Kang4a47a302016-02-29 17:40:23 +0900290 checkPermission(UI_WRITE);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800291 extensions.remove(extension);
Thomas Vachuskafa74dd72016-03-20 19:11:12 -0700292 extension.views().stream().map(UiView::id).collect(toSet()).forEach(views::remove);
293 UiWebSocketServlet.sendToAll(GUI_REMOVED, null);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800294 }
295
296 @Override
297 public synchronized List<UiExtension> getExtensions() {
Heedo Kang4a47a302016-02-29 17:40:23 +0900298 checkPermission(UI_READ);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800299 return ImmutableList.copyOf(extensions);
300 }
301
302 @Override
303 public synchronized UiExtension getViewExtension(String viewId) {
Heedo Kang4a47a302016-02-29 17:40:23 +0900304 checkPermission(UI_READ);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800305 return views.get(viewId);
306 }
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700307
Thomas Vachuska0af26912016-03-21 21:37:30 -0700308 @Override
309 public Set<String> getUserNames() {
310 ImmutableSet.Builder<String> builder = ImmutableSet.builder();
311 prefs.keySet().forEach(k -> builder.add(userName(k)));
312 return builder.build();
313 }
314
315 @Override
316 public Map<String, ObjectNode> getPreferences(String userName) {
317 ImmutableMap.Builder<String, ObjectNode> builder = ImmutableMap.builder();
318 prefs.entrySet().stream()
Simon Hunt3678c2a2016-03-28 14:48:07 -0700319 .filter(e -> e.getKey().startsWith(userName + SLASH))
Thomas Vachuska0af26912016-03-21 21:37:30 -0700320 .forEach(e -> builder.put(keyName(e.getKey()), e.getValue()));
321 return builder.build();
322 }
323
324 @Override
325 public void setPreference(String userName, String preference, ObjectNode value) {
326 prefs.put(key(userName, preference), value);
327 }
328
Simon Huntc54cd1b2015-05-11 13:43:44 -0700329 // =====================================================================
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700330 // Provisional tracking of sprite definitions
Simon Huntc54cd1b2015-05-11 13:43:44 -0700331
332 private final Map<String, JsonNode> sprites = Maps.newHashMap();
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700333
334 @Override
335 public Set<String> getNames() {
336 return ImmutableSet.copyOf(sprites.keySet());
337 }
338
339 @Override
340 public void put(String name, JsonNode spriteData) {
Simon Huntfd8c7d72015-04-14 17:53:37 -0700341 log.info("Registered sprite definition [{}]", name);
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700342 sprites.put(name, spriteData);
343 }
344
345 @Override
346 public JsonNode get(String name) {
347 return sprites.get(name);
348 }
349
Thomas Vachuska0af26912016-03-21 21:37:30 -0700350 private String key(String userName, String keyName) {
Simon Hunt3678c2a2016-03-28 14:48:07 -0700351 return userName + SLASH + keyName;
Thomas Vachuska0af26912016-03-21 21:37:30 -0700352 }
353
Simon Hunt3678c2a2016-03-28 14:48:07 -0700354
Thomas Vachuska0af26912016-03-21 21:37:30 -0700355 private String userName(String key) {
Simon Hunt3678c2a2016-03-28 14:48:07 -0700356 return key.split(SLASH)[IDX_USER];
Thomas Vachuska0af26912016-03-21 21:37:30 -0700357 }
358
359 private String keyName(String key) {
Simon Hunt3678c2a2016-03-28 14:48:07 -0700360 return key.split(SLASH)[IDX_KEY];
Thomas Vachuska0af26912016-03-21 21:37:30 -0700361 }
362
Simon Hunt1169c952017-06-05 11:20:11 -0700363
364 // =====================================================================
365 // UiTokenService
366
367 @Override
368 public UiSessionToken issueToken(String username) {
369 UiSessionToken token = new UiSessionToken(tokenGen.nextSessionId());
370 tokens.put(token, username);
371 log.debug("UiSessionToken issued: {}", token);
372 return token;
373 }
374
375 @Override
376 public void revokeToken(UiSessionToken token) {
377 if (token != null) {
378 tokens.remove(token);
379 log.debug("UiSessionToken revoked: {}", token);
380 }
381 }
382
383 @Override
384 public boolean isTokenValid(UiSessionToken token) {
385 return token != null && tokens.containsKey(token);
386 }
387
388 private final class SessionTokenGenerator {
389 private final SecureRandom random = new SecureRandom();
390
391 /*
392 This works by choosing 130 bits from a cryptographically secure
393 random bit generator, and encoding them in base-32.
394
395 128 bits is considered to be cryptographically strong, but each
396 digit in a base 32 number can encode 5 bits, so 128 is rounded up
397 to the next multiple of 5.
398
399 This encoding is compact and efficient, with 5 random bits per
400 character. Compare this to a random UUID, which only has 3.4 bits
401 per character in standard layout, and only 122 random bits in total.
402
403 Note that SecureRandom objects are expensive to initialize, so
404 we'll want to keep it around and re-use it.
405 */
406
407 private String nextSessionId() {
408 return new BigInteger(130, random).toString(32);
409 }
410 }
411
Thomas Vachuska0af26912016-03-21 21:37:30 -0700412 // Auxiliary listener to preference map events.
413 private class InternalPrefsListener
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700414 implements MapEventListener<String, ObjectNode> {
Thomas Vachuska0af26912016-03-21 21:37:30 -0700415 @Override
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700416 public void event(MapEvent<String, ObjectNode> event) {
Madan Jampanibf8ee802016-05-04 14:07:36 -0700417 eventHandlingExecutor.execute(() -> {
418 String userName = userName(event.key());
419 if (event.type() == MapEvent.Type.INSERT || event.type() == MapEvent.Type.UPDATE) {
420 UiWebSocketServlet.sendToUser(userName, UPDATE_PREFS, jsonPrefs());
421 }
422 });
Thomas Vachuska0af26912016-03-21 21:37:30 -0700423 }
424
425 private ObjectNode jsonPrefs() {
426 ObjectNode json = mapper.createObjectNode();
Simon Hunt1169c952017-06-05 11:20:11 -0700427 prefs.forEach((key, value) -> json.set(keyName(key), value));
Thomas Vachuska0af26912016-03-21 21:37:30 -0700428 return json;
429 }
430 }
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800431}