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

sergeychugunov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/master by this push:
     new ebbc96d4b63 IGNITE-26728 Perform enum fields 
serialization/deserialization with external mappers (#12554)
ebbc96d4b63 is described below

commit ebbc96d4b63b4f9303f026bb6ac9138b5e57dfe9
Author: Sergey Chugunov <[email protected]>
AuthorDate: Thu Dec 25 11:32:25 2025 +0400

    IGNITE-26728 Perform enum fields serialization/deserialization with 
external mappers (#12554)
---
 .../org/apache/ignite/internal/CustomMapper.java   |  51 +++++++
 .../apache/ignite/internal/MessageProcessor.java   |  62 ++++++++-
 .../internal/MessageSerializerGenerator.java       | 154 +++++++++++++++++++--
 modules/core/pom.xml                               |   4 +
 .../ignite/internal/GridJobExecuteRequest.java     |  24 ++--
 .../communication/DeploymentModeMessage.java       | 113 ---------------
 .../communication/GridIoMessageFactory.java        |   5 -
 .../communication/TransactionIsolationMessage.java | 117 ----------------
 .../distributed/GridDistributedLockRequest.java    |  15 +-
 .../GridDistributedTxFinishRequest.java            |  30 ++--
 .../dht/GridDhtTransactionalCacheAdapter.java      |   4 +-
 .../internal/processors/job/GridJobProcessor.java  |   4 +-
 .../communication/mappers/DefaultEnumMapper.java   |  92 ++++++++++++
 .../communication/mappers/EnumMapper.java          |  86 ++++++++++++
 .../internal/codegen/MessageProcessorTest.java     |  96 ++++++++++++-
 .../communication/DefaultEnumMapperTest.java       |  63 +++++++++
 .../communication/DeploymentModeMessageTest.java   |  89 ------------
 .../TransactionIsolationMessageTest.java           |  79 -----------
 .../ignite/testsuites/IgniteBasicTestSuite.java    |   6 +-
 ...va => CustomEnumMapperOnArrayFieldMessage.java} |  17 +--
 ...> CustomEnumMapperOnPrimitiveFieldMessage.java} |  16 +--
 ...age.java => CustomMapperEnumFieldsMessage.java} |  15 +-
 .../CustomMapperEnumFieldsMessageSerializer.java   |  76 ++++++++++
 ...va => CustomMapperEnumFieldsSecondMessage.java} |  15 +-
 ...ge.java => DefaultMapperEnumFieldsMessage.java} |  24 +++-
 .../DefaultMapperEnumFieldsMessageSerializer.java  |  93 +++++++++++++
 ...a => DefaultMapperEnumFieldsSecondMessage.java} |  14 +-
 ...ge.java => TransactionIsolationEnumMapper.java} |  21 +--
 28 files changed, 855 insertions(+), 530 deletions(-)

diff --git 
a/modules/codegen2/src/main/java/org/apache/ignite/internal/CustomMapper.java 
b/modules/codegen2/src/main/java/org/apache/ignite/internal/CustomMapper.java
new file mode 100644
index 00000000000..2f7ba68faff
--- /dev/null
+++ 
b/modules/codegen2/src/main/java/org/apache/ignite/internal/CustomMapper.java
@@ -0,0 +1,51 @@
+/*
+ * 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.internal;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation used to specify a custom mapping strategy for an enum type 
during code generation.
+ * It allows associating a custom mapper class that defines how enum constants 
are serialized and deserialized.
+ *
+ * <p>It is used in conjunction with {@link Order} annotation and used to mark 
fields of enum type
+ * that should be serialized and deserialized using custom logic.</p>
+ *
+ * <p>The class specified by {@link #value()} must implement {@code 
org.apache.ignite.plugin.extensions.communication.mappers.CustomMapper}
+ * interface to provide methods for converting enum values to and from their 
external representations.</p>
+ *
+ * @see #value()
+ * @see org.apache.ignite.internal.Order
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.FIELD)
+public @interface CustomMapper {
+    /**
+     * Returns the fully qualified class name of the custom mapper 
implementation
+     * used to handle the enum's value conversion.
+     *
+     * <p>The specified class must be available on the classpath during code 
generation
+     * and must adhere to the expected mapper interface contract.</p>
+     *
+     * @return The fully qualified name of the mapper class. Must not be null 
or empty.
+     */
+    String value() default "";
+}
diff --git 
a/modules/codegen2/src/main/java/org/apache/ignite/internal/MessageProcessor.java
 
b/modules/codegen2/src/main/java/org/apache/ignite/internal/MessageProcessor.java
index afbc411bb32..460a0631b07 100644
--- 
a/modules/codegen2/src/main/java/org/apache/ignite/internal/MessageProcessor.java
+++ 
b/modules/codegen2/src/main/java/org/apache/ignite/internal/MessageProcessor.java
@@ -34,9 +34,14 @@ import javax.lang.model.element.ElementKind;
 import javax.lang.model.element.Modifier;
 import javax.lang.model.element.TypeElement;
 import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeKind;
 import javax.lang.model.type.TypeMirror;
 import javax.tools.Diagnostic;
 
+import org.apache.ignite.lang.IgniteBiTuple;
+
+import static 
org.apache.ignite.internal.MessageSerializerGenerator.DLFT_ENUM_MAPPER_CLS;
+
 /**
  * Annotation processor that generates serialization and deserialization code 
for classes implementing the {@code Message} interface.
  * <p>
@@ -71,6 +76,9 @@ public class MessageProcessor extends AbstractProcessor {
     /** This is the only message with zero fields. A serializer must be 
generated due to restrictions in our communication process. */
     static final String HANDSHAKE_WAIT_MESSAGE = 
"org.apache.ignite.spi.communication.tcp.messages.HandshakeWaitMessage";
 
+    /** */
+    private final Map<String, IgniteBiTuple<String, String>> enumMappersInUse 
= new HashMap<>();
+
     /**
      * Processes all classes implementing the {@code Message} interface and 
generates corresponding serializer code.
      */
@@ -132,9 +140,11 @@ public class MessageProcessor extends AbstractProcessor {
                     if (el.getModifiers().contains(Modifier.STATIC)) {
                         processingEnv.getMessager().printMessage(
                             Diagnostic.Kind.ERROR,
-                            "Annotation @Order must be used only for 
non-static fields.",
+                            "Annotation @Order must only be used for 
non-static fields.",
                             el);
                     }
+
+                    validateEnumFieldMapping(type, el);
                 }
             }
 
@@ -156,4 +166,54 @@ public class MessageProcessor extends AbstractProcessor {
 
         return result;
     }
+
+    /**
+     * Validates consistency of enum field mappers configuration: the same 
mapper is used for the same enum in different messages,
+     * CustomMapper annotation is used only for enum fields.
+     *
+     * @param type Type implementing Message interface.
+     * @param el Enclosed element of the type.
+     */
+    private void validateEnumFieldMapping(TypeElement type, Element el) {
+        CustomMapper custMappAnn = el.getAnnotation(CustomMapper.class);
+        if (isEnumField(el)) {
+            String enumClsFullName = el.asType().toString();
+            String enumMapperClsName = custMappAnn != null ? 
custMappAnn.value() : DLFT_ENUM_MAPPER_CLS;
+            String msgClsName = type.toString();
+
+            IgniteBiTuple<String, String> otherMsgAndMapperClassesNames =
+                enumMappersInUse.put(enumClsFullName, new 
IgniteBiTuple<>(msgClsName, enumMapperClsName));
+
+            if (otherMsgAndMapperClassesNames != null) {
+                String otherMsgClsName = otherMsgAndMapperClassesNames.get1();
+                String otherEnumMapperClsName = 
otherMsgAndMapperClassesNames.get2();
+
+                if (!otherEnumMapperClsName.equals(enumMapperClsName)) {
+                    processingEnv.getMessager().printMessage(
+                        Diagnostic.Kind.ERROR,
+                        "Enum " + enumClsFullName + " is declared with 
different mappers: " +
+                            otherEnumMapperClsName + " in " + otherMsgClsName 
+ " and " +
+                            enumMapperClsName + " in " + msgClsName +
+                            ". Only one mapper is allowed per enum type.",
+                        el);
+                }
+            }
+        }
+        else if (custMappAnn != null) {
+            processingEnv.getMessager().printMessage(
+                Diagnostic.Kind.ERROR,
+                "Annotation @CustomMapper must only be used for enum fields.",
+                el);
+        }
+    }
+
+    /** */
+    private boolean isEnumField(Element el) {
+        TypeMirror elType = el.asType();
+
+        if (elType.getKind() != TypeKind.DECLARED)
+            return false;
+
+        return processingEnv.getTypeUtils().asElement(elType).getKind() == 
ElementKind.ENUM;
+    }
 }
diff --git 
a/modules/codegen2/src/main/java/org/apache/ignite/internal/MessageSerializerGenerator.java
 
b/modules/codegen2/src/main/java/org/apache/ignite/internal/MessageSerializerGenerator.java
index d0c0d14e3b3..03420b425cc 100644
--- 
a/modules/codegen2/src/main/java/org/apache/ignite/internal/MessageSerializerGenerator.java
+++ 
b/modules/codegen2/src/main/java/org/apache/ignite/internal/MessageSerializerGenerator.java
@@ -83,6 +83,12 @@ class MessageSerializerGenerator {
     /** */
     private static final String METHOD_JAVADOC = "/** */";
 
+    /** */
+    private static final String RETURN_FALSE_STMT = "return false;";
+
+    /** */
+    static final String DLFT_ENUM_MAPPER_CLS = 
"org.apache.ignite.plugin.extensions.communication.mappers.DefaultEnumMapper";
+
     /** Collection of lines for {@code writeTo} method. */
     private final List<String> write = new ArrayList<>();
 
@@ -92,6 +98,9 @@ class MessageSerializerGenerator {
     /** Collection of message-specific imports. */
     private final Set<String> imports = new TreeSet<>();
 
+    /** Collection of Serializer class fields containing mappers for message 
enum fields. */
+    private final Set<String> fields = new TreeSet<>();
+
     /** */
     private final ProcessingEnvironment env;
 
@@ -142,6 +151,8 @@ class MessageSerializerGenerator {
         try (Writer writer = new StringWriter()) {
             writeClassHeader(writer, PKG_NAME, serClsName);
 
+            writeClassFields(writer);
+
             // Write #writeTo method.
             for (String w: write)
                 writer.write(w + NL);
@@ -238,10 +249,6 @@ class MessageSerializerGenerator {
         if (assignableFrom(field.asType(), type(Throwable.class.getName())))
             throw new UnsupportedOperationException("You should use 
ErrorMessage for serialization of throwables.");
 
-        if (enumType(erasedType(field.asType())))
-            throw new IllegalArgumentException("Unsupported enum type: " + 
field.asType() +
-                    ". The enum must be wrapped into a Message (see, for 
example, TransactionIsolationMessage).");
-
         writeField(field, opt);
         readField(field, opt);
     }
@@ -396,6 +403,45 @@ class MessageSerializerGenerator {
                     "MessageCollectionItemType." + 
messageCollectionItemType(typeArgs.get(0)));
             }
 
+            else if (enumType(type)) {
+                Element element = env.getTypeUtils().asElement(type);
+                imports.add(element.toString());
+
+                String enumName = element.getSimpleName().toString();
+                String enumFieldPrefix = typeNameToFieldName(enumName);
+
+                String mapperCallStmnt;
+
+                CustomMapper custMapperAnn = 
field.getAnnotation(CustomMapper.class);
+
+                if (custMapperAnn != null) {
+                    String fullMapperName = custMapperAnn.value();
+                    if (fullMapperName == null || fullMapperName.isEmpty())
+                        throw new IllegalArgumentException("Please specify a 
not-null not-empty EnumMapper class name");
+
+                    
imports.add("org.apache.ignite.plugin.extensions.communication.mappers.EnumMapper");
+                    imports.add(fullMapperName);
+
+                    String simpleName = 
fullMapperName.substring(fullMapperName.lastIndexOf('.') + 1);
+
+                    String mapperFieldName = enumFieldPrefix + "Mapper";
+
+                    fields.add("private final EnumMapper<" + enumName + "> " + 
mapperFieldName + " = new " + simpleName + "();");
+
+                    mapperCallStmnt = mapperFieldName + ".encode";
+                }
+                else {
+                    imports.add(DLFT_ENUM_MAPPER_CLS);
+                    String enumValuesFieldName = enumFieldPrefix + "Vals";
+
+                    fields.add("private final " + enumName + "[] " + 
enumValuesFieldName + " = " + enumName + ".values();");
+
+                    mapperCallStmnt = "DefaultEnumMapper.INSTANCE.encode";
+                }
+
+                returnFalseIfEnumWriteFailed(write, "writer.writeByte", 
mapperCallStmnt, getExpr);
+            }
+
             else
                 throw new IllegalArgumentException("Unsupported declared type: 
" + type);
 
@@ -405,6 +451,15 @@ class MessageSerializerGenerator {
         throw new IllegalArgumentException("Unsupported type kind: " + 
type.getKind());
     }
 
+    /**
+     * Converts type name to camel case field name. Example: {@code "MyType"} 
-> {@code "myType"}.
+     */
+    private String typeNameToFieldName(String typeName) {
+        char[] typeNameChars = typeName.toCharArray();
+        typeNameChars[0] = Character.toLowerCase(typeNameChars[0]);
+        return new String(typeNameChars);
+    }
+
     /**
      * Generate code of writing single field:
      * <pre>
@@ -419,7 +474,24 @@ class MessageSerializerGenerator {
 
         indent++;
 
-        code.add(line("return false;"));
+        code.add(line(RETURN_FALSE_STMT));
+
+        indent--;
+    }
+
+    /**
+     * Generate code of writing single enum field mapped with EnumMapper:
+     * <pre>
+     * if (!writer.writeByte(myEnumMapper.encode(msg.myEnum()))
+     *     return false;
+     * </pre>
+     */
+    private void returnFalseIfEnumWriteFailed(Collection<String> code, String 
writerCall, String mapperCall, String fieldGetterCall) {
+        code.add(line("if (!%s(%s(msg.%s)))", writerCall, mapperCall, 
fieldGetterCall));
+
+        indent++;
+
+        code.add(line(RETURN_FALSE_STMT));
 
         indent--;
     }
@@ -538,6 +610,17 @@ class MessageSerializerGenerator {
                     "MessageCollectionItemType." + 
messageCollectionItemType(typeArgs.get(0)));
             }
 
+            else if (enumType(type)) {
+                String fieldPrefix = 
typeNameToFieldName(env.getTypeUtils().asElement(type).getSimpleName().toString());
+
+                boolean hasCustMapperAnn = 
field.getAnnotation(CustomMapper.class) != null;
+
+                String mapperCallStmnt = hasCustMapperAnn ? fieldPrefix + 
"Mapper.decode" : "DefaultEnumMapper.INSTANCE.decode";
+                String enumValsFieldName = hasCustMapperAnn ? null : 
fieldPrefix + "Vals";
+
+                returnFalseIfEnumReadFailed(name, mapperCallStmnt, 
enumValsFieldName);
+            }
+
             else
                 throw new IllegalArgumentException("Unsupported declared type: 
" + type);
 
@@ -646,7 +729,36 @@ class MessageSerializerGenerator {
 
         indent++;
 
-        read.add(line("return false;"));
+        read.add(line(RETURN_FALSE_STMT));
+
+        indent--;
+    }
+
+    /**
+     * Generate code of reading single field:
+     * <pre>
+     * msg.id(reader.readInt());
+     *
+     * if (!reader.isLastRead())
+     *     return false;
+     * </pre>
+     *
+     * @param msgSetterName Variable name.
+     * @param mapperDecodeCallStmnt Method name.
+     */
+    private void returnFalseIfEnumReadFailed(String msgSetterName, String 
mapperDecodeCallStmnt, String enumValuesFieldName) {
+        if (enumValuesFieldName == null)
+            read.add(line("msg.%s(%s(reader.readByte()));", msgSetterName, 
mapperDecodeCallStmnt));
+        else
+            read.add(line("msg.%s(%s(%s, reader.readByte()));", msgSetterName, 
mapperDecodeCallStmnt, enumValuesFieldName));
+
+        read.add(EMPTY);
+
+        read.add(line("if (!reader.isLastRead())"));
+
+        indent++;
+
+        read.add(line(RETURN_FALSE_STMT));
 
         indent--;
     }
@@ -680,6 +792,24 @@ class MessageSerializerGenerator {
         return sb.toString();
     }
 
+    /** Write serializer class fields: enum values, custom enum mappers. */
+    private void writeClassFields(Writer writer) throws IOException {
+        if (fields.isEmpty())
+            return;
+
+        indent = 1;
+
+        for (String field: fields) {
+            writer.write(line(METHOD_JAVADOC));
+            writer.write(NL);
+            writer.write(line(field));
+            writer.write(NL);
+        }
+        writer.write(NL);
+
+        indent = 0;
+    }
+
     /** Write header of serializer class: license, imports, class declaration. 
*/
     private void writeClassHeader(Writer writer, String pkgName, String 
serClsName) throws IOException {
         try (InputStream in = 
getClass().getClassLoader().getResourceAsStream("license.txt");
@@ -701,8 +831,8 @@ class MessageSerializerGenerator {
         
imports.add("org.apache.ignite.plugin.extensions.communication.MessageWriter");
         
imports.add("org.apache.ignite.plugin.extensions.communication.MessageReader");
 
-        for (String i: imports)
-            writer.write("import " + i + ";" + NL);
+        for (String regularImport: imports)
+            writer.write("import " + regularImport + ";" + NL);
 
         writer.write(NL);
         writer.write(CLS_JAVADOC);
@@ -727,13 +857,9 @@ class MessageSerializerGenerator {
 
     /** */
     private boolean enumType(TypeMirror type) {
-        if (type.getKind() == TypeKind.DECLARED) {
-            Element element = env.getTypeUtils().asElement(type);
+        Element element = env.getTypeUtils().asElement(type);
 
-            return element != null && element.getKind() == ElementKind.ENUM;
-        }
-
-        return false;
+        return element != null && element.getKind() == ElementKind.ENUM;
     }
 
     /** */
diff --git a/modules/core/pom.xml b/modules/core/pom.xml
index 29e2dc642c5..536bfb76019 100644
--- a/modules/core/pom.xml
+++ b/modules/core/pom.xml
@@ -387,6 +387,10 @@
                     <annotationProcessors>
                         
<annotationProcessor>org.apache.ignite.internal.MessageProcessor</annotationProcessor>
                     </annotationProcessors>
+                    <compilerArgs combine.children="append">
+                        <arg>-Xmaxerrs</arg>
+                        <arg>1000</arg>
+                    </compilerArgs>
                 </configuration>
             </plugin>
         </plugins>
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/GridJobExecuteRequest.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/GridJobExecuteRequest.java
index 27f3e6aa683..22bfb3f3a39 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/GridJobExecuteRequest.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/GridJobExecuteRequest.java
@@ -26,7 +26,6 @@ import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.compute.ComputeJob;
 import org.apache.ignite.compute.ComputeJobSibling;
 import org.apache.ignite.configuration.DeploymentMode;
-import org.apache.ignite.internal.managers.communication.DeploymentModeMessage;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.util.tostring.GridToStringExclude;
 import org.apache.ignite.internal.util.tostring.GridToStringInclude;
@@ -121,8 +120,8 @@ public class GridJobExecuteRequest implements 
ExecutorAwareMessage {
     private IgniteUuid clsLdrId;
 
     /** */
-    @Order(value = 14, method = "deploymentModeMessage")
-    private DeploymentModeMessage depModeMsg;
+    @Order(value = 14, method = "deploymentMode")
+    private DeploymentMode depMode;
 
     /** */
     @Order(15)
@@ -254,7 +253,7 @@ public class GridJobExecuteRequest implements 
ExecutorAwareMessage {
         this.sesAttrs = sesAttrs;
         this.jobAttrs = jobAttrs;
         this.clsLdrId = clsLdrId;
-        depModeMsg = new DeploymentModeMessage(depMode);
+        this.depMode = depMode;
         this.dynamicSiblings = dynamicSiblings;
         this.ldrParticipants = ldrParticipants;
         this.forceLocDep = forceLocDep;
@@ -490,22 +489,15 @@ public class GridJobExecuteRequest implements 
ExecutorAwareMessage {
     /**
      * @return Deployment mode.
      */
-    public DeploymentMode getDeploymentMode() {
-        return depModeMsg.value();
+    public DeploymentMode deploymentMode() {
+        return depMode;
     }
 
     /**
-     * @return Deployment mode messsage.
+     * @param depMode New deployment mode value.
      */
-    public DeploymentModeMessage deploymentModeMessage() {
-        return depModeMsg;
-    }
-
-    /**
-     * @param depModeMsg New deployment mode messsage.
-     */
-    public void deploymentModeMessage(DeploymentModeMessage depModeMsg) {
-        this.depModeMsg = depModeMsg;
+    public void deploymentMode(DeploymentMode depMode) {
+        this.depMode = depMode;
     }
 
     /**
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/DeploymentModeMessage.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/DeploymentModeMessage.java
deleted file mode 100644
index ecd95b3fd9e..00000000000
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/DeploymentModeMessage.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * 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.internal.managers.communication;
-
-import org.apache.ignite.configuration.DeploymentMode;
-import org.apache.ignite.internal.MessageProcessor;
-import org.apache.ignite.internal.Order;
-import org.apache.ignite.plugin.extensions.communication.Message;
-import org.jetbrains.annotations.Nullable;
-
-import static org.apache.ignite.configuration.DeploymentMode.CONTINUOUS;
-import static org.apache.ignite.configuration.DeploymentMode.ISOLATED;
-import static org.apache.ignite.configuration.DeploymentMode.PRIVATE;
-import static org.apache.ignite.configuration.DeploymentMode.SHARED;
-
-/**
- * Message wrapper for {@link DeploymentMode}. See {@link MessageProcessor} 
for details.
- */
-public class DeploymentModeMessage implements Message {
-    /** Type code. */
-    public static final short TYPE_CODE = 515;
-
-    /** Deployment mode. */
-    private DeploymentMode val;
-
-    /** Code. */
-    @Order(0)
-    private byte code = -1;
-
-    /**
-     * Constructor.
-     */
-    public DeploymentModeMessage() {
-    }
-
-    /**
-     * Constructor.
-     */
-    public DeploymentModeMessage(DeploymentMode depMode) {
-        val = depMode;
-        code = encode(depMode);
-    }
-
-    /**
-     * @return Code.
-     */
-    public byte code() {
-        return code;
-    }
-
-    /**
-     * @param code New code.
-     */
-    public void code(byte code) {
-        this.code = code;
-        val = decode(code);
-    }
-
-    /**
-     * @return Deployment mode.
-     */
-    public DeploymentMode value() {
-        return val;
-    }
-
-    /** @param depMode Deployment mode to encode. */
-    private static byte encode(@Nullable DeploymentMode depMode) {
-        if (depMode == null)
-            return -1;
-
-        switch (depMode) {
-            case PRIVATE: return 0;
-            case ISOLATED: return 1;
-            case SHARED: return 2;
-            case CONTINUOUS: return 3;
-        }
-
-        throw new IllegalArgumentException("Unknown deployment mode: " + 
depMode);
-    }
-
-    /** @param code Deployment mode code to decode back to a deployment mode 
value. */
-    @Nullable private static DeploymentMode decode(byte code) {
-        switch (code) {
-            case -1: return null;
-            case 0: return PRIVATE;
-            case 1: return ISOLATED;
-            case 2: return SHARED;
-            case 3: return CONTINUOUS;
-        }
-
-        throw new IllegalArgumentException("Unknown deployment mode code: " + 
code);
-    }
-
-    /** {@inheritDoc} */
-    @Override public short directType() {
-        return TYPE_CODE;
-    }
-}
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoMessageFactory.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoMessageFactory.java
index 8310d963ff7..190e61faea4 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoMessageFactory.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoMessageFactory.java
@@ -48,7 +48,6 @@ import 
org.apache.ignite.internal.codegen.CachePartitionsToReloadMapSerializer;
 import org.apache.ignite.internal.codegen.CacheVersionedValueSerializer;
 import 
org.apache.ignite.internal.codegen.CacheWriteSynchronizationModeMessageSerializer;
 import 
org.apache.ignite.internal.codegen.ClusterMetricsUpdateMessageSerializer;
-import org.apache.ignite.internal.codegen.DeploymentModeMessageSerializer;
 import org.apache.ignite.internal.codegen.ErrorMessageSerializer;
 import org.apache.ignite.internal.codegen.ExchangeInfoSerializer;
 import 
org.apache.ignite.internal.codegen.GenerateEncryptionKeyRequestSerializer;
@@ -160,7 +159,6 @@ import 
org.apache.ignite.internal.codegen.SnapshotFilesFailureMessageSerializer;
 import 
org.apache.ignite.internal.codegen.SnapshotFilesRequestMessageSerializer;
 import 
org.apache.ignite.internal.codegen.TcpInverseConnectionResponseMessageSerializer;
 import 
org.apache.ignite.internal.codegen.TransactionAttributesAwareRequestSerializer;
-import 
org.apache.ignite.internal.codegen.TransactionIsolationMessageSerializer;
 import org.apache.ignite.internal.codegen.TxEntriesInfoSerializer;
 import org.apache.ignite.internal.codegen.TxInfoSerializer;
 import org.apache.ignite.internal.codegen.TxLockListSerializer;
@@ -500,8 +498,6 @@ public class GridIoMessageFactory implements 
MessageFactoryProvider {
             new CachePartitionPartialCountersMapSerializer());
         factory.register(IgniteDhtDemandedPartitionsMap.TYPE_CODE, 
IgniteDhtDemandedPartitionsMap::new,
             new IgniteDhtDemandedPartitionsMapSerializer());
-        factory.register(TransactionIsolationMessage.TYPE_CODE, 
TransactionIsolationMessage::new,
-            new TransactionIsolationMessageSerializer());
         factory.register(CacheWriteSynchronizationModeMessage.TYPE_CODE, 
CacheWriteSynchronizationModeMessage::new,
             new CacheWriteSynchronizationModeMessageSerializer());
         factory.register(GridCacheOperationMessage.TYPE_CODE, 
GridCacheOperationMessage::new, new GridCacheOperationMessageSerializer());
@@ -520,7 +516,6 @@ public class GridIoMessageFactory implements 
MessageFactoryProvider {
         factory.register(IgniteDhtPartitionsToReloadMap.TYPE_CODE, 
IgniteDhtPartitionsToReloadMap::new,
             new IgniteDhtPartitionsToReloadMapSerializer());
         factory.register(IntLongMap.TYPE_CODE, IntLongMap::new, new 
IntLongMapSerializer());
-        factory.register(DeploymentModeMessage.TYPE_CODE, 
DeploymentModeMessage::new, new DeploymentModeMessageSerializer());
         factory.register(IndexKeyTypeMessage.TYPE_CODE, 
IndexKeyTypeMessage::new, new IndexKeyTypeMessageSerializer());
 
         // [-3..119] [124..129] [-23..-28] [-36..-55] [183..188] - this
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/TransactionIsolationMessage.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/TransactionIsolationMessage.java
deleted file mode 100644
index 829cc55bfe9..00000000000
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/TransactionIsolationMessage.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * 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.internal.managers.communication;
-
-import org.apache.ignite.internal.Order;
-import org.apache.ignite.plugin.extensions.communication.Message;
-import org.apache.ignite.transactions.TransactionIsolation;
-import org.jetbrains.annotations.Nullable;
-
-/**
- * Message for {@link TransactionIsolation}.
- * Consistency between code-to-value and value-to-code conversions must be 
provided.
- */
-public class TransactionIsolationMessage implements Message {
-    /** Type code. */
-    public static final short TYPE_CODE = 502;
-
-    /** Transaction isolation. */
-    private TransactionIsolation val;
-
-    /** Code. */
-    @Order(0)
-    private byte code = -1;
-
-    /** Constructor. */
-    public TransactionIsolationMessage() {
-        // No-op.
-    }
-
-    /** Constructor. */
-    public TransactionIsolationMessage(TransactionIsolation val) {
-        this.val = val;
-        code = code(val);
-    }
-
-    /** @return Code. */
-    public byte code() {
-        return code;
-    }
-
-    /** @param code Code. */
-    public void code(byte code) {
-        this.code = code;
-        val = value(code);
-    }
-
-    /** @return Transaction isolation. */
-    public TransactionIsolation value() {
-        return val;
-    }
-
-    /** {@inheritDoc} */
-    @Override public short directType() {
-        return TYPE_CODE;
-    }
-
-    /**
-     * @param val Transaction isolation.
-     * @return Code.
-     */
-    private byte code(@Nullable TransactionIsolation val) {
-        if (val == null)
-            return -1;
-
-        switch (val) {
-            case READ_COMMITTED:
-                return 0;
-
-            case REPEATABLE_READ:
-                return 1;
-
-            case SERIALIZABLE:
-                return 2;
-
-            default:
-                throw new IllegalArgumentException("Unknown transaction 
isolation value: " + val);
-        }
-    }
-
-    /**
-     * @param code Code.
-     * @return Transaction isolation or null.
-     */
-    @Nullable private TransactionIsolation value(byte code) {
-        switch (code) {
-            case -1:
-                return null;
-
-            case 0:
-                return TransactionIsolation.READ_COMMITTED;
-
-            case 1:
-                return TransactionIsolation.REPEATABLE_READ;
-
-            case 2:
-                return TransactionIsolation.SERIALIZABLE;
-
-            default:
-                throw new IllegalArgumentException("Unknown transaction 
isolation code: " + code);
-        }
-    }
-}
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedLockRequest.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedLockRequest.java
index bc1d37828ec..caca8c405ce 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedLockRequest.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedLockRequest.java
@@ -23,7 +23,6 @@ import java.util.UUID;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.internal.Order;
-import 
org.apache.ignite.internal.managers.communication.TransactionIsolationMessage;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
 import org.apache.ignite.internal.processors.cache.KeyCacheObject;
@@ -82,9 +81,9 @@ public class GridDistributedLockRequest extends 
GridDistributedBaseMessage {
     @Order(value = 14, method = "txRead")
     private boolean isRead;
 
-    /** Transaction isolation message. */
+    /** Transaction isolation level. */
     @Order(15)
-    private TransactionIsolationMessage isolation;
+    private TransactionIsolation isolation;
 
     /** Key bytes for keys to lock. */
     @Order(16)
@@ -162,7 +161,7 @@ public class GridDistributedLockRequest extends 
GridDistributedBaseMessage {
         this.futId = futId;
         this.isInTx = isInTx;
         this.isRead = isRead;
-        this.isolation = new TransactionIsolationMessage(isolation);
+        this.isolation = isolation;
         this.isInvalidate = isInvalidate;
         this.timeout = timeout;
         this.txSize = txSize;
@@ -358,16 +357,16 @@ public class GridDistributedLockRequest extends 
GridDistributedBaseMessage {
     }
 
     /**
-     * @return Transaction isolation message.
+     * @return Transaction isolation level.
      */
-    public TransactionIsolationMessage isolation() {
+    public TransactionIsolation isolation() {
         return isolation;
     }
 
     /**
-     * @param isolation Transaction isolation message.
+     * @param isolation Transaction isolation level.
      */
-    public void isolation(TransactionIsolationMessage isolation) {
+    public void isolation(TransactionIsolation isolation) {
         this.isolation = isolation;
     }
 
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedTxFinishRequest.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedTxFinishRequest.java
index 4b7c1a0ad92..20fb93ad205 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedTxFinishRequest.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedTxFinishRequest.java
@@ -21,7 +21,6 @@ import java.util.Collection;
 import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.cache.CacheWriteSynchronizationMode;
 import org.apache.ignite.internal.Order;
-import 
org.apache.ignite.internal.managers.communication.CacheWriteSynchronizationModeMessage;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
 import org.apache.ignite.internal.processors.cache.transactions.IgniteTxState;
@@ -96,9 +95,9 @@ public class GridDistributedTxFinishRequest extends 
GridDistributedBaseMessage i
     @Order(16)
     private byte flags;
 
-    /** Write synchronization mode wrapper message. */
-    @Order(value = 17, method = "writeSynchronizationModeMessage")
-    private CacheWriteSynchronizationModeMessage syncModeMsg;
+    /** Write synchronization mode. */
+    @Order(value = 17)
+    private CacheWriteSynchronizationMode syncMode;
 
     /** Transient TX state. */
     private IgniteTxState txState;
@@ -152,7 +151,7 @@ public class GridDistributedTxFinishRequest extends 
GridDistributedBaseMessage i
         this.commit = commit;
         this.invalidate = invalidate;
         this.plc = plc;
-        syncModeMsg = new CacheWriteSynchronizationModeMessage(syncMode);
+        this.syncMode = syncMode;
         this.baseVer = baseVer;
         this.taskNameHash = taskNameHash;
 
@@ -160,24 +159,17 @@ public class GridDistributedTxFinishRequest extends 
GridDistributedBaseMessage i
     }
 
     /**
-     * @return Transaction write synchronization mode (can be null is message 
sent from old nodes).
+     * @return Transaction write synchronization mode.
      */
     @Nullable public final CacheWriteSynchronizationMode syncMode() {
-        return syncModeMsg != null ? syncModeMsg.value() : null;
+        return syncMode;
     }
 
     /**
-     * @return Transaction write synchronization mode wrapper message.
+     * @param syncMode Transaction write synchronization mode.
      */
-    public CacheWriteSynchronizationModeMessage 
writeSynchronizationModeMessage() {
-        return syncModeMsg;
-    }
-
-    /**
-     * @param syncModeMsg Transaction write synchronization mode wrapper 
message.
-     */
-    public void 
writeSynchronizationModeMessage(CacheWriteSynchronizationModeMessage 
syncModeMsg) {
-        this.syncModeMsg = syncModeMsg;
+    public void syncMode(CacheWriteSynchronizationMode syncMode) {
+        this.syncMode = syncMode;
     }
 
     /**
@@ -346,9 +338,9 @@ public class GridDistributedTxFinishRequest extends 
GridDistributedBaseMessage i
      * @return {@code True} if reply is required.
      */
     public boolean replyRequired() {
-        assert syncModeMsg != null && syncModeMsg.value() != null;
+        assert syncMode != null;
 
-        return syncModeMsg.value() == FULL_SYNC;
+        return syncMode == FULL_SYNC;
     }
 
     /** {@inheritDoc} */
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTransactionalCacheAdapter.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTransactionalCacheAdapter.java
index f6348b142c7..9a813db7180 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTransactionalCacheAdapter.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTransactionalCacheAdapter.java
@@ -235,7 +235,7 @@ public abstract class GridDhtTransactionalCacheAdapter<K, 
V> extends GridDhtCach
                                     ctx.systemTx(),
                                     ctx.ioPolicy(),
                                     PESSIMISTIC,
-                                    req.isolation().value(),
+                                    req.isolation(),
                                     req.isInvalidate(),
                                     req.timeout(),
                                     req.txSize(),
@@ -919,7 +919,7 @@ public abstract class GridDhtTransactionalCacheAdapter<K, 
V> extends GridDhtCach
                             false,
                             ctx.ioPolicy(),
                             PESSIMISTIC,
-                            req.isolation().value(),
+                            req.isolation(),
                             req.timeout(),
                             req.isInvalidate(),
                             !req.skipStore(),
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/job/GridJobProcessor.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/job/GridJobProcessor.java
index 956396c2e39..c65d739518b 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/job/GridJobProcessor.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/job/GridJobProcessor.java
@@ -1220,7 +1220,7 @@ public class GridJobProcessor extends 
GridProcessorAdapter {
             GridDeployment tmpDep = req.forceLocalDeployment() ?
                 ctx.deploy().getLocalDeployment(req.taskClassName()) :
                 ctx.deploy().getGlobalDeployment(
-                    req.getDeploymentMode(),
+                    req.deploymentMode(),
                     req.taskName(),
                     req.taskClassName(),
                     req.userVersion(),
@@ -1385,7 +1385,7 @@ public class GridJobProcessor extends 
GridProcessorAdapter {
                     IgniteException ex = new IgniteDeploymentException("Task 
was not deployed or was redeployed since " +
                         "task execution [taskName=" + req.taskName() + ", 
taskClsName=" + req.taskClassName() +
                         ", codeVer=" + req.userVersion() + ", clsLdrId=" + 
req.classLoaderId() +
-                        ", seqNum=" + req.classLoaderId().localId() + ", 
depMode=" + req.getDeploymentMode() +
+                        ", seqNum=" + req.classLoaderId().localId() + ", 
depMode=" + req.deploymentMode() +
                         ", dep=" + dep + ']');
 
                     U.error(log, ex.getMessage(), ex);
diff --git 
a/modules/core/src/main/java/org/apache/ignite/plugin/extensions/communication/mappers/DefaultEnumMapper.java
 
b/modules/core/src/main/java/org/apache/ignite/plugin/extensions/communication/mappers/DefaultEnumMapper.java
new file mode 100644
index 00000000000..14abc09619f
--- /dev/null
+++ 
b/modules/core/src/main/java/org/apache/ignite/plugin/extensions/communication/mappers/DefaultEnumMapper.java
@@ -0,0 +1,92 @@
+/*
+ * 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.plugin.extensions.communication.mappers;
+
+/**
+ * A default enum mapper that uses enum ordinal values to perform 
serialization and deserialization.
+ *
+ * <p>This mapper encodes an enum constant into a {@code byte} by taking its 
{@link Enum#ordinal()},
+ * and decodes a {@code byte} value back into the corresponding enum constant 
using array indexing.
+ * The encoding returns {@code -1} for {@code null} inputs, and decoding 
returns {@code null} for
+ * negative codes. If a provided code is out of range (greater than or equal 
to the number of enum
+ * constants), an {@link IllegalArgumentException} is thrown.</p>
+ *
+ * <p>This class assumes that:
+ * <ul>
+ *     <li>Enums have no more than 127 constants (to fit in a positive {@code 
byte}).</li>
+ *     <li>The order of enum constants (i.e., their ordinals) is stable and 
not subject to change.</li>
+ * </ul>
+ * </p>
+ *
+ * <p>Example usage:</p>
+ * <pre>{@code
+ * public enum Color {
+ *     RED, GREEN, BLUE;
+ * }
+ *
+ * Color[] values = Color.values();
+ * byte code = DefaultEnumMapper.INSTANCE.encode(Color.RED); // Returns 0
+ * Color color = DefaultEnumMapper.INSTANCE.decode(values, code); // Returns 
Color.RED
+ * }</pre>
+ *
+ * <p><strong>Note:</strong> This class is thread-safe and uses a singleton 
pattern.
+ * Use {@link #INSTANCE} to access the shared instance.</p>
+ *
+ * @see Enum#ordinal()
+ */
+public final class DefaultEnumMapper {
+    /** */
+    public static final DefaultEnumMapper INSTANCE = new DefaultEnumMapper();
+
+    /** */
+    private DefaultEnumMapper() {}
+
+    /**
+     * Encodes the given enum value into a {@code byte} using its ordinal.
+     *
+     * @param <T> The enum type.
+     * @param enumVal The enum value to encode; may be {@code null}.
+     * @return The {@code byte} representation of the enum value 
(non-negative) or {@code -1} if the value is {@code null}.
+     */
+    public <T extends Enum<T>> byte encode(T enumVal) {
+        if (enumVal == null)
+            return -1;
+
+        return (byte)enumVal.ordinal();
+    }
+
+    /**
+     * Decodes a {@code byte} code into the corresponding enum constant.
+     *
+     * @param <T> The enum type.
+     * @param vals Array of all possible values of the enum type. Must not be 
{@code null}.
+     * @param enumCode The {@code byte} representation of the enum value.
+     * @return The corresponding enum constant, or {@code null} if {@code 
enumCode} is negative.
+     * @throws IllegalArgumentException if {@code enumCode} is out of range
+     * (i.e., greater than or equal to the length of {@code vals} array).
+     */
+    public <T extends Enum<T>> T decode(T[] vals, byte enumCode) {
+        if (enumCode < 0)
+            return null;
+
+        if (enumCode > vals.length - 1)
+            throw new IllegalArgumentException("Enum code " + enumCode + " is 
out of range for enum type " + vals[0].getClass().getName());
+
+        return vals[enumCode];
+    }
+}
diff --git 
a/modules/core/src/main/java/org/apache/ignite/plugin/extensions/communication/mappers/EnumMapper.java
 
b/modules/core/src/main/java/org/apache/ignite/plugin/extensions/communication/mappers/EnumMapper.java
new file mode 100644
index 00000000000..f4866bac3c0
--- /dev/null
+++ 
b/modules/core/src/main/java/org/apache/ignite/plugin/extensions/communication/mappers/EnumMapper.java
@@ -0,0 +1,86 @@
+/*
+ * 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.plugin.extensions.communication.mappers;
+
+/**
+ * An interface to implement custom serialization and deserialization of enum 
values used in network communication.
+ *
+ * <p>This interface allows users to define a custom mapping between enum 
constants and their {@code byte}
+ * representations sent over the wire, instead of relying on the default 
ordinal-based encoding.
+ * It can be used in conjunction with the {@code 
org.apache.ignite.internal.CustomMapper} annotation
+ * to plug in user-defined mapping logic during generation of serialization 
code.</p>
+ *
+ * <p>Implementations must ensure that:
+ * <ul>
+ *     <li>Each enum constant maps to a unique {@code byte} value.</li>
+ *     <li>The {@code byte} codes are stable and consistent across all nodes 
in the cluster.</li>
+ *     <li>The {@link #decode(byte)} method handles invalid or unknown codes 
appropriately
+ *     (e.g., throws an exception or returns a default).</li>
+ * </ul>
+ * </p>
+ *
+ * <p>Example implementation:</p>
+ * <pre>{@code
+ * public class MyColorMapper implements CustomMapper<Color> {
+ *     public byte encode(Color color) {
+ *         switch (color) {
+ *             case null:  return -1;
+ *             case RED:   return 0;
+ *             case GREEN: return 1;
+ *             case BLUE:  return 2;
+ *             default:    throw new IllegalArgumentException("Unknown color: 
" + color);
+ *         }
+ *     }
+ *
+ *     public Color decode(byte code) {
+ *         switch (code) {
+ *             case -1: return null;
+ *             case 0: return Color.RED;
+ *             case 1: return Color.GREEN;
+ *             case 2: return Color.BLUE;
+ *             default: throw new IllegalArgumentException("Unknown color 
code: " + code);
+ *         }
+ *     }
+ * }
+ * }</pre>
+ *
+ * <p><strong>Note:</strong> This interface is used in Ignite's communication 
layer
+ * to enable evolution of enum types between different versions of the 
software.</p>
+ *
+ * @param <T> The enum type for which this mapper provides encoding/decoding 
logic.
+ * @see org.apache.ignite.internal.CustomMapper
+ * @see DefaultEnumMapper
+ */
+public interface EnumMapper<T extends Enum<T>> {
+    /**
+     * Encodes given enum value into a {@code byte} representation to be sent 
over the network.
+     *
+     * @param val The enum value to encode; may be {@code null} depending on 
implementation contract.
+     * @return The {@code byte} representation of the enum value.
+     */
+    public byte encode(T val);
+
+    /**
+     * Decodes a {@code byte} code received from the network into the 
corresponding enum constant.
+     *
+     * @param code The {@code byte} representation of the enum value.
+     * @return The decoded enum value.
+     * @throws IllegalArgumentException if the code does not correspond to any 
valid enum constant.
+     */
+    public T decode(byte code);
+}
diff --git 
a/modules/core/src/test/java/org/apache/ignite/internal/codegen/MessageProcessorTest.java
 
b/modules/core/src/test/java/org/apache/ignite/internal/codegen/MessageProcessorTest.java
index 18e7783f570..f2611ca9e3c 100644
--- 
a/modules/core/src/test/java/org/apache/ignite/internal/codegen/MessageProcessorTest.java
+++ 
b/modules/core/src/test/java/org/apache/ignite/internal/codegen/MessageProcessorTest.java
@@ -30,6 +30,7 @@ import org.apache.ignite.internal.Order;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.lang.IgniteUuid;
 import org.apache.ignite.plugin.extensions.communication.Message;
+import 
org.apache.ignite.plugin.extensions.communication.mappers.DefaultEnumMapper;
 import org.apache.ignite.transactions.TransactionIsolation;
 import org.junit.Test;
 
@@ -171,13 +172,100 @@ public class MessageProcessorTest {
         assertThat(compilation).hadErrorContaining("You should use 
ErrorMessage for serialization of throwables.");
     }
 
-    /** */
+    /**
+     * Positive test for default enum mapper for enum fields.
+     */
+    @Test
+    public void testDefaultMapperEnumFields() {
+        Compilation compilation = 
compile("DefaultMapperEnumFieldsMessage.java");
+
+        assertThat(compilation).succeeded();
+
+        assertThat(compilation)
+            
.generatedSourceFile("org.apache.ignite.internal.codegen.DefaultMapperEnumFieldsMessageSerializer")
+            
.hasSourceEquivalentTo(javaFile("DefaultMapperEnumFieldsMessageSerializer.java"));
+    }
+
+    /**
+     * Negative test for CustomMapper annotation verifying an error is thrown 
by codegeneration tool if
+     * the annotation is used with a field of a primitive type.
+     */
     @Test
-    public void testEnumFieldFailed() {
-        Compilation compilation = compile("UnwrappedEnumFieldMessage.java");
+    public void testCustomMapperCannotBeUsedOnPrimitiveField() {
+        Compilation compilation = 
compile("CustomEnumMapperOnPrimitiveFieldMessage.java");
 
         assertThat(compilation).failed();
-        assertThat(compilation).hadErrorContaining("Unsupported enum type: " + 
TransactionIsolation.class.getName());
+        assertThat(compilation).hadErrorContaining("Annotation @CustomMapper 
must only be used for enum fields.");
+    }
+
+    /**
+     * Negative test for CustomMapper annotation verifying an error is thrown 
by codegeneration tool if
+     * the annotation is used with a field of an array type.
+     */
+    @Test
+    public void testCustomMapperCannotBeUsedOnArrayField() {
+        Compilation compilation = 
compile("CustomEnumMapperOnArrayFieldMessage.java");
+
+        assertThat(compilation).failed();
+        assertThat(compilation).hadErrorContaining("Annotation @CustomMapper 
must only be used for enum fields.");
+    }
+
+    /**
+     * Positive test for custom EnumMapper implementation for enum field: 
codegeneration tool
+     * generates a serializer using provided EnumMapper implementation.
+     * Generated serializer compiles successfully.
+     */
+    @Test
+    public void testCustomMapperEnumFieldsMessage() {
+        Compilation compilation = 
compile("CustomMapperEnumFieldsMessage.java", 
"TransactionIsolationEnumMapper.java");
+
+        assertThat(compilation).succeeded();
+
+        assertThat(compilation)
+            
.generatedSourceFile("org.apache.ignite.internal.codegen.CustomMapperEnumFieldsMessageSerializer")
+            
.hasSourceEquivalentTo(javaFile("CustomMapperEnumFieldsMessageSerializer.java"));
+    }
+
+    /**
+     * Negative test for a coflict situation when two enum mappers are used 
for the same enum in different messages.
+     */
+    @Test
+    public void testDifferentMappersForTheSameEnumAreProhibited() {
+        Compilation compilation = 
compile("DefaultMapperEnumFieldsMessage.java",
+            "CustomMapperEnumFieldsMessage.java",
+            "TransactionIsolationEnumMapper.java");
+
+        assertThat(compilation).failed();
+
+        String errMsg = "Enum " + TransactionIsolation.class.getName() + " is 
declared with different mappers: " +
+            DefaultEnumMapper.class.getName() + " in 
org.apache.ignite.internal.DefaultMapperEnumFieldsMessage" +
+            " and org.apache.ignite.internal.TransactionIsolationEnumMapper in 
org.apache.ignite.internal.CustomMapperEnumFieldsMessage.";
+
+        assertThat(compilation).hadErrorContaining(errMsg);
+    }
+
+    /**
+     * Positive test verifies that codegeneration is successful when two 
messages use DefaultEnumMapper for the same enum type.
+     */
+    @Test
+    public void testDefaultMapperForSameEnumTypeInDifferentMessagesIsAllowed() 
{
+        Compilation compilation = 
compile("DefaultMapperEnumFieldsMessage.java",
+            "DefaultMapperEnumFieldsSecondMessage.java");
+
+        assertThat(compilation).succeeded();
+    }
+
+    /**
+     * Positive test verifies that codegeneration is successful when two 
messages use
+     * the same custom EnumMapper for the same enum type.
+     */
+    @Test
+    public void 
testSameCustomMapperForSameEnumTypeInDifferentMessagesIsAllowed() {
+        Compilation compilation = compile("CustomMapperEnumFieldsMessage.java",
+            "CustomMapperEnumFieldsSecondMessage.java",
+            "TransactionIsolationEnumMapper.java");
+
+        assertThat(compilation).succeeded();
     }
 
     /** */
diff --git 
a/modules/core/src/test/java/org/apache/ignite/internal/managers/communication/DefaultEnumMapperTest.java
 
b/modules/core/src/test/java/org/apache/ignite/internal/managers/communication/DefaultEnumMapperTest.java
new file mode 100644
index 00000000000..67a94dd80ae
--- /dev/null
+++ 
b/modules/core/src/test/java/org/apache/ignite/internal/managers/communication/DefaultEnumMapperTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.internal.managers.communication;
+
+import 
org.apache.ignite.plugin.extensions.communication.mappers.DefaultEnumMapper;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.transactions.TransactionIsolation;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+/**
+ * Verifies behavior of {@link DefaultEnumMapper} on a single enum type.
+ */
+public class DefaultEnumMapperTest {
+    /** */
+    private final TransactionIsolation[] txIsolationVals = 
TransactionIsolation.values();
+
+    /** */
+    @Test
+    public void testEncode() {
+        assertEquals(-1, DefaultEnumMapper.INSTANCE.encode(null));
+
+        for (TransactionIsolation txIsolation : txIsolationVals)
+            assertEquals(txIsolation.ordinal(), 
DefaultEnumMapper.INSTANCE.encode(txIsolation));
+    }
+
+    /** */
+    @Test
+    public void testDecode() {
+        assertNull(DefaultEnumMapper.INSTANCE.decode(txIsolationVals, 
(byte)-1));
+
+        for (TransactionIsolation txIsolation : txIsolationVals) {
+            assertEquals(txIsolationVals[txIsolation.ordinal()],
+                DefaultEnumMapper.INSTANCE.decode(txIsolationVals, 
(byte)txIsolation.ordinal()));
+        }
+
+        Throwable ex = GridTestUtils.assertThrowsWithCause(
+            () -> DefaultEnumMapper.INSTANCE.decode(txIsolationVals, 
(byte)txIsolationVals.length),
+            IllegalArgumentException.class
+        );
+
+        assertEquals("Enum code " + txIsolationVals.length + " is out of range 
for enum type " +
+                TransactionIsolation.class.getName(),
+            ex.getMessage());
+    }
+}
diff --git 
a/modules/core/src/test/java/org/apache/ignite/internal/managers/communication/DeploymentModeMessageTest.java
 
b/modules/core/src/test/java/org/apache/ignite/internal/managers/communication/DeploymentModeMessageTest.java
deleted file mode 100644
index 5821a8d73e9..00000000000
--- 
a/modules/core/src/test/java/org/apache/ignite/internal/managers/communication/DeploymentModeMessageTest.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * 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.internal.managers.communication;
-
-import org.apache.ignite.configuration.DeploymentMode;
-import org.apache.ignite.internal.util.typedef.F;
-import org.junit.Test;
-
-import static org.apache.ignite.configuration.DeploymentMode.CONTINUOUS;
-import static org.apache.ignite.configuration.DeploymentMode.ISOLATED;
-import static org.apache.ignite.configuration.DeploymentMode.PRIVATE;
-import static org.apache.ignite.configuration.DeploymentMode.SHARED;
-import static 
org.apache.ignite.testframework.GridTestUtils.assertThrowsWithCause;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-
-/** */
-public class DeploymentModeMessageTest {
-    /** */
-    @Test
-    public void testDeploymentModeCode() {
-        assertEquals(-1, new DeploymentModeMessage(null).code());
-        assertEquals(0, new DeploymentModeMessage(PRIVATE).code());
-        assertEquals(1, new DeploymentModeMessage(ISOLATED).code());
-        assertEquals(2, new DeploymentModeMessage(SHARED).code());
-        assertEquals(3, new DeploymentModeMessage(CONTINUOUS).code());
-
-        for (DeploymentMode depMode : DeploymentMode.values()) {
-            assertTrue(new DeploymentModeMessage(depMode).code() >= 0);
-            assertTrue(new DeploymentModeMessage(depMode).code() < 4);
-        }
-    }
-
-    /** */
-    @Test
-    public void testDeploymentModeFromCode() {
-        DeploymentModeMessage msg = new DeploymentModeMessage(null);
-
-        msg.code((byte)-1);
-        assertNull(msg.value());
-
-        msg.code((byte)0);
-        assertSame(PRIVATE, msg.value());
-
-        msg.code((byte)1);
-        assertSame(ISOLATED, msg.value());
-
-        msg.code((byte)2);
-        assertSame(SHARED, msg.value());
-
-        msg.code((byte)3);
-        assertSame(CONTINUOUS, msg.value());
-
-        Throwable t = assertThrowsWithCause(() -> msg.code((byte)4), 
IllegalArgumentException.class);
-        assertEquals("Unknown deployment mode code: 4", t.getMessage());
-    }
-
-    /** */
-    @Test
-    public void testConversionConsistency() {
-        for (DeploymentMode depMode : F.concat(DeploymentMode.values(), 
(DeploymentMode)null)) {
-            DeploymentModeMessage msg = new DeploymentModeMessage(depMode);
-
-            assertEquals(depMode, msg.value());
-
-            DeploymentModeMessage newMsg = new DeploymentModeMessage();
-            newMsg.code(msg.code());
-
-            assertEquals(msg.value(), newMsg.value());
-        }
-    }
-}
diff --git 
a/modules/core/src/test/java/org/apache/ignite/internal/managers/communication/TransactionIsolationMessageTest.java
 
b/modules/core/src/test/java/org/apache/ignite/internal/managers/communication/TransactionIsolationMessageTest.java
deleted file mode 100644
index 1b41cb79044..00000000000
--- 
a/modules/core/src/test/java/org/apache/ignite/internal/managers/communication/TransactionIsolationMessageTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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.internal.managers.communication;
-
-import org.apache.ignite.internal.util.typedef.F;
-import org.apache.ignite.transactions.TransactionIsolation;
-import org.junit.Test;
-
-import static 
org.apache.ignite.testframework.GridTestUtils.assertThrowsWithCause;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-
-/** */
-public class TransactionIsolationMessageTest {
-    /** */
-    @Test
-    public void testTransactionIsolationCode() {
-        assertEquals(-1, new TransactionIsolationMessage(null).code());
-        assertEquals(0, new 
TransactionIsolationMessage(TransactionIsolation.READ_COMMITTED).code());
-        assertEquals(1, new 
TransactionIsolationMessage(TransactionIsolation.REPEATABLE_READ).code());
-        assertEquals(2, new 
TransactionIsolationMessage(TransactionIsolation.SERIALIZABLE).code());
-
-        for (TransactionIsolation isolation : TransactionIsolation.values())
-            assertTrue(new TransactionIsolationMessage(isolation).code() != 
-1);
-    }
-
-    /** */
-    @Test
-    public void testTransactionIsolationFromCode() {
-        TransactionIsolationMessage msg = new 
TransactionIsolationMessage(null);
-
-        msg.code((byte)-1);
-        assertNull(msg.value());
-
-        msg.code((byte)0);
-        assertSame(TransactionIsolation.READ_COMMITTED, msg.value());
-
-        msg.code((byte)1);
-        assertSame(TransactionIsolation.REPEATABLE_READ, msg.value());
-
-        msg.code((byte)2);
-        assertSame(TransactionIsolation.SERIALIZABLE, msg.value());
-
-        Throwable t = assertThrowsWithCause(() -> msg.code((byte)3), 
IllegalArgumentException.class);
-        assertEquals("Unknown transaction isolation code: 3", t.getMessage());
-    }
-
-    /** */
-    @Test
-    public void testConversionConsistency() {
-        for (TransactionIsolation isolation : 
F.concat(TransactionIsolation.values(), (TransactionIsolation)null)) {
-            TransactionIsolationMessage msg = new 
TransactionIsolationMessage(isolation);
-
-            assertEquals(isolation, msg.value());
-
-            TransactionIsolationMessage newMsg = new 
TransactionIsolationMessage();
-            newMsg.code(msg.code());
-
-            assertEquals(msg.value(), newMsg.value());
-        }
-    }
-}
diff --git 
a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java
 
b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java
index ffb0e72b29d..303b2431d78 100644
--- 
a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java
+++ 
b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java
@@ -39,11 +39,10 @@ import 
org.apache.ignite.internal.TransactionsMXBeanImplTest;
 import org.apache.ignite.internal.codegen.MessageProcessorTest;
 import 
org.apache.ignite.internal.managers.communication.CacheEntryPredicateAdapterMessageTest;
 import 
org.apache.ignite.internal.managers.communication.CacheWriteSynchroizationModeMessageTest;
-import 
org.apache.ignite.internal.managers.communication.DeploymentModeMessageTest;
+import org.apache.ignite.internal.managers.communication.DefaultEnumMapperTest;
 import org.apache.ignite.internal.managers.communication.ErrorMessageSelfTest;
 import 
org.apache.ignite.internal.managers.communication.GridCacheOperationModeMessageTest;
 import 
org.apache.ignite.internal.managers.communication.IndexKeyTypeMessageTest;
-import 
org.apache.ignite.internal.managers.communication.TransactionIsolationMessageTest;
 import 
org.apache.ignite.internal.processors.affinity.GridAffinityAssignmentV2Test;
 import 
org.apache.ignite.internal.processors.affinity.GridAffinityAssignmentV2TestNoOptimizations;
 import 
org.apache.ignite.internal.processors.affinity.GridAffinityProcessorRendezvousSelfTest;
@@ -152,11 +151,10 @@ import org.junit.runners.Suite;
 
     MessageProcessorTest.class,
     ErrorMessageSelfTest.class,
-    TransactionIsolationMessageTest.class,
     GridCacheOperationModeMessageTest.class,
     CacheWriteSynchroizationModeMessageTest.class,
     CacheEntryPredicateAdapterMessageTest.class,
-    DeploymentModeMessageTest.class,
+    DefaultEnumMapperTest.class,
     IndexKeyTypeMessageTest.class
 })
 public class IgniteBasicTestSuite {
diff --git 
a/modules/core/src/test/resources/codegen/UnwrappedEnumFieldMessage.java 
b/modules/core/src/test/resources/codegen/CustomEnumMapperOnArrayFieldMessage.java
similarity index 74%
copy from modules/core/src/test/resources/codegen/UnwrappedEnumFieldMessage.java
copy to 
modules/core/src/test/resources/codegen/CustomEnumMapperOnArrayFieldMessage.java
index fa16ff85d54..e8a755c6d52 100644
--- a/modules/core/src/test/resources/codegen/UnwrappedEnumFieldMessage.java
+++ 
b/modules/core/src/test/resources/codegen/CustomEnumMapperOnArrayFieldMessage.java
@@ -18,21 +18,22 @@
 package org.apache.ignite.internal;
 
 import org.apache.ignite.plugin.extensions.communication.Message;
-import org.apache.ignite.transactions.TransactionIsolation;
 
-public class UnwrappedEnumFieldMessage implements Message {
+public class CustomEnumMapperOnArrayFieldMessage implements Message {
     @Order(0)
-    private TransactionIsolation isolation;
+    @CustomMapper("org.apache.ignite.internal.CustomEnumMapper")
+    private int[] intArrayField;
 
-    public TransactionIsolation isolation() {
-        return isolation;
+    public int[] intArrayField() {
+        return intField;
     }
 
-    public void isolation(TransactionIsolation isolation) {
-        this.isolation = isolation;
+    public void intArrayField(int[] intArrayField) {
+        this.intArrayField = intArrayField;
     }
 
+    @Override
     public short directType() {
         return 0;
     }
-}
+}
\ No newline at end of file
diff --git 
a/modules/core/src/test/resources/codegen/UnwrappedEnumFieldMessage.java 
b/modules/core/src/test/resources/codegen/CustomEnumMapperOnPrimitiveFieldMessage.java
similarity index 74%
copy from modules/core/src/test/resources/codegen/UnwrappedEnumFieldMessage.java
copy to 
modules/core/src/test/resources/codegen/CustomEnumMapperOnPrimitiveFieldMessage.java
index fa16ff85d54..a428f93a893 100644
--- a/modules/core/src/test/resources/codegen/UnwrappedEnumFieldMessage.java
+++ 
b/modules/core/src/test/resources/codegen/CustomEnumMapperOnPrimitiveFieldMessage.java
@@ -18,21 +18,21 @@
 package org.apache.ignite.internal;
 
 import org.apache.ignite.plugin.extensions.communication.Message;
-import org.apache.ignite.transactions.TransactionIsolation;
 
-public class UnwrappedEnumFieldMessage implements Message {
+public class CustomEnumMapperOnPrimitiveFieldMessage implements Message {
     @Order(0)
-    private TransactionIsolation isolation;
+    @CustomMapper("org.apache.ignite.internal.CustomEnumMapper")
+    private int intField;
 
-    public TransactionIsolation isolation() {
-        return isolation;
+    public int intField() {
+        return intField;
     }
 
-    public void isolation(TransactionIsolation isolation) {
-        this.isolation = isolation;
+    public void intField(int intField) {
+        this.intField = intField;
     }
 
     public short directType() {
         return 0;
     }
-}
+}
\ No newline at end of file
diff --git 
a/modules/core/src/test/resources/codegen/UnwrappedEnumFieldMessage.java 
b/modules/core/src/test/resources/codegen/CustomMapperEnumFieldsMessage.java
similarity index 75%
copy from modules/core/src/test/resources/codegen/UnwrappedEnumFieldMessage.java
copy to 
modules/core/src/test/resources/codegen/CustomMapperEnumFieldsMessage.java
index fa16ff85d54..a681362e794 100644
--- a/modules/core/src/test/resources/codegen/UnwrappedEnumFieldMessage.java
+++ b/modules/core/src/test/resources/codegen/CustomMapperEnumFieldsMessage.java
@@ -20,19 +20,20 @@ package org.apache.ignite.internal;
 import org.apache.ignite.plugin.extensions.communication.Message;
 import org.apache.ignite.transactions.TransactionIsolation;
 
-public class UnwrappedEnumFieldMessage implements Message {
+public class CustomMapperEnumFieldsMessage implements Message {
     @Order(0)
-    private TransactionIsolation isolation;
+    @CustomMapper("org.apache.ignite.internal.TransactionIsolationEnumMapper")
+    private TransactionIsolation txMode;
 
-    public TransactionIsolation isolation() {
-        return isolation;
+    public TransactionIsolation txMode() {
+        return txMode;
     }
 
-    public void isolation(TransactionIsolation isolation) {
-        this.isolation = isolation;
+    public void txMode(TransactionIsolation txMode) {
+        this.txMode = txMode;
     }
 
     public short directType() {
         return 0;
     }
-}
+}
\ No newline at end of file
diff --git 
a/modules/core/src/test/resources/codegen/CustomMapperEnumFieldsMessageSerializer.java
 
b/modules/core/src/test/resources/codegen/CustomMapperEnumFieldsMessageSerializer.java
new file mode 100644
index 00000000000..011e5424e8a
--- /dev/null
+++ 
b/modules/core/src/test/resources/codegen/CustomMapperEnumFieldsMessageSerializer.java
@@ -0,0 +1,76 @@
+/*
+ * 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.internal.codegen;
+
+import org.apache.ignite.internal.CustomMapperEnumFieldsMessage;
+import org.apache.ignite.internal.TransactionIsolationEnumMapper;
+import org.apache.ignite.plugin.extensions.communication.Message;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageSerializer;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+import org.apache.ignite.plugin.extensions.communication.mappers.EnumMapper;
+import org.apache.ignite.transactions.TransactionIsolation;
+
+/**
+ * This class is generated automatically.
+ *
+ * @see org.apache.ignite.internal.MessageProcessor
+ */
+public class CustomMapperEnumFieldsMessageSerializer implements 
MessageSerializer {
+    /** */
+    private final EnumMapper<TransactionIsolation> transactionIsolationMapper 
= new TransactionIsolationEnumMapper();
+
+    /** */
+    @Override public boolean writeTo(Message m, MessageWriter writer) {
+        CustomMapperEnumFieldsMessage msg = (CustomMapperEnumFieldsMessage)m;
+
+        if (!writer.isHeaderWritten()) {
+            if (!writer.writeHeader(msg.directType()))
+                return false;
+
+            writer.onHeaderWritten();
+        }
+
+        switch (writer.state()) {
+            case 0:
+                if 
(!writer.writeByte(transactionIsolationMapper.encode(msg.txMode())))
+                    return false;
+
+                writer.incrementState();
+        }
+
+        return true;
+    }
+
+    /** */
+    @Override public boolean readFrom(Message m, MessageReader reader) {
+        CustomMapperEnumFieldsMessage msg = (CustomMapperEnumFieldsMessage)m;
+
+        switch (reader.state()) {
+            case 0:
+                
msg.txMode(transactionIsolationMapper.decode(reader.readByte()));
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+        }
+
+        return true;
+    }
+}
\ No newline at end of file
diff --git 
a/modules/core/src/test/resources/codegen/UnwrappedEnumFieldMessage.java 
b/modules/core/src/test/resources/codegen/CustomMapperEnumFieldsSecondMessage.java
similarity index 75%
copy from modules/core/src/test/resources/codegen/UnwrappedEnumFieldMessage.java
copy to 
modules/core/src/test/resources/codegen/CustomMapperEnumFieldsSecondMessage.java
index fa16ff85d54..9b5bc91d032 100644
--- a/modules/core/src/test/resources/codegen/UnwrappedEnumFieldMessage.java
+++ 
b/modules/core/src/test/resources/codegen/CustomMapperEnumFieldsSecondMessage.java
@@ -20,19 +20,20 @@ package org.apache.ignite.internal;
 import org.apache.ignite.plugin.extensions.communication.Message;
 import org.apache.ignite.transactions.TransactionIsolation;
 
-public class UnwrappedEnumFieldMessage implements Message {
+public class CustomMapperEnumFieldsSecondMessage implements Message {
     @Order(0)
-    private TransactionIsolation isolation;
+    @CustomMapper("org.apache.ignite.internal.TransactionIsolationEnumMapper")
+    private TransactionIsolation txMode;
 
-    public TransactionIsolation isolation() {
-        return isolation;
+    public TransactionIsolation txMode() {
+        return txMode;
     }
 
-    public void isolation(TransactionIsolation isolation) {
-        this.isolation = isolation;
+    public void txMode(TransactionIsolation txMode) {
+        this.txMode = txMode;
     }
 
     public short directType() {
         return 0;
     }
-}
+}
\ No newline at end of file
diff --git 
a/modules/core/src/test/resources/codegen/UnwrappedEnumFieldMessage.java 
b/modules/core/src/test/resources/codegen/DefaultMapperEnumFieldsMessage.java
similarity index 63%
copy from modules/core/src/test/resources/codegen/UnwrappedEnumFieldMessage.java
copy to 
modules/core/src/test/resources/codegen/DefaultMapperEnumFieldsMessage.java
index fa16ff85d54..ceaf1bfc236 100644
--- a/modules/core/src/test/resources/codegen/UnwrappedEnumFieldMessage.java
+++ 
b/modules/core/src/test/resources/codegen/DefaultMapperEnumFieldsMessage.java
@@ -17,19 +17,31 @@
 
 package org.apache.ignite.internal;
 
+import org.apache.ignite.internal.processors.cache.GridCacheOperation;
 import org.apache.ignite.plugin.extensions.communication.Message;
 import org.apache.ignite.transactions.TransactionIsolation;
 
-public class UnwrappedEnumFieldMessage implements Message {
+public class DefaultMapperEnumFieldsMessage implements Message {
     @Order(0)
-    private TransactionIsolation isolation;
+    private TransactionIsolation publicEnum;
 
-    public TransactionIsolation isolation() {
-        return isolation;
+    @Order(1)
+    private GridCacheOperation internalEnum;
+
+    public TransactionIsolation publicEnum() {
+        return publicEnum;
+    }
+
+    public void publicEnum(TransactionIsolation publicEnum) {
+        this.publicEnum = publicEnum;
+    }
+
+    public GridCacheOperation internalEnum() {
+        return internalEnum;
     }
 
-    public void isolation(TransactionIsolation isolation) {
-        this.isolation = isolation;
+    public void internalEnum(GridCacheOperation internalEnum) {
+        this.internalEnum = internalEnum;
     }
 
     public short directType() {
diff --git 
a/modules/core/src/test/resources/codegen/DefaultMapperEnumFieldsMessageSerializer.java
 
b/modules/core/src/test/resources/codegen/DefaultMapperEnumFieldsMessageSerializer.java
new file mode 100644
index 00000000000..aaa59863997
--- /dev/null
+++ 
b/modules/core/src/test/resources/codegen/DefaultMapperEnumFieldsMessageSerializer.java
@@ -0,0 +1,93 @@
+/*
+ * 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.internal.codegen;
+
+import org.apache.ignite.internal.DefaultMapperEnumFieldsMessage;
+import org.apache.ignite.internal.processors.cache.GridCacheOperation;
+import org.apache.ignite.plugin.extensions.communication.Message;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageSerializer;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+import 
org.apache.ignite.plugin.extensions.communication.mappers.DefaultEnumMapper;
+import org.apache.ignite.transactions.TransactionIsolation;
+
+/**
+ * This class is generated automatically.
+ *
+ * @see org.apache.ignite.internal.MessageProcessor
+ */
+public class DefaultMapperEnumFieldsMessageSerializer implements 
MessageSerializer {
+    /** */
+    private final GridCacheOperation[] gridCacheOperationVals = 
GridCacheOperation.values();
+
+    /** */
+    private final TransactionIsolation[] transactionIsolationVals = 
TransactionIsolation.values();
+
+    /** {@inheritDoc} */
+    @Override public boolean writeTo(Message m, MessageWriter writer) {
+        DefaultMapperEnumFieldsMessage msg = (DefaultMapperEnumFieldsMessage)m;
+
+        if (!writer.isHeaderWritten()) {
+            if (!writer.writeHeader(msg.directType()))
+                return false;
+
+            writer.onHeaderWritten();
+        }
+
+        switch (writer.state()) {
+            case 0:
+                if 
(!writer.writeByte(DefaultEnumMapper.INSTANCE.encode(msg.publicEnum())))
+                    return false;
+
+                writer.incrementState();
+
+            case 1:
+                if 
(!writer.writeByte(DefaultEnumMapper.INSTANCE.encode(msg.internalEnum())))
+                    return false;
+
+                writer.incrementState();
+        }
+
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean readFrom(Message m, MessageReader reader) {
+        DefaultMapperEnumFieldsMessage msg = (DefaultMapperEnumFieldsMessage)m;
+
+        switch (reader.state()) {
+            case 0:
+                
msg.publicEnum(DefaultEnumMapper.INSTANCE.decode(transactionIsolationVals, 
reader.readByte()));
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
+            case 1:
+                
msg.internalEnum(DefaultEnumMapper.INSTANCE.decode(gridCacheOperationVals, 
reader.readByte()));
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+        }
+
+        return true;
+    }
+}
\ No newline at end of file
diff --git 
a/modules/core/src/test/resources/codegen/UnwrappedEnumFieldMessage.java 
b/modules/core/src/test/resources/codegen/DefaultMapperEnumFieldsSecondMessage.java
similarity index 78%
copy from modules/core/src/test/resources/codegen/UnwrappedEnumFieldMessage.java
copy to 
modules/core/src/test/resources/codegen/DefaultMapperEnumFieldsSecondMessage.java
index fa16ff85d54..a9ad7816d09 100644
--- a/modules/core/src/test/resources/codegen/UnwrappedEnumFieldMessage.java
+++ 
b/modules/core/src/test/resources/codegen/DefaultMapperEnumFieldsSecondMessage.java
@@ -20,19 +20,19 @@ package org.apache.ignite.internal;
 import org.apache.ignite.plugin.extensions.communication.Message;
 import org.apache.ignite.transactions.TransactionIsolation;
 
-public class UnwrappedEnumFieldMessage implements Message {
+public class DefaultMapperEnumFieldsSecondMessage implements Message {
     @Order(0)
-    private TransactionIsolation isolation;
+    private TransactionIsolation txMode;
 
-    public TransactionIsolation isolation() {
-        return isolation;
+    public TransactionIsolation txMode() {
+        return txMode;
     }
 
-    public void isolation(TransactionIsolation isolation) {
-        this.isolation = isolation;
+    public void txMode(TransactionIsolation txMode) {
+        this.txMode = txMode;
     }
 
     public short directType() {
-        return 0;
+        return 1;
     }
 }
diff --git 
a/modules/core/src/test/resources/codegen/UnwrappedEnumFieldMessage.java 
b/modules/core/src/test/resources/codegen/TransactionIsolationEnumMapper.java
similarity index 70%
rename from 
modules/core/src/test/resources/codegen/UnwrappedEnumFieldMessage.java
rename to 
modules/core/src/test/resources/codegen/TransactionIsolationEnumMapper.java
index fa16ff85d54..5a5773f219a 100644
--- a/modules/core/src/test/resources/codegen/UnwrappedEnumFieldMessage.java
+++ 
b/modules/core/src/test/resources/codegen/TransactionIsolationEnumMapper.java
@@ -17,22 +17,15 @@
 
 package org.apache.ignite.internal;
 
-import org.apache.ignite.plugin.extensions.communication.Message;
+import org.apache.ignite.plugin.extensions.communication.mappers.EnumMapper;
 import org.apache.ignite.transactions.TransactionIsolation;
 
-public class UnwrappedEnumFieldMessage implements Message {
-    @Order(0)
-    private TransactionIsolation isolation;
-
-    public TransactionIsolation isolation() {
-        return isolation;
-    }
-
-    public void isolation(TransactionIsolation isolation) {
-        this.isolation = isolation;
+public class TransactionIsolationEnumMapper implements 
EnumMapper<TransactionIsolation> {
+    @Override public byte encode(TransactionIsolation val) {
+        return 0;
     }
 
-    public short directType() {
-        return 0;
+    @Override public TransactionIsolation decode(byte code) {
+        return null;
     }
-}
+}
\ No newline at end of file

Reply via email to