blob: 0325e033cebf9a688d44b4e1ac082a1fa541f861 [file] [log] [blame]
Christian van Spaandonk63814412008-08-02 09:56:01 +00001/*
Richard S. Hall8df9ab12009-07-24 17:06:37 +00002 * Copyright (c) OSGi Alliance (2004, 2008). All Rights Reserved.
Christian van Spaandonk63814412008-08-02 09:56:01 +00003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package info.dmtree;
17
18import java.io.PrintStream;
19import java.util.Vector;
20
21/**
22 * Checked exception received when a DMT operation fails. Beside the exception
23 * message, a <code>DmtException</code> always contains an error code (one of
24 * the constants specified in this class), and may optionally contain the URI of
25 * the related node, and information about the cause of the exception.
26 * <p>
27 * Some of the error codes defined in this class have a corresponding error code
28 * defined in OMA DM, in these cases the name and numerical value from OMA DM is
29 * used. Error codes without counterparts in OMA DM were given numbers from a
30 * different range, starting from 1.
31 * <p>
32 * The cause of the exception (if specified) can either be a single
33 * <code>Throwable</code> instance, or a list of such instances if several
34 * problems occurred during the execution of a method. An example for the latter
35 * is the <code>close</code> method of <code>DmtSession</code> that tries to
36 * close multiple plugins, and has to report the exceptions of all failures.
37 * <p>
38 * Each constructor has two variants, one accepts a <code>String</code> node
Richard S. Hall8df9ab12009-07-24 17:06:37 +000039 * URI, the other accepts a <code>String[]</code> node path. The former is used
40 * by the DmtAdmin implementation, the latter by the plugins, who receive the
41 * node URI as an array of segment names. The constructors are otherwise
Christian van Spaandonk63814412008-08-02 09:56:01 +000042 * identical.
43 * <p>
44 * Getter methods are provided to retrieve the values of the additional
45 * parameters, and the <code>printStackTrace(PrintWriter)</code> method is
46 * extended to print the stack trace of all causing throwables as well.
Richard S. Hall8df9ab12009-07-24 17:06:37 +000047 *
48 * @version $Revision: 5837 $
Christian van Spaandonk63814412008-08-02 09:56:01 +000049 */
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
Richard S. Hall8df9ab12009-07-24 17:06:37 +0000334 /**
335 * The message associated with the exception, or <code>null</code> if there
336 * is no error message.
337 */
338 private final String message;
Christian van Spaandonk63814412008-08-02 09:56:01 +0000339
Richard S. Hall8df9ab12009-07-24 17:06:37 +0000340 /**
341 * The list of originating exceptions, or empty list or <code>null</code> if
342 * there are no originating exceptions.
343 */
Christian van Spaandonk63814412008-08-02 09:56:01 +0000344 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) {
Richard S. Hall8df9ab12009-07-24 17:06:37 +0000419 super((Throwable) null);
Christian van Spaandonk63814412008-08-02 09:56:01 +0000420 this.uri = uri;
421 this.code = code;
422 this.message = message;
423 this.causes = causes;
424 this.fatal = fatal;
425 }
426
427 /**
428 * Create an instance of the exception, specifying the target node as an
429 * array of path segments. This method behaves in exactly the same way as if
430 * the path was given as a URI string.
431 *
432 * @param path the path of the node on which the failed DMT operation was
433 * issued, or <code>null</code> if the operation is not associated
434 * with a node
435 * @param code the error code of the failure
436 * @param message the message associated with the exception, or
437 * <code>null</code> if there is no error message
438 * @see #DmtException(String, int, String)
439 */
440 public DmtException(String[] path, int code, String message) {
441 this(pathToUri(path), code, message);
442 }
443
444 /**
445 * Create an instance of the exception, specifying the target node as an
446 * array of path segments, and specifying the cause exception. This method
447 * behaves in exactly the same way as if the path was given as a URI string.
448 *
449 * @param path the path of the node on which the failed DMT operation was
450 * issued, or <code>null</code> if the operation is not associated
451 * with a node
452 * @param code the error code of the failure
453 * @param message the message associated with the exception, or
454 * <code>null</code> if there is no error message
455 * @param cause the originating exception, or <code>null</code> if there
456 * is no originating exception
457 * @see #DmtException(String, int, String, Throwable)
458 */
459 public DmtException(String[] path, int code, String message, Throwable cause) {
460 this(pathToUri(path), code, message, cause);
461 }
462
463 /**
464 * Create an instance of the exception, specifying the target node as an
465 * array of path segments, the list of cause exceptions, and whether the
466 * exception is a fatal one. This method behaves in exactly the same way as
467 * if the path was given as a URI string.
468 *
469 * @param path the path of the node on which the failed DMT operation was
470 * issued, or <code>null</code> if the operation is not associated
471 * with a node
472 * @param code the error code of the failure
473 * @param message the message associated with the exception, or
474 * <code>null</code> if there is no error message
475 * @param causes the list of originating exceptions, or empty list or
476 * <code>null</code> if there are no originating exceptions
477 * @param fatal whether the exception is fatal
478 * @see #DmtException(String, int, String, Vector, boolean)
479 */
480 public DmtException(String[] path, int code, String message, Vector causes,
481 boolean fatal) {
482 this(pathToUri(path), code, message, causes, fatal);
483 }
484
485 // ----- Public methods -----//
486
487 /**
488 * Get the node on which the failed DMT operation was issued. Some
489 * operations like <code>DmtSession.close()</code> don't require an URI,
490 * in this case this method returns <code>null</code>.
491 *
492 * @return the URI of the node, or <code>null</code>
493 */
494 public String getURI() {
495 return uri;
496 }
497
498 /**
499 * Get the error code associated with this exception. Most of the error
500 * codes within this exception correspond to OMA DM error codes.
501 *
502 * @return the error code
503 */
504 public int getCode() {
505 return code;
506 }
507
508 /**
509 * Get the message associated with this exception. The returned string also
510 * contains the associated URI (if any) and the exception code. The
511 * resulting message has the following format (parts in square brackets are
512 * only included if the field inside them is not <code>null</code>):
513 *
514 * <pre>
515 * &lt;exception_code&gt;[: '&lt;uri&gt;'][: &lt;error_message&gt;]
516 * </pre>
517 *
518 * @return the error message in the format described above
519 */
520 public String getMessage() {
521 StringBuffer sb = new StringBuffer(getCodeText(code));
522 if (uri != null)
523 sb.append(": '").append(uri).append('\'');
524 if (message != null)
Richard S. Hall8df9ab12009-07-24 17:06:37 +0000525 sb.append(": ").append(message);
Christian van Spaandonk63814412008-08-02 09:56:01 +0000526
527 return sb.toString();
528 }
529
530 /**
531 * Get the cause of this exception. Returns non-<code>null</code>, if
532 * this exception is caused by one or more other exceptions (like a
533 * <code>NullPointerException</code> in a DmtPlugin). If there are more
534 * than one cause exceptions, the first one is returned.
535 *
536 * @return the cause of this exception, or <code>null</code> if no cause
537 * was given
538 */
539 public Throwable getCause() {
540 return causes.length == 0 ? null : causes[0];
541 }
542
543 /**
544 * Get all causes of this exception. Returns the causing exceptions in an
545 * array. If no cause was specified, an empty array is returned.
546 *
547 * @return the list of causes of this exception
548 */
549 public Throwable[] getCauses() {
550 return (Throwable[]) causes.clone();
551 }
552
553 /**
554 * Check whether this exception is marked as fatal in the session. Fatal
555 * exceptions trigger an automatic rollback of atomic sessions.
556 *
557 * @return whether the exception is marked as fatal
558 */
559 public boolean isFatal() {
560 return fatal;
561 }
562
563 /**
564 * Prints the exception and its backtrace to the specified print stream. Any
565 * causes that were specified for this exception are also printed, together
566 * with their backtraces.
567 *
568 * @param s <code>PrintStream</code> to use for output
569 */
570 public void printStackTrace(PrintStream s) {
571 super.printStackTrace(s);
572 for (int i = 0; i<causes.length; i++) {
573 s.print("Caused by" + (i > 0 ? " (" + (i+1) + ")" : "") + ": ");
574 causes[i].printStackTrace(s);
575 }
576 }
577
578 // ----- Utility methods -----//
579
580 /**
581 * Converts the given path, given as an array of path segments, to a single
582 * URI string.
583 *
584 * @param path the path to convert
585 * @return the URI string representing the same node as the given path
586 */
587 static String pathToUri(String[] path) {
588 if (path == null)
589 return null;
590
591 return Uri.toUri(path);
592 }
593
594 /**
595 * Returns the name of the given error code.
596 *
597 * @param code the error code
598 * @return a string containing the error code name
599 */
600 private static String getCodeText(int code) {
601 // todo sync codes
602 switch (code) {
603 case NODE_NOT_FOUND:
604 return "NODE_NOT_FOUND";
605 case COMMAND_NOT_ALLOWED:
606 return "COMMAND_NOT_ALLOWED";
607 case FEATURE_NOT_SUPPORTED:
608 return "FEATURE_NOT_SUPPORTED";
609 case URI_TOO_LONG:
610 return "URI_TOO_LONG";
611 case NODE_ALREADY_EXISTS:
612 return "NODE_ALREADY_EXISTS";
613 case PERMISSION_DENIED:
614 return "PERMISSION_DENIED";
615 case COMMAND_FAILED:
616 return "COMMAND_FAILED";
617 case DATA_STORE_FAILURE:
618 return "DATA_STORE_FAILURE";
619 case ROLLBACK_FAILED:
620 return "ROLLBACK_FAILED";
621
622 case REMOTE_ERROR:
623 return "REMOTE_ERROR";
624 case METADATA_MISMATCH:
625 return "METADATA_MISMATCH";
626 case INVALID_URI:
627 return "INVALID_URI";
628 case CONCURRENT_ACCESS:
629 return "CONCURRENT_ACCESS";
630 case ALERT_NOT_ROUTED:
631 return "ALERT_NOT_ROUTED";
632 case TRANSACTION_ERROR:
633 return "TRANSACTION_ERROR";
634 case SESSION_CREATION_TIMEOUT:
635 return "SESSION_CREATION_TIMEOUT";
636 default:
637 return "<unknown code>";
638 }
639 }
640}