blob: c723bc0ee489d53b8e796e5be261265364fdf331 [file] [log] [blame]
Christian van Spaandonk569a4c52008-08-02 09:56:01 +00001/*
2 * $Header: /cvshome/build/info.dmtree/src/info/dmtree/DmtException.java,v 1.9 2006/07/12 21:21:37 hargrave Exp $
3 *
4 * Copyright (c) OSGi Alliance (2004, 2006). All Rights Reserved.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18package info.dmtree;
19
20import java.io.PrintStream;
21import java.util.Vector;
22
23/**
24 * Checked exception received when a DMT operation fails. Beside the exception
25 * message, a <code>DmtException</code> always contains an error code (one of
26 * the constants specified in this class), and may optionally contain the URI of
27 * the related node, and information about the cause of the exception.
28 * <p>
29 * Some of the error codes defined in this class have a corresponding error code
30 * defined in OMA DM, in these cases the name and numerical value from OMA DM is
31 * used. Error codes without counterparts in OMA DM were given numbers from a
32 * different range, starting from 1.
33 * <p>
34 * The cause of the exception (if specified) can either be a single
35 * <code>Throwable</code> instance, or a list of such instances if several
36 * problems occurred during the execution of a method. An example for the latter
37 * is the <code>close</code> method of <code>DmtSession</code> that tries to
38 * close multiple plugins, and has to report the exceptions of all failures.
39 * <p>
40 * Each constructor has two variants, one accepts a <code>String</code> node
41 * URI, the other accepts a <code>String[]</code> node path. The former is
42 * used by the DmtAdmin implementation, the latter by the plugins, who receive
43 * the node URI as an array of segment names. The constructors are otherwise
44 * identical.
45 * <p>
46 * Getter methods are provided to retrieve the values of the additional
47 * parameters, and the <code>printStackTrace(PrintWriter)</code> method is
48 * extended to print the stack trace of all causing throwables as well.
49 */
50public class DmtException extends Exception {
51 private static final long serialVersionUID = -63006267148118655L;
52
53 // ----- Public constants -----//
54
55 /**
56 * The originator's authentication credentials specify a principal with
57 * insufficient rights to complete the command.
58 * <p>
59 * This status code is used as response to device originated sessions if the
60 * remote management server cannot authorize the device to perform the
61 * requested operation.
62 * <p>
63 * This error code corresponds to the OMA DM response status code 401
64 * &quot;Unauthorized&quot;.
65 */
66 public static final int UNAUTHORIZED = 401;
67
68 /**
69 * The requested target node was not found. No indication is given as to
70 * whether this is a temporary or permanent condition, unless otherwise
71 * noted.
72 * <p>
73 * This is only used when the requested node name is valid, otherwise the
74 * more specific error codes {@link #URI_TOO_LONG} or {@link #INVALID_URI}
75 * are used. This error code corresponds to the OMA DM response status code
76 * 404 &quot;Not Found&quot;.
77 */
78 public static final int NODE_NOT_FOUND = 404;
79
80 /**
81 * The requested command is not allowed on the target node. This includes
82 * the following situations:
83 * <ul>
84 * <li>an interior node operation is requested for a leaf node, or vice
85 * versa (e.g. trying to retrieve the children of a leaf node)
86 * <li>an attempt is made to create a node where the parent is a leaf node
87 * <li>an attempt is made to rename or delete the root node of the tree
88 * <li>an attempt is made to rename or delete the root node of the session
89 * <li>a write operation (other than setting the ACL) is performed in a
90 * non-atomic write session on a node provided by a plugin that is read-only
91 * or does not support non-atomic writing
92 * <li>a node is copied to its descendant
93 * <li>the ACL of the root node is changed not to include Add rights for
94 * all principals
95 * </ul>
96 * <p>
97 * This error code corresponds to the OMA DM response status code 405
98 * &quot;Command not allowed&quot;.
99 */
100 public static final int COMMAND_NOT_ALLOWED = 405;
101
102 /**
103 * The requested command failed because an optional feature required by the
104 * command is not supported. For example, opening an atomic session might
105 * return this error code if the DmtAdmin implementation does not support
106 * transactions. Similarly, accessing the optional node properties (Title,
107 * Timestamp, Version, Size) might not succeed if either the DmtAdmin
108 * implementation or the underlying plugin does not support the property.
109 * <p>
110 * When getting or setting values for interior nodes (an optional
111 * optimization feature), a plugin can use this error code to indicate that
112 * the given interior node does not support values.
113 * <p>
114 * This error code corresponds to the OMA DM response status code 406
115 * &quot;Optional feature not supported&quot;.
116 */
117 public static final int FEATURE_NOT_SUPPORTED = 406;
118
119 /**
120 * The requested command failed because the target URI or one of its
121 * segments is too long for what the recipient is able or willing to
122 * process, or the target URI contains too many segments. The length and
123 * segment number limits are implementation dependent, their minimum values
124 * can be found in the Non Functional Requirements section of the OSGi
125 * specification.
126 * <p>
127 * The {@link Uri#mangle(String)} method provides support for ensuring that
128 * a URI segment conforms to the length limits set by the implementation.
129 * <p>
130 * This error code corresponds to the OMA DM response status code 414
131 * &quot;URI too long&quot;.
132 *
133 * @see "OSGi Service Platform, Mobile Specification Release 4"
134 */
135 public static final int URI_TOO_LONG = 414;
136
137 /**
138 * The requested node creation operation failed because the target already
139 * exists. This can occur if the node is created directly (with one of the
140 * <code>create...</code> methods), or indirectly (during a
141 * <code>copy</code> operation).
142 * <p>
143 * This error code corresponds to the OMA DM response status code 418
144 * &quot;Already exists&quot;.
145 */
146 public static final int NODE_ALREADY_EXISTS = 418;
147
148 /**
149 * The requested command failed because the principal associated with the
150 * session does not have adequate access control permissions (ACL) on the
151 * target. This can only appear in case of remote sessions, i.e. if the
152 * session is associated with an authenticated principal.
153 * <p>
154 * This error code corresponds to the OMA DM response status code 425
155 * &quot;Permission denied&quot;.
156 */
157 public static final int PERMISSION_DENIED = 425;
158
159 /**
160 * The recipient encountered an error which prevented it from fulfilling the
161 * request.
162 * <p>
163 * This error code is only used in situations not covered by any of the
164 * other error codes that a method may use. Some methods specify more
165 * specific error situations for this code, but it can generally be used for
166 * any unexpected condition that causes the command to fail.
167 * <p>
168 * This error code corresponds to the OMA DM response status code 500
169 * &quot;Command Failed&quot;.
170 */
171 public static final int COMMAND_FAILED = 500;
172
173 /**
174 * An error related to the recipient data store occurred while processing
175 * the request. This error code may be thrown by any of the methods
176 * accessing the tree, but whether it is really used depends on the
177 * implementation, and the data store it uses.
178 * <p>
179 * This error code corresponds to the OMA DM response status code 510
180 * &quot;Data store failure&quot;.
181 */
182 public static final int DATA_STORE_FAILURE = 510;
183
184 /**
185 * The rollback command was not completed successfully. The tree might be in
186 * an inconsistent state after this error.
187 * <p>
188 * This error code corresponds to the OMA DM response status code 516
189 * &quot;Atomic roll back failed&quot;.
190 */
191 public static final int ROLLBACK_FAILED = 516;
192
193
194 /**
195 * A device initiated remote operation failed. This is used when the
196 * protocol adapter fails to send an alert for any reason.
197 * <p>
198 * Alert routing errors (that occur while looking for the proper protocol
199 * adapter to use) are indicated by {@link #ALERT_NOT_ROUTED}, this code is
200 * only for errors encountered while sending the routed alert. This error
201 * code does not correspond to any OMA DM response status code. It should be
202 * translated to the code 500 &quot;Command Failed&quot; when transferring
203 * over OMA DM.
204 */
205 public static final int REMOTE_ERROR = 1;
206
207 /**
208 * Operation failed because of meta data restrictions. This covers any
209 * attempted deviation from the parameters defined by the
210 * <code>MetaNode</code> objects of the affected nodes, for example in the
211 * following situations:
212 * <ul>
213 * <li>creating, deleting or renaming a permanent node, or modifying its
214 * type or value
215 * <li>creating an interior node where the meta-node defines it as a leaf,
216 * or vice versa
217 * <li>any operation on a node which does not have the required access type
218 * (e.g. executing a node that lacks the <code>MetaNode.CMD_EXECUTE</code>
219 * access type)
220 * <li>any node creation or deletion that would violate the cardinality
221 * constraints
222 * <li>any leaf node value setting that would violate the allowed formats,
223 * values, mime types, etc.
224 * <li>any node creation that would violate the allowed node names
225 * </ul>
226 * <p>
227 * This error code can also be used to indicate any other meta data
228 * violation, even if it cannot be described by the <code>MetaNode</code>
229 * class. For example, detecting a multi-node constraint violation while
230 * committing an atomic session should result in this error.
231 * <p>
232 * This error code does not correspond to any OMA DM response status code.
233 * It should be translated to the code 405 &quot;Command not allowed&quot;
234 * when transferring over OMA DM.
235 */
236 public static final int METADATA_MISMATCH = 2;
237
238 /**
239 * The requested command failed because the target URI or node name is
240 * <code>null</code> or syntactically invalid. This covers the following
241 * cases:
242 * <ul>
243 * <li>the URI or node name ends with the '\' or '/' character
244 * <li>the URI is an empty string (only invalid if the method does not
245 * accept relative URIs)
246 * <li>the URI contains the segment &quot;<code>.</code>&quot; at a position
247 * other than the beginning of the URI
248 * <li>the node name is &quot;<code>..</code>&quot; or the URI contains such
249 * a segment
250 * <li>the node name is an empty string or the URI contains an empty segment
251 * <li>the node name contains an unescaped '/' character
252 * </ul>
253 * <p>
254 * See the {@link Uri#mangle(String)} method for support on escaping invalid
255 * characters in a URI.
256 * <p>
257 * This code is only used if the URI or node name does not match any of the
258 * criteria for {@link #URI_TOO_LONG}. This error code does not correspond
259 * to any OMA DM response status code. It should be translated to the code
260 * 404 &quot;Not Found&quot; when transferring over OMA DM.
261 */
262 public static final int INVALID_URI = 3;
263
264 /**
265 * An error occurred related to concurrent access of nodes. This can happen
266 * for example if a configuration node was deleted directly through the
267 * Configuration Admin service, while the node was manipulated via the tree.
268 * <p>
269 * This error code does not correspond to any OMA DM response status code.
270 * It should be translated to the code 500 &quot;Command Failed&quot; when
271 * transferring over OMA DM.
272 */
273 public static final int CONCURRENT_ACCESS = 4;
274
275 /**
276 * An alert can not be sent from the device to the given principal. This can
277 * happen if there is no Remote Alert Sender willing to forward the alert to
278 * the given principal, or if no principal was given and the DmtAdmin did
279 * not find an appropriate default destination.
280 * <p>
281 * This error code does not correspond to any OMA DM response status code.
282 * It should be translated to the code 500 &quot;Command Failed&quot; when
283 * transferring over OMA DM.
284 */
285 public static final int ALERT_NOT_ROUTED = 5;
286
287 /**
288 * A transaction-related error occurred in an atomic session. This error is
289 * caused by one of the following situations:
290 * <ul>
291 * <li>an updating method within an atomic session can not be executed
292 * because the underlying plugin is read-only or does not support atomic
293 * writing</li>
294 * <li>a commit operation at the end of an atomic session failed because
295 * one of the underlying plugins failed to close</li>
296 * </ul>
297 * The latter case may leave the tree in an inconsistent state due to the
298 * lack of a two-phase commit system, see {@link DmtSession#commit} for
299 * details.
300 * <p>
301 * This error code does not correspond to any OMA DM response status code.
302 * It should be translated to the code 500 &quot;Command Failed&quot; when
303 * transferring over OMA DM.
304 */
305 public static final int TRANSACTION_ERROR = 6;
306
307 /**
308 * Creation of a session timed out because of another ongoing session. The
309 * length of time while the DmtAdmin waits for the blocking session(s) to
310 * finish is implementation dependant.
311 * <p>
312 * This error code does not correspond to any OMA DM response status code.
313 * OMA has several status codes related to timeout, but these are meant to
314 * be used when a request times out, not if a session can not be
315 * established. This error code should be translated to the code 500
316 * &quot;Command Failed&quot; when transferring over OMA DM.
317 */
318 public static final int SESSION_CREATION_TIMEOUT = 7;
319
320 // ----- Content fields -----//
321
322 /**
323 * The URI of the node on which the failed DMT operation was issued, or
324 * <code>null</code> if the operation was not associated with a node.
325 */
326 private final String uri;
327
328 /**
329 * The error code of the failure, one of the constants defined in this
330 * class.
331 */
332 private final int code;
333
334 /**
335 * The message associated with the exception, or <code>null</code> if
336 * there is no error message.
337 */
338 private final String message;
339
340 /**
341 * The list of originating exceptions, or empty list or <code>null</code>
342 * if there are no originating exceptions.
343 */
344 private final Throwable[] causes;
345
346 /**
347 * Determines whether the exception is fatal or not. This is basically a
348 * two-state severity indicator, with the 'fatal' severity being the more
349 * serious one.
350 */
351 private final boolean fatal;
352
353 // ----- Constructors -----//
354
355 /**
356 * Create an instance of the exception. The <code>uri</code> and
357 * <code>message</code> parameters are optional. No originating exception
358 * is specified.
359 *
360 * @param uri the node on which the failed DMT operation was issued, or
361 * <code>null</code> if the operation is not associated with a node
362 * @param code the error code of the failure
363 * @param message the message associated with the exception, or
364 * <code>null</code> if there is no error message
365 */
366 public DmtException(String uri, int code, String message) {
367 this(uri, code, message, new Throwable[0], false);
368 }
369
370 /**
371 * Create an instance of the exception, specifying the cause exception. The
372 * <code>uri</code>, <code>message</code> and <code>cause</code>
373 * parameters are optional.
374 *
375 * @param uri the node on which the failed DMT operation was issued, or
376 * <code>null</code> if the operation is not associated with a node
377 * @param code the error code of the failure
378 * @param message the message associated with the exception, or
379 * <code>null</code> if there is no error message
380 * @param cause the originating exception, or <code>null</code> if there
381 * is no originating exception
382 */
383 public DmtException(String uri, int code, String message, Throwable cause) {
384 this(uri, code, message, (cause == null) ? new Throwable[0]
385 : new Throwable[] { cause }, false);
386 }
387
388 /**
389 * Create an instance of the exception, specifying the list of cause
390 * exceptions and whether the exception is a fatal one. This constructor is
391 * meant to be used by plugins wishing to indicate that a serious error
392 * occurred which should invalidate the ongoing atomic session. The
393 * <code>uri</code>, <code>message</code> and <code>causes</code>
394 * parameters are optional.
395 * <p>
396 * If a fatal exception is thrown, no further business methods will be
397 * called on the originator plugin. In case of atomic sessions, all other
398 * open plugins will be rolled back automatically, except if the fatal
399 * exception was thrown during commit.
400 *
401 * @param uri the node on which the failed DMT operation was issued, or
402 * <code>null</code> if the operation is not associated with a node
403 * @param code the error code of the failure
404 * @param message the message associated with the exception, or
405 * <code>null</code> if there is no error message
406 * @param causes the list of originating exceptions, or empty list or
407 * <code>null</code> if there are no originating exceptions
408 * @param fatal whether the exception is fatal
409 */
410 public DmtException(String uri, int code, String message, Vector causes,
411 boolean fatal) {
412 this(uri, code, message, (causes == null) ? new Throwable[0]
413 : (Throwable[]) causes.toArray(new Throwable[causes.size()]),
414 fatal);
415 }
416
417 private DmtException(String uri, int code, String message,
418 Throwable[] causes, boolean fatal) {
419 this.uri = uri;
420 this.code = code;
421 this.message = message;
422 this.causes = causes;
423 this.fatal = fatal;
424 }
425
426 /**
427 * Create an instance of the exception, specifying the target node as an
428 * array of path segments. This method behaves in exactly the same way as if
429 * the path was given as a URI string.
430 *
431 * @param path the path of the node on which the failed DMT operation was
432 * issued, or <code>null</code> if the operation is not associated
433 * with a node
434 * @param code the error code of the failure
435 * @param message the message associated with the exception, or
436 * <code>null</code> if there is no error message
437 * @see #DmtException(String, int, String)
438 */
439 public DmtException(String[] path, int code, String message) {
440 this(pathToUri(path), code, message);
441 }
442
443 /**
444 * Create an instance of the exception, specifying the target node as an
445 * array of path segments, and specifying the cause exception. This method
446 * behaves in exactly the same way as if the path was given as a URI string.
447 *
448 * @param path the path of the node on which the failed DMT operation was
449 * issued, or <code>null</code> if the operation is not associated
450 * with a node
451 * @param code the error code of the failure
452 * @param message the message associated with the exception, or
453 * <code>null</code> if there is no error message
454 * @param cause the originating exception, or <code>null</code> if there
455 * is no originating exception
456 * @see #DmtException(String, int, String, Throwable)
457 */
458 public DmtException(String[] path, int code, String message, Throwable cause) {
459 this(pathToUri(path), code, message, cause);
460 }
461
462 /**
463 * Create an instance of the exception, specifying the target node as an
464 * array of path segments, the list of cause exceptions, and whether the
465 * exception is a fatal one. This method behaves in exactly the same way as
466 * if the path was given as a URI string.
467 *
468 * @param path the path of the node on which the failed DMT operation was
469 * issued, or <code>null</code> if the operation is not associated
470 * with a node
471 * @param code the error code of the failure
472 * @param message the message associated with the exception, or
473 * <code>null</code> if there is no error message
474 * @param causes the list of originating exceptions, or empty list or
475 * <code>null</code> if there are no originating exceptions
476 * @param fatal whether the exception is fatal
477 * @see #DmtException(String, int, String, Vector, boolean)
478 */
479 public DmtException(String[] path, int code, String message, Vector causes,
480 boolean fatal) {
481 this(pathToUri(path), code, message, causes, fatal);
482 }
483
484 // ----- Public methods -----//
485
486 /**
487 * Get the node on which the failed DMT operation was issued. Some
488 * operations like <code>DmtSession.close()</code> don't require an URI,
489 * in this case this method returns <code>null</code>.
490 *
491 * @return the URI of the node, or <code>null</code>
492 */
493 public String getURI() {
494 return uri;
495 }
496
497 /**
498 * Get the error code associated with this exception. Most of the error
499 * codes within this exception correspond to OMA DM error codes.
500 *
501 * @return the error code
502 */
503 public int getCode() {
504 return code;
505 }
506
507 /**
508 * Get the message associated with this exception. The returned string also
509 * contains the associated URI (if any) and the exception code. The
510 * resulting message has the following format (parts in square brackets are
511 * only included if the field inside them is not <code>null</code>):
512 *
513 * <pre>
514 * &lt;exception_code&gt;[: '&lt;uri&gt;'][: &lt;error_message&gt;]
515 * </pre>
516 *
517 * @return the error message in the format described above
518 */
519 public String getMessage() {
520 StringBuffer sb = new StringBuffer(getCodeText(code));
521 if (uri != null)
522 sb.append(": '").append(uri).append('\'');
523 if (message != null)
524 sb.append(": ").append(message);
525
526 return sb.toString();
527 }
528
529 /**
530 * Get the cause of this exception. Returns non-<code>null</code>, if
531 * this exception is caused by one or more other exceptions (like a
532 * <code>NullPointerException</code> in a DmtPlugin). If there are more
533 * than one cause exceptions, the first one is returned.
534 *
535 * @return the cause of this exception, or <code>null</code> if no cause
536 * was given
537 */
538 public Throwable getCause() {
539 return causes.length == 0 ? null : causes[0];
540 }
541
542 /**
543 * Get all causes of this exception. Returns the causing exceptions in an
544 * array. If no cause was specified, an empty array is returned.
545 *
546 * @return the list of causes of this exception
547 */
548 public Throwable[] getCauses() {
549 return (Throwable[]) causes.clone();
550 }
551
552 /**
553 * Check whether this exception is marked as fatal in the session. Fatal
554 * exceptions trigger an automatic rollback of atomic sessions.
555 *
556 * @return whether the exception is marked as fatal
557 */
558 public boolean isFatal() {
559 return fatal;
560 }
561
562 /**
563 * Prints the exception and its backtrace to the specified print stream. Any
564 * causes that were specified for this exception are also printed, together
565 * with their backtraces.
566 *
567 * @param s <code>PrintStream</code> to use for output
568 */
569 public void printStackTrace(PrintStream s) {
570 super.printStackTrace(s);
571 for (int i = 0; i<causes.length; i++) {
572 s.print("Caused by" + (i > 0 ? " (" + (i+1) + ")" : "") + ": ");
573 causes[i].printStackTrace(s);
574 }
575 }
576
577 // ----- Utility methods -----//
578
579 /**
580 * Converts the given path, given as an array of path segments, to a single
581 * URI string.
582 *
583 * @param path the path to convert
584 * @return the URI string representing the same node as the given path
585 */
586 static String pathToUri(String[] path) {
587 if (path == null)
588 return null;
589
590 return Uri.toUri(path);
591 }
592
593 /**
594 * Returns the name of the given error code.
595 *
596 * @param code the error code
597 * @return a string containing the error code name
598 */
599 private static String getCodeText(int code) {
600 // todo sync codes
601 switch (code) {
602 case NODE_NOT_FOUND:
603 return "NODE_NOT_FOUND";
604 case COMMAND_NOT_ALLOWED:
605 return "COMMAND_NOT_ALLOWED";
606 case FEATURE_NOT_SUPPORTED:
607 return "FEATURE_NOT_SUPPORTED";
608 case URI_TOO_LONG:
609 return "URI_TOO_LONG";
610 case NODE_ALREADY_EXISTS:
611 return "NODE_ALREADY_EXISTS";
612 case PERMISSION_DENIED:
613 return "PERMISSION_DENIED";
614 case COMMAND_FAILED:
615 return "COMMAND_FAILED";
616 case DATA_STORE_FAILURE:
617 return "DATA_STORE_FAILURE";
618 case ROLLBACK_FAILED:
619 return "ROLLBACK_FAILED";
620
621 case REMOTE_ERROR:
622 return "REMOTE_ERROR";
623 case METADATA_MISMATCH:
624 return "METADATA_MISMATCH";
625 case INVALID_URI:
626 return "INVALID_URI";
627 case CONCURRENT_ACCESS:
628 return "CONCURRENT_ACCESS";
629 case ALERT_NOT_ROUTED:
630 return "ALERT_NOT_ROUTED";
631 case TRANSACTION_ERROR:
632 return "TRANSACTION_ERROR";
633 case SESSION_CREATION_TIMEOUT:
634 return "SESSION_CREATION_TIMEOUT";
635 default:
636 return "<unknown code>";
637 }
638 }
639}