001package jmri.server.json.power;
002
003import static jmri.server.json.JSON.DEFAULT;
004import static jmri.server.json.JSON.NAME;
005import static jmri.server.json.JSON.OFF;
006import static jmri.server.json.JSON.ON;
007import static jmri.server.json.JSON.PREFIX;
008import static jmri.server.json.JSON.STATE;
009import static jmri.server.json.JSON.UNKNOWN;
010import static jmri.server.json.power.JsonPowerServiceFactory.POWER;
011
012import com.fasterxml.jackson.databind.JsonNode;
013import com.fasterxml.jackson.databind.ObjectMapper;
014import com.fasterxml.jackson.databind.node.ArrayNode;
015import com.fasterxml.jackson.databind.node.ObjectNode;
016import javax.annotation.CheckForNull;
017import javax.servlet.http.HttpServletResponse;
018import jmri.InstanceManager;
019import jmri.JmriException;
020import jmri.PowerManager;
021import jmri.SystemConnectionMemo;
022import jmri.jmrix.SystemConnectionMemoManager;
023import jmri.server.json.JsonException;
024import jmri.server.json.JsonHttpService;
025import jmri.server.json.JsonRequest;
026
027/**
028 * @author Randall Wood Copyright 2016, 2018
029 */
030public class JsonPowerHttpService extends JsonHttpService {
031
032    public JsonPowerHttpService(ObjectMapper mapper) {
033        super(mapper);
034    }
035
036    @Override
037    // Nullable to override inherited NonNull requirement
038    public JsonNode doGet(String type, @CheckForNull String name, JsonNode parameters, JsonRequest request)
039            throws JsonException {
040        ObjectNode data = mapper.createObjectNode();
041        PowerManager manager = resolvePowerManager(name, parameters, request);
042        if (manager != null) {
043            data.put(NAME, manager.getUserName());
044            switch (manager.getPower()) {
045                case PowerManager.OFF:
046                    data.put(STATE, OFF);
047                    break;
048                case PowerManager.ON:
049                    data.put(STATE, ON);
050                    break;
051                default:
052                    data.put(STATE, UNKNOWN);
053                    break;
054            }
055            data.put(DEFAULT, manager.equals(InstanceManager.getDefault(PowerManager.class)));
056            String managerPrefix = getPrefixForManager(manager);
057            if (managerPrefix != null) {
058                data.put(PREFIX, managerPrefix);
059            }
060        } else {
061            // No PowerManager is defined; just report it as UNKNOWN
062            data.put(STATE, UNKNOWN);
063            data.put(NAME, "");
064            data.put(DEFAULT, false);
065        }
066        return message(POWER, data, request.id);
067    }
068
069    @Override
070    public JsonNode doPost(String type, String name, JsonNode data, JsonRequest request) throws JsonException {
071        int state = data.path(STATE).asInt(UNKNOWN);
072        if (state != UNKNOWN) {
073            try {
074                PowerManager manager = resolvePowerManager(name, data, request);
075                if (manager != null) {
076                    switch (state) {
077                        case OFF:
078                            manager.setPower(PowerManager.OFF);
079                            break;
080                        case ON:
081                            manager.setPower(PowerManager.ON);
082                            break;
083                        default:
084                            throw new JsonException(HttpServletResponse.SC_BAD_REQUEST,
085                                    Bundle.getMessage(request.locale, "ErrorUnknownState", POWER, state), request.id);
086                    }
087                }
088            } catch (JmriException ex) {
089                throw new JsonException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex, request.id);
090            }
091        }
092        return this.doGet(type, name, data, request);
093    }
094
095    /**
096     * Resolves the PowerManager to use for a request. Checks for a
097     * {@code prefix} field in {@code data} first (connection-specific routing),
098     * then falls back to matching by {@code name} (user name), then falls back
099     * to the default PowerManager. When {@code prefix} is present but does not
100     * match a known connection, throws a {@link JsonException} with HTTP 400.
101     *
102     * @param name    the power manager user name, or empty/null for default
103     * @param data    the JSON data node; may contain an optional {@code prefix} field
104     * @param request the originating request, used for locale and id in error messages
105     * @return the resolved PowerManager, or null if none is configured
106     * @throws JsonException if a prefix is supplied but does not match any known connection
107     */
108    @CheckForNull
109    private PowerManager resolvePowerManager(@CheckForNull String name, JsonNode data, JsonRequest request)
110            throws JsonException {
111        String prefix = data.path(PREFIX).asText();
112        if (!prefix.isEmpty()) {
113            SystemConnectionMemo memo = SystemConnectionMemoManager.getDefault()
114                    .getSystemConnectionMemoForSystemPrefix(prefix);
115            if (memo != null && memo.provides(PowerManager.class)) {
116                return memo.get(PowerManager.class);
117            }
118            throw new JsonException(HttpServletResponse.SC_BAD_REQUEST,
119                    Bundle.getMessage(request.locale, "ErrorUnknownPrefix", prefix), request.id);
120        }
121        if (name != null && !name.isEmpty()) {
122            for (PowerManager pm : InstanceManager.getList(PowerManager.class)) {
123                if (pm.getUserName().equals(name)) {
124                    return pm;
125                }
126            }
127        }
128        return InstanceManager.getNullableDefault(PowerManager.class);
129    }
130
131    /**
132     * Returns the system prefix of the connection that provides the given
133     * PowerManager, or null if no connection can be found for it.
134     *
135     * @param manager the PowerManager to look up
136     * @return system prefix string, or null
137     */
138    @CheckForNull
139    private String getPrefixForManager(PowerManager manager) {
140        for (SystemConnectionMemo memo : InstanceManager.getList(SystemConnectionMemo.class)) {
141            if (memo.provides(PowerManager.class) && manager.equals(memo.get(PowerManager.class))) {
142                return memo.getSystemPrefix();
143            }
144        }
145        return null;
146    }
147
148    @Override
149    public JsonNode doGetList(String type, JsonNode data, JsonRequest request) throws JsonException {
150        ArrayNode array = this.mapper.createArrayNode();
151        for (PowerManager manager : InstanceManager.getList(PowerManager.class)) {
152            array.add(this.doGet(type, manager.getUserName(), data, request));
153        }
154        return message(array, request.id);
155    }
156
157    @Override
158    public JsonNode doSchema(String type, boolean server, JsonRequest request) throws JsonException {
159        if (POWER.equals(type)) {
160            return doSchema(type,
161                    server,
162                    "jmri/server/json/power/power-server.json",
163                    "jmri/server/json/power/power-client.json",
164                    request.id);
165        } else {
166            throw new JsonException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
167                    Bundle.getMessage(request.locale, JsonException.ERROR_UNKNOWN_TYPE, type), request.id);
168        }
169    }
170}