|
1 | 1 | package tools.jackson.core;
|
2 | 2 |
|
| 3 | +import java.io.Closeable; |
3 | 4 | import java.io.Serializable;
|
| 5 | +import java.lang.reflect.InvocationTargetException; |
4 | 6 | import java.util.Collection;
|
| 7 | +import java.util.LinkedList; |
5 | 8 | import java.util.Objects;
|
6 | 9 |
|
7 | 10 | /**
|
|
10 | 13 | * Note that in Jackson 2.x this exception extended {@link java.io.IOException}
|
11 | 14 | * but in 3.x {@link RuntimeException}
|
12 | 15 | */
|
13 |
| -public abstract class JacksonException |
| 16 | +public class JacksonException |
14 | 17 | extends RuntimeException
|
15 | 18 | {
|
16 | 19 | private final static long serialVersionUID = 3L; // eclipse complains otherwise
|
17 | 20 |
|
| 21 | + /** |
| 22 | + * Let's limit length of reference chain, to limit damage in cases |
| 23 | + * of infinite recursion. |
| 24 | + */ |
| 25 | + private final static int MAX_REFS_TO_LIST = 1000; |
| 26 | + |
18 | 27 | /**
|
19 | 28 | * Simple bean class used to contain references. References
|
20 | 29 | * can be added to indicate execution/reference path that
|
@@ -151,6 +160,20 @@ Object writeReplace() {
|
151 | 160 |
|
152 | 161 | protected JsonLocation _location;
|
153 | 162 |
|
| 163 | + /** |
| 164 | + * Path through which problem that triggering throwing of |
| 165 | + * this exception was reached. |
| 166 | + */ |
| 167 | + protected LinkedList<Reference> _path; |
| 168 | + |
| 169 | + /** |
| 170 | + * Underlying processor ({@link JsonParser} or {@link JsonGenerator}), |
| 171 | + * if known. |
| 172 | + *<p> |
| 173 | + * NOTE: typically not serializable hence <code>transient</code> |
| 174 | + */ |
| 175 | + protected transient Closeable _processor; |
| 176 | + |
154 | 177 | /*
|
155 | 178 | /**********************************************************************
|
156 | 179 | /* Life-cycle
|
@@ -192,6 +215,116 @@ public JacksonException clearLocation() {
|
192 | 215 | return this;
|
193 | 216 | }
|
194 | 217 |
|
| 218 | + /* |
| 219 | + /********************************************************************** |
| 220 | + /* Life-cycle: more advanced factory-like methods |
| 221 | + /********************************************************************** |
| 222 | + */ |
| 223 | + |
| 224 | + /** |
| 225 | + * Method that can be called to either create a new DatabindException |
| 226 | + * (if underlying exception is not a DatabindException), or augment |
| 227 | + * given exception with given path/reference information. |
| 228 | + * |
| 229 | + * This version of method is called when the reference is through a |
| 230 | + * non-indexed object, such as a Map or POJO/bean. |
| 231 | + */ |
| 232 | + public static JacksonException wrapWithPath(Throwable src, Object refFrom, |
| 233 | + String refPropertyName) { |
| 234 | + return wrapWithPath(src, new Reference(refFrom, refPropertyName)); |
| 235 | + } |
| 236 | + |
| 237 | + /** |
| 238 | + * Method that can be called to either create a new DatabindException |
| 239 | + * (if underlying exception is not a DatabindException), or augment |
| 240 | + * given exception with given path/reference information. |
| 241 | + * |
| 242 | + * This version of method is called when the reference is through an |
| 243 | + * index, which happens with arrays and Collections. |
| 244 | + */ |
| 245 | + public static JacksonException wrapWithPath(Throwable src, Object refFrom, int index) { |
| 246 | + return wrapWithPath(src, new Reference(refFrom, index)); |
| 247 | + } |
| 248 | + |
| 249 | + /** |
| 250 | + * Method that can be called to either create a new DatabindException |
| 251 | + * (if underlying exception is not a DatabindException), or augment |
| 252 | + * given exception with given path/reference information. |
| 253 | + */ |
| 254 | + @SuppressWarnings("resource") |
| 255 | + public static JacksonException wrapWithPath(Throwable src, Reference ref) |
| 256 | + { |
| 257 | + JacksonException jme; |
| 258 | + if (src instanceof JacksonException) { |
| 259 | + jme = (JacksonException) src; |
| 260 | + } else { |
| 261 | + // [databind#2128]: try to avoid duplication |
| 262 | + String msg = _exceptionMessage(src); |
| 263 | + // Let's use a more meaningful placeholder if all we have is null |
| 264 | + if (msg == null || msg.isEmpty()) { |
| 265 | + msg = "(was "+src.getClass().getName()+")"; |
| 266 | + } |
| 267 | + // 17-Aug-2015, tatu: Let's also pass the processor (parser/generator) along |
| 268 | + Closeable proc = null; |
| 269 | + if (src instanceof JacksonException) { |
| 270 | + Object proc0 = ((JacksonException) src).processor(); |
| 271 | + if (proc0 instanceof Closeable) { |
| 272 | + proc = (Closeable) proc0; |
| 273 | + } |
| 274 | + } |
| 275 | + jme = new JacksonException(msg, src); |
| 276 | + jme._processor = proc; |
| 277 | + } |
| 278 | + jme.prependPath(ref); |
| 279 | + return jme; |
| 280 | + } |
| 281 | + |
| 282 | + private static String _exceptionMessage(Throwable t) { |
| 283 | + if (t instanceof JacksonException) { |
| 284 | + return ((JacksonException) t).getOriginalMessage(); |
| 285 | + } |
| 286 | + if (t instanceof InvocationTargetException && t.getCause() != null) { |
| 287 | + return t.getCause().getMessage(); |
| 288 | + } |
| 289 | + return t.getMessage(); |
| 290 | + } |
| 291 | + |
| 292 | + /* |
| 293 | + /********************************************************************** |
| 294 | + /* Life-cycle: information augmentation (cannot use factory style, alas) |
| 295 | + /********************************************************************** |
| 296 | + */ |
| 297 | + |
| 298 | + /** |
| 299 | + * Method called to prepend a reference information in front of |
| 300 | + * current path |
| 301 | + */ |
| 302 | + public JacksonException prependPath(Object referrer, String propertyName) { |
| 303 | + return prependPath(new Reference(referrer, propertyName)); |
| 304 | + } |
| 305 | + |
| 306 | + /** |
| 307 | + * Method called to prepend a reference information in front of |
| 308 | + * current path |
| 309 | + */ |
| 310 | + public JacksonException prependPath(Object referrer, int index) { |
| 311 | + return prependPath(new Reference(referrer, index)); |
| 312 | + } |
| 313 | + |
| 314 | + public JacksonException prependPath(Reference r) |
| 315 | + { |
| 316 | + if (_path == null) { |
| 317 | + _path = new LinkedList<Reference>(); |
| 318 | + } |
| 319 | + // Also: let's not increase without bounds. Could choose either |
| 320 | + // head or tail; tail is easier (no need to ever remove), as |
| 321 | + // well as potentially more useful so let's use it: |
| 322 | + if (_path.size() < MAX_REFS_TO_LIST) { |
| 323 | + _path.addFirst(r); |
| 324 | + } |
| 325 | + return this; |
| 326 | + } |
| 327 | + |
195 | 328 | /*
|
196 | 329 | /**********************************************************************
|
197 | 330 | /* Extended API
|
@@ -238,7 +371,9 @@ public JacksonException clearLocation() {
|
238 | 371 | *
|
239 | 372 | * @return Originating processor, if available; {@code null} if not.
|
240 | 373 | */
|
241 |
| - public abstract Object processor(); |
| 374 | + public Object processor() { |
| 375 | + return _processor; |
| 376 | + } |
242 | 377 |
|
243 | 378 | /*
|
244 | 379 | /**********************************************************************
|
|
0 commit comments