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}