001package jmri.server.json.util; 002 003import static jmri.server.json.JSON.CONTROL_PANEL; 004import static jmri.server.json.JSON.LAYOUT_PANEL; 005import static jmri.server.json.JSON.NAME; 006import static jmri.server.json.JSON.PANEL; 007import static jmri.server.json.JSON.PANEL_PANEL; 008import static jmri.server.json.JSON.SWITCHBOARD_PANEL; 009import static jmri.server.json.JSON.TYPE; 010import static jmri.server.json.JSON.URL; 011import static jmri.server.json.JSON.USERNAME; 012 013import com.fasterxml.jackson.databind.JsonNode; 014import com.fasterxml.jackson.databind.ObjectMapper; 015import com.fasterxml.jackson.databind.node.ArrayNode; 016import com.fasterxml.jackson.databind.node.ObjectNode; 017 018import java.io.IOException; 019import java.lang.reflect.Field; 020import java.util.ArrayList; 021import java.util.Arrays; 022import java.util.Enumeration; 023import java.util.Locale; 024import java.util.Objects; 025 026import javax.annotation.CheckForNull; 027import javax.annotation.Nonnull; 028import javax.servlet.http.HttpServletResponse; 029import javax.swing.JFrame; 030 031import jmri.DccLocoAddress; 032import jmri.InstanceManager; 033import jmri.Metadata; 034import jmri.PermissionManager; 035import jmri.server.json.JsonServerPreferences; 036import jmri.jmrit.display.Editor; 037import jmri.jmrit.display.EditorManager; 038import jmri.jmrit.display.controlPanelEditor.ControlPanelEditor; 039import jmri.jmrit.display.layoutEditor.LayoutEditor; 040import jmri.jmrit.display.switchboardEditor.SwitchboardEditor; 041import jmri.jmrix.ConnectionConfig; 042import jmri.jmrix.ConnectionConfigManager; 043import jmri.SystemConnectionMemo; 044import jmri.jmrix.internal.InternalSystemConnectionMemo; 045import jmri.profile.Profile; 046import jmri.profile.ProfileManager; 047import jmri.server.json.JSON; 048import jmri.server.json.JsonException; 049import jmri.server.json.JsonHttpService; 050import jmri.server.json.JsonRequest; 051import jmri.util.node.NodeIdentity; 052import jmri.util.zeroconf.ZeroConfService; 053import jmri.util.zeroconf.ZeroConfServiceManager; 054import jmri.web.server.WebServerPreferences; 055import org.slf4j.Logger; 056import org.slf4j.LoggerFactory; 057 058/** 059 * @author Randall Wood Copyright 2016, 2017, 2018 060 */ 061public class JsonUtilHttpService extends JsonHttpService { 062 063 private static final String RESOURCE_PATH = "jmri/server/json/util/"; 064 private static final Logger log = LoggerFactory.getLogger(JsonUtilHttpService.class); 065 066 public JsonUtilHttpService(ObjectMapper mapper) { 067 super(mapper); 068 } 069 070 @Override 071 // use @CheckForNull to override @Nonnull specified in superclass 072 public JsonNode doGet(String type, @CheckForNull String name, JsonNode data, JsonRequest request) 073 throws JsonException { 074 switch (type) { 075 case JSON.HELLO: 076 return this.getHello( 077 InstanceManager.getDefault(JsonServerPreferences.class).getHeartbeatInterval(), request); 078 case JSON.METADATA: 079 if (name == null) { 080 return this.getMetadata(request); 081 } 082 return this.getMetadata(request.locale, name, request.id); 083 case JSON.NETWORK_SERVICE: 084 case JSON.NETWORK_SERVICES: 085 if (name == null) { 086 return this.getNetworkServices(request.locale, request.id); 087 } 088 return this.getNetworkService(name, request); 089 case JSON.NODE: 090 return this.getNode(request); 091 case JSON.PANEL: 092 case JSON.PANELS: 093 if (name == null) { 094 return this.getPanels(request.id); 095 } 096 return this.getPanel(request.locale, name, request.id); 097 case JSON.RAILROAD: 098 return this.getRailroad(request); 099 case JSON.SYSTEM_CONNECTION: 100 case JSON.SYSTEM_CONNECTIONS: 101 if (name == null) { 102 return this.getSystemConnections(request); 103 } 104 return this.getSystemConnection(name, request); 105 case JSON.CONFIG_PROFILE: 106 case JSON.CONFIG_PROFILES: 107 if (name == null) { 108 return this.getConfigProfiles(request); 109 } 110 return this.getConfigProfile(name, request); 111 case JSON.VERSION: 112 return this.getVersion(); 113 default: 114 throw new JsonException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, 115 Bundle.getMessage(request.locale, JsonException.ERROR_UNKNOWN_TYPE, type), request.id); 116 } 117 } 118 119 @Override 120 public ArrayNode doGetList(String type, JsonNode data, JsonRequest request) throws JsonException { 121 switch (type) { 122 case JSON.METADATA: 123 return this.getMetadata(request); 124 case JSON.NETWORK_SERVICE: 125 case JSON.NETWORK_SERVICES: 126 return this.getNetworkServices(request); 127 case JSON.PANEL: 128 case JSON.PANELS: 129 return this.getPanels(request.id); 130 case JSON.SYSTEM_CONNECTION: 131 case JSON.SYSTEM_CONNECTIONS: 132 return this.getSystemConnections(request); 133 case JSON.CONFIG_PROFILE: 134 case JSON.CONFIG_PROFILES: 135 return this.getConfigProfiles(request); 136 default: 137 ArrayNode array = this.mapper.createArrayNode(); 138 JsonNode node = this.doGet(type, null, data, request); 139 if (node.isArray()) { 140 array.addAll((ArrayNode) node); 141 } else { 142 array.add(node); 143 } 144 return array; 145 } 146 } 147 148 @Override 149 // Use @CheckForNull to override non-null requirement of superclass 150 public JsonNode doPost(String type, @CheckForNull String name, 151 JsonNode data, JsonRequest request) throws JsonException { 152 log.debug("doPost(type='{}', name='{}', data='{}')", type, name, data); 153 // This will be expanded with more cases and warrants a CASE rather than an IF 154 switch (type) { 155 case JSON.RAILROAD: 156 InstanceManager.getDefault(WebServerPreferences.class).setRailroadName(name); 157 break; 158 case JSON.SESSION_LOGIN: 159 return this.postSessionLogin(name, data, request); 160 case JSON.SESSION_LOGOUT: 161 return this.postSessionLogout(name, data, request); 162 default: 163 log.debug("Received unexpected POST command: '{}'", type); 164 break; 165 } 166 // Implicitly answer all doPost the way an equivalent doGet would be answered. 167 return this.doGet(type, name, data, request); 168 } 169 170 /** 171 * @return JSON map of complete versions and URL part for protocols 172 * @throws JsonException if a protocol version is not available 173 */ 174 public JsonNode getVersion() throws JsonException { 175 ObjectNode data = mapper.createObjectNode(); 176 for (String version : JSON.VERSIONS) { 177 try { 178 Field field; 179 field = JSON.class.getDeclaredField(version.toUpperCase() + "_PROTOCOL_VERSION"); 180 data.put(field.get(null).toString(), version); 181 } catch ( 182 IllegalAccessException | 183 IllegalArgumentException | 184 NoSuchFieldException | 185 SecurityException ex) { 186 throw new JsonException(500, ex, 0); 187 } 188 } 189 return message(JSON.VERSION, data, 0); 190 } 191 192 /** 193 * Send a JSON {@link jmri.server.json.JSON#HELLO} message. 194 * 195 * @param heartbeat seconds in which a client must send a message before its 196 * connection is broken 197 * @param request the JSON request 198 * @return the JSON hello message 199 */ 200 public JsonNode getHello(int heartbeat, @Nonnull JsonRequest request) { 201 ObjectNode data = mapper.createObjectNode(); 202 data.put(JSON.JMRI, jmri.Version.name()); 203 data.put(JSON.JSON, JSON.V5_PROTOCOL_VERSION); 204 data.put(JSON.VERSION, JSON.V5); 205 data.put(JSON.HEARTBEAT, Math.round(heartbeat * 0.9f)); 206 data.put(JSON.RAILROAD, InstanceManager.getDefault(WebServerPreferences.class).getRailroadName()); 207 data.put(JSON.NODE, NodeIdentity.networkIdentity()); 208 Profile activeProfile = ProfileManager.getDefault().getActiveProfile(); 209 data.put(JSON.ACTIVE_PROFILE, activeProfile != null ? activeProfile.getName() : null); 210 return message(JSON.HELLO, data, request.id); 211 } 212 213 /** 214 * Get a JSON message with a metadata element from {@link jmri.Metadata}. 215 * 216 * @param name The metadata element to get 217 * @param request the JSON request 218 * @return JSON metadata element 219 * @throws JsonException if name is not a recognized metadata element 220 */ 221 public JsonNode getMetadata(@Nonnull String name, @Nonnull JsonRequest request) throws JsonException { 222 String metadata = Metadata.getBySystemName(name); 223 ObjectNode data = mapper.createObjectNode(); 224 if (metadata != null) { 225 data.put(JSON.NAME, name); 226 data.put(JSON.VALUE, Metadata.getBySystemName(name)); 227 } else { 228 throw new JsonException(404, 229 Bundle.getMessage(request.locale, JsonException.ERROR_OBJECT, JSON.METADATA, name), 230 request.id); 231 } 232 return message(JSON.METADATA, data, request.id); 233 } 234 235 /** 236 * Get a JSON message with a metadata element from {@link jmri.Metadata}. 237 * 238 * @param locale The client's Locale. 239 * @param name The metadata element to get. 240 * @param id message id set by client 241 * @return JSON metadata element. 242 * @throws JsonException if name is not a recognized metadata element. 243 */ 244 public JsonNode getMetadata(Locale locale, String name, int id) throws JsonException { 245 return getMetadata(name, new JsonRequest(locale, JSON.V5, JSON.GET, id)); 246 } 247 248 /** 249 * Get a JSON array of metadata elements as listed by 250 * {@link jmri.Metadata#getSystemNameList()}. 251 * 252 * @param request the JSON request 253 * @return Array of JSON metadata elements 254 * @throws JsonException if thrown by 255 * {@link #getMetadata(java.util.Locale, java.lang.String, int)} 256 */ 257 public ArrayNode getMetadata(@Nonnull JsonRequest request) throws JsonException { 258 ArrayNode root = mapper.createArrayNode(); 259 for (String name : Metadata.getSystemNameList()) { 260 root.add(getMetadata(name, request)); 261 } 262 return root; 263 } 264 265 /** 266 * Get a running {@link jmri.util.zeroconf.ZeroConfService} using the 267 * protocol as the name of the service. 268 * 269 * @param name the service protocol 270 * @param request the JSON request 271 * @return the JSON networkService message 272 * @throws JsonException if type is not a running zeroconf networking 273 * protocol 274 */ 275 public JsonNode getNetworkService(@Nonnull String name, @Nonnull JsonRequest request) throws JsonException { 276 for (ZeroConfService service : InstanceManager.getDefault(ZeroConfServiceManager.class).allServices()) { 277 if (service.getType().equals(name)) { 278 return this.getNetworkService(service, request.id); 279 } 280 } 281 throw new JsonException(404, 282 Bundle.getMessage(request.locale, JsonException.ERROR_OBJECT, JSON.NETWORK_SERVICE, name), 283 request.id); 284 } 285 286 private JsonNode getNetworkService(ZeroConfService service, int id) { 287 ObjectNode data = mapper.createObjectNode(); 288 data.put(JSON.NAME, service.getType()); 289 data.put(JSON.USERNAME, service.getName()); 290 data.put(JSON.PORT, service.getServiceInfo().getPort()); 291 data.put(JSON.TYPE, service.getType()); 292 Enumeration<String> pe = service.getServiceInfo().getPropertyNames(); 293 while (pe.hasMoreElements()) { 294 String pn = pe.nextElement(); 295 data.put(pn, service.getServiceInfo().getPropertyString(pn)); 296 } 297 return message(JSON.NETWORK_SERVICE, data, id); 298 } 299 300 /** 301 * @param request the JSON request 302 * @return the JSON networkServices message. 303 */ 304 public ArrayNode getNetworkServices(@Nonnull JsonRequest request) { 305 ArrayNode root = mapper.createArrayNode(); 306 InstanceManager.getDefault(ZeroConfServiceManager.class).allServices().stream() 307 .forEach(service -> root.add(this.getNetworkService(service, request.id))); 308 return root; 309 } 310 311 /** 312 * @param locale the client's Locale. 313 * @param id message id set by client 314 * @return the JSON networkServices message. 315 */ 316 public ArrayNode getNetworkServices(Locale locale, int id) { 317 return getNetworkServices(new JsonRequest(locale, JSON.V5, JSON.GET, id)); 318 } 319 320 /** 321 * Send a JSON {@link jmri.server.json.JSON#NODE} message containing the 322 * JMRI node identity and former identities. 323 * 324 * @param request the JSON request 325 * @return the JSON node message 326 * @see jmri.util.node.NodeIdentity 327 */ 328 public JsonNode getNode(JsonRequest request) { 329 ObjectNode data = mapper.createObjectNode(); 330 data.put(JSON.NODE, NodeIdentity.networkIdentity()); 331 ArrayNode nodes = mapper.createArrayNode(); 332 NodeIdentity.formerIdentities().stream().forEach(nodes::add); 333 data.set(JSON.FORMER_NODES, nodes); 334 return message(JSON.NODE, data, request.id); 335 } 336 337 /** 338 * return a JSON {@link jmri.server.json.JSON#NODE} message containing the 339 * requested panel details 340 * 341 * @param locale the client's Locale 342 * @param name panel name to return 343 * @param id message id set by client 344 * @return the JSON panel message. 345 * @throws JsonException if panel not found 346 */ 347 public JsonNode getPanel(Locale locale, String name, int id) throws JsonException { 348 ArrayNode panels = getPanels(JSON.XML, id); 349 for (JsonNode panel : panels) { 350 if (panel.path(JSON.DATA).path(JSON.NAME).asText().equals(name)) { 351 return message(JSON.PANEL, panel.path(JSON.DATA), id); 352 } 353 } 354 throw new JsonException(404, Bundle.getMessage(locale, JsonException.ERROR_OBJECT, JSON.PANEL, name), id); 355 } 356 357 public ObjectNode getPanel(Editor editor, String format, int id) { 358 if (editor.getAllowInFrameServlet()) { 359 JFrame frame = editor.getTargetFrame(); 360 if (frame != null) { 361 String title = frame.getTitle(); 362 if (!title.isEmpty() && 363 !Arrays.asList(InstanceManager.getDefault(WebServerPreferences.class).getDisallowedFrames()) 364 .contains(title)) { 365 String type = PANEL_PANEL; 366 String name = "Panel"; 367 if (editor instanceof ControlPanelEditor) { 368 type = CONTROL_PANEL; 369 name = "ControlPanel"; 370 } else if (editor instanceof LayoutEditor) { 371 type = LAYOUT_PANEL; 372 name = "Layout"; 373 } else if (editor instanceof SwitchboardEditor) { 374 type = SWITCHBOARD_PANEL; 375 name = "Switchboard"; 376 } 377 ObjectNode data = this.mapper.createObjectNode(); 378 data.put(NAME, name + "/" + title.replace(" ", "%20").replace("#", "%23")); // NOI18N 379 data.put(URL, "/panel/" + data.path(NAME).asText() + "?format=" + format); // NOI18N 380 data.put(USERNAME, title); 381 data.put(TYPE, type); 382 return message(PANEL, data, id); 383 } 384 } 385 } 386 return null; 387 } 388 389 public ArrayNode getPanels(String format, int id) { 390 ArrayNode root = mapper.createArrayNode(); 391 // list loaded Panels (ControlPanelEditor, PanelEditor, LayoutEditor, 392 // SwitchboardEditor) 393 InstanceManager.getDefault(EditorManager.class).getAll().stream() 394 .map(editor -> this.getPanel(editor, format, id)) 395 .filter(Objects::nonNull).forEach(root::add); 396 return root; 397 } 398 399 public ArrayNode getPanels(int id) { 400 return this.getPanels(JSON.XML, id); 401 } 402 403 /** 404 * return a JSON {@link jmri.server.json.JSON#NODE} message containing the 405 * Railroad from the Railroad Name preferences. 406 * 407 * @param request the JSON request 408 * @return the JSON railroad name message 409 */ 410 public JsonNode getRailroad(@Nonnull JsonRequest request) { 411 ObjectNode data = mapper.createObjectNode(); 412 data.put(JSON.NAME, InstanceManager.getDefault(WebServerPreferences.class).getRailroadName()); 413 return message(JSON.RAILROAD, data, request.id); 414 } 415 416 /** 417 * return a JSON {@link jmri.server.json.JSON#NODE} message containing the 418 * requested systemConnection details 419 * 420 * @param name system connection name to return 421 * @param request the JSON request 422 * @return the JSON systemConnections message 423 * @throws JsonException if systemConnection not found 424 */ 425 public JsonNode getSystemConnection(String name, JsonRequest request) throws JsonException { 426 for (JsonNode connection : getSystemConnections(request)) { 427 JsonNode data = connection.path(JSON.DATA); 428 if (data.path(JSON.NAME).asText().equals(name)) { 429 return message(JSON.SYSTEM_CONNECTION, data, request.id); 430 } 431 } 432 throw new JsonException(HttpServletResponse.SC_NOT_FOUND, 433 Bundle.getMessage(request.locale, JsonException.ERROR_NOT_FOUND, JSON.SYSTEM_CONNECTION, name), 434 request.id); 435 } 436 437 /** 438 * return a JSON array containing the defined system connections 439 * 440 * @param request the JSON request 441 * @return the JSON systemConnections message. 442 */ 443 public ArrayNode getSystemConnections(@Nonnull JsonRequest request) { 444 ArrayNode root = mapper.createArrayNode(); 445 ArrayList<String> prefixes = new ArrayList<>(); 446 for (ConnectionConfig config : InstanceManager.getDefault(ConnectionConfigManager.class)) { 447 if (!config.getDisabled()) { 448 ObjectNode data = mapper.createObjectNode(); 449 data.put(JSON.NAME, config.getConnectionName()); 450 data.put(JSON.PREFIX, config.getAdapter().getSystemConnectionMemo().getSystemPrefix()); 451 data.put(JSON.MFG, config.getManufacturer()); 452 data.put(JSON.DESCRIPTION, 453 Bundle.getMessage(request.locale, "ConnectionSucceeded", config.getConnectionName(), 454 config.name(), config.getInfo())); 455 prefixes.add(config.getAdapter().getSystemConnectionMemo().getSystemPrefix()); 456 root.add(message(JSON.SYSTEM_CONNECTION, data, request.id)); 457 } 458 } 459 InstanceManager.getList(SystemConnectionMemo.class).stream().map(instance -> instance) 460 .filter(memo -> (!memo.getDisabled() && !prefixes.contains(memo.getSystemPrefix()))) 461 .forEach(memo -> { 462 ObjectNode data = mapper.createObjectNode(); 463 data.put(JSON.NAME, memo.getUserName()); 464 data.put(JSON.PREFIX, memo.getSystemPrefix()); 465 data.putNull(JSON.MFG); 466 data.putNull(JSON.DESCRIPTION); 467 prefixes.add(memo.getSystemPrefix()); 468 root.add(message(JSON.SYSTEM_CONNECTION, data, request.id)); 469 }); 470 // Following is required because despite there being a 471 // SystemConnectionMemo for the default internal connection, it is not 472 // used for the default internal connection. This allows a client to map 473 // the server's internal objects. 474 SystemConnectionMemo internal = InstanceManager.getDefault(InternalSystemConnectionMemo.class); 475 if (!prefixes.contains(internal.getSystemPrefix())) { 476 ObjectNode data = mapper.createObjectNode(); 477 data.put(JSON.NAME, internal.getUserName()); 478 data.put(JSON.PREFIX, internal.getSystemPrefix()); 479 data.putNull(JSON.MFG); 480 data.putNull(JSON.DESCRIPTION); 481 root.add(message(JSON.SYSTEM_CONNECTION, data, request.id)); 482 } 483 return root; 484 } 485 486 /** 487 * Get a JSON message containing the requested configuration profile. 488 * 489 * @param profile the requested profile 490 * @param manager the in use profile manager 491 * @param request the JSON request 492 * @return the data for this profile as a JSON Node 493 */ 494 private JsonNode getConfigProfile(@Nonnull Profile profile, @Nonnull ProfileManager manager, 495 @Nonnull JsonRequest request) { 496 boolean active = profile == manager.getActiveProfile(); 497 boolean next = profile == manager.getNextActiveProfile(); 498 boolean isAutoStart = (active && manager.isAutoStartActiveProfile()); 499 ObjectNode data = mapper.createObjectNode(); 500 data.put(JSON.USERNAME, profile.getName()); 501 data.put(JSON.UNIQUE_ID, profile.getUniqueId()); 502 data.put(JSON.NAME, profile.getId()); 503 data.put(JSON.IS_ACTIVE_PROFILE, active); 504 if (request.version.equals(JSON.V5)) { 505 // this is not a property of a profile 506 data.put(JSON.IS_AUTO_START, isAutoStart); 507 } 508 data.put(JSON.IS_NEXT_PROFILE, next); 509 return message(JSON.CONFIG_PROFILE, data, request.id); 510 } 511 512 /** 513 * Get the named configuration profile. 514 * 515 * @param name the Profile name 516 * @param request the JSON request 517 * @return the JSON configProfiles message 518 * @throws JsonException if the requested configProfile is not found 519 */ 520 public JsonNode getConfigProfile(@Nonnull String name, @Nonnull JsonRequest request) throws JsonException { 521 ProfileManager manager = ProfileManager.getDefault(); 522 for (Profile profile : manager.getProfiles()) { 523 if (profile.getId().equals(name)) { 524 return getConfigProfile(profile, manager, request); 525 } 526 } 527 throw new JsonException(HttpServletResponse.SC_NOT_FOUND, 528 Bundle.getMessage(request.locale, JsonException.ERROR_OBJECT, JSON.CONFIG_PROFILE, name), 529 request.id); 530 } 531 532 /** 533 * Get a JSON array of all configuration profiles. 534 * 535 * @param request the JSON request 536 * @return the JSON configProfiles message 537 */ 538 public ArrayNode getConfigProfiles(@Nonnull JsonRequest request) { 539 ArrayNode root = mapper.createArrayNode(); 540 ProfileManager manager = ProfileManager.getDefault(); 541 for (Profile profile : manager.getProfiles()) { 542 if (profile != null) { 543 root.add(getConfigProfile(profile, manager, request)); 544 } 545 } 546 return root; 547 } 548 549 /** 550 * Handle session login request. 551 * 552 * @param username the username (may be null if not provided in path) 553 * @param data JSON data containing username and password 554 * @param request the JSON request 555 * @return JSON response with session token or error 556 * @throws JsonException if login fails or parameters are missing 557 */ 558 private JsonNode postSessionLogin(@CheckForNull String username, JsonNode data, JsonRequest request) throws JsonException { 559 // Extract username and password from JSON data 560 String user = username != null ? username : data.path("username").asText(""); 561 String password = data.path("password").asText(""); 562 563 if (user.isEmpty()) { 564 throw new JsonException(HttpServletResponse.SC_BAD_REQUEST, 565 Bundle.getMessage(request.locale, "ErrorMissingParameter", "username"), request.id); 566 } 567 568 PermissionManager mngr = InstanceManager.getDefault(PermissionManager.class); 569 570 // Check if guest user 571 if (mngr.isAGuestUser(user)) { 572 throw new JsonException(HttpServletResponse.SC_FORBIDDEN, 573 Bundle.getMessage(request.locale, "ErrorGuestLogin"), request.id); 574 } 575 576 // Attempt login 577 StringBuilder sessionId = new StringBuilder(""); 578 boolean result = mngr.remoteLogin(sessionId, request.locale, user, password); 579 580 if (!result) { 581 throw new JsonException(HttpServletResponse.SC_UNAUTHORIZED, 582 Bundle.getMessage(request.locale, "ErrorInvalidCredentials"), request.id); 583 } 584 585 // Build success response 586 ObjectNode root = mapper.createObjectNode(); 587 root.put(TYPE, JSON.SESSION_LOGIN); 588 ObjectNode dataNode = root.putObject(JSON.DATA); 589 dataNode.put("authenticationToken", sessionId.toString()); 590 591 log.debug("Successful login for user: {}", user); 592 return root; 593 } 594 595 /** 596 * Handle session logout request. 597 * 598 * @param username the username (may be null) 599 * @param data JSON data containing session token or username 600 * @param request the JSON request 601 * @return JSON response confirming logout 602 * @throws JsonException if logout fails 603 */ 604 private JsonNode postSessionLogout(@CheckForNull String username, JsonNode data, JsonRequest request) throws JsonException { 605 // Extract token 606 String token = data.path("token").asText(""); 607 608 PermissionManager mngr = InstanceManager.getDefault(PermissionManager.class); 609 mngr.remoteLogout(token); 610 611 // Build success response 612 ObjectNode root = mapper.createObjectNode(); 613 root.put(TYPE, JSON.SESSION_LOGOUT); 614 ObjectNode dataNode = root.putObject(JSON.DATA); 615 dataNode.put("authenticationToken", token); 616 617 log.debug("Successful logout for token: {}", token); 618 return root; 619 } 620 621 /** 622 * Gets the {@link jmri.DccLocoAddress} for a String in the form 623 * {@code number(type)} or {@code number}. 624 * <p> 625 * Type may be {@code L} for long or {@code S} for short. If the type is not 626 * specified, type is assumed to be short. 627 * 628 * @param address the address 629 * @return The DccLocoAddress for address 630 */ 631 public static DccLocoAddress addressForString(String address) { 632 String[] components = address.split("[()]"); 633 int number = Integer.parseInt(components[0]); 634 boolean isLong = false; 635 if (components.length > 1 && "L".equalsIgnoreCase(components[1])) { 636 isLong = true; 637 } 638 return new DccLocoAddress(number, isLong); 639 } 640 641 @Override 642 public JsonNode doSchema(String type, boolean server, JsonRequest request) throws JsonException { 643 int id = request.id; 644 try { 645 switch (type) { 646 case JSON.CONFIG_PROFILE: 647 case JSON.CONFIG_PROFILES: 648 return doSchema(type, 649 server, 650 "jmri/server/json/util/configProfile-server.json", 651 "jmri/server/json/util/configProfile-client.json", 652 id); 653 case JSON.NETWORK_SERVICE: 654 case JSON.NETWORK_SERVICES: 655 return doSchema(type, 656 server, 657 "jmri/server/json/util/networkService-server.json", 658 "jmri/server/json/util/networkService-client.json", 659 id); 660 case JSON.PANEL: 661 case JSON.PANELS: 662 return doSchema(type, 663 server, 664 "jmri/server/json/util/panel-server.json", 665 "jmri/server/json/util/panel-client.json", 666 id); 667 case JSON.SYSTEM_CONNECTION: 668 case JSON.SYSTEM_CONNECTIONS: 669 return doSchema(type, 670 server, 671 "jmri/server/json/util/systemConnection-server.json", 672 "jmri/server/json/util/systemConnection-client.json", 673 id); 674 case JsonException.ERROR: 675 case JSON.LIST: 676 case JSON.PONG: 677 if (server) { 678 return doSchema(type, server, 679 this.mapper.readTree(this.getClass().getClassLoader() 680 .getResource(RESOURCE_PATH + type + "-server.json")), 681 id); 682 } else { 683 throw new JsonException(HttpServletResponse.SC_BAD_REQUEST, 684 Bundle.getMessage(request.locale, "NotAClientType", type), id); 685 } 686 case JSON.LOCALE: 687 case JSON.PING: 688 if (!server) { 689 return doSchema(type, server, 690 this.mapper.readTree(this.getClass().getClassLoader() 691 .getResource(RESOURCE_PATH + type + "-client.json")), 692 id); 693 } else { 694 throw new JsonException(HttpServletResponse.SC_BAD_REQUEST, 695 Bundle.getMessage(request.locale, "NotAServerType", type), id); 696 } 697 case JSON.GOODBYE: 698 case JSON.HELLO: 699 case JSON.METADATA: 700 case JSON.NODE: 701 case JSON.RAILROAD: 702 case JSON.VERSION: 703 case JSON.SESSION_LOGIN: 704 case JSON.SESSION_LOGOUT: 705 return doSchema(type, 706 server, 707 RESOURCE_PATH + type + "-server.json", 708 RESOURCE_PATH + type + "-client.json", 709 id); 710 default: 711 throw new JsonException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, 712 Bundle.getMessage(request.locale, JsonException.ERROR_UNKNOWN_TYPE, type), id); 713 } 714 } catch (IOException ex) { 715 throw new JsonException(500, ex, id); 716 } 717 } 718}