You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@ignite.apache.org by "ibessonov (via GitHub)" <gi...@apache.org> on 2023/06/21 09:46:10 UTC

[GitHub] [ignite-3] ibessonov commented on a diff in pull request #2225: IGNITE-19539 Added exception mapper utility

ibessonov commented on code in PR #2225:
URL: https://github.com/apache/ignite-3/pull/2225#discussion_r1236693712


##########
modules/api/src/main/java/org/apache/ignite/lang/IgniteExceptionMapperFuture.java:
##########
@@ -0,0 +1,436 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.ignite.lang;
+
+import static org.apache.ignite.internal.util.ExceptionUtils.unwrapCause;
+import static org.apache.ignite.lang.IgniteExceptionMapperUtil.mapToPublicException;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+/**
+ * This class represents a {@link CompletableFuture}, the main purpose of this future is to automatically map
+ * internal exceptions to public ones on completion.
+ *
+ * @param <T> The result type.
+ */
+public class IgniteExceptionMapperFuture<T> extends CompletableFuture<T> {
+    /** Internal future, whose result will be used to map to a public exception if this delegate completed exceptionally. */
+    private final CompletableFuture<T> delegate;
+
+    /**
+     * Creates a new instance {@link IgniteExceptionMapperFuture} with the given {@code delegate}
+     * as an underlying future that serves all requests.
+     *
+     * @param delegate Future to be wrapped.
+     * @param <U> The result Type.
+     * @return New instance {@link IgniteExceptionMapperFuture}.
+     */
+    public static <U> IgniteExceptionMapperFuture<U> of(CompletableFuture<U> delegate) {

Review Comment:
   Why not returning `delegate.handle(...)`? This entire subclass does nothing useful as far as I see.
   If it does, it must be documented. Please consider removing the inheritance and making this class utilitarian.



##########
modules/api/src/main/java/org/apache/ignite/lang/IgniteExceptionMapperUtil.java:
##########
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.ignite.lang;
+
+import static java.util.Collections.unmodifiableMap;
+import static org.apache.ignite.lang.ErrorGroups.Common.INTERNAL_ERR;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.ServiceLoader;
+
+/**
+ * This utility class provides an ability to map Ignite internal exceptions to Ignite public ones.
+ */
+public class IgniteExceptionMapperUtil {
+    /** All exception mappers to be used to map internal exceptions to public ones. */
+    private static final Map<Class<? extends Exception>, IgniteExceptionMapper<? extends Exception, ? extends Exception>>
+            EXCEPTION_CONVERTERS;
+
+    static {
+        Map<Class<? extends Exception>, IgniteExceptionMapper<? extends Exception, ? extends Exception>> mappers = new HashMap<>();
+
+        ServiceLoader
+                .load(IgniteExceptionMappersProvider.class)
+                .forEach(provider -> provider.mappers().forEach(m -> registerMapping(m, mappers)));
+
+        EXCEPTION_CONVERTERS = unmodifiableMap(mappers);
+    }
+
+    /**
+     * Add a new mapping to already registered ones.
+     *
+     * @param mapper Exception mapper from internal exception to a public one.
+     * @param registeredMappings Already registered mappings.
+     * @throws IgniteException If a mapper for the given {@code clazz} already registered,
+     *      or {@code clazz} represents Java standard exception like {@link NullPointerException}, {@link IllegalArgumentException}.
+     */
+    static void registerMapping(
+            IgniteExceptionMapper<? extends Exception, ? extends Exception> mapper,
+            Map<Class<? extends Exception>, IgniteExceptionMapper<? extends Exception, ? extends Exception>> registeredMappings) {
+        if (registeredMappings.containsKey(mapper.mappingFrom())) {
+            throw new IgniteException(
+                    INTERNAL_ERR,
+                    "Failed to register exception mapper, duplicate found [class=" + mapper.mappingFrom().getCanonicalName() + ']');
+        }
+
+        if (isJavaStandardException(mapper.mappingFrom())) {
+            throw new IgniteException(
+                    INTERNAL_ERR,
+                    "Failed to register exception mapper. "
+                            + "Mapping for this class is prohibited [class=" + mapper.mappingFrom().getCanonicalName() + ']');
+        }
+
+        registeredMappings.put(mapper.mappingFrom(), mapper);
+    }
+
+    /**
+     * This method provides a mapping from internal exception to Ignite public ones.
+     *
+     * <p>The rules of mapping are the following:</p>
+     * <ul>
+     *     <li>any instance of {@link Error} is returned as is, except {@link AssertionError}
+     *     that always be mapped to {@link IgniteException} with the {@link ErrorGroups.Common#INTERNAL_ERR} error code.</li>
+     *     <li>any instance of Java standard exception like {@link NullPointerException} is returned as is.</li>
+     *     <li>any instance of {@link IgniteException} or {@link IgniteCheckedException} is returned as is.</li>
+     *     <li>if there are no any mappers that can do a mapping from the given error to a public exception,
+     *     then {@link IgniteException} with the {@link ErrorGroups.Common#INTERNAL_ERR} error code is returned.</li>
+     * </ul>
+     *
+     * @param origin Exception to be mapped.
+     * @return Public exception.
+     */
+    public static Throwable mapToPublicException(Throwable origin) {
+        if (origin instanceof Error) {
+            if (origin instanceof AssertionError) {
+                return new IgniteException(INTERNAL_ERR, origin);
+            }
+
+            return origin;
+        }
+
+        if (origin instanceof IgniteException || origin instanceof IgniteCheckedException) {
+            return origin;
+        }
+
+        if (isJavaStandardException(origin)) {
+            return origin;
+        }
+
+        IgniteExceptionMapper<? extends Exception, ? extends Exception> m = EXCEPTION_CONVERTERS.get(origin.getClass());
+        if (m != null) {
+            Exception mapped = map(m, origin);
+
+            assert mapped instanceof IgniteException || mapped instanceof IgniteCheckedException :
+                    "Unexpected mapping of internal exception to a public one [origin=" + origin + ", mapped=" + mapped + ']';
+
+            return mapped;
+        }
+
+        // There are no exception mappings for the given exception. This case should be considered as internal error.
+        return new IgniteException(INTERNAL_ERR, origin);

Review Comment:
   Shouldn't we have a specific exception class for each error code?



##########
modules/api/src/main/java/org/apache/ignite/lang/IgniteExceptionMapperUtil.java:
##########
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.ignite.lang;
+
+import static java.util.Collections.unmodifiableMap;
+import static org.apache.ignite.lang.ErrorGroups.Common.INTERNAL_ERR;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.ServiceLoader;
+
+/**
+ * This utility class provides an ability to map Ignite internal exceptions to Ignite public ones.
+ */
+public class IgniteExceptionMapperUtil {
+    /** All exception mappers to be used to map internal exceptions to public ones. */
+    private static final Map<Class<? extends Exception>, IgniteExceptionMapper<? extends Exception, ? extends Exception>>
+            EXCEPTION_CONVERTERS;
+
+    static {
+        Map<Class<? extends Exception>, IgniteExceptionMapper<? extends Exception, ? extends Exception>> mappers = new HashMap<>();
+
+        ServiceLoader
+                .load(IgniteExceptionMappersProvider.class)
+                .forEach(provider -> provider.mappers().forEach(m -> registerMapping(m, mappers)));
+
+        EXCEPTION_CONVERTERS = unmodifiableMap(mappers);
+    }
+
+    /**
+     * Add a new mapping to already registered ones.
+     *
+     * @param mapper Exception mapper from internal exception to a public one.
+     * @param registeredMappings Already registered mappings.
+     * @throws IgniteException If a mapper for the given {@code clazz} already registered,
+     *      or {@code clazz} represents Java standard exception like {@link NullPointerException}, {@link IllegalArgumentException}.
+     */
+    static void registerMapping(
+            IgniteExceptionMapper<? extends Exception, ? extends Exception> mapper,

Review Comment:
   Given that `Exception` is already an upped bound for type parameters, this could be safely replaced with `IgniteExceptionMapper<?, ?>`.



##########
modules/api/src/main/java/org/apache/ignite/lang/IgniteExceptionMappersProvider.java:
##########
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.ignite.lang;
+
+import java.util.Collection;
+
+/**
+ * This interface provides the ability to register specific mappers from Ignite internal exceptions a public ones.
+ *
+ * <p>
+ *     Each module can provide such a mapping for its own internal exceptions.
+ *     Designed for integration with {@link java.util.ServiceLoader} mechanism, so IgniteExceptionMapper instances
+ *     provided by a library are to be defined as services either
+ *     in {@code META-INF/services/org.apache.ignite.lang.IgniteExceptionMapper}, or in a {@code module-info.java}.
+ * </p>
+ *
+ * <p>There are the following constraints that should be taken into account by a particular implementation of this interface:</p>
+ * <ul>
+ *     <li>it is prohibited to register more than one mapper for the same internal exception.</li>
+ *     <li>mapper should only provide mappings either to {@link IgniteException}, or {@link IgniteCheckedException}.</li>
+ *     <li>mapper should not provide mapping for Java standard exception like {@link NullPointerException},
+ *     {@link IllegalArgumentException}, etc.</li>
+ *     <li>mapper should not provide any mappings for errors {@link Error}.</li>
+ * </ul>
+ */
+public interface IgniteExceptionMappersProvider {
+    /**
+     * Returns a collection of mappers to be used to map internal exceptions to public ones.
+     *
+     * @return Collection of mappers.
+     */
+    Collection<IgniteExceptionMapper<? extends Exception, ? extends Exception>> mappers();

Review Comment:
   Same not about the signature. I get that explicit is better than implicit, but this name is too long to justify the argument



##########
modules/api/src/test/java/org/apache/ignite/lang/IgniteExceptionMapperUtilTest.java:
##########
@@ -0,0 +1,238 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.ignite.lang;
+
+import static org.apache.ignite.lang.ErrorGroups.Common.COMMON_ERR_GROUP;
+import static org.apache.ignite.lang.ErrorGroups.Common.INTERNAL_ERR;
+import static org.apache.ignite.lang.ErrorGroups.Common.NODE_STOPPING_ERR;
+import static org.apache.ignite.lang.IgniteExceptionMapper.checked;
+import static org.apache.ignite.lang.IgniteExceptionMapper.unchecked;
+import static org.apache.ignite.lang.IgniteExceptionMapperUtil.mapToPublicException;
+import static org.apache.ignite.lang.IgniteExceptionMapperUtil.registerMapping;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.isA;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import java.util.stream.Stream;
+import org.junit.jupiter.api.Named;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+/**
+ * Tests mapping internal exceptions to public ones.
+ */
+public class IgniteExceptionMapperUtilTest {
+    /** Internal collection of mappers for tests. */
+    private Map<Class<? extends Exception>, IgniteExceptionMapper<? extends Exception, ? extends Exception>> mappers = new HashMap<>();

Review Comment:
   I get my point, please make types shorter



##########
modules/api/src/main/java/org/apache/ignite/lang/IgniteExceptionMapperUtil.java:
##########
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.ignite.lang;
+
+import static java.util.Collections.unmodifiableMap;
+import static org.apache.ignite.lang.ErrorGroups.Common.INTERNAL_ERR;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.ServiceLoader;
+
+/**
+ * This utility class provides an ability to map Ignite internal exceptions to Ignite public ones.
+ */
+public class IgniteExceptionMapperUtil {
+    /** All exception mappers to be used to map internal exceptions to public ones. */
+    private static final Map<Class<? extends Exception>, IgniteExceptionMapper<? extends Exception, ? extends Exception>>
+            EXCEPTION_CONVERTERS;
+
+    static {
+        Map<Class<? extends Exception>, IgniteExceptionMapper<? extends Exception, ? extends Exception>> mappers = new HashMap<>();
+
+        ServiceLoader
+                .load(IgniteExceptionMappersProvider.class)
+                .forEach(provider -> provider.mappers().forEach(m -> registerMapping(m, mappers)));
+
+        EXCEPTION_CONVERTERS = unmodifiableMap(mappers);
+    }
+
+    /**
+     * Add a new mapping to already registered ones.
+     *
+     * @param mapper Exception mapper from internal exception to a public one.
+     * @param registeredMappings Already registered mappings.
+     * @throws IgniteException If a mapper for the given {@code clazz} already registered,
+     *      or {@code clazz} represents Java standard exception like {@link NullPointerException}, {@link IllegalArgumentException}.
+     */
+    static void registerMapping(
+            IgniteExceptionMapper<? extends Exception, ? extends Exception> mapper,
+            Map<Class<? extends Exception>, IgniteExceptionMapper<? extends Exception, ? extends Exception>> registeredMappings) {
+        if (registeredMappings.containsKey(mapper.mappingFrom())) {
+            throw new IgniteException(
+                    INTERNAL_ERR,
+                    "Failed to register exception mapper, duplicate found [class=" + mapper.mappingFrom().getCanonicalName() + ']');
+        }
+
+        if (isJavaStandardException(mapper.mappingFrom())) {
+            throw new IgniteException(
+                    INTERNAL_ERR,
+                    "Failed to register exception mapper. "
+                            + "Mapping for this class is prohibited [class=" + mapper.mappingFrom().getCanonicalName() + ']');
+        }
+
+        registeredMappings.put(mapper.mappingFrom(), mapper);
+    }
+
+    /**
+     * This method provides a mapping from internal exception to Ignite public ones.
+     *
+     * <p>The rules of mapping are the following:</p>
+     * <ul>
+     *     <li>any instance of {@link Error} is returned as is, except {@link AssertionError}
+     *     that always be mapped to {@link IgniteException} with the {@link ErrorGroups.Common#INTERNAL_ERR} error code.</li>
+     *     <li>any instance of Java standard exception like {@link NullPointerException} is returned as is.</li>
+     *     <li>any instance of {@link IgniteException} or {@link IgniteCheckedException} is returned as is.</li>
+     *     <li>if there are no any mappers that can do a mapping from the given error to a public exception,
+     *     then {@link IgniteException} with the {@link ErrorGroups.Common#INTERNAL_ERR} error code is returned.</li>
+     * </ul>
+     *
+     * @param origin Exception to be mapped.
+     * @return Public exception.
+     */
+    public static Throwable mapToPublicException(Throwable origin) {
+        if (origin instanceof Error) {
+            if (origin instanceof AssertionError) {
+                return new IgniteException(INTERNAL_ERR, origin);
+            }
+
+            return origin;
+        }
+
+        if (origin instanceof IgniteException || origin instanceof IgniteCheckedException) {
+            return origin;
+        }
+
+        if (isJavaStandardException(origin)) {
+            return origin;
+        }
+
+        IgniteExceptionMapper<? extends Exception, ? extends Exception> m = EXCEPTION_CONVERTERS.get(origin.getClass());
+        if (m != null) {
+            Exception mapped = map(m, origin);
+
+            assert mapped instanceof IgniteException || mapped instanceof IgniteCheckedException :
+                    "Unexpected mapping of internal exception to a public one [origin=" + origin + ", mapped=" + mapped + ']';
+
+            return mapped;
+        }
+
+        // There are no exception mappings for the given exception. This case should be considered as internal error.
+        return new IgniteException(INTERNAL_ERR, origin);
+    }
+
+    /**
+     * Returns a new instance of public exception provided by the {@code mapper}.
+     *
+     * @param mapper Mapper function to produce a public exception.
+     * @param t Internal exception.
+     * @param <T> Type of an internal exception.
+     * @param <R> Type of a public exception.
+     * @return New public exception.
+     */
+    private static <T extends Exception, R extends Exception> Exception map(IgniteExceptionMapper<T, R> mapper, Throwable t) {
+        return mapper.map(mapper.mappingFrom().cast(t));
+    }
+
+    /**
+     * Returns {@code true} if the given exception {@code t} is instance of {@link NullPointerException}, {@link IllegalArgumentException}.
+     *
+     * @return {@code true} if the given exception is Java standard exception.
+     */
+    private static boolean isJavaStandardException(Throwable t) {
+        return t instanceof NullPointerException || t instanceof IllegalArgumentException;

Review Comment:
   Interesting. Is this all that we support?



##########
modules/api/src/main/java/org/apache/ignite/lang/IgniteExceptionMapperUtil.java:
##########
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.ignite.lang;
+
+import static java.util.Collections.unmodifiableMap;
+import static org.apache.ignite.lang.ErrorGroups.Common.INTERNAL_ERR;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.ServiceLoader;
+
+/**
+ * This utility class provides an ability to map Ignite internal exceptions to Ignite public ones.
+ */
+public class IgniteExceptionMapperUtil {
+    /** All exception mappers to be used to map internal exceptions to public ones. */
+    private static final Map<Class<? extends Exception>, IgniteExceptionMapper<? extends Exception, ? extends Exception>>
+            EXCEPTION_CONVERTERS;
+
+    static {
+        Map<Class<? extends Exception>, IgniteExceptionMapper<? extends Exception, ? extends Exception>> mappers = new HashMap<>();
+
+        ServiceLoader
+                .load(IgniteExceptionMappersProvider.class)
+                .forEach(provider -> provider.mappers().forEach(m -> registerMapping(m, mappers)));
+
+        EXCEPTION_CONVERTERS = unmodifiableMap(mappers);
+    }
+
+    /**
+     * Add a new mapping to already registered ones.
+     *
+     * @param mapper Exception mapper from internal exception to a public one.
+     * @param registeredMappings Already registered mappings.
+     * @throws IgniteException If a mapper for the given {@code clazz} already registered,
+     *      or {@code clazz} represents Java standard exception like {@link NullPointerException}, {@link IllegalArgumentException}.
+     */
+    static void registerMapping(
+            IgniteExceptionMapper<? extends Exception, ? extends Exception> mapper,
+            Map<Class<? extends Exception>, IgniteExceptionMapper<? extends Exception, ? extends Exception>> registeredMappings) {
+        if (registeredMappings.containsKey(mapper.mappingFrom())) {
+            throw new IgniteException(
+                    INTERNAL_ERR,
+                    "Failed to register exception mapper, duplicate found [class=" + mapper.mappingFrom().getCanonicalName() + ']');
+        }
+
+        if (isJavaStandardException(mapper.mappingFrom())) {
+            throw new IgniteException(
+                    INTERNAL_ERR,
+                    "Failed to register exception mapper. "
+                            + "Mapping for this class is prohibited [class=" + mapper.mappingFrom().getCanonicalName() + ']');
+        }
+
+        registeredMappings.put(mapper.mappingFrom(), mapper);
+    }
+
+    /**
+     * This method provides a mapping from internal exception to Ignite public ones.
+     *
+     * <p>The rules of mapping are the following:</p>
+     * <ul>
+     *     <li>any instance of {@link Error} is returned as is, except {@link AssertionError}
+     *     that always be mapped to {@link IgniteException} with the {@link ErrorGroups.Common#INTERNAL_ERR} error code.</li>

Review Comment:
   "...that _will_ always be mapped..."? I think that some word is missing here



##########
modules/api/src/test/java/org/apache/ignite/lang/IgniteExceptionMapperUtilTest.java:
##########
@@ -0,0 +1,238 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.ignite.lang;
+
+import static org.apache.ignite.lang.ErrorGroups.Common.COMMON_ERR_GROUP;
+import static org.apache.ignite.lang.ErrorGroups.Common.INTERNAL_ERR;
+import static org.apache.ignite.lang.ErrorGroups.Common.NODE_STOPPING_ERR;
+import static org.apache.ignite.lang.IgniteExceptionMapper.checked;
+import static org.apache.ignite.lang.IgniteExceptionMapper.unchecked;
+import static org.apache.ignite.lang.IgniteExceptionMapperUtil.mapToPublicException;
+import static org.apache.ignite.lang.IgniteExceptionMapperUtil.registerMapping;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.isA;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import java.util.stream.Stream;
+import org.junit.jupiter.api.Named;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+/**
+ * Tests mapping internal exceptions to public ones.
+ */
+public class IgniteExceptionMapperUtilTest {
+    /** Internal collection of mappers for tests. */
+    private Map<Class<? extends Exception>, IgniteExceptionMapper<? extends Exception, ? extends Exception>> mappers = new HashMap<>();
+
+    /**
+     * Tests a simple scenario od registering mapper for internal exceptions.

Review Comment:
   ```suggestion
        * Tests a simple scenario of registering mapper for internal exceptions.
   ```



##########
modules/api/src/main/java/org/apache/ignite/lang/IgniteExceptionMappersProvider.java:
##########
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.ignite.lang;
+
+import java.util.Collection;
+
+/**
+ * This interface provides the ability to register specific mappers from Ignite internal exceptions a public ones.

Review Comment:
   ''...exceptions _to_ a public ones", I guess. I think a word is missing here as well



##########
modules/api/src/main/java/org/apache/ignite/lang/IgniteExceptionMapperUtil.java:
##########
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.ignite.lang;
+
+import static java.util.Collections.unmodifiableMap;
+import static org.apache.ignite.lang.ErrorGroups.Common.INTERNAL_ERR;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.ServiceLoader;
+
+/**
+ * This utility class provides an ability to map Ignite internal exceptions to Ignite public ones.
+ */
+public class IgniteExceptionMapperUtil {
+    /** All exception mappers to be used to map internal exceptions to public ones. */
+    private static final Map<Class<? extends Exception>, IgniteExceptionMapper<? extends Exception, ? extends Exception>>
+            EXCEPTION_CONVERTERS;
+
+    static {
+        Map<Class<? extends Exception>, IgniteExceptionMapper<? extends Exception, ? extends Exception>> mappers = new HashMap<>();
+
+        ServiceLoader
+                .load(IgniteExceptionMappersProvider.class)
+                .forEach(provider -> provider.mappers().forEach(m -> registerMapping(m, mappers)));
+
+        EXCEPTION_CONVERTERS = unmodifiableMap(mappers);
+    }
+
+    /**
+     * Add a new mapping to already registered ones.
+     *
+     * @param mapper Exception mapper from internal exception to a public one.
+     * @param registeredMappings Already registered mappings.
+     * @throws IgniteException If a mapper for the given {@code clazz} already registered,
+     *      or {@code clazz} represents Java standard exception like {@link NullPointerException}, {@link IllegalArgumentException}.
+     */
+    static void registerMapping(
+            IgniteExceptionMapper<? extends Exception, ? extends Exception> mapper,
+            Map<Class<? extends Exception>, IgniteExceptionMapper<? extends Exception, ? extends Exception>> registeredMappings) {

Review Comment:
   Same here, shorter types are easier to read



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org