001package jmri.server.json; 002 003import com.fasterxml.jackson.databind.JsonNode; 004import com.fasterxml.jackson.databind.ObjectMapper; 005import java.io.DataOutputStream; 006import java.io.IOException; 007import javax.annotation.Nonnull; 008import jmri.InstanceManager; 009import jmri.jmris.JmriConnection; 010import jmri.server.json.schema.JsonSchemaServiceCache; 011import org.eclipse.jetty.websocket.api.Session; 012 013/** 014 * Abstraction of DataOutputStream and WebSocket.Connection classes for JSON 015 * clients. 016 * 017 * @author Randall Wood Copyright 2019 018 */ 019public class JsonConnection extends JmriConnection { 020 021 private final ObjectMapper objectMapper = new ObjectMapper(); 022 private String version = JSON.V5; 023 protected final JsonServerPreferences preferences = InstanceManager.getDefault(JsonServerPreferences.class); 024 protected final JsonSchemaServiceCache schemas = InstanceManager.getDefault(JsonSchemaServiceCache.class); 025 026 public JsonConnection(Session connection) { 027 super(connection); 028 } 029 030 public JsonConnection(DataOutputStream output) { 031 super(output); 032 } 033 034 /** 035 * Get the ObjectMapper for this connection. 036 * 037 * @return the ObjectMapper 038 */ 039 @Nonnull 040 public ObjectMapper getObjectMapper() { 041 return objectMapper; 042 } 043 044 /** 045 * Send a JsonNode to the instantiated connection. 046 * <p> 047 * This method throws an IOException so the server or servlet holding the 048 * connection open can respond to the exception. 049 * <p> 050 * If {@link JsonServerPreferences#getValidateServerMessages()} is 051 * {@code true}, a message is sent to the client that validation failed 052 * instead of the intended message. 053 * <p> 054 * Overriding methods must ensure that {@code message} is only sent if 055 * validated. 056 * 057 * @param message the object or array to send as a message 058 * @param request the JSON request 059 * @throws IOException if unable to send the message 060 */ 061 public void sendMessage(@Nonnull JsonNode message, @Nonnull JsonRequest request) throws IOException { 062 if (preferences.getValidateServerMessages()) { 063 try { 064 schemas.validateMessage(message, true, request); 065 } catch (JsonException ex) { 066 super.sendMessage(getObjectMapper().writeValueAsString(ex.getJsonMessage())); 067 return; 068 } 069 } 070 super.sendMessage(getObjectMapper().writeValueAsString(message)); 071 } 072 073 /** 074 * Send a JsonNode to the instantiated connection. 075 * <p> 076 * This method throws an IOException so the server or servlet holding the 077 * connection open can respond to the exception. 078 * <p> 079 * If {@link JsonServerPreferences#getValidateServerMessages()} is 080 * {@code true}, a message is sent to the client that validation failed 081 * instead of the intended message. 082 * <p> 083 * Overriding methods must ensure that {@code message} is only sent if 084 * validated. 085 * 086 * @param message the object or array to send as a message 087 * @param id the message id set by the client 088 * @throws IOException if unable to send the message 089 */ 090 public void sendMessage(@Nonnull JsonNode message, int id) throws IOException { 091 sendMessage(message, new JsonRequest(getLocale(), getVersion(), JSON.GET, id)); 092 } 093 094 public String getVersion() { 095 return version; 096 } 097 098 public void setVersion(String version) { 099 this.version = version; 100 } 101 102}