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}