001package jmri.server.json;
002
003import com.fasterxml.jackson.databind.JsonNode;
004import com.fasterxml.jackson.databind.ObjectMapper;
005import com.fasterxml.jackson.databind.node.ObjectNode;
006
007/**
008 * Throw an exception, but include an HTTP error code.
009 *
010 * @author Randall Wood Copyright (C) 2015, 2016
011 */
012public class JsonException extends Exception {
013
014    /* JSON protocol keys */
015    /**
016     * {@value #ERROR}
017     */
018    public static final String ERROR = "error"; // NOI18N
019    /**
020     * {@value #CODE}
021     */
022    public static final String CODE = "code"; // NOI18N
023    /**
024     * {@value #MESSAGE}
025     */
026    public static final String MESSAGE = "message"; // NOI18N
027
028    /* Error message localization keys */
029    /**
030     * {@value #ERROR_BAD_PROPERTY_VALUE}, a key for localized error messages
031     * indicating the value for the given property is not valid.
032     */
033    public static final String ERROR_BAD_PROPERTY_VALUE = "ErrorBadPropertyValue"; // NOI18N
034    /**
035     * {@value #ERROR_MISSING_PROPERTY_PUT}, a key for localized error messages
036     * indicating a property required to complete a PUT request is missing.
037     */
038    public static final String ERROR_MISSING_PROPERTY_PUT = "ErrorMissingPropertyPut"; // NOI18N
039    /**
040     * {@value #ERROR_NOT_FOUND}, a key for localized error messages indicating
041     * a resource was not found.
042     */
043    public static final String ERROR_NOT_FOUND = "ErrorNotFound"; // NOI18N
044    /**
045     * {@value #ERROR_OBJECT}, a key for localized error messages indicating
046     * an inability to get a requested object.
047     */
048    public static final String ERROR_OBJECT = "ErrorObject"; // NOI18N
049    /**
050     * {@value #ERROR_UNKNOWN_TYPE}, a key for localized error messages
051     * indicating the requested type cannot be handled.
052     */
053    public static final String ERROR_UNKNOWN_TYPE = "ErrorUnknownType"; // NOI18N
054    /**
055     * {@value #LOGGED_ERROR}, a key for localized error messages indicating
056     * that JMRI logs contain required information to resolve the error.
057     */
058    public static final String LOGGED_ERROR = "LoggedError"; // NOI18N
059
060    /* Internal objects */
061    private static final ObjectMapper MAPPER = new ObjectMapper();
062    private final int errorCode;
063    private final transient ObjectNode additionalData;
064    /**
065     * Only access through {@link #getId()}, even when used within this class
066     */
067    private final int id;
068
069    /**
070     * Create an exception that can be passed to a JSON client.
071     * 
072     * @param code      the error code
073     * @param message   message, displayable to the user, in the client's
074     *                  preferred locale
075     * @param throwable the cause of the exception
076     * @param id        the message id passed by the client, or the additive
077     *                  inverse of that id
078     */
079    public JsonException(int code, String message, Throwable throwable, int id) {
080        this(code, message, throwable, null, id);
081    }
082
083    /**
084     * Create an exception that can be passed to a JSON client.
085     * 
086     * @param code           the error code
087     * @param message        message, displayable to the user, in the client's
088     *                       preferred locale
089     * @param throwable      the cause of the exception
090     * @param additionalData additional data to be passed to the client
091     * @param id             the message id passed by the client, or the
092     *                       additive inverse of that id
093     */
094    public JsonException(int code, String message, Throwable throwable, ObjectNode additionalData, int id) {
095        super(message, throwable);
096        this.errorCode = code;
097        this.additionalData = additionalData;
098        this.id = id;
099    }
100
101    /**
102     * Create an exception that can be passed to a JSON client.
103     * 
104     * @param code      the error code
105     * @param throwable the cause of the exception
106     * @param id        the message id passed by the client, or the additive
107     *                  inverse of that id
108     */
109    public JsonException(int code, Throwable throwable, int id) {
110        this(code, throwable, null, id);
111    }
112
113    /**
114     * Create an exception that can be passed to a JSON client.
115     * 
116     * @param code           the error code
117     * @param throwable      the cause of the exception
118     * @param additionalData additional data to be passed to the client
119     * @param id             the message id passed by the client, or the
120     *                       additive inverse of that id
121     */
122    public JsonException(int code, Throwable throwable, ObjectNode additionalData, int id) {
123        super(throwable);
124        this.errorCode = code;
125        this.additionalData = additionalData;
126        this.id = id;
127    }
128
129    /**
130     * Create an exception that can be passed to a JSON client.
131     * 
132     * @param code    the error code
133     * @param message message, displayable to the user, in the client's
134     *                preferred locale
135     * @param id      the message id passed by the client, or the additive
136     *                inverse of that id
137     */
138    public JsonException(int code, String message, int id) {
139        this(code, message, (ObjectNode) null, id);
140    }
141
142    /**
143     * Create an exception that can be passed to a JSON client.
144     * 
145     * @param code           the error code
146     * @param message        message, displayable to the user, in the client's
147     *                       preferred locale
148     * @param additionalData additional data to be passed to the client
149     * @param id             the message id passed by the client, or the
150     *                       additive inverse of that id
151     */
152    public JsonException(int code, String message, ObjectNode additionalData, int id) {
153        super(message);
154        this.errorCode = code;
155        this.additionalData = additionalData;
156        this.id = id;
157    }
158
159    /**
160     * Get the error code (usually an HTTP error code)
161     * 
162     * @return the code
163     */
164    public int getCode() {
165        return this.errorCode;
166    }
167
168    /**
169     * Get any additional data passed to the client. This is specific to, and
170     * will vary based on, the original exception.
171     * 
172     * @return the additional data or null if none
173     */
174    public ObjectNode getAdditionalData() {
175        return this.additionalData.deepCopy();
176    }
177
178    /**
179     * Get the id passed to the client.
180     * 
181     * @return the absolute value of the id
182     * @see JsonHttpService
183     */
184    public int getId() {
185        return Math.abs(id);
186    }
187
188    /**
189     * Get the JSON formatted error message.
190     * 
191     * @return the error message in a JSON format
192     */
193    public JsonNode getJsonMessage() {
194        ObjectNode data = MAPPER.createObjectNode();
195        data.put(CODE, this.getCode());
196        data.put(MESSAGE, this.getMessage());
197        if (additionalData != null) {
198            data.setAll(additionalData);
199        }
200        return JsonHttpService.message(MAPPER, ERROR, data, null, getId());
201    }
202}