Remove RestconfDocumentedException.throwIf() 38/108538/6
authorRobert Varga <robert.varga@pantheon.tech>
Fri, 20 Oct 2023 13:07:46 +0000 (15:07 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Fri, 20 Oct 2023 15:12:11 +0000 (17:12 +0200)
These convenience methods are not overly convenient, as they are used
only in a few places. Replace them with explicit throws in our quest to
simplify RestconfDocumentedException.

JIRA: NETCONF-1188
Change-Id: I6fe188a134887e4e387800e2abecbaf4a10da502
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/errors/RestconfDocumentedException.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/databind/AbstractBody.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/databind/JsonChildBody.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/databind/ResourceBody.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/databind/XmlChildBody.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/databind/XmlOperationInputBody.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/databind/XmlResourceBody.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/ParserIdentifier.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/YangInstanceIdentifierDeserializer.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/YangInstanceIdentifierSerializer.java

index cc5f15b2bb4892cd5738cda8599b7ac9c0c687f9..6abb2fd9950e82c90f3cac4635a8c5725968a949 100644 (file)
@@ -12,14 +12,11 @@ import static java.util.Objects.requireNonNull;
 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;
 
 /**
@@ -52,14 +49,10 @@ public class RestconfDocumentedException extends RuntimeException {
     /**
      * 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) {
@@ -69,42 +62,33 @@ public class RestconfDocumentedException extends RuntimeException {
     /**
      * 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,
@@ -142,6 +126,18 @@ public class RestconfDocumentedException extends RuntimeException {
         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);
@@ -164,87 +160,6 @@ public class RestconfDocumentedException extends RuntimeException {
         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;
index cb9d0d06034a65d460992b958ce6b13c8f121cc7..5c73ccea178d706f54d94b40df68b2a1889f745e 100644 (file)
@@ -15,6 +15,10 @@ import java.lang.invoke.MethodHandles;
 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;
 
@@ -62,6 +66,22 @@ public abstract sealed class AbstractBody implements AutoCloseable
         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);
     }
index 39a37ed1a290eb9aaaa68e31ac0317f60713dee1..95a608ff0c83da2b20845aae779deb1eee8dc5d6 100644 (file)
@@ -56,7 +56,7 @@ public final class JsonChildBody extends ChildBody {
                         + "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);
         }
index f37342e96e4762f67aeeb48868ca53dd9de7559c..a455511fee2afb9f4618fde759abe276eb34d725 100644 (file)
@@ -69,7 +69,7 @@ public abstract sealed class ResourceBody extends AbstractBody permits JsonResou
         } catch (RestconfDocumentedException e) {
             throw e;
         } catch (RuntimeException e) {
-            RestconfDocumentedException.throwIfYangError(e);
+            throwIfYangError(e);
             throw e;
         }
 
@@ -128,9 +128,11 @@ public abstract sealed class ResourceBody extends AbstractBody permits JsonResou
             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)) {
index c19b225e884ede5748349aefa5b8210a2070f580..ca7f84a40db287857e5576f59a4f2991e37cebaf 100644 (file)
@@ -56,7 +56,7 @@ public final class XmlChildBody extends ChildBody {
             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);
         }
index 798a299fc41829e191ea7a3a4405b6f909080eae..8d99dadeac0f357908a584e150a49cd8e24b1ee5 100644 (file)
@@ -38,7 +38,7 @@ public final class XmlOperationInputBody extends OperationInputBody {
             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);
         }
index 9479fa345f05556c3dd516426e66b82c4d1e4c67..8e4762f3c0e58f50c9b9d574dcffe28069321e9b 100644 (file)
@@ -52,7 +52,7 @@ public final class XmlResourceBody extends ResourceBody {
             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);
         }
index d2e398cd2d6a3983c5f836c9a9f144d00e5c07de..c0982d51d43d86c51608ee9e5fd6e5b60a616989 100644 (file)
@@ -227,8 +227,10 @@ public final class ParserIdentifier {
      */
     @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) {
@@ -244,19 +246,24 @@ public final class ParserIdentifier {
      */
     @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;
     }
 
index 24834be811a97fdbeee767e8ae0d4b70f1392ec4..257d456c28d1fe4e4fe1cdab2e99cc369a960d59 100644 (file)
@@ -132,9 +132,13 @@ public final class YangInstanceIdentifierDeserializer {
         //
         // 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);
 
@@ -184,9 +188,12 @@ public final class YangInstanceIdentifierDeserializer {
             }
 
             // 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;
@@ -204,10 +211,11 @@ public final class YangInstanceIdentifierDeserializer {
                     ? 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();
             }
 
@@ -316,9 +324,11 @@ public final class YangInstanceIdentifierDeserializer {
     }
 
     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);
     }
 }
index b1c690048eaf159d9bc4f3b0aeb9b4c4db86b872..fb9d2a52c8c92a6f48df0293cfe82c3a6e194fd2 100644 (file)
@@ -83,10 +83,12 @@ public final class YangInstanceIdentifierSerializer {
 
             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;