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