001package com.nimbusds.oauth2.sdk; 002 003 004import java.net.URI; 005 006import net.jcip.annotations.Immutable; 007 008import net.minidev.json.JSONObject; 009 010import com.nimbusds.oauth2.sdk.http.HTTPResponse; 011import com.nimbusds.oauth2.sdk.util.JSONObjectUtils; 012 013 014/** 015 * Error object, used to encapsulate OAuth 2.0 and other errors. 016 * 017 * <p>Example error object as HTTP response: 018 * 019 * <pre> 020 * HTTP/1.1 400 Bad Request 021 * Content-Type: application/json;charset=UTF-8 022 * Cache-Control: no-store 023 * Pragma: no-cache 024 * 025 * { 026 * "error" : "invalid_request" 027 * } 028 * </pre> 029 */ 030@Immutable 031public class ErrorObject { 032 033 034 /** 035 * The error code, may not always be defined. 036 */ 037 private final String code; 038 039 040 /** 041 * Optional error description. 042 */ 043 private final String description; 044 045 046 /** 047 * Optional HTTP status code, 0 if not specified. 048 */ 049 private final int httpStatusCode; 050 051 052 /** 053 * Optional URI of a web page that includes additional information 054 * about the error. 055 */ 056 private final URI uri; 057 058 059 /** 060 * Creates a new error with the specified code. 061 * 062 * @param code The error code, {@code null} if not specified. 063 */ 064 public ErrorObject(final String code) { 065 066 this(code, null, 0, null); 067 } 068 069 070 /** 071 * Creates a new error with the specified code and description. 072 * 073 * @param code The error code, {@code null} if not specified. 074 * @param description The error description, {@code null} if not 075 * specified. 076 */ 077 public ErrorObject(final String code, final String description) { 078 079 this(code, description, 0, null); 080 } 081 082 083 /** 084 * Creates a new error with the specified code, description and HTTP 085 * status code. 086 * 087 * @param code The error code, {@code null} if not specified. 088 * @param description The error description, {@code null} if not 089 * specified. 090 * @param httpStatusCode The HTTP status code, zero if not specified. 091 */ 092 public ErrorObject(final String code, final String description, 093 final int httpStatusCode) { 094 095 this(code, description, httpStatusCode, null); 096 } 097 098 099 /** 100 * Creates a new error with the specified code, description, HTTP 101 * status code and page URI. 102 * 103 * @param code The error code, {@code null} if not specified. 104 * @param description The error description, {@code null} if not 105 * specified. 106 * @param httpStatusCode The HTTP status code, zero if not specified. 107 * @param uri The error page URI, {@code null} if not 108 * specified. 109 */ 110 public ErrorObject(final String code, final String description, 111 final int httpStatusCode, final URI uri) { 112 113 this.code = code; 114 this.description = description; 115 this.httpStatusCode = httpStatusCode; 116 this.uri = uri; 117 } 118 119 120 /** 121 * Gets the error code. 122 * 123 * @return The error code, {@code null} if not specified. 124 */ 125 public String getCode() { 126 127 return code; 128 } 129 130 131 /** 132 * Gets the error description. 133 * 134 * @return The error description, {@code null} if not specified. 135 */ 136 public String getDescription() { 137 138 return description; 139 } 140 141 142 /** 143 * Sets the error description. 144 * 145 * @param description The error description, {@code null} if not 146 * specified. 147 * 148 * @return A copy of this error with the specified description. 149 */ 150 public ErrorObject setDescription(final String description) { 151 152 return new ErrorObject(getCode(), description, getHTTPStatusCode(), getURI()); 153 } 154 155 156 /** 157 * Appends the specified text to the error description. 158 * 159 * @param text The text to append to the error description, 160 * {@code null} if not specified. 161 * 162 * @return A copy of this error with the specified appended 163 * description. 164 */ 165 public ErrorObject appendDescription(final String text) { 166 167 String newDescription; 168 169 if (getDescription() != null) 170 newDescription = getDescription() + text; 171 else 172 newDescription = text; 173 174 return new ErrorObject(getCode(), newDescription, getHTTPStatusCode(), getURI()); 175 } 176 177 178 /** 179 * Gets the HTTP status code. 180 * 181 * @return The HTTP status code, zero if not specified. 182 */ 183 public int getHTTPStatusCode() { 184 185 return httpStatusCode; 186 } 187 188 189 /** 190 * Sets the HTTP status code. 191 * 192 * @param httpStatusCode The HTTP status code, zero if not specified. 193 * 194 * @return A copy of this error with the specified HTTP status code. 195 */ 196 public ErrorObject setHTTPStatusCode(final int httpStatusCode) { 197 198 return new ErrorObject(getCode(), getDescription(), httpStatusCode, getURI()); 199 } 200 201 202 /** 203 * Gets the error page URI. 204 * 205 * @return The error page URI, {@code null} if not specified. 206 */ 207 public URI getURI() { 208 209 return uri; 210 } 211 212 213 /** 214 * Sets the error page URI. 215 * 216 * @param uri The error page URI, {@code null} if not specified. 217 * 218 * @return A copy of this error with the specified page URI. 219 */ 220 public ErrorObject setURI(final URI uri) { 221 222 return new ErrorObject(getCode(), getDescription(), getHTTPStatusCode(), uri); 223 } 224 225 226 /** 227 * Returns a JSON object representation of this error object. 228 * 229 * <p>Example: 230 * 231 * <pre> 232 * { 233 * "error" : "invalid_grant", 234 * "error_description" : "Invalid resource owner credentials" 235 } 236 * </pre> 237 * 238 * @return The JSON object. 239 */ 240 public JSONObject toJSONObject() { 241 242 JSONObject o = new JSONObject(); 243 244 if (code != null) { 245 o.put("error", code); 246 } 247 248 if (description != null) { 249 o.put("error_description", description); 250 } 251 252 if (uri != null) { 253 o.put("error_uri", uri.toString()); 254 } 255 256 return o; 257 } 258 259 260 /** 261 * @see #getCode 262 */ 263 @Override 264 public String toString() { 265 266 return code != null ? code : "null"; 267 } 268 269 270 @Override 271 public int hashCode() { 272 273 return code != null ? code.hashCode() : "null".hashCode(); 274 } 275 276 277 @Override 278 public boolean equals(final Object object) { 279 280 return object instanceof ErrorObject && 281 this.toString().equals(object.toString()); 282 } 283 284 285 /** 286 * Parses an error object from the specified JSON object. 287 * 288 * @param jsonObject The JSON object to parse. Must not be 289 * {@code null}. 290 * 291 * @return The error object. 292 */ 293 public static ErrorObject parse(final JSONObject jsonObject) { 294 295 String code = null; 296 String description = null; 297 URI uri = null; 298 299 try { 300 if (jsonObject.containsKey("error")) { 301 code = JSONObjectUtils.getString(jsonObject, "error"); 302 } 303 304 if (jsonObject.containsKey("error_description")) { 305 description = JSONObjectUtils.getString(jsonObject, "error_description"); 306 } 307 308 if (jsonObject.containsKey("error_uri")) { 309 uri = JSONObjectUtils.getURI(jsonObject, "error_uri"); 310 } 311 } catch (ParseException e) { 312 // ignore and continue 313 } 314 315 return new ErrorObject(code, description, 0, uri); 316 } 317 318 319 /** 320 * Parses an error object from the specified HTTP response. 321 * 322 * @param httpResponse The HTTP response to parse. Must not be 323 * {@code null}. 324 * 325 * @return The error object. 326 */ 327 public static ErrorObject parse(final HTTPResponse httpResponse) { 328 329 JSONObject jsonObject; 330 331 try { 332 jsonObject = httpResponse.getContentAsJSONObject(); 333 334 } catch (ParseException e) { 335 336 return new ErrorObject(null, null, httpResponse.getStatusCode()); 337 } 338 339 ErrorObject intermediary = parse(jsonObject); 340 341 return new ErrorObject( 342 intermediary.getCode(), 343 intermediary.description, 344 httpResponse.getStatusCode(), 345 intermediary.getURI()); 346 } 347}