You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by sd...@apache.org on 2022/10/24 12:10:25 UTC

[ignite] branch ignite-17962 created (now 1cb48a09fbf)

This is an automated email from the ASF dual-hosted git repository.

sdanilov pushed a change to branch ignite-17962
in repository https://gitbox.apache.org/repos/asf/ignite.git


      at 1cb48a09fbf IGNITE-17962 Fix serializable lambda serialization with Java17

This branch includes the following new commits:

     new 1cb48a09fbf IGNITE-17962 Fix serializable lambda serialization with Java17

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[ignite] 01/01: IGNITE-17962 Fix serializable lambda serialization with Java17

Posted by sd...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

sdanilov pushed a commit to branch ignite-17962
in repository https://gitbox.apache.org/repos/asf/ignite.git

commit 1cb48a09fbfc28f59cef35ce1c31e6264aae73eb
Author: Semyon Danilov <sa...@yandex.ru>
AuthorDate: Fri Oct 21 20:46:09 2022 +0400

    IGNITE-17962 Fix serializable lambda serialization with Java17
---
 bin/include/jvmdefaults.bat                        |   2 +
 bin/include/jvmdefaults.sh                         |   2 +
 .../internal/binary/BinaryClassDescriptor.java     |  80 ++++----
 .../optimized/OptimizedClassDescriptor.java        | 220 +++++++++++----------
 .../apache/ignite/internal/util/IgniteUtils.java   |  31 +++
 .../GridMultipleVersionsDeploymentSelfTest.java    |   1 +
 .../IgniteExplicitImplicitDeploymentSelfTest.java  |   9 +-
 .../ignite/internal/util/IgniteUtilsSelfTest.java  |  66 +++++++
 .../p2p/GridP2PRemoteClassLoadersSelfTest.java     |   3 +
 9 files changed, 271 insertions(+), 143 deletions(-)

diff --git a/bin/include/jvmdefaults.bat b/bin/include/jvmdefaults.bat
index e533e3be51c..e9002a41412 100644
--- a/bin/include/jvmdefaults.bat
+++ b/bin/include/jvmdefaults.bat
@@ -68,8 +68,10 @@ if %java_version% GEQ 15 (
     --add-opens=java.base/java.util=ALL-UNNAMED ^
     --add-opens=java.base/java.util.concurrent=ALL-UNNAMED ^
     --add-opens=java.base/java.util.concurrent.locks=ALL-UNNAMED ^
+    --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED ^
     --add-opens=java.base/java.lang=ALL-UNNAMED ^
     --add-opens=java.base/java.lang.invoke=ALL-UNNAMED ^
+    --add-opens=java.base/java.math=ALL-UNNAMED ^
     --add-opens=java.sql/java.sql=ALL-UNNAMED ^
     %current_value%
 )
diff --git a/bin/include/jvmdefaults.sh b/bin/include/jvmdefaults.sh
index 4f801a78297..8be1133aeec 100644
--- a/bin/include/jvmdefaults.sh
+++ b/bin/include/jvmdefaults.sh
@@ -65,8 +65,10 @@ getJavaSpecificOpts() {
           --add-opens=java.base/java.util=ALL-UNNAMED \
           --add-opens=java.base/java.util.concurrent=ALL-UNNAMED \
           --add-opens=java.base/java.util.concurrent.locks=ALL-UNNAMED \
+          --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED \
           --add-opens=java.base/java.lang=ALL-UNNAMED \
           --add-opens=java.base/java.lang.invoke=ALL-UNNAMED \
+          --add-opens=java.base/java.math=ALL-UNNAMED \
           --add-opens=java.sql/java.sql=ALL-UNNAMED \
           ${current_value}"
   fi
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryClassDescriptor.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryClassDescriptor.java
index 6ded1729edb..a6d8e079d10 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryClassDescriptor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryClassDescriptor.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.internal.binary;
 
+import java.io.Serializable;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
@@ -54,6 +55,7 @@ import org.apache.ignite.marshaller.MarshallerExclusions;
 import org.jetbrains.annotations.Nullable;
 
 import static org.apache.ignite.internal.processors.query.QueryUtils.isGeometryClass;
+import static org.apache.ignite.internal.util.IgniteUtils.isLambda;
 
 /**
  * Binary class descriptor.
@@ -318,61 +320,71 @@ public class BinaryClassDescriptor {
                 // Must not use constructor to honor transient fields semantics.
                 ctor = null;
 
-                Map<Object, BinaryFieldAccessor> fields0;
+                if (isLambda(cls)) {
+                    if (!Serializable.class.isAssignableFrom(cls))
+                        throw new BinaryObjectException("Lambda is not serializable: " + cls);
 
-                if (BinaryUtils.FIELDS_SORTED_ORDER) {
-                    fields0 = new TreeMap<>();
+                    // We don't need fields for serializable lambdas, because we resort to SerializedLambda.
+                    fields = null;
+                    stableFieldsMeta = null;
+                    stableSchema = null;
+                } else {
+                    Map<Object, BinaryFieldAccessor> fields0;
 
-                    stableFieldsMeta = metaDataEnabled ? new TreeMap<String, BinaryFieldMetadata>() : null;
-                }
-                else {
-                    fields0 = new LinkedHashMap<>();
+                    if (BinaryUtils.FIELDS_SORTED_ORDER) {
+                        fields0 = new TreeMap<>();
 
-                    stableFieldsMeta = metaDataEnabled ? new LinkedHashMap<String, BinaryFieldMetadata>() : null;
-                }
+                        stableFieldsMeta = metaDataEnabled ? new TreeMap<String, BinaryFieldMetadata>() : null;
+                    }
+                    else {
+                        fields0 = new LinkedHashMap<>();
+
+                        stableFieldsMeta = metaDataEnabled ? new LinkedHashMap<String, BinaryFieldMetadata>() : null;
+                    }
 
-                Set<String> duplicates = duplicateFields(cls);
+                    Set<String> duplicates = duplicateFields(cls);
 
-                Collection<String> names = new HashSet<>();
-                Collection<Integer> ids = new HashSet<>();
+                    Collection<String> names = new HashSet<>();
+                    Collection<Integer> ids = new HashSet<>();
 
-                for (Class<?> c = cls; c != null && !c.equals(Object.class); c = c.getSuperclass()) {
-                    for (Field f : c.getDeclaredFields()) {
-                        if (serializeField(f)) {
-                            f.setAccessible(true);
+                    for (Class<?> c = cls; c != null && !c.equals(Object.class); c = c.getSuperclass()) {
+                        for (Field f : c.getDeclaredFields()) {
+                            if (serializeField(f)) {
+                                f.setAccessible(true);
 
-                            String name = f.getName();
+                                String name = f.getName();
 
-                            if (duplicates.contains(name))
-                                name = BinaryUtils.qualifiedFieldName(c, name);
+                                if (duplicates.contains(name))
+                                    name = BinaryUtils.qualifiedFieldName(c, name);
 
-                            boolean added = names.add(name);
+                                boolean added = names.add(name);
 
-                            assert added : name;
+                                assert added : name;
 
-                            int fieldId = this.mapper.fieldId(typeId, name);
+                                int fieldId = this.mapper.fieldId(typeId, name);
 
-                            if (!ids.add(fieldId))
-                                throw new BinaryObjectException("Duplicate field ID: " + name);
+                                if (!ids.add(fieldId))
+                                    throw new BinaryObjectException("Duplicate field ID: " + name);
 
-                            BinaryFieldAccessor fieldInfo = BinaryFieldAccessor.create(f, fieldId);
+                                BinaryFieldAccessor fieldInfo = BinaryFieldAccessor.create(f, fieldId);
 
-                            fields0.put(name, fieldInfo);
+                                fields0.put(name, fieldInfo);
 
-                            if (metaDataEnabled)
-                                stableFieldsMeta.put(name, new BinaryFieldMetadata(fieldInfo));
+                                if (metaDataEnabled)
+                                    stableFieldsMeta.put(name, new BinaryFieldMetadata(fieldInfo));
+                            }
                         }
                     }
-                }
 
-                fields = fields0.values().toArray(new BinaryFieldAccessor[fields0.size()]);
+                    fields = fields0.values().toArray(new BinaryFieldAccessor[fields0.size()]);
 
-                BinarySchema.Builder schemaBuilder = BinarySchema.Builder.newBuilder();
+                    BinarySchema.Builder schemaBuilder = BinarySchema.Builder.newBuilder();
 
-                for (BinaryFieldAccessor field : fields)
-                    schemaBuilder.addField(field.id);
+                    for (BinaryFieldAccessor field : fields)
+                        schemaBuilder.addField(field.id);
 
-                stableSchema = schemaBuilder.build();
+                    stableSchema = schemaBuilder.build();
+                }
 
                 intfs = null;
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/marshaller/optimized/OptimizedClassDescriptor.java b/modules/core/src/main/java/org/apache/ignite/internal/marshaller/optimized/OptimizedClassDescriptor.java
index c6917c7bc24..e15f3743326 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/marshaller/optimized/OptimizedClassDescriptor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/marshaller/optimized/OptimizedClassDescriptor.java
@@ -95,6 +95,7 @@ import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshalle
 import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.STR;
 import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.UUID;
 import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.computeSerialVersionUid;
+import static org.apache.ignite.internal.util.IgniteUtils.isLambda;
 import static org.apache.ignite.marshaller.MarshallerUtils.jobReceiverVersion;
 import static org.apache.ignite.marshaller.MarshallerUtils.jobSenderVersion;
 
@@ -446,160 +447,165 @@ class OptimizedClassDescriptor {
                     readObjMtds = new ArrayList<>();
                     List<ClassFields> fields = new ArrayList<>();
 
-                    for (c = cls; c != null && !c.equals(Object.class); c = c.getSuperclass()) {
-                        Method mtd;
+                    if (isLambda(cls)) {
+                        if (!isSerial)
+                            throw new NotSerializableException("Lambda is not serializable: " + cls);
+                    } else {
+                        for (c = cls; c != null && !c.equals(Object.class); c = c.getSuperclass()) {
+                            Method mtd;
 
-                        try {
-                            mtd = c.getDeclaredMethod("writeObject", ObjectOutputStream.class);
+                            try {
+                                mtd = c.getDeclaredMethod("writeObject", ObjectOutputStream.class);
 
-                            int mod = mtd.getModifiers();
+                                int mod = mtd.getModifiers();
 
-                            if (!isStatic(mod) && isPrivate(mod) && mtd.getReturnType() == Void.TYPE)
-                                mtd.setAccessible(true);
-                            else
-                                // Set method back to null if it has incorrect signature.
+                                if (!isStatic(mod) && isPrivate(mod) && mtd.getReturnType() == Void.TYPE)
+                                    mtd.setAccessible(true);
+                                else
+                                    // Set method back to null if it has incorrect signature.
+                                    mtd = null;
+                            }
+                            catch (NoSuchMethodException ignored) {
                                 mtd = null;
-                        }
-                        catch (NoSuchMethodException ignored) {
-                            mtd = null;
-                        }
+                            }
 
-                        writeObjMtds.add(mtd);
+                            writeObjMtds.add(mtd);
 
-                        try {
-                            mtd = c.getDeclaredMethod("readObject", ObjectInputStream.class);
+                            try {
+                                mtd = c.getDeclaredMethod("readObject", ObjectInputStream.class);
 
-                            int mod = mtd.getModifiers();
+                                int mod = mtd.getModifiers();
 
-                            if (!isStatic(mod) && isPrivate(mod) && mtd.getReturnType() == Void.TYPE)
-                                mtd.setAccessible(true);
-                            else
-                                // Set method back to null if it has incorrect signature.
+                                if (!isStatic(mod) && isPrivate(mod) && mtd.getReturnType() == Void.TYPE)
+                                    mtd.setAccessible(true);
+                                else
+                                    // Set method back to null if it has incorrect signature.
+                                    mtd = null;
+                            }
+                            catch (NoSuchMethodException ignored) {
                                 mtd = null;
-                        }
-                        catch (NoSuchMethodException ignored) {
-                            mtd = null;
-                        }
+                            }
 
-                        readObjMtds.add(mtd);
+                            readObjMtds.add(mtd);
 
-                        final SerializableTransient serTransAn = c.getAnnotation(SerializableTransient.class);
-                        final TransientSerializable transSerAn = c.getAnnotation(TransientSerializable.class);
+                            final SerializableTransient serTransAn = c.getAnnotation(SerializableTransient.class);
+                            final TransientSerializable transSerAn = c.getAnnotation(TransientSerializable.class);
 
-                        // Custom serialization policy for transient fields.
-                        if (serTransAn != null) {
-                            try {
-                                serTransMtd = c.getDeclaredMethod(serTransAn.methodName(), IgniteProductVersion.class);
+                            // Custom serialization policy for transient fields.
+                            if (serTransAn != null) {
+                                try {
+                                    serTransMtd = c.getDeclaredMethod(serTransAn.methodName(), IgniteProductVersion.class);
 
-                                int mod = serTransMtd.getModifiers();
+                                    int mod = serTransMtd.getModifiers();
 
-                                if (isStatic(mod) && isPrivate(mod) && serTransMtd.getReturnType() == String[].class)
-                                    serTransMtd.setAccessible(true);
-                                else
-                                    // Set method back to null if it has incorrect signature.
+                                    if (isStatic(mod) && isPrivate(mod) && serTransMtd.getReturnType() == String[].class)
+                                        serTransMtd.setAccessible(true);
+                                    else
+                                        // Set method back to null if it has incorrect signature.
+                                        serTransMtd = null;
+                                }
+                                catch (NoSuchMethodException ignored) {
                                     serTransMtd = null;
+                                }
                             }
-                            catch (NoSuchMethodException ignored) {
-                                serTransMtd = null;
-                            }
-                        }
 
-                        // Custom serialization policy for non-transient fields.
-                        if (transSerAn != null) {
-                            try {
-                                transSerMtd = c.getDeclaredMethod(transSerAn.methodName(), IgniteProductVersion.class);
+                            // Custom serialization policy for non-transient fields.
+                            if (transSerAn != null) {
+                                try {
+                                    transSerMtd = c.getDeclaredMethod(transSerAn.methodName(), IgniteProductVersion.class);
 
-                                int mod = transSerMtd.getModifiers();
+                                    int mod = transSerMtd.getModifiers();
 
-                                if (isStatic(mod) && isPrivate(mod) && transSerMtd.getReturnType() == String[].class)
-                                    transSerMtd.setAccessible(true);
-                                else
-                                    // Set method back to null if it has incorrect signature.
+                                    if (isStatic(mod) && isPrivate(mod) && transSerMtd.getReturnType() == String[].class)
+                                        transSerMtd.setAccessible(true);
+                                    else
+                                        // Set method back to null if it has incorrect signature.
+                                        transSerMtd = null;
+                                }
+                                catch (NoSuchMethodException ignored) {
                                     transSerMtd = null;
+                                }
                             }
-                            catch (NoSuchMethodException ignored) {
-                                transSerMtd = null;
-                            }
-                        }
 
-                        Field[] clsFields0 = c.getDeclaredFields();
+                            Field[] clsFields0 = c.getDeclaredFields();
 
-                        Map<String, Field> fieldNames = new HashMap<>();
+                            Map<String, Field> fieldNames = new HashMap<>();
 
-                        for (Field f : clsFields0)
-                            fieldNames.put(f.getName(), f);
+                            for (Field f : clsFields0)
+                                fieldNames.put(f.getName(), f);
 
-                        List<FieldInfo> clsFields = new ArrayList<>(clsFields0.length);
+                            List<FieldInfo> clsFields = new ArrayList<>(clsFields0.length);
 
-                        boolean hasSerialPersistentFields = false;
+                            boolean hasSerialPersistentFields = false;
 
-                        try {
-                            Field serFieldsDesc = c.getDeclaredField("serialPersistentFields");
+                            try {
+                                Field serFieldsDesc = c.getDeclaredField("serialPersistentFields");
 
-                            int mod = serFieldsDesc.getModifiers();
+                                int mod = serFieldsDesc.getModifiers();
 
-                            if (serFieldsDesc.getType() == ObjectStreamField[].class &&
-                                isPrivate(mod) && isStatic(mod) && isFinal(mod)) {
-                                hasSerialPersistentFields = true;
+                                if (serFieldsDesc.getType() == ObjectStreamField[].class &&
+                                        isPrivate(mod) && isStatic(mod) && isFinal(mod)) {
+                                    hasSerialPersistentFields = true;
 
-                                serFieldsDesc.setAccessible(true);
+                                    serFieldsDesc.setAccessible(true);
 
                                 ObjectStreamField[] serFields = (ObjectStreamField[])serFieldsDesc.get(null);
 
-                                for (int i = 0; i < serFields.length; i++) {
-                                    ObjectStreamField serField = serFields[i];
+                                    for (int i = 0; i < serFields.length; i++) {
+                                        ObjectStreamField serField = serFields[i];
 
-                                    FieldInfo fieldInfo;
+                                        FieldInfo fieldInfo;
 
-                                    if (!fieldNames.containsKey(serField.getName())) {
-                                        fieldInfo = new FieldInfo(null,
-                                            serField.getName(),
-                                            -1,
-                                            fieldType(serField.getType()));
-                                    }
-                                    else {
-                                        Field f = fieldNames.get(serField.getName());
+                                        if (!fieldNames.containsKey(serField.getName())) {
+                                            fieldInfo = new FieldInfo(null,
+                                                    serField.getName(),
+                                                    -1,
+                                                    fieldType(serField.getType()));
+                                        }
+                                        else {
+                                            Field f = fieldNames.get(serField.getName());
 
-                                        fieldInfo = new FieldInfo(f,
-                                            serField.getName(),
-                                            GridUnsafe.objectFieldOffset(f),
-                                            fieldType(serField.getType()));
-                                    }
+                                            fieldInfo = new FieldInfo(f,
+                                                    serField.getName(),
+                                                    GridUnsafe.objectFieldOffset(f),
+                                                    fieldType(serField.getType()));
+                                        }
 
-                                    clsFields.add(fieldInfo);
+                                        clsFields.add(fieldInfo);
+                                    }
                                 }
                             }
-                        }
-                        catch (NoSuchFieldException ignored) {
-                            // No-op.
-                        }
-                        catch (IllegalAccessException e) {
-                            throw new IOException("Failed to get value of 'serialPersistentFields' field in class: " +
-                                cls.getName(), e);
-                        }
+                            catch (NoSuchFieldException ignored) {
+                                // No-op.
+                            }
+                            catch (IllegalAccessException e) {
+                                throw new IOException("Failed to get value of 'serialPersistentFields' field in class: " +
+                                        cls.getName(), e);
+                            }
 
-                        if (!hasSerialPersistentFields) {
-                            for (int i = 0; i < clsFields0.length; i++) {
-                                Field f = clsFields0[i];
+                            if (!hasSerialPersistentFields) {
+                                for (int i = 0; i < clsFields0.length; i++) {
+                                    Field f = clsFields0[i];
 
-                                int mod = f.getModifiers();
+                                    int mod = f.getModifiers();
 
-                                if (!isStatic(mod) && !isTransient(mod)) {
-                                    FieldInfo fieldInfo = new FieldInfo(f, f.getName(),
-                                        GridUnsafe.objectFieldOffset(f), fieldType(f.getType()));
+                                    if (!isStatic(mod) && !isTransient(mod)) {
+                                        FieldInfo fieldInfo = new FieldInfo(f, f.getName(),
+                                                GridUnsafe.objectFieldOffset(f), fieldType(f.getType()));
 
-                                    clsFields.add(fieldInfo);
+                                        clsFields.add(fieldInfo);
+                                    }
                                 }
                             }
-                        }
 
-                        Collections.sort(clsFields, new Comparator<FieldInfo>() {
-                            @Override public int compare(FieldInfo t1, FieldInfo t2) {
-                                return t1.name().compareTo(t2.name());
-                            }
-                        });
+                            Collections.sort(clsFields, new Comparator<FieldInfo>() {
+                                @Override public int compare(FieldInfo t1, FieldInfo t2) {
+                                    return t1.name().compareTo(t2.name());
+                                }
+                            });
 
-                        fields.add(new ClassFields(clsFields));
+                            fields.add(new ClassFields(clsFields));
+                        }
                     }
 
                     Collections.reverse(writeObjMtds);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
index ec8764393c0..49b8954cfa1 100755
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
@@ -12419,4 +12419,35 @@ public abstract class IgniteUtils {
     public static boolean isRestartEnabled() {
         return IGNITE_SUCCESS_FILE_PROPERTY != null;
     }
+
+    /**
+     * Returns {@code true} if class is a lambda.
+     *
+     * @param objectClass Class.
+     * @return {@code true} if class is a lambda, {@code false} otherwise.
+     */
+    public static boolean isLambda(Class<?> objectClass) {
+        return !objectClass.isPrimitive() && !objectClass.isArray()
+            // Order is crucial here, isAnonymousClass and isLocalClass may fail if
+            // class' outer class was loaded with different classloader.
+            && objectClass.isSynthetic()
+            && !objectClass.isAnonymousClass() && !objectClass.isLocalClass()
+            && classCannotBeLoadedByName(objectClass);
+    }
+
+    /**
+     * Returns {@code true} if class can not be loaded by name.
+     *
+     * @param objectClass Class.
+     * @return {@code true} if class can not be loaded by name, {@code false} otherwise.
+     */
+    public static boolean classCannotBeLoadedByName(Class<?> objectClass) {
+        try {
+            Class.forName(objectClass.getName());
+            return false;
+        }
+        catch (ClassNotFoundException e) {
+            return true;
+        }
+    }
 }
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/GridMultipleVersionsDeploymentSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/GridMultipleVersionsDeploymentSelfTest.java
index 64f07420e9e..b4e424fc90d 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/GridMultipleVersionsDeploymentSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/GridMultipleVersionsDeploymentSelfTest.java
@@ -58,6 +58,7 @@ import static org.apache.ignite.events.EventType.EVT_TASK_UNDEPLOYED;
 public class GridMultipleVersionsDeploymentSelfTest extends GridCommonAbstractTest {
     /** Excluded classes. */
     private static final String[] EXCLUDE_CLASSES = new String[] {
+        GridMultipleVersionsDeploymentSelfTest.class.getName(),
         GridDeploymentTestTask.class.getName(),
         GridDeploymentTestJob.class.getName()
     };
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/IgniteExplicitImplicitDeploymentSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/IgniteExplicitImplicitDeploymentSelfTest.java
index fb89b410ee9..fd17c01471e 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/IgniteExplicitImplicitDeploymentSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/IgniteExplicitImplicitDeploymentSelfTest.java
@@ -64,8 +64,11 @@ public class IgniteExplicitImplicitDeploymentSelfTest extends GridCommonAbstract
         IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
 
         // Override P2P configuration to exclude Task and Job classes
-        cfg.setPeerClassLoadingLocalClassPathExclude(GridDeploymentResourceTestTask.class.getName(),
-            GridDeploymentResourceTestJob.class.getName());
+        cfg.setPeerClassLoadingLocalClassPathExclude(
+            IgniteExplicitImplicitDeploymentSelfTest.class.getName(),
+            GridDeploymentResourceTestTask.class.getName(),
+            GridDeploymentResourceTestJob.class.getName()
+        );
 
         cfg.setDeploymentMode(DeploymentMode.ISOLATED);
 
@@ -316,6 +319,7 @@ public class IgniteExplicitImplicitDeploymentSelfTest extends GridCommonAbstract
             ClassLoader ldr1 = new GridTestClassLoader(
                 Collections.singletonMap("testResource", "1"),
                 getClass().getClassLoader(),
+                IgniteExplicitImplicitDeploymentSelfTest.class.getName(),
                 GridDeploymentResourceTestTask.class.getName(),
                 GridDeploymentResourceTestJob.class.getName()
             );
@@ -323,6 +327,7 @@ public class IgniteExplicitImplicitDeploymentSelfTest extends GridCommonAbstract
             ClassLoader ldr2 = new GridTestClassLoader(
                 Collections.singletonMap("testResource", "2"),
                 getClass().getClassLoader(),
+                IgniteExplicitImplicitDeploymentSelfTest.class.getName(),
                 GridDeploymentResourceTestTask.class.getName(),
                 GridDeploymentResourceTestJob.class.getName()
             );
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/util/IgniteUtilsSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/util/IgniteUtilsSelfTest.java
index 9fbd2b916f0..30e31709f3d 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/util/IgniteUtilsSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/util/IgniteUtilsSelfTest.java
@@ -78,6 +78,7 @@ import org.apache.ignite.lang.IgniteBiTuple;
 import org.apache.ignite.lang.IgnitePredicate;
 import org.apache.ignite.lang.IgniteProductVersion;
 import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryNode;
+import org.apache.ignite.testframework.GridTestClassLoader;
 import org.apache.ignite.testframework.GridTestUtils;
 import org.apache.ignite.testframework.http.GridEmbeddedHttpServer;
 import org.apache.ignite.testframework.junits.WithSystemProperty;
@@ -1511,6 +1512,71 @@ public class IgniteUtilsSelfTest extends GridCommonAbstractTest {
         testAddressResolveWithLocalHostDefined();
     }
 
+    /**
+     * Tests {@link IgniteUtils#isLambda(Class)} on lambdas.
+     */
+    @Test
+    public void testIsLambdaOnLambdas() {
+        Runnable someLambda = () -> {};
+
+        int localVar = 0;
+        Runnable capturingLocalLambda = () -> {
+            System.out.println(localVar);
+        };
+
+        Runnable capturingOuterClassLambda = () -> {
+            System.out.println(repeatRule);
+        };
+
+        Runnable methodReference = this::testIsLambdaOnLambdas;
+
+        assertTrue(IgniteUtils.isLambda(someLambda.getClass()));
+        assertTrue(IgniteUtils.isLambda(capturingLocalLambda.getClass()));
+        assertTrue(IgniteUtils.isLambda(capturingOuterClassLambda.getClass()));
+        assertTrue(IgniteUtils.isLambda(methodReference.getClass()));
+    }
+
+    // Test nested class.
+    private static class TestNestedClass {
+    }
+
+    // Test inner class.
+    private class TestInnerClass {
+    }
+
+    /**
+     * Tests {@link IgniteUtils#isLambda(Class)} on non-lambda classes.
+     */
+    @Test
+    public void testIsLambdaOnOrdinaryClasses() throws Exception {
+        assertFalse(IgniteUtils.isLambda(Object.class));
+
+        Runnable anonCls = new Runnable() {
+            /** {@inheritDoc} */
+            @Override public void run() {
+                // No-op.
+            }
+        };
+
+        assertFalse(IgniteUtils.isLambda(anonCls.getClass()));
+        assertFalse(IgniteUtils.isLambda(TestEnum.class));
+
+        // Loading only inner class with test classloader, while outer class
+        // will be loaded with the default classloader. Thus, if we execute method like isAnonymousClass
+        // on the loaded class, it will fail with the IncompatibleClassChangeError. That's why order in
+        // IgniteUtils isLambda is important.
+        GridTestClassLoader clsLdr = new GridTestClassLoader(
+            TestNestedClass.class.getName(),
+            TestInnerClass.class.getName()
+        );
+
+        Class<?> nestedCls = clsLdr.loadClass(TestNestedClass.class.getName());
+        assertFalse(IgniteUtils.isLambda(nestedCls));
+
+        Class<?> innerCls = clsLdr.loadClass(TestInnerClass.class.getName());
+        assertFalse(IgniteUtils.isLambda(innerCls));
+    }
+
     /**
      * Tests {@link IgniteUtils#resolveLocalAddresses(InetAddress)} with different values set to
      * {@link IgniteSystemProperties#IGNITE_LOCAL_HOST} and {@link IgniteSystemProperties#IGNITE_IGNORE_LOCAL_HOST_NAME}.
diff --git a/modules/core/src/test/java/org/apache/ignite/p2p/GridP2PRemoteClassLoadersSelfTest.java b/modules/core/src/test/java/org/apache/ignite/p2p/GridP2PRemoteClassLoadersSelfTest.java
index a19fb8c570a..7364efbddfd 100644
--- a/modules/core/src/test/java/org/apache/ignite/p2p/GridP2PRemoteClassLoadersSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/p2p/GridP2PRemoteClassLoadersSelfTest.java
@@ -84,6 +84,7 @@ public class GridP2PRemoteClassLoadersSelfTest extends GridCommonAbstractTest {
             ClassLoader tstClsLdr =
                 new GridTestClassLoader(
                     Collections.<String, String>emptyMap(), getClass().getClassLoader(),
+                    GridP2PRemoteClassLoadersSelfTest.class.getName(),
                     GridP2PRemoteTestTask.class.getName(), GridP2PRemoteTestTask1.class.getName(),
                     GridP2PRemoteTestJob.class.getName());
 
@@ -136,12 +137,14 @@ public class GridP2PRemoteClassLoadersSelfTest extends GridCommonAbstractTest {
             ClassLoader tstClsLdr1 =
                 new GridTestClassLoader(
                     Collections.EMPTY_MAP, getClass().getClassLoader(),
+                    GridP2PRemoteClassLoadersSelfTest.class.getName(),
                     GridP2PRemoteTestTask.class.getName(), GridP2PRemoteTestJob.class.getName()
                 );
 
             ClassLoader tstClsLdr2 =
                 new GridTestClassLoader(
                     Collections.EMPTY_MAP, getClass().getClassLoader(),
+                    GridP2PRemoteClassLoadersSelfTest.class.getName(),
                     GridP2PRemoteTestTask1.class.getName(), GridP2PRemoteTestJob.class.getName());
 
             Class<? extends ComputeTask<?, ?>> task1 =