org.osgi.service.dmt.DmtException Maven / Gradle / Ivy
/*
* Copyright (c) OSGi Alliance (2004, 2013). All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.osgi.service.dmt;
import java.io.PrintStream;
import java.util.Vector;
/**
* Checked exception received when a DMT operation fails. Beside the exception
* message, a {@code DmtException} always contains an error code (one of the
* constants specified in this class), and may optionally contain the URI of the
* related node, and information about the cause of the exception.
*
* Some of the error codes defined in this class have a corresponding error code
* defined in OMA DM, in these cases the name and numerical value from OMA DM is
* used. Error codes without counterparts in OMA DM were given numbers from a
* different range, starting from 1.
*
* The cause of the exception (if specified) can either be a single
* {@code Throwable} instance, or a list of such instances if several problems
* occurred during the execution of a method. An example for the latter is the
* {@code close} method of {@code DmtSession} that tries to close multiple
* plugins, and has to report the exceptions of all failures.
*
* Each constructor has two variants, one accepts a {@code String} node URI, the
* other accepts a {@code String[]} node path. The former is used by the
* DmtAdmin implementation, the latter by the plugins, who receive the node URI
* as an array of segment names. The constructors are otherwise identical.
*
* Getter methods are provided to retrieve the values of the additional
* parameters, and the {@code printStackTrace(PrintWriter)} method is extended
* to print the stack trace of all causing throwables as well.
*
* @author $Id: d56c95a49eed00fce7fd6eadb7b6f5c41ae18588 $
*/
public class DmtException extends Exception {
private static final long serialVersionUID = -63006267148118655L;
// ----- Public constants -----//
/**
* The originator's authentication credentials specify a principal with
* insufficient rights to complete the command.
*
* This status code is used as response to device originated sessions if the
* remote management server cannot authorize the device to perform the
* requested operation.
*
* This error code corresponds to the OMA DM response status code 401
* "Unauthorized".
*/
public static final int UNAUTHORIZED = 401;
/**
* The requested target node was not found. No indication is given as to
* whether this is a temporary or permanent condition, unless otherwise
* noted.
*
* This is only used when the requested node name is valid, otherwise the
* more specific error codes {@link #URI_TOO_LONG} or {@link #INVALID_URI}
* are used. This error code corresponds to the OMA DM response status code
* 404 "Not Found".
*/
public static final int NODE_NOT_FOUND = 404;
/**
* The requested command is not allowed on the target node. This includes
* the following situations:
*
* - an interior node operation is requested for a leaf node, or vice
* versa (e.g. trying to retrieve the children of a leaf node)
* - an attempt is made to create a node where the parent is a leaf node
* - an attempt is made to rename or delete the root node of the tree
* - an attempt is made to rename or delete the root node of the session
* - a write operation (other than setting the ACL) is performed in a
* non-atomic write session on a node provided by a plugin that is read-only
* or does not support non-atomic writing
* - a node is copied to its descendant
* - the ACL of the root node is changed not to include Add rights for all
* principals
*
*
* This error code corresponds to the OMA DM response status code 405
* "Command not allowed".
*/
public static final int COMMAND_NOT_ALLOWED = 405;
/**
* The requested command failed because an optional feature required by the
* command is not supported. For example, opening an atomic session might
* return this error code if the DmtAdmin implementation does not support
* transactions. Similarly, accessing the optional node properties (Title,
* Timestamp, Version, Size) might not succeed if either the DmtAdmin
* implementation or the underlying plugin does not support the property.
*
* When getting or setting values for interior nodes (an optional
* optimization feature), a plugin can use this error code to indicate that
* the given interior node does not support values.
*
* This error code corresponds to the OMA DM response status code 406
* "Optional feature not supported".
*/
public static final int FEATURE_NOT_SUPPORTED = 406;
/**
* The requested operation failed because a specific limit was exceeded,
* e.g. if a requested resource exceeds a size limit.
*
* This error code corresponds to the OMA DM response status code 413
* "Request entity too large".
*
* @since 2.0
*/
public static final int LIMIT_EXCEEDED = 413;
/**
* The requested command failed because the target URI is too long for what
* the recipient is able or willing to process.
*
* This error code corresponds to the OMA DM response status code 414
* "URI too long".
*
* @see "OSGi Service Platform, Mobile Specification Release 4"
*/
public static final int URI_TOO_LONG = 414;
/**
* The requested node creation operation failed because the target already
* exists. This can occur if the node is created directly (with one of the
* {@code create...} methods), or indirectly (during a {@code copy}
* operation).
*
* This error code corresponds to the OMA DM response status code 418
* "Already exists".
*/
public static final int NODE_ALREADY_EXISTS = 418;
/**
* The requested command failed because the principal associated with the
* session does not have adequate access control permissions (ACL) on the
* target. This can only appear in case of remote sessions, i.e. if the
* session is associated with an authenticated principal.
*
* This error code corresponds to the OMA DM response status code 425
* "Permission denied".
*/
public static final int PERMISSION_DENIED = 425;
/**
* The recipient encountered an error which prevented it from fulfilling the
* request.
*
* This error code is only used in situations not covered by any of the
* other error codes that a method may use. Some methods specify more
* specific error situations for this code, but it can generally be used for
* any unexpected condition that causes the command to fail.
*
* This error code corresponds to the OMA DM response status code 500
* "Command Failed".
*/
public static final int COMMAND_FAILED = 500;
/**
* An error related to the recipient data store occurred while processing
* the request. This error code may be thrown by any of the methods
* accessing the tree, but whether it is really used depends on the
* implementation, and the data store it uses.
*
* This error code corresponds to the OMA DM response status code 510
* "Data store failure".
*/
public static final int DATA_STORE_FAILURE = 510;
/**
* The rollback command was not completed successfully. The tree might be in
* an inconsistent state after this error.
*
* This error code corresponds to the OMA DM response status code 516
* "Atomic roll back failed".
*/
public static final int ROLLBACK_FAILED = 516;
/**
* A device initiated remote operation failed. This is used when the
* protocol adapter fails to send an alert for any reason.
*
* Alert routing errors (that occur while looking for the proper protocol
* adapter to use) are indicated by {@link #ALERT_NOT_ROUTED}, this code is
* only for errors encountered while sending the routed alert. This error
* code does not correspond to any OMA DM response status code. It should be
* translated to the code 500 "Command Failed" when transferring
* over OMA DM.
*/
public static final int REMOTE_ERROR = 1;
/**
* Operation failed because of meta data restrictions. This covers any
* attempted deviation from the parameters defined by the {@code MetaNode}
* objects of the affected nodes, for example in the following situations:
*
* - creating, deleting or renaming a permanent node, or modifying its
* type
* - creating an interior node where the meta-node defines it as a leaf,
* or vice versa
* - any operation on a node which does not have the required access type
* (e.g. executing a node that lacks the {@code MetaNode.CMD_EXECUTE} access
* type)
* - any node creation or deletion that would violate the cardinality
* constraints
* - any leaf node value setting that would violate the allowed formats,
* values, mime types, etc.
* - any node creation that would violate the allowed node names
*
*
* This error code can also be used to indicate any other meta data
* violation, even if it cannot be described by the {@code MetaNode} class.
* For example, detecting a multi-node constraint violation while committing
* an atomic session should result in this error.
*
* This error code does not correspond to any OMA DM response status code.
* It should be translated to the code 405 "Command not allowed"
* when transferring over OMA DM.
*/
public static final int METADATA_MISMATCH = 2;
/**
* The requested command failed because the target URI or node name is
* {@code null} or syntactically invalid. This covers the following cases:
*
* - the URI or node name ends with the '\' or '/' character
* - the URI is an empty string (only invalid if the method does not
* accept relative URIs)
* - the URI contains the segment "{@code .}" at a position
* other than the beginning of the URI
* - the node name is "{@code ..}" or the URI contains such a
* segment
* - the node name contains an unescaped '/' character
*
*
* See the {@link Uri#encode(String)} method for support on escaping invalid
* characters in a URI.
*
* This code is only used if the URI or node name does not match any of the
* criteria for {@link #URI_TOO_LONG}. This error code does not correspond
* to any OMA DM response status code. It should be translated to the code
* 404 "Not Found" when transferring over OMA DM.
*/
public static final int INVALID_URI = 3;
/**
* An error occurred related to concurrent access of nodes. This can happen
* for example if a configuration node was deleted directly through the
* Configuration Admin service, while the node was manipulated via the tree.
*
* This error code does not correspond to any OMA DM response status code.
* It should be translated to the code 500 "Command Failed" when
* transferring over OMA DM.
*/
public static final int CONCURRENT_ACCESS = 4;
/**
* An alert can not be sent from the device to the given principal. This can
* happen if there is no Remote Alert Sender willing to forward the alert to
* the given principal, or if no principal was given and the DmtAdmin did
* not find an appropriate default destination.
*
* This error code does not correspond to any OMA DM response status code.
* It should be translated to the code 500 "Command Failed" when
* transferring over OMA DM.
*/
public static final int ALERT_NOT_ROUTED = 5;
/**
* A transaction-related error occurred in an atomic session. This error is
* caused by one of the following situations:
*
* - an updating method within an atomic session can not be executed
* because the underlying plugin is read-only or does not support atomic
* writing
* - a commit operation at the end of an atomic session failed because one
* of the underlying plugins failed to close
*
* The latter case may leave the tree in an inconsistent state due to the
* lack of a two-phase commit system, see {@link DmtSession#commit()} for
* details.
*
* This error code does not correspond to any OMA DM response status code.
* It should be translated to the code 500 "Command Failed" when
* transferring over OMA DM.
*/
public static final int TRANSACTION_ERROR = 6;
/**
* Creation of a session timed out because of another ongoing session. The
* length of time while the DmtAdmin waits for the blocking session(s) to
* finish is implementation dependent.
*
* This error code does not correspond to any OMA DM response status code.
* OMA has several status codes related to timeout, but these are meant to
* be used when a request times out, not if a session can not be
* established. This error code should be translated to the code 500
* "Command Failed" when transferring over OMA DM.
*/
public static final int SESSION_CREATION_TIMEOUT = 7;
// ----- Content fields -----//
/**
* The URI of the node on which the failed DMT operation was issued, or
* {@code null} if the operation was not associated with a node.
*/
private final String uri;
/**
* The error code of the failure, one of the constants defined in this
* class.
*/
private final int code;
/**
* The message associated with the exception, or {@code null} if there is no
* error message.
*/
private final String message;
/**
* The list of originating exceptions, or empty list or {@code null} if
* there are no originating exceptions.
*/
private final Throwable[] causes;
/**
* Determines whether the exception is fatal or not. This is basically a
* two-state severity indicator, with the 'fatal' severity being the more
* serious one.
*/
private final boolean fatal;
// ----- Constructors -----//
/**
* Create an instance of the exception. The {@code uri} and {@code message}
* parameters are optional. No originating exception is specified.
*
* @param uri the node on which the failed DMT operation was issued, or
* {@code null} if the operation is not associated with a node
* @param code the error code of the failure
* @param message the message associated with the exception, or {@code null}
* if there is no error message
*/
public DmtException(String uri, int code, String message) {
this(uri, code, message, new Throwable[0], false);
}
/**
* Create an instance of the exception, specifying the cause exception. The
* {@code uri}, {@code message} and {@code cause} parameters are optional.
*
* @param uri the node on which the failed DMT operation was issued, or
* {@code null} if the operation is not associated with a node
* @param code the error code of the failure
* @param message the message associated with the exception, or {@code null}
* if there is no error message
* @param cause the originating exception, or {@code null} if there is no
* originating exception
*/
public DmtException(String uri, int code, String message, Throwable cause) {
this(uri, code, message, (cause == null) ? new Throwable[0] : new Throwable[] {cause}, false);
}
/**
* Create an instance of the exception, specifying the list of cause
* exceptions and whether the exception is a fatal one. This constructor is
* meant to be used by plugins wishing to indicate that a serious error
* occurred which should invalidate the ongoing atomic session. The
* {@code uri}, {@code message} and {@code causes} parameters are optional.
*
* If a fatal exception is thrown, no further business methods will be
* called on the originator plugin. In case of atomic sessions, all other
* open plugins will be rolled back automatically, except if the fatal
* exception was thrown during commit.
*
* @param uri the node on which the failed DMT operation was issued, or
* {@code null} if the operation is not associated with a node
* @param code the error code of the failure
* @param message the message associated with the exception, or {@code null}
* if there is no error message
* @param causes the list of originating exceptions, or empty list or
* {@code null} if there are no originating exceptions
* @param fatal whether the exception is fatal
*/
public DmtException(String uri, int code, String message, Vector causes, boolean fatal) {
this(uri, code, message, (causes == null) ? new Throwable[0] : (Throwable[]) causes.toArray(new Throwable[causes.size()]), fatal);
}
private DmtException(String uri, int code, String message, Throwable[] causes, boolean fatal) {
super((Throwable) null);
this.uri = uri;
this.code = code;
this.message = message;
this.causes = causes;
this.fatal = fatal;
}
/**
* Create an instance of the exception, specifying the target node as an
* array of path segments. This method behaves in exactly the same way as if
* the path was given as a URI string.
*
* @param path the path of the node on which the failed DMT operation was
* issued, or {@code null} if the operation is not associated with a
* node
* @param code the error code of the failure
* @param message the message associated with the exception, or {@code null}
* if there is no error message
* @see #DmtException(String, int, String)
*/
public DmtException(String[] path, int code, String message) {
this(pathToUri(path), code, message);
}
/**
* Create an instance of the exception, specifying the target node as an
* array of path segments, and specifying the cause exception. This method
* behaves in exactly the same way as if the path was given as a URI string.
*
* @param path the path of the node on which the failed DMT operation was
* issued, or {@code null} if the operation is not associated with a
* node
* @param code the error code of the failure
* @param message the message associated with the exception, or {@code null}
* if there is no error message
* @param cause the originating exception, or {@code null} if there is no
* originating exception
* @see #DmtException(String, int, String, Throwable)
*/
public DmtException(String[] path, int code, String message, Throwable cause) {
this(pathToUri(path), code, message, cause);
}
/**
* Create an instance of the exception, specifying the target node as an
* array of path segments, the list of cause exceptions, and whether the
* exception is a fatal one. This method behaves in exactly the same way as
* if the path was given as a URI string.
*
* @param path the path of the node on which the failed DMT operation was
* issued, or {@code null} if the operation is not associated with a
* node
* @param code the error code of the failure
* @param message the message associated with the exception, or {@code null}
* if there is no error message
* @param causes the list of originating exceptions, or empty list or
* {@code null} if there are no originating exceptions
* @param fatal whether the exception is fatal
* @see #DmtException(String, int, String, Vector, boolean)
*/
public DmtException(String[] path, int code, String message, Vector causes, boolean fatal) {
this(pathToUri(path), code, message, causes, fatal);
}
// ----- Public methods -----//
/**
* Get the node on which the failed DMT operation was issued. Some
* operations like {@code DmtSession.close()} don't require an URI, in this
* case this method returns {@code null}.
*
* @return the URI of the node, or {@code null}
*/
public String getURI() {
return uri;
}
/**
* Get the error code associated with this exception. Most of the error
* codes within this exception correspond to OMA DM error codes.
*
* @return the error code
*/
public int getCode() {
return code;
}
/**
* Get the message associated with this exception. The returned string also
* contains the associated URI (if any) and the exception code. The
* resulting message has the following format (parts in square brackets are
* only included if the field inside them is not {@code null}):
*
*
* <exception_code>[: '<uri>'][: <error_message>]
*
*
* @return the error message in the format described above
*/
public String getMessage() {
StringBuffer sb = new StringBuffer(getCodeText(code));
if (uri != null)
sb.append(": '").append(uri).append('\'');
if (message != null)
sb.append(": ").append(message);
return sb.toString();
}
/**
* Get the cause of this exception. Returns non-{@code null}, if this
* exception is caused by one or more other exceptions (like a
* {@code NullPointerException} in a DmtPlugin). If there are more than one
* cause exceptions, the first one is returned.
*
* @return the cause of this exception, or {@code null} if no cause was
* given
*/
public Throwable getCause() {
return causes.length == 0 ? null : causes[0];
}
/**
* Get all causes of this exception. Returns the causing exceptions in an
* array. If no cause was specified, an empty array is returned.
*
* @return the list of causes of this exception
*/
public Throwable[] getCauses() {
return (Throwable[]) causes.clone();
}
/**
* Check whether this exception is marked as fatal in the session. Fatal
* exceptions trigger an automatic rollback of atomic sessions.
*
* @return whether the exception is marked as fatal
*/
public boolean isFatal() {
return fatal;
}
/**
* Prints the exception and its backtrace to the specified print stream. Any
* causes that were specified for this exception are also printed, together
* with their backtraces.
*
* @param s {@code PrintStream} to use for output
*/
public void printStackTrace(PrintStream s) {
super.printStackTrace(s);
for (int i = 0; i < causes.length; i++) {
s.print("Caused by" + (i > 0 ? " (" + (i + 1) + ")" : "") + ": ");
causes[i].printStackTrace(s);
}
}
// ----- Utility methods -----//
/**
* Converts the given path, given as an array of path segments, to a single
* URI string.
*
* @param path the path to convert
* @return the URI string representing the same node as the given path
*/
static String pathToUri(String[] path) {
if (path == null)
return null;
return Uri.toUri(path);
}
/**
* Returns the name of the given error code.
*
* @param code the error code
* @return a string containing the error code name
*/
private static String getCodeText(int code) {
// todo sync codes
switch (code) {
case NODE_NOT_FOUND :
return "NODE_NOT_FOUND";
case COMMAND_NOT_ALLOWED :
return "COMMAND_NOT_ALLOWED";
case FEATURE_NOT_SUPPORTED :
return "FEATURE_NOT_SUPPORTED";
case URI_TOO_LONG :
return "URI_TOO_LONG";
case NODE_ALREADY_EXISTS :
return "NODE_ALREADY_EXISTS";
case PERMISSION_DENIED :
return "PERMISSION_DENIED";
case COMMAND_FAILED :
return "COMMAND_FAILED";
case DATA_STORE_FAILURE :
return "DATA_STORE_FAILURE";
case ROLLBACK_FAILED :
return "ROLLBACK_FAILED";
case REMOTE_ERROR :
return "REMOTE_ERROR";
case METADATA_MISMATCH :
return "METADATA_MISMATCH";
case INVALID_URI :
return "INVALID_URI";
case CONCURRENT_ACCESS :
return "CONCURRENT_ACCESS";
case ALERT_NOT_ROUTED :
return "ALERT_NOT_ROUTED";
case TRANSACTION_ERROR :
return "TRANSACTION_ERROR";
case SESSION_CREATION_TIMEOUT :
return "SESSION_CREATION_TIMEOUT";
default :
return "";
}
}
}