001package jmri.server.json.message;
002
003import static jmri.server.json.message.JsonMessage.CLIENT;
004
005import com.fasterxml.jackson.databind.JsonNode;
006import com.fasterxml.jackson.databind.node.ArrayNode;
007import com.fasterxml.jackson.databind.node.ObjectNode;
008import java.io.IOException;
009import java.util.Set;
010import java.util.UUID;
011import javax.servlet.http.HttpServletResponse;
012import jmri.InstanceManager;
013import jmri.JmriException;
014import jmri.server.json.JSON;
015import jmri.server.json.JsonConnection;
016import jmri.server.json.JsonException;
017import jmri.server.json.JsonRequest;
018import jmri.server.json.JsonSocketService;
019
020/**
021 *
022 * @author Randall Wood Copyright 2017, 2019
023 */
024public class JsonMessageSocketService extends JsonSocketService<JsonMessageHttpService> {
025
026    public JsonMessageSocketService(JsonConnection connection) {
027        super(connection, new JsonMessageHttpService(connection.getObjectMapper()));
028    }
029
030    @Override
031    public void onMessage(String type, JsonNode data, JsonRequest request)
032            throws IOException, JmriException, JsonException {
033        switch (type) {
034            case JSON.HELLO:
035                if (!data.path(CLIENT).isMissingNode()) {
036                    String client = data.path(CLIENT).asText();
037                    if (!client.isEmpty()) {
038                        subscribe(client, request.id);
039                    } else {
040                        throw new JsonException(HttpServletResponse.SC_BAD_REQUEST,
041                                Bundle.getMessage(request.locale, "ErrorEmptyAttribute", JsonMessage.CLIENT, type), request.id);
042                    }
043                }
044                break;
045            case JsonMessage.CLIENT:
046                switch (request.method) {
047                    case JSON.DELETE:
048                        // remove client id
049                        if (!data.path(CLIENT).isMissingNode()) {
050                            String client = data.path(CLIENT).asText();
051                            if (!client.isEmpty()) {
052                                getManager().unsubscribe(client);
053                            } else {
054                                throw new JsonException(HttpServletResponse.SC_BAD_REQUEST,
055                                        Bundle.getMessage(request.locale, "ErrorEmptyAttribute", JsonMessage.CLIENT, type), request.id);
056                            }
057                        } else {
058                            throw new JsonException(HttpServletResponse.SC_BAD_REQUEST,
059                                    Bundle.getMessage(request.locale, "ErrorMissingAttribute", JsonMessage.CLIENT, type), request.id);
060                        }
061                        break;
062                    case JSON.GET:
063                        // if client is not specified, and connection is not subscribing, create a client id for it
064                        // if client is not specified, and connection is subscribing, return onList results
065                        // if client is specified, and not empty, throw JsonException if client
066                        //     is not for this connection, otherwise return client
067                        // if client is specified, and empty, return first client for connection
068                        if (!data.path(CLIENT).isMissingNode()) {
069                            String client = data.path(CLIENT).asText();
070                            if (!client.isEmpty()) {
071                                if (getManager().getClients(connection).contains(client)) {
072                                    connection.sendMessage(getClient(client, request), request.id);
073                                } else {
074                                    throw new JsonException(HttpServletResponse.SC_NOT_FOUND,
075                                            Bundle.getMessage(request.locale, "MessageClientNotForThisConnection", client), request.id);
076                                }
077                            } else {
078                                connection.sendMessage(getClient(getManager().getClient(connection), request), request.id);
079                            }
080                        } else {
081                            if (getManager().getClients(connection).isEmpty()) {
082                                String client = UUID.randomUUID().toString();
083                                subscribe(client, request.id);
084                                connection.sendMessage(getClient(client, request), request.id);
085                            } else {
086                                onList(type, data, request);
087                            }
088                        }
089                        break;
090                    case JSON.POST:
091                    case JSON.PUT:
092                    default:
093                        // add client using client-provided id
094                        if (!data.path(CLIENT).isMissingNode()) {
095                            String client = data.path(CLIENT).asText();
096                            if (!client.isEmpty()) {
097                                subscribe(client, request.id);
098                            } else {
099                                throw new JsonException(HttpServletResponse.SC_BAD_REQUEST,
100                                        Bundle.getMessage(request.locale, "ErrorEmptyAttribute", JsonMessage.CLIENT, type), request.id);
101                            }
102                        } else {
103                            throw new JsonException(HttpServletResponse.SC_BAD_REQUEST,
104                                    Bundle.getMessage(request.locale, "ErrorMissingAttribute", JsonMessage.CLIENT, type), request.id);
105                        }
106                        break;
107                }
108                break; // break inside gets to here, then this goes out
109            default:
110                // ignore anything else
111                break;
112        }
113    }
114
115    @Override
116    public void onList(String type, JsonNode data, JsonRequest request) throws IOException, JmriException, JsonException {
117        switch (type) {
118            case JSON.HELLO:
119                throw new JsonException(HttpServletResponse.SC_BAD_REQUEST,
120                        Bundle.getMessage(request.locale, "UnlistableService", type), request.id);
121            case JsonMessage.CLIENT:
122                Set<String> clients = getManager().getClients(connection);
123                if (clients.isEmpty()) {
124                    throw new JsonException(HttpServletResponse.SC_NOT_FOUND,
125                            Bundle.getMessage(request.locale, "NoMessageClientForThisConnection", type), request.id);
126                }
127                ArrayNode array = service.getObjectMapper().createArrayNode();
128                for (String client : clients) {
129                    array.add(getClient(client, request));
130                }
131                connection.sendMessage(service.message(array, request.id), request.id);
132                break;
133            default:
134                // silently ignore
135        }
136    }
137
138    @Override
139    public void onClose() {
140        getManager().unsubscribe(this.connection);
141    }
142
143    private JsonNode getClient(String client, JsonRequest request) throws JsonException {
144        if (client == null) {
145            throw new JsonException(HttpServletResponse.SC_NOT_FOUND,
146                    Bundle.getMessage(request.locale, "NoMessageClientForThisConnection"), request.id);
147        }
148        ObjectNode root = this.connection.getObjectMapper().createObjectNode();
149        root.put(JSON.TYPE, JsonMessage.CLIENT);
150        ObjectNode data = root.putObject(JSON.DATA);
151        data.put(JsonMessage.CLIENT, client);
152        return root;
153    }
154
155    private void subscribe(String client, int id) throws JsonException {
156        try {
157            getManager().subscribe(client, this.connection);
158        } catch (IllegalArgumentException ex) {
159            throw new JsonException(HttpServletResponse.SC_CONFLICT,
160                    Bundle.getMessage(this.connection.getLocale(), "ErrorClientConflict", CLIENT), id);
161        }
162    }
163
164    private JsonMessageClientManager getManager() {
165        return InstanceManager.getDefault(JsonMessageClientManager.class);
166    }
167}