import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
-import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.yangtools.yang.common.ErrorTag;
import org.opendaylight.yangtools.yang.common.ErrorType;
import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangNetconfError;
-import org.opendaylight.yangtools.yang.data.api.YangNetconfErrorAware;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
/**
/**
* Constructs an instance with an error message, error type, error tag and exception cause.
*
- * @param message
- * A string which provides a plain text string describing the error.
- * @param errorType
- * The enumerated type indicating the layer where the error occurred.
- * @param errorTag
- * The enumerated tag representing a more specific error cause.
- * @param cause
- * The underlying exception cause.
+ * @param message A string which provides a plain text string describing the error.
+ * @param errorType The enumerated type indicating the layer where the error occurred.
+ * @param errorTag The enumerated tag representing a more specific error cause.
+ * @param cause The underlying exception cause.
*/
public RestconfDocumentedException(final String message, final ErrorType errorType, final ErrorTag errorTag,
final Throwable cause) {
/**
* Constructs an instance with an error message, error type, and error tag.
*
- * @param message
- * A string which provides a plain text string describing the error.
- * @param errorType
- * The enumerated type indicating the layer where the error occurred.
- * @param errorTag
- * The enumerated tag representing a more specific error cause.
+ * @param message A string which provides a plain text string describing the error.
+ * @param errorType The enumerated type indicating the layer where the error occurred.
+ * @param errorTag The enumerated tag representing a more specific error cause.
*/
public RestconfDocumentedException(final String message, final ErrorType errorType, final ErrorTag errorTag) {
- this(null, new RestconfError(errorType, errorTag, message));
+ this(new RestconfError(errorType, errorTag, message));
}
/**
* Constructs an instance with an error message, error type, error tag and error path.
*
- * @param message
- * A string which provides a plain text string describing the error.
- * @param errorType
- * The enumerated type indicating the layer where the error occurred.
- * @param errorTag
- * The enumerated tag representing a more specific error cause.
- * @param errorPath
- * The instance identifier representing error path
+ * @param message A string which provides a plain text string describing the error.
+ * @param errorType The enumerated type indicating the layer where the error occurred.
+ * @param errorTag The enumerated tag representing a more specific error cause.
+ * @param errorPath The instance identifier representing error path
*/
public RestconfDocumentedException(final String message, final ErrorType errorType, final ErrorTag errorTag,
final YangInstanceIdentifier errorPath) {
- this(null, new RestconfError(errorType, errorTag, message, errorPath));
+ this(new RestconfError(errorType, errorTag, message, errorPath));
}
/**
* Constructs an instance with an error message and exception cause.
* The underlying exception is included in the error-info.
*
- * @param message
- * A string which provides a plain text string describing the error.
- * @param cause
- * The underlying exception cause.
+ * @param message A string which provides a plain text string describing the error.
+ * @param cause The underlying exception cause.
*/
public RestconfDocumentedException(final String message, final Throwable cause) {
this(cause, new RestconfError(ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, message, null,
this(message, cause, convertToRestconfErrors(rpcErrors));
}
+ private static List<RestconfError> convertToRestconfErrors(final Collection<? extends RpcError> rpcErrors) {
+ if (rpcErrors == null || rpcErrors.isEmpty()) {
+ return List.of();
+ }
+
+ final var errorList = new ArrayList<RestconfError>();
+ for (var rpcError : rpcErrors) {
+ errorList.add(new RestconfError(rpcError));
+ }
+ return errorList;
+ }
+
public RestconfDocumentedException(final Throwable cause, final RestconfError error) {
super(cause);
errors = List.of(error);
modelContext = null;
}
- /**
- * Throw an instance of this exception if an expression evaluates to true. If the expression evaluates to false,
- * this method does nothing.
- *
- * @param expression Expression to be evaluated
- * @param errorType The enumerated type indicating the layer where the error occurred.
- * @param errorTag The enumerated tag representing a more specific error cause.
- * @param format Format string, according to {@link String#format(String, Object...)}.
- * @param args Format string arguments, according to {@link String#format(String, Object...)}
- * @throws RestconfDocumentedException if the expression evaluates to true.
- */
- public static void throwIf(final boolean expression, final ErrorType errorType, final ErrorTag errorTag,
- final @NonNull String format, final Object... args) {
- if (expression) {
- throw new RestconfDocumentedException(String.format(format, args), errorType, errorTag);
- }
- }
-
- /**
- * Throw an instance of this exception if an expression evaluates to true. If the expression evaluates to false,
- * this method does nothing.
- *
- * @param expression Expression to be evaluated
- * @param message error message
- * @param errorType The enumerated type indicating the layer where the error occurred.
- * @param errorTag The enumerated tag representing a more specific error cause.
- * @throws RestconfDocumentedException if the expression evaluates to true.
- */
- public static void throwIf(final boolean expression, final @NonNull String message,
- final ErrorType errorType, final ErrorTag errorTag) {
- if (expression) {
- throw new RestconfDocumentedException(message, errorType, errorTag);
- }
- }
-
- /**
- * Throw an instance of this exception if an object is null. If the object is non-null, it will
- * be returned as the result of this method.
- *
- * @param obj Object reference to be checked
- * @param errorType The enumerated type indicating the layer where the error occurred.
- * @param errorTag The enumerated tag representing a more specific error cause.
- * @param format Format string, according to {@link String#format(String, Object...)}.
- * @param args Format string arguments, according to {@link String#format(String, Object...)}
- * @throws RestconfDocumentedException if the expression evaluates to true.
- */
- public static <T> @NonNull T throwIfNull(final @Nullable T obj, final ErrorType errorType, final ErrorTag errorTag,
- final @NonNull String format, final Object... args) {
- if (obj == null) {
- throw new RestconfDocumentedException(String.format(format, args), errorType, errorTag);
- }
- return obj;
- }
-
- /**
- * Throw an instance of this exception if the specified exception has a {@link YangNetconfError} attachment.
- *
- * @param cause Proposed cause of a RestconfDocumented exception
- */
- public static void throwIfYangError(final Throwable cause) {
- if (cause instanceof YangNetconfErrorAware infoAware) {
- throw new RestconfDocumentedException(cause, infoAware.getNetconfErrors().stream()
- .map(error -> new RestconfError(error.type(), error.tag(), error.message(), error.appTag(),
- // FIXME: pass down error info
- null, error.path()))
- .toList());
- }
- }
-
- private static List<RestconfError> convertToRestconfErrors(final Collection<? extends RpcError> rpcErrors) {
- if (rpcErrors == null || rpcErrors.isEmpty()) {
- return List.of();
- }
-
- final var errorList = new ArrayList<RestconfError>();
- for (var rpcError : rpcErrors) {
- errorList.add(new RestconfError(rpcError));
- }
- return errorList;
- }
-
@Override
public String getMessage() {
return "errors: " + errors;
import java.lang.invoke.VarHandle;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.common.errors.RestconfError;
+import org.opendaylight.yangtools.yang.data.api.YangNetconfError;
+import org.opendaylight.yangtools.yang.data.api.YangNetconfErrorAware;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
return is;
}
+
+ /**
+ * Throw a {@link RestconfDocumentedException} if the specified exception has a {@link YangNetconfError} attachment.
+ *
+ * @param cause Proposed cause of a RestconfDocumentedException
+ */
+ static void throwIfYangError(final Exception cause) {
+ if (cause instanceof YangNetconfErrorAware infoAware) {
+ throw new RestconfDocumentedException(cause, infoAware.getNetconfErrors().stream()
+ .map(error -> new RestconfError(error.type(), error.tag(), error.message(), error.appTag(),
+ // FIXME: pass down error info
+ null, error.path()))
+ .toList());
+ }
+ }
+
private @Nullable InputStream getStream() {
return (InputStream) INPUT_STREAM.getAndSet(this, null);
}
+ "Are you creating multiple resources/subresources in POST request?", e);
}
- RestconfDocumentedException.throwIfYangError(e);
+ throwIfYangError(e);
throw new RestconfDocumentedException("Error parsing input: " + e.getMessage(), ErrorType.PROTOCOL,
ErrorTag.MALFORMED_MESSAGE, e);
}
} catch (RestconfDocumentedException e) {
throw e;
} catch (RuntimeException e) {
- RestconfDocumentedException.throwIfYangError(e);
+ throwIfYangError(e);
throw e;
}
final List<QName> keyDefinitions) {
final var mutableCopyUriKeyValues = new HashMap<>(uriKeyValues);
for (var keyDefinition : keyDefinitions) {
- final var uriKeyValue = RestconfDocumentedException.throwIfNull(
- mutableCopyUriKeyValues.remove(keyDefinition), ErrorType.PROTOCOL, ErrorTag.DATA_MISSING,
- "Missing key %s in URI.", keyDefinition);
+ final var uriKeyValue = mutableCopyUriKeyValues.remove(keyDefinition);
+ if (uriKeyValue == null) {
+ throw new RestconfDocumentedException("Missing key " + keyDefinition + " in URI.",
+ ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
+ }
final var dataKeyValue = payload.name().getValue(keyDefinition);
if (!uriKeyValue.equals(dataKeyValue)) {
throw e;
} catch (final Exception e) {
LOG.debug("Error parsing xml input", e);
- RestconfDocumentedException.throwIfYangError(e);
+ throwIfYangError(e);
throw new RestconfDocumentedException("Error parsing input: " + e.getMessage(), ErrorType.PROTOCOL,
ErrorTag.MALFORMED_MESSAGE, e);
}
XmlParserStream.create(writer, stack.toInference()).parse(UntrustedXML.createXMLStreamReader(inputStream));
} catch (XMLStreamException e) {
LOG.debug("Error parsing XML input", e);
- RestconfDocumentedException.throwIfYangError(e);
+ throwIfYangError(e);
throw new RestconfDocumentedException("Error parsing input: " + e.getMessage(), ErrorType.PROTOCOL,
ErrorTag.MALFORMED_MESSAGE, e);
}
xmlParser.traverse(new DOMSource(docRoot));
} catch (SAXException | XMLStreamException e) {
LOG.debug("Error parsing XML input", e);
- RestconfDocumentedException.throwIfYangError(e);
+ throwIfYangError(e);
throw new RestconfDocumentedException("Error parsing input: " + e.getMessage(), ErrorType.PROTOCOL,
ErrorTag.MALFORMED_MESSAGE, e);
}
*/
@VisibleForTesting
static Revision validateAndGetRevision(final Iterator<String> revisionDate) {
- RestconfDocumentedException.throwIf(!revisionDate.hasNext(), "Revision date must be supplied.",
- ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+ if (!revisionDate.hasNext()) {
+ throw new RestconfDocumentedException("Revision date must be supplied.",
+ ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+ }
try {
return Revision.of(revisionDate.next());
} catch (final DateTimeParseException e) {
*/
@VisibleForTesting
static String validateAndGetModulName(final Iterator<String> moduleName) {
- RestconfDocumentedException.throwIf(!moduleName.hasNext(), "Module name must be supplied.", ErrorType.PROTOCOL,
- ErrorTag.INVALID_VALUE);
- final String name = moduleName.next();
-
- RestconfDocumentedException.throwIf(
- name.isEmpty() || !YangNames.IDENTIFIER_START.matches(name.charAt(0)),
- "Identifier must start with character from set 'a-zA-Z_", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
- RestconfDocumentedException.throwIf(name.toUpperCase(Locale.ROOT).startsWith("XML"),
- "Identifier must NOT start with XML ignore case.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
- RestconfDocumentedException.throwIf(
- YangNames.NOT_IDENTIFIER_PART.matchesAnyOf(name.substring(1)),
- "Supplied name has not expected identifier format.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+ if (!moduleName.hasNext()) {
+ throw new RestconfDocumentedException("Module name must be supplied.",
+ ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+ }
+ final var name = moduleName.next();
+ if (name.isEmpty() || !YangNames.IDENTIFIER_START.matches(name.charAt(0))) {
+ throw new RestconfDocumentedException("Identifier must start with character from set 'a-zA-Z_",
+ ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+ }
+ if (name.toUpperCase(Locale.ROOT).startsWith("XML")) {
+ throw new RestconfDocumentedException("Identifier must NOT start with XML ignore case.",
+ ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+ }
+ if (YangNames.NOT_IDENTIFIER_PART.matchesAnyOf(name.substring(1))) {
+ throw new RestconfDocumentedException("Supplied name has not expected identifier format.",
+ ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+ }
return name;
}
//
// We therefore peel that first iteration here and not worry about those details in further iterations
var step = it.next();
- final var firstModule = RestconfDocumentedException.throwIfNull(step.module(),
- ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE,
- "First member must use namespace-qualified form, '%s' does not", step.identifier());
+ final var firstModule = step.module();
+ if (firstModule == null) {
+ throw new RestconfDocumentedException(
+ "First member must use namespace-qualified form, '" + step.identifier() + "' does not",
+ ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
+ }
+
var namespace = resolveNamespace(firstModule);
var qname = step.identifier().bindTo(namespace);
}
// Resolve the child step with respect to data schema tree
- final var found = RestconfDocumentedException.throwIfNull(
- parentNode instanceof DataSchemaContext.Composite composite ? composite.enterChild(stack, qname) : null,
- ErrorType.PROTOCOL, ErrorTag.DATA_MISSING, "Schema for '%s' not found", qname);
+ final var found = parentNode instanceof DataSchemaContext.Composite composite
+ ? composite.enterChild(stack, qname) : null;
+ if (found == null) {
+ throw new RestconfDocumentedException("Schema for '" + qname + "' not found",
+ ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
+ }
// Now add all mixins encountered to the path
var childNode = found;
? prepareNodeWithPredicates(stack, qname, listSchema, values)
: prepareNodeWithValue(stack, qname, schema, values);
} else {
- RestconfDocumentedException.throwIf(childNode.dataSchemaNode() instanceof ListSchemaNode listChild
- && !listChild.getKeyDefinition().isEmpty(),
- ErrorType.PROTOCOL, ErrorTag.MISSING_ATTRIBUTE,
- "Entry '%s' requires key or value predicate to be present.", qname);
+ if (childNode.dataSchemaNode() instanceof ListSchemaNode list && !list.getKeyDefinition().isEmpty()) {
+ throw new RestconfDocumentedException(
+ "Entry '" + qname + "' requires key or value predicate to be present.",
+ ErrorType.PROTOCOL, ErrorTag.MISSING_ATTRIBUTE);
+ }
pathArg = childNode.getPathStep();
}
}
private @NonNull QNameModule resolveNamespace(final String moduleName) {
- final var modules = schemaContext.findModules(moduleName);
- RestconfDocumentedException.throwIf(modules.isEmpty(), ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT,
- "Failed to lookup for module with name '%s'.", moduleName);
- return modules.iterator().next().getQNameModule();
+ final var it = schemaContext.findModuleStatements(moduleName).iterator();
+ if (it.hasNext()) {
+ return it.next().localQNameModule();
+ }
+ throw new RestconfDocumentedException("Failed to lookup for module with name '" + moduleName + "'.",
+ ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT);
}
}
final var childContext = currentContext instanceof DataSchemaContext.Composite composite
? composite.childByArg(arg) : null;
+ if (childContext == null) {
+ throw new RestconfDocumentedException(
+ "Invalid input '%s': schema for argument '%s' (after '%s') not found".formatted(data, arg, path),
+ ErrorType.APPLICATION, ErrorTag.UNKNOWN_ELEMENT);
+ }
- RestconfDocumentedException.throwIfNull(childContext, ErrorType.APPLICATION,
- ErrorTag.UNKNOWN_ELEMENT, "Invalid input '%s': schema for argument '%s' (after '%s') not found",
- data, arg, path);
variables.setCurrent(childContext);
if (childContext instanceof PathMixin) {
continue;