diff --git a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/direct/JmhDirectMessageReaderBenchmark.java b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/direct/JmhDirectMessageReaderBenchmark.java index 7c93e2be90b60..6735e9514b407 100644 --- a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/direct/JmhDirectMessageReaderBenchmark.java +++ b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/direct/JmhDirectMessageReaderBenchmark.java @@ -44,6 +44,7 @@ import org.openjdk.jmh.annotations.Warmup; import static java.util.concurrent.TimeUnit.SECONDS; +import static org.apache.ignite.internal.MessageSerializationContext.IGNORED; import static org.openjdk.jmh.annotations.Mode.Throughput; /** Benchmarks the {@link DirectMessageReader} compressed-field hot path. */ @@ -96,7 +97,7 @@ public void setup() { writer.setBuffer(buf); - boolean finished = writer.writeMessage(msg, true); + boolean finished = writer.writeMessage(msg, true, IGNORED); if (!finished) throw new IllegalStateException("Message does not fit into the buffer."); @@ -111,7 +112,7 @@ public Message compressedMessage() { reader.setBuffer(buf); - Message msg = reader.readMessage(true); + Message msg = reader.readMessage(true, IGNORED); reader.reset(); diff --git a/modules/codegen/src/main/java/org/apache/ignite/internal/FeatureRegistry.java b/modules/codegen/src/main/java/org/apache/ignite/internal/FeatureRegistry.java new file mode 100644 index 0000000000000..144d6e87324b2 --- /dev/null +++ b/modules/codegen/src/main/java/org/apache/ignite/internal/FeatureRegistry.java @@ -0,0 +1,41 @@ +/* + * 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; +import org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteFeature; + +/** + * Links the annotated class to the specified {@link IgniteFeature} registry. The registry + * is used to resolve fully qualified names of features that introduced or deprecated fields + * (see {@link Order#introducedBy()} and {@link Order#deprecatedBy()}). + * + *

If this annotation is absent, the Ignite Core Feature Registry is used.

+ * + * @see Order + * @see IgniteFeature + */ +@Retention(RetentionPolicy.CLASS) +@Target(ElementType.TYPE) +public @interface FeatureRegistry { + /** @return Class of the feature registry. */ + Class value(); +} diff --git a/modules/codegen/src/main/java/org/apache/ignite/internal/MessageCompanionGenerator.java b/modules/codegen/src/main/java/org/apache/ignite/internal/MessageCompanionGenerator.java index 9156694a62160..39be2c7299c3a 100644 --- a/modules/codegen/src/main/java/org/apache/ignite/internal/MessageCompanionGenerator.java +++ b/modules/codegen/src/main/java/org/apache/ignite/internal/MessageCompanionGenerator.java @@ -35,6 +35,7 @@ import java.util.stream.Collectors; import javax.annotation.processing.FilerException; import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; @@ -181,6 +182,11 @@ protected void writeClassHeader(Writer writer, String interfaceName, String clsN writer.write("public final class " + clsName + " implements " + interfaceName + "<" + simpleNameWithGeneric(type) + ">"); } + /** */ + protected void printError(Element el, String msg) { + env.getMessager().printMessage(Diagnostic.Kind.ERROR, msg, el); + } + /** @return {@code format} formatted with {@code args}, prefixed with {@link #indent} tabs. */ protected String indentedLine(String format, Object... args) { return TAB.repeat(indent) + String.format(format, args); diff --git a/modules/codegen/src/main/java/org/apache/ignite/internal/MessageSerializerGenerator.java b/modules/codegen/src/main/java/org/apache/ignite/internal/MessageSerializerGenerator.java index 81680f1b1088c..a950f9e1a4eb4 100644 --- a/modules/codegen/src/main/java/org/apache/ignite/internal/MessageSerializerGenerator.java +++ b/modules/codegen/src/main/java/org/apache/ignite/internal/MessageSerializerGenerator.java @@ -33,11 +33,13 @@ import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; +import javax.lang.model.element.Modifier; import javax.lang.model.element.QualifiedNameable; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.MirroredTypeException; import javax.lang.model.type.PrimitiveType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; @@ -68,6 +70,16 @@ public class MessageSerializerGenerator extends MessageCompanionGenerator { /** */ private static final String MESSAGE_READER_CLS = "org.apache.ignite.plugin.extensions.communication.MessageReader"; + /** */ + private static final String MESSAGE_SER_CTX_CLS = "org.apache.ignite.internal.MessageSerializationContext"; + + /** */ + private static final String DFLT_FEATURE_REG_CLS = + "org.apache.ignite.internal.processors.rollingupgrade.feature.SupportedFeatureRegistry"; + + /** */ + private static final String IGNITE_FEATURE_CLS = "org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteFeature"; + /** */ private static final String ENUM_MAPPER_CLS = "org.apache.ignite.plugin.extensions.communication.mappers.EnumMapper"; @@ -145,6 +157,7 @@ public class MessageSerializerGenerator extends MessageCompanionGenerator { imports.add(MESSAGE_SERIALIZER_CLS); imports.add(MESSAGE_WRITER_CLS); imports.add(MESSAGE_READER_CLS); + imports.add(MESSAGE_SER_CTX_CLS); writeClassHeader(writer, "MessageSerializer", serClsName); @@ -195,8 +208,10 @@ private void generateMethods(List fields) throws Exception { private void generateMethod(List code, List fields, boolean write) throws Exception { code.add(indentedLine(METHOD_JAVADOC)); - code.add(indentedLine("@Override public final boolean %s(" + simpleNameWithGeneric(type) + " msg, %s) {", - write ? "writeTo" : "readFrom", write ? "MessageWriter writer" : "MessageReader reader")); + code.add(indentedLine( + "@Override public final boolean %s(" + simpleNameWithGeneric(type) + " msg, %s, MessageSerializationContext ctx) {", + write ? "writeTo" : "readFrom", + write ? "MessageWriter writer" : "MessageReader reader")); indent++; @@ -261,7 +276,7 @@ private void processField(VariableElement field, int opt, boolean write) throws throw new UnsupportedOperationException("You should use ErrorMessage for serialization of throwables."); if (write) - writeField(opt, callExpr(field, true)); + writeField(field, opt, callExpr(field, true)); else readField(field, opt, callExpr(field, false)); } @@ -288,6 +303,93 @@ private String callExpr(VariableElement field, boolean write) throws Exception { return write ? call.expr("writer.write", fieldRef(field)) : call.expr("reader.read", null); } + /** */ + @Nullable private FieldFeatureGuard buildFeatureGuard(VariableElement field) { + Order ann = field.getAnnotation(Order.class); + + String introducingFeature = ann.introducedBy(); + String deprecatingFeature = ann.deprecatedBy(); + + if (introducingFeature.isEmpty() && deprecatingFeature.isEmpty()) + return null; + + if (introducingFeature.equals(deprecatingFeature)) { + printError(field, "Elements introducedBy and deprecatedBy of the @Order annotation must not reference the same feature."); + + return null; + } + + String regCls = resolveFeatureRegistry(field.getEnclosingElement()); + + String regName = regCls.substring(regCls.lastIndexOf('.') + 1); + + List conditions = new ArrayList<>(); + + if (!introducingFeature.isEmpty()) { + validateFeature(field, introducingFeature, regCls); + + conditions.add("ctx.includeFieldIntroducedBy(" + regName + '.' + introducingFeature + ")"); + } + + if (!deprecatingFeature.isEmpty()) { + validateFeature(field, deprecatingFeature, regCls); + + conditions.add("ctx.includeFieldDeprecatedBy(" + regName + '.' + deprecatingFeature + ")"); + } + + return new FieldFeatureGuard(regCls, String.join(" && ", conditions)); + } + + /** */ + private void validateFeature(VariableElement field, String featureName, String regCls) { + TypeElement regElem = env.getElementUtils().getTypeElement(regCls); + + if (regElem == null) { + printError(field, "Cannot resolve the feature registry class [reg=" + regCls + ']'); + + return; + } + + for (Element featureElem : regElem.getEnclosedElements()) { + if (featureElem.getKind() != ElementKind.FIELD || !featureElem.getSimpleName().contentEquals(featureName)) + continue; + + Set mods = featureElem.getModifiers(); + + if (!mods.contains(Modifier.PUBLIC) || !mods.contains(Modifier.STATIC) || !mods.contains(Modifier.FINAL)) + printError(field, "Feature constant must be public static final [reg=" + regCls + ", feature=" + featureName + ']'); + else if (!isIgniteFeature(featureElem)) + printError(field, "Feature constant must be of type IgniteFeature [reg=" + regCls + ", feature=" + featureName + ']'); + + return; + } + + printError(field, + "Failed to resolve feature in the registry by its name [reg=" + regCls + ", feature=" + featureName + ']'); + } + + /** */ + private boolean isIgniteFeature(Element featureElem) { + TypeElement igniteFeatureType = env.getElementUtils().getTypeElement(IGNITE_FEATURE_CLS); + + return igniteFeatureType != null && env.getTypeUtils().isAssignable(featureElem.asType(), igniteFeatureType.asType()); + } + + /** */ + static String resolveFeatureRegistry(Element cls) { + FeatureRegistry ann = cls.getAnnotation(FeatureRegistry.class); + + if (ann == null) + return DFLT_FEATURE_REG_CLS; + + try { + return ann.value().getName(); + } + catch (MirroredTypeException e) { + return qualifiedClassName(e.getTypeMirror()); + } + } + /** * Generate code for processing write of single field: *
@@ -301,13 +403,29 @@ private String callExpr(VariableElement field, boolean write) throws Exception {
      * @param opt Case option.
      * @param writeExpr Writer call expression.
      */
-    private void writeField(int opt, String writeExpr) {
+    private void writeField(VariableElement field, int opt, String writeExpr) {
         write.add(indentedLine("case %d:", opt));
 
         indent++;
 
+        FieldFeatureGuard guard = buildFeatureGuard(field);
+
+        if (guard != null) {
+            imports.add(guard.registry());
+
+            write.add(indentedLine("if (%s) {", guard.expression()));
+
+            indent++;
+        }
+
         returnFalseIf(write, "!" + writeExpr);
 
+        if (guard != null) {
+            indent--;
+
+            write.add(indentedLine("}"));
+        }
+
         write.add(EMPTY);
         write.add(indentedLine("writer.incrementState();"));
         write.add(EMPTY);
@@ -335,11 +453,27 @@ private void readField(VariableElement field, int opt, String readExpr) {
 
         indent++;
 
+        FieldFeatureGuard guard = buildFeatureGuard(field);
+
+        if (guard != null) {
+            imports.add(guard.registry());
+
+            read.add(indentedLine("if (%s) {", guard.expression()));
+
+            indent++;
+        }
+
         read.add(indentedLine("%s = %s;", fieldRef(field), readExpr));
         read.add(EMPTY);
 
         returnFalseIf(read, "!reader.isLastRead()");
 
+        if (guard != null) {
+            indent--;
+
+            read.add(indentedLine("}"));
+        }
+
         read.add(EMPTY);
         read.add(indentedLine("reader.incrementState();"));
         read.add(EMPTY);
@@ -360,13 +494,13 @@ private FieldCall fieldCall(VariableElement field) throws Exception {
             checkTypeForCompress(type);
 
         if (type.getKind().isPrimitive())
-            return new FieldCall(capitalizeOnlyFirst(type.getKind().name()), null, false);
+            return FieldCall.scalar(capitalizeOnlyFirst(type.getKind().name()));
 
         if (type.getKind() == TypeKind.ARRAY) {
             TypeMirror compType = ((ArrayType)type).getComponentType();
 
             if (compType.getKind().isPrimitive())
-                return new FieldCall(capitalizeOnlyFirst(compType.getKind().name()) + "Array", null, false);
+                return FieldCall.scalar(capitalizeOnlyFirst(compType.getKind().name()) + "Array");
 
             if (compType.getKind() == TypeKind.DECLARED) {
                 Element compElem = ((DeclaredType)compType).asElement();
@@ -375,52 +509,52 @@ private FieldCall fieldCall(VariableElement field) throws Exception {
                     imports.add(((QualifiedNameable)compElem).getQualifiedName().toString());
             }
 
-            return new FieldCall("ObjectArray", messageCollectionItemTypes(field, type), false);
+            return FieldCall.collection("ObjectArray", messageCollectionItemTypes(field, type), false);
         }
 
         if (type.getKind() == TypeKind.DECLARED) {
             if (sameType(type, String.class))
-                return new FieldCall("String", null, false);
+                return FieldCall.scalar("String");
 
             if (sameType(type, BitSet.class))
-                return new FieldCall("BitSet", null, false);
+                return FieldCall.scalar("BitSet");
 
             if (sameType(type, UUID.class))
-                return new FieldCall("Uuid", null, false);
+                return FieldCall.scalar("Uuid");
 
             if (sameType(type, IGNITE_UUID_CLS))
-                return new FieldCall("IgniteUuid", null, false);
+                return FieldCall.scalar("IgniteUuid");
 
             if (sameType(type, AFFINITY_TOPOLOGY_VERSION_CLS))
-                return new FieldCall("AffinityTopologyVersion", null, false);
+                return FieldCall.scalar("AffinityTopologyVersion");
 
             if (assignableFrom(erasedType(type), type(Map.class.getName())))
-                return new FieldCall("Map", messageCollectionItemTypes(field, type), compress);
+                return FieldCall.collection("Map", messageCollectionItemTypes(field, type), compress);
 
             if (assignableFrom(type, type(KEY_CACHE_OBJECT_CLS)))
-                return new FieldCall("KeyCacheObject", null, false);
+                return FieldCall.scalar("KeyCacheObject");
 
             if (assignableFrom(type, type(CACHE_OBJECT_CLS)))
-                return new FieldCall("CacheObject", null, false);
+                return FieldCall.scalar("CacheObject");
 
             if (assignableFrom(type, type(GRID_LONG_LIST_CLS)))
-                return new FieldCall("GridLongList", null, false);
+                return FieldCall.scalar("GridLongList");
 
             if (assignableFrom(type, type(IGNITE_PRODUCT_VERSION_CLS)))
-                return new FieldCall("IgniteProductVersion", null, false);
+                return FieldCall.scalar("IgniteProductVersion");
 
             if (assignableFrom(type, type(GRID_CACHE_VERSION_CLS)))
-                return new FieldCall("GridCacheVersion", null, false);
+                return FieldCall.scalar("GridCacheVersion");
 
             if (assignableFrom(type, type(MESSAGE_INTERFACE))) {
                 if (sameType(type, COMPRESSED_MESSAGE_CLASS))
                     throw new IllegalArgumentException(COMPRESSED_MSG_ERROR);
 
-                return new FieldCall("Message", null, compress);
+                return FieldCall.message(compress);
             }
 
             if (assignableFrom(erasedType(type), type(Collection.class.getName())))
-                return new FieldCall("Collection", messageCollectionItemTypes(field, type), false);
+                return FieldCall.collection("Collection", messageCollectionItemTypes(field, type), false);
 
             throw new IllegalArgumentException("Unsupported declared type: " + type);
         }
@@ -756,10 +890,14 @@ private static final class FieldCall {
         private final boolean compress;
 
         /** */
-        private FieldCall(String mtd, @Nullable String collDesc, boolean compress) {
+        private final boolean isSerCtxRequired;
+
+        /** */
+        private FieldCall(String mtd, @Nullable String collDesc, boolean compress, boolean isSerCtxRequired) {
             this.mtd = mtd;
             this.collDesc = collDesc;
             this.compress = compress;
+            this.isSerCtxRequired = isSerCtxRequired;
         }
 
         /** @return Full call expression; {@code valArg}, when given, is passed as the first argument (write side). */
@@ -775,8 +913,26 @@ private String expr(String mtdPrefix, @Nullable String valArg) {
             if (compress)
                 args.add("true");
 
+            if (isSerCtxRequired)
+                args.add("ctx");
+
             return mtdPrefix + mtd + "(" + String.join(", ", args) + ")";
         }
+
+        /** */
+        private static FieldCall scalar(String mtd) {
+            return new FieldCall(mtd, null, false, false);
+        }
+
+        /** */
+        private static FieldCall collection(String mtd, String collDesc, boolean compress) {
+            return new FieldCall(mtd, collDesc, compress, true);
+        }
+
+        /** */
+        private static FieldCall message(boolean compress) {
+            return new FieldCall("Message", null, compress, true);
+        }
     }
 
     /** */
@@ -801,4 +957,7 @@ public static String qualifiedClassName(TypeMirror type) {
 
         return type.toString();
     }
+
+    /** */
+    public record FieldFeatureGuard(String registry, String expression) { }
 }
diff --git a/modules/codegen/src/main/java/org/apache/ignite/internal/Order.java b/modules/codegen/src/main/java/org/apache/ignite/internal/Order.java
index 0e4562537c435..3eecc4cbca38c 100644
--- a/modules/codegen/src/main/java/org/apache/ignite/internal/Order.java
+++ b/modules/codegen/src/main/java/org/apache/ignite/internal/Order.java
@@ -21,6 +21,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
+import org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteFeature;
 
 /**
  * The annotation specifies the position of a field in the serialized and deserialized byte sequence of a {@code Message} class.
@@ -28,14 +29,38 @@
  * The {@code value} indicates the index of the field in the serialization order.
  * Fields annotated with {@code @Order} are processed in ascending order of their index.
  * 

By default, it is assumed that getters and setters are named as the annotated fields, - * e.g. field 'val' should have getters and satters with name 'val' (according Ignite's to code-style). + * e.g. field 'val' should have getters and setters with name 'val' (according Ignite's to code-style). *

This annotation must be used on non-static fields, and access to those fields * should be performed strictly through corresponding getter and setter methods * following the naming convention: {@code fieldName()} for getter and {@code fieldName(Type)} for setter. + * + * @see FeatureRegistry */ @Retention(RetentionPolicy.CLASS) @Target(ElementType.FIELD) public @interface Order { /** @return Order of the field. */ int value(); + + /** + * {@link IgniteFeature} that introduced the field marked with the current annotation. + * + *

An annotated field is included in message serialization only when doing so does not break backward compatibility + * during a Rolling Upgrade.

+ * + * @return Name of the Ignite feature that introduced this field, or an empty string if the field is not guarded. + */ + String introducedBy() default ""; + + /** + * {@link IgniteFeature} that deprecated the field marked with the current annotation. + * + *

Deprecation means that the field is planned for removal in a future release.

+ * + *

An annotated field is excluded from message serialization only when doing so does not break backward compatibility + * during a Rolling Upgrade.

+ * + * @return Name of the Ignite feature that deprecated this field, or an empty string if the field is not guarded. + */ + String deprecatedBy() default ""; } diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/MessageSerializationContext.java b/modules/commons/src/main/java/org/apache/ignite/internal/MessageSerializationContext.java new file mode 100644 index 0000000000000..7db50eac2b28e --- /dev/null +++ b/modules/commons/src/main/java/org/apache/ignite/internal/MessageSerializationContext.java @@ -0,0 +1,88 @@ +/* + * 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 org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteFeature; + +/** Represents the context that determines how message fields are serialized and deserialized when transmitted between nodes. */ +public interface MessageSerializationContext { + /** + * @param feature Feature that deprecated the field. + * @return {@code true} if the message field should be included during message serialization or deserialization. + */ + boolean includeFieldDeprecatedBy(IgniteFeature feature); + + /** + * @param feature Feature that introduced the field. + * @return {@code true} if the message field should be included during message serialization or deserialization. + */ + boolean includeFieldIntroducedBy(IgniteFeature feature); + + /** + * {@link MessageSerializationContext} implementation that instructs the serialization framework to always + * serialize the actual message state: all newly introduced fields are included, and all deprecated fields are + * excluded. + */ + MessageSerializationContext IGNORED = new MessageSerializationContext() { + /** {@inheritDoc} */ + @Override public boolean includeFieldDeprecatedBy(IgniteFeature feature) { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean includeFieldIntroducedBy(IgniteFeature feature) { + return true; + } + + /** {@inheritDoc} */ + @Override public String toString() { + return "MessageSerializationContext [IGNORED]"; + } + }; + + /** + * Stub {@link MessageSerializationContext} implementation used when the serialization context has not yet been determined. + * + *

The serialization context is unavailable between connection establishment and serialization protocol negotiation. + * Messages sent during this period cannot rely on the {@link IgniteFeature} mechanism to adjust the message serialization + * in an RU-compatible way.

+ */ + MessageSerializationContext UNNEGOTIATED = new MessageSerializationContext() { + /** {@inheritDoc} */ + @Override public boolean includeFieldDeprecatedBy(IgniteFeature feature) { + throw buildError(feature); + } + + /** {@inheritDoc} */ + @Override public boolean includeFieldIntroducedBy(IgniteFeature feature) { + throw buildError(feature); + } + + /** {@inheritDoc} */ + @Override public String toString() { + return "MessageSerializationContext [UNNEGOTIATED]"; + } + + /** */ + private IllegalStateException buildError(IgniteFeature feature) { + return new IllegalStateException( + "A feature-guarded field was serialized before the peer's features were negotiated [feature=" + feature + ']' + ); + } + }; +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/feature/IgniteFeature.java b/modules/commons/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/feature/IgniteFeature.java similarity index 100% rename from modules/core/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/feature/IgniteFeature.java rename to modules/commons/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/feature/IgniteFeature.java diff --git a/modules/core/src/main/java/org/apache/ignite/internal/direct/DirectMessageReader.java b/modules/core/src/main/java/org/apache/ignite/internal/direct/DirectMessageReader.java index 9ec9b68cc189e..b070629a38ba3 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/direct/DirectMessageReader.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/direct/DirectMessageReader.java @@ -24,6 +24,7 @@ import java.util.UUID; import java.util.function.Function; import org.apache.ignite.IgniteException; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.direct.state.DirectMessageState; import org.apache.ignite.internal.direct.state.DirectMessageStateItem; import org.apache.ignite.internal.direct.stream.DirectByteBufferStream; @@ -344,7 +345,7 @@ public ByteBuffer getBuffer() { } /** {@inheritDoc} */ - @Nullable @Override public T readMessage(boolean compress) { + @Nullable @Override public T readMessage(boolean compress, MessageSerializationContext ctx) { DirectByteBufferStream stream = curStream; T msg; @@ -352,10 +353,11 @@ public ByteBuffer getBuffer() { if (compress) msg = readCompressedMessageAndDeserialize( stream, - r -> r.state.item().stream.readMessage(r) + r -> r.state.item().stream.readMessage(r, ctx), + ctx ); else { - msg = stream.readMessage(this); + msg = stream.readMessage(this, ctx); lastRead = stream.lastFinished(); } @@ -397,10 +399,10 @@ public ByteBuffer getBuffer() { } /** {@inheritDoc} */ - @Override public T[] readObjectArray(MessageArrayType type) { + @Override public T[] readObjectArray(MessageArrayType type, MessageSerializationContext ctx) { DirectByteBufferStream stream = curStream; - T[] msg = stream.readObjectArray(type, this); + T[] msg = stream.readObjectArray(type, this, ctx); lastRead = stream.lastFinished(); @@ -408,10 +410,10 @@ public ByteBuffer getBuffer() { } /** {@inheritDoc} */ - @Override public > C readCollection(MessageCollectionType type) { + @Override public > C readCollection(MessageCollectionType type, MessageSerializationContext ctx) { DirectByteBufferStream stream = curStream; - C col = stream.readCollection(type, this); + C col = stream.readCollection(type, this, ctx); lastRead = stream.lastFinished(); @@ -419,7 +421,7 @@ public ByteBuffer getBuffer() { } /** {@inheritDoc} */ - @Override public > M readMap(MessageMapType type, boolean compress) { + @Override public > M readMap(MessageMapType type, boolean compress, MessageSerializationContext ctx) { DirectByteBufferStream stream = curStream; M map; @@ -427,10 +429,11 @@ public ByteBuffer getBuffer() { if (compress) map = readCompressedMessageAndDeserialize( stream, - r -> r.state.item().stream.readMap(type, r) + r -> r.state.item().stream.readMap(type, r, ctx), + ctx ); else { - map = stream.readMap(type, this); + map = stream.readMap(type, this, ctx); lastRead = stream.lastFinished(); } @@ -509,8 +512,12 @@ public ByteBuffer getBuffer() { } /** @return Deserialized object. */ - private T readCompressedMessageAndDeserialize(DirectByteBufferStream stream, Function fun) { - Message msg = stream.readMessage(this); + private T readCompressedMessageAndDeserialize( + DirectByteBufferStream stream, + Function fun, + MessageSerializationContext ctx + ) { + Message msg = stream.readMessage(this, ctx); lastRead = stream.lastFinished(); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/direct/DirectMessageWriter.java b/modules/core/src/main/java/org/apache/ignite/internal/direct/DirectMessageWriter.java index 319aae7b7e947..f09632cfbac4e 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/direct/DirectMessageWriter.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/direct/DirectMessageWriter.java @@ -23,6 +23,7 @@ import java.util.Map; import java.util.UUID; import java.util.function.Consumer; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.direct.state.DirectMessageState; import org.apache.ignite.internal.direct.state.DirectMessageStateItem; import org.apache.ignite.internal.direct.stream.DirectByteBufferStream; @@ -334,17 +335,18 @@ public ByteBuffer getBuffer() { } /** {@inheritDoc} */ - @Override public boolean writeMessage(@Nullable Message msg, boolean compress) { + @Override public boolean writeMessage(@Nullable Message msg, boolean compress, MessageSerializationContext ctx) { DirectByteBufferStream stream = curStream; if (compress) writeCompressedMessage( - w -> w.state.item().stream.writeMessage(msg, w), + w -> w.state.item().stream.writeMessage(msg, w, ctx), msg == null, - stream + stream, + ctx ); else - stream.writeMessage(msg, this); + stream.writeMessage(msg, this, ctx); return stream.lastFinished(); } @@ -377,35 +379,36 @@ public ByteBuffer getBuffer() { } /** {@inheritDoc} */ - @Override public boolean writeObjectArray(T[] arr, MessageArrayType type) { + @Override public boolean writeObjectArray(T[] arr, MessageArrayType type, MessageSerializationContext ctx) { DirectByteBufferStream stream = curStream; - stream.writeObjectArray(arr, type, this); + stream.writeObjectArray(arr, type, this, ctx); return stream.lastFinished(); } /** {@inheritDoc} */ - @Override public boolean writeCollection(Collection col, MessageCollectionType type) { + @Override public boolean writeCollection(Collection col, MessageCollectionType type, MessageSerializationContext ctx) { DirectByteBufferStream stream = curStream; - stream.writeCollection(col, type, this); + stream.writeCollection(col, type, this, ctx); return stream.lastFinished(); } /** {@inheritDoc} */ - @Override public boolean writeMap(Map map, MessageMapType type, boolean compress) { + @Override public boolean writeMap(Map map, MessageMapType type, boolean compress, MessageSerializationContext ctx) { DirectByteBufferStream stream = curStream; if (compress) writeCompressedMessage( - w -> w.state.item().stream.writeMap(map, type, w), + w -> w.state.item().stream.writeMap(map, type, w, ctx), map == null, - stream + stream, + ctx ); else - stream.writeMap(map, type, this); + stream.writeMap(map, type, this, ctx); return stream.lastFinished(); } @@ -485,8 +488,14 @@ public ByteBuffer getBuffer() { * @param consumer Consumer. * @param isNull {@code True} if message is null. * @param stream Byte buffer stream. + * @param ctx Serialization context. */ - private void writeCompressedMessage(Consumer consumer, boolean isNull, DirectByteBufferStream stream) { + private void writeCompressedMessage( + Consumer consumer, + boolean isNull, + DirectByteBufferStream stream, + MessageSerializationContext ctx + ) { if (isNull) { stream.writeShort(Short.MIN_VALUE); @@ -536,7 +545,7 @@ private void writeCompressedMessage(Consumer consumer, bool stream.serializeFinished(true); } - stream.writeMessage(stream.compressedMessage(), this); + stream.writeMessage(stream.compressedMessage(), this, ctx); if (stream.lastFinished()) { stream.compressedMessage(null); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/direct/IgniteMessageSerializationContext.java b/modules/core/src/main/java/org/apache/ignite/internal/direct/IgniteMessageSerializationContext.java new file mode 100644 index 0000000000000..ddfe4a3693c48 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/direct/IgniteMessageSerializationContext.java @@ -0,0 +1,233 @@ +/* + * 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.direct; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import org.apache.ignite.Ignite; +import org.apache.ignite.cluster.ClusterNode; +import org.apache.ignite.internal.GridKernalContext; +import org.apache.ignite.internal.IgniteEx; +import org.apache.ignite.internal.MessageSerializationContext; +import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException; +import org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteComponentFeatureSet; +import org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteFeature; +import org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteFeatureSet; +import org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteNodeFeatureSet; +import org.apache.ignite.internal.util.tostring.GridToStringInclude; +import org.apache.ignite.internal.util.typedef.internal.S; +import org.apache.ignite.spi.discovery.tcp.internal.UnsupportedNodeVersionException; +import org.jetbrains.annotations.Nullable; + +/** */ +public class IgniteMessageSerializationContext implements MessageSerializationContext { + /** */ + @GridToStringInclude + private final Map ctxByComponent; + + /** */ + private IgniteMessageSerializationContext(Map ctxByComponent) { + this.ctxByComponent = ctxByComponent; + } + + /** {@inheritDoc} */ + @Override public boolean includeFieldIntroducedBy(IgniteFeature feature) { + return componentContext(feature).includeFieldIntroducedBy(feature.id()); + } + + /** {@inheritDoc} */ + @Override public boolean includeFieldDeprecatedBy(IgniteFeature feature) { + return componentContext(feature).includeFieldDeprecatedBy(feature.id()); + } + + /** */ + private ComponentMessageSerializationContext componentContext(IgniteFeature feature) { + ComponentMessageSerializationContext cmpCtx = ctxByComponent.get(feature.componentName()); + + if (cmpCtx == null) { + throw new IllegalStateException( + "A field is guarded by a feature of an undeclared component" + + " [feature=" + feature + + ", component=" + feature.componentName() + + ", declaredComponents=" + ctxByComponent.keySet() + ']' + ); + } + + return cmpCtx; + } + + /** */ + public static IgniteMessageSerializationContext buildForPeers( + Ignite loc, + ClusterNode rmt + ) throws UnsupportedNodeVersionException, ClusterTopologyCheckedException { + GridKernalContext ctx = ((IgniteEx)loc).context(); + + return buildForPeers(ctx.localNodeFeatures(), ctx.discovery().resolveNodeFeatures(rmt)); + } + + /** */ + public static IgniteMessageSerializationContext buildForPeers( + IgniteNodeFeatureSet loc, + IgniteNodeFeatureSet rmt + ) throws UnsupportedNodeVersionException { + assert loc != null; + + if (rmt == null) { + throw new UnsupportedNodeVersionException("Failed to build the message serialization context for the remote node." + + " The remote node's feature set is unavailable."); + } + + Set components = new HashSet<>(loc.components()); + + components.addAll(rmt.components()); + + Map ctxByComponent = new HashMap<>(); + + for (String cmp : components) { + ComponentMessageSerializationContext ctx = resolveComponentSerializationContext( + cmp, + loc.componentFeatures(cmp), + rmt.componentFeatures(cmp) + ); + + ctxByComponent.put(cmp, ctx); + } + + return new IgniteMessageSerializationContext(ctxByComponent); + } + + /** */ + private static ComponentMessageSerializationContext resolveComponentSerializationContext( + String cmpName, + @Nullable IgniteComponentFeatureSet locCmpFeatures, + @Nullable IgniteComponentFeatureSet rmtCmpFeatures + ) throws UnsupportedNodeVersionException { + assert locCmpFeatures != null || rmtCmpFeatures != null; + + // One of the sides has no component configured. This may happen when one side uses an RU-unaware plugin version + // while the other uses an RU-aware version. In this case, all newly introduced fields are skipped, while all + // deprecated fields are included. + if (locCmpFeatures == null || rmtCmpFeatures == null) + return new ComponentMessageSerializationContext(null, null); + + int c = locCmpFeatures.version().compareTo(rmtCmpFeatures.version()); + + if (c == 0) { + assert locCmpFeatures.features().equals(rmtCmpFeatures.features()); + + // Both newly introduced and deprecated fields are included. During an RU, a node builds messages according + // to both the old logical version (while RU is in progress, deprecated fields are used and newly introduced + // fields are not) and the new logical version (after RU is finished, newly introduced fields are used and + // deprecated fields are not). + return new ComponentMessageSerializationContext(null, rmtCmpFeatures.features()); + } + else { + IgniteComponentFeatureSet src = c < 0 ? locCmpFeatures : rmtCmpFeatures; + IgniteComponentFeatureSet target = c < 0 ? rmtCmpFeatures : locCmpFeatures; + + if (!src.isUpgradableTo(target)) { + throw new UnsupportedNodeVersionException("Remote node component versions are not supported" + + " [component=" + cmpName + + ", locComponent=" + locCmpFeatures + + ", rmtComponent=" + rmtCmpFeatures + ']'); + } + + // The old version dictates the serialization rules. + return new ComponentMessageSerializationContext(src.features(), src.features()); + } + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(IgniteMessageSerializationContext.class, this); + } + + /** {@inheritDoc} */ + @Override public boolean equals(Object o) { + if (this == o) + return true; + + if (o == null || getClass() != o.getClass()) + return false; + + return Objects.equals(ctxByComponent, ((IgniteMessageSerializationContext)o).ctxByComponent); + } + + /** {@inheritDoc} */ + @Override public int hashCode() { + return Objects.hashCode(ctxByComponent); + } + + /** */ + private static final class ComponentMessageSerializationContext { + /** */ + @GridToStringInclude + @Nullable private final IgniteFeatureSet excludedDeprecatedFields; + + /** */ + @GridToStringInclude + @Nullable private final IgniteFeatureSet includedIntroducedFields; + + /** */ + private ComponentMessageSerializationContext( + @Nullable IgniteFeatureSet excludedDeprecatedFields, + @Nullable IgniteFeatureSet includedIntroducedFields + ) { + this.excludedDeprecatedFields = excludedDeprecatedFields; + this.includedIntroducedFields = includedIntroducedFields; + } + + /** */ + boolean includeFieldIntroducedBy(int featureId) { + return includedIntroducedFields != null && includedIntroducedFields.contains(featureId); + } + + /** */ + boolean includeFieldDeprecatedBy(int featureId) { + return excludedDeprecatedFields == null || !excludedDeprecatedFields.contains(featureId); + } + + /** {@inheritDoc} */ + @Override public boolean equals(Object o) { + if (this == o) + return true; + + if (o == null || getClass() != o.getClass()) + return false; + + ComponentMessageSerializationContext other = (ComponentMessageSerializationContext)o; + + return Objects.equals(excludedDeprecatedFields, other.excludedDeprecatedFields) + && Objects.equals(includedIntroducedFields, other.includedIntroducedFields); + } + + /** {@inheritDoc} */ + @Override public int hashCode() { + return Objects.hash(includedIntroducedFields, excludedDeprecatedFields); + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(ComponentMessageSerializationContext.class, this); + } + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/direct/stream/DirectByteBufferStream.java b/modules/core/src/main/java/org/apache/ignite/internal/direct/stream/DirectByteBufferStream.java index c0752343b5441..2828f9e493ce8 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/direct/stream/DirectByteBufferStream.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/direct/stream/DirectByteBufferStream.java @@ -33,6 +33,7 @@ import java.util.function.Supplier; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteException; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.managers.communication.CompressedMessage; import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; import org.apache.ignite.internal.processors.cache.CacheObject; @@ -926,11 +927,12 @@ public void writeGridLongList(@Nullable GridLongList val) { /** * @param msg Message. * @param writer Writer. + * @param ctx Serialization context. */ - public void writeMessage(Message msg, MessageWriter writer) { + public void writeMessage(Message msg, MessageWriter writer, MessageSerializationContext ctx) { if (msg != null) { if (buf.hasRemaining()) - nestedWrite(writer, () -> MessageSerialization.writeTo(msgFactory, msg, writer)); + nestedWrite(writer, () -> MessageSerialization.writeTo(msgFactory, msg, writer, ctx)); else lastFinished = false; } @@ -942,8 +944,9 @@ public void writeMessage(Message msg, MessageWriter writer) { * @param arr Array. * @param type Type. * @param writer Writer. + * @param ctx Serialization context. */ - public void writeObjectArray(T[] arr, MessageArrayType type, MessageWriter writer) { + public void writeObjectArray(T[] arr, MessageArrayType type, MessageWriter writer, MessageSerializationContext ctx) { if (arr != null) { int len = arr.length; @@ -960,7 +963,7 @@ public void writeObjectArray(T[] arr, MessageArrayType type, MessageWriter w if (arrCur == NULL) arrCur = arr[arrPos++]; - write(type.valueType(), arrCur, writer); + write(type.valueType(), arrCur, writer, ctx); if (!lastFinished) return; @@ -978,11 +981,12 @@ public void writeObjectArray(T[] arr, MessageArrayType type, MessageWriter w * @param col Collection. * @param type Type. * @param writer Writer. + * @param ctx Serialization context. */ - public void writeCollection(Collection col, MessageCollectionType type, MessageWriter writer) { + public void writeCollection(Collection col, MessageCollectionType type, MessageWriter writer, MessageSerializationContext ctx) { if (col != null) { if (col instanceof List && col instanceof RandomAccess) - writeRandomAccessList((List)col, type, writer); + writeRandomAccessList((List)col, type, writer, ctx); else { if (it == null) { writeInt(col.size()); @@ -997,7 +1001,7 @@ public void writeCollection(Collection col, MessageCollectionType type, M if (cur == NULL) cur = it.next(); - write(type.valueType(), cur, writer); + write(type.valueType(), cur, writer, ctx); if (!lastFinished) return; @@ -1016,8 +1020,14 @@ public void writeCollection(Collection col, MessageCollectionType type, M * @param list List. * @param type Type. * @param writer Writer. - */ - private void writeRandomAccessList(List list, MessageCollectionType type, MessageWriter writer) { + * @param ctx Serialization context. + */ + private void writeRandomAccessList( + List list, + MessageCollectionType type, + MessageWriter writer, + MessageSerializationContext ctx + ) { assert list instanceof RandomAccess; int size = list.size(); @@ -1035,7 +1045,7 @@ private void writeRandomAccessList(List list, MessageCollectionType type, if (arrCur == NULL) arrCur = list.get(arrPos++); - write(type.valueType(), arrCur, writer); + write(type.valueType(), arrCur, writer, ctx); if (!lastFinished) return; @@ -1050,8 +1060,9 @@ private void writeRandomAccessList(List list, MessageCollectionType type, * @param map Map. * @param type Type. * @param writer Writer. + * @param ctx Serialization context. */ - public void writeMap(Map map, MessageMapType type, MessageWriter writer) { + public void writeMap(Map map, MessageMapType type, MessageWriter writer, MessageSerializationContext ctx) { if (map != null) { if (mapIt == null) { writeInt(map.size()); @@ -1071,7 +1082,7 @@ public void writeMap(Map map, MessageMapType type, MessageWriter wr e = (Map.Entry)mapCur; if (!keyDone) { - write(type.keyType(), e.getKey(), writer); + write(type.keyType(), e.getKey(), writer, ctx); if (!lastFinished) return; @@ -1079,7 +1090,7 @@ public void writeMap(Map map, MessageMapType type, MessageWriter wr keyDone = true; } - write(type.valueType(), e.getValue(), writer); + write(type.valueType(), e.getValue(), writer, ctx); if (!lastFinished) return; @@ -1555,9 +1566,10 @@ public GridLongList readGridLongList() { /** * @param reader Reader. + * @param ctx Serialization context. * @return Message. */ - public T readMessage(MessageReader reader) { + public T readMessage(MessageReader reader, MessageSerializationContext ctx) { if (!msgTypeDone) { if (buf.remaining() < Message.DIRECT_TYPE_SIZE) { lastFinished = false; @@ -1576,7 +1588,7 @@ public T readMessage(MessageReader reader) { try { reader.beforeNestedRead(); - lastFinished = MessageSerialization.readFrom(msgFactory, msg, reader); + lastFinished = MessageSerialization.readFrom(msgFactory, msg, reader, ctx); } finally { reader.afterNestedRead(lastFinished); @@ -1600,9 +1612,10 @@ public T readMessage(MessageReader reader) { /** * @param type Item type. * @param reader Reader. + * @param ctx Serialization context. * @return Array. */ - public T[] readObjectArray(MessageArrayType type, MessageReader reader) { + public T[] readObjectArray(MessageArrayType type, MessageReader reader, MessageSerializationContext ctx) { if (readSize == -1) { int size = readInt(); @@ -1617,7 +1630,7 @@ public T[] readObjectArray(MessageArrayType type, MessageReader reader) { objArr = type.clazz() != null ? (Object[])Array.newInstance(type.clazz(), readSize) : new Object[readSize]; for (int i = readItems; i < readSize; i++) { - Object item = read(type.valueType(), reader); + Object item = read(type.valueType(), reader, ctx); if (!lastFinished) return null; @@ -1644,9 +1657,10 @@ public T[] readObjectArray(MessageArrayType type, MessageReader reader) { * * @param type Item type. * @param reader Reader. + * @param ctx Serialization context. * @return {@link ArrayList}, {@link HashSet} or {@link EnumSet}. */ - public > C readCollection(MessageCollectionType type, MessageReader reader) { + public > C readCollection(MessageCollectionType type, MessageReader reader, MessageSerializationContext ctx) { if (readSize == -1) { int size = readInt(); @@ -1661,7 +1675,7 @@ public > C readCollection(MessageCollectionType type, Me col = newCollection(type); for (int i = readItems; i < readSize; i++) { - Object item = read(type.valueType(), reader); + Object item = read(type.valueType(), reader, ctx); if (!lastFinished) return null; @@ -1696,9 +1710,10 @@ private Collection newCollection(MessageCollectionType type) { /** * @param type Value type. * @param reader Reader. + * @param ctx Serialization context. * @return Map. */ - public > M readMap(MessageMapType type, MessageReader reader) { + public > M readMap(MessageMapType type, MessageReader reader, MessageSerializationContext ctx) { if (readSize == -1) { int size = readInt(); @@ -1714,7 +1729,7 @@ private Collection newCollection(MessageCollectionType type) { for (int i = readItems; i < readSize; i++) { if (!keyDone) { - Object key = read(type.keyType(), reader); + Object key = read(type.keyType(), reader, ctx); if (!lastFinished) return null; @@ -1723,7 +1738,7 @@ private Collection newCollection(MessageCollectionType type) { keyDone = true; } - Object val = read(type.valueType(), reader); + Object val = read(type.valueType(), reader, ctx); if (!lastFinished) return null; @@ -2003,8 +2018,9 @@ T readArrayLE(ArrayCreator creator, int typeSize, int lenShift, long off) * @param type Type. * @param val Value. * @param writer Writer. + * @param ctx Serialization context. */ - protected void write(MessageType type, Object val, MessageWriter writer) { + protected void write(MessageType type, Object val, MessageWriter writer, MessageSerializationContext ctx) { switch (type.type()) { case BYTE: writeByte((Byte)val); @@ -2132,17 +2148,17 @@ protected void write(MessageType type, Object val, MessageWriter writer) break; case MAP: - nestedWrite(writer, () -> writer.writeMap((Map)val, (MessageMapType)type)); + nestedWrite(writer, () -> writer.writeMap((Map)val, (MessageMapType)type, ctx)); break; case COLLECTION: - nestedWrite(writer, () -> writer.writeCollection((Collection)val, (MessageCollectionType)type)); + nestedWrite(writer, () -> writer.writeCollection((Collection)val, (MessageCollectionType)type, ctx)); break; case ARRAY: - nestedWrite(writer, () -> writer.writeObjectArray((V[])val, (MessageArrayType)type)); + nestedWrite(writer, () -> writer.writeObjectArray((V[])val, (MessageArrayType)type, ctx)); break; @@ -2152,7 +2168,7 @@ protected void write(MessageType type, Object val, MessageWriter writer) break; case MSG: - writeMessage((Message)val, writer); + writeMessage((Message)val, writer, ctx); break; @@ -2176,9 +2192,10 @@ private void nestedWrite(MessageWriter writer, BooleanSupplier s) { /** * @param type Type. * @param reader Reader. + * @param ctx Serialization context. * @return Value. */ - protected Object read(MessageType type, MessageReader reader) { + protected Object read(MessageType type, MessageReader reader, MessageSerializationContext ctx) { switch (type.type()) { case BYTE: return readByte(); @@ -2256,19 +2273,19 @@ protected Object read(MessageType type, MessageReader reader) { return readGridLongList(); case MAP: - return nestedRead(reader, () -> reader.readMap((MessageMapType)type)); + return nestedRead(reader, () -> reader.readMap((MessageMapType)type, ctx)); case COLLECTION: - return nestedRead(reader, () -> reader.readCollection((MessageCollectionType)type)); + return nestedRead(reader, () -> reader.readCollection((MessageCollectionType)type, ctx)); case ARRAY: - return nestedRead(reader, () -> reader.readObjectArray((MessageArrayType)type)); + return nestedRead(reader, () -> reader.readObjectArray((MessageArrayType)type, ctx)); case ENUM: return ((MessageEnumType)type).decode(readByte()); case MSG: - return readMessage(reader); + return readMessage(reader, ctx); default: throw new IllegalArgumentException("Unknown type: " + type); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/CompressedMessageSerializer.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/CompressedMessageSerializer.java index 7661a11421972..4f96b37f88e63 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/CompressedMessageSerializer.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/CompressedMessageSerializer.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import org.apache.ignite.IgniteException; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.plugin.extensions.communication.MessageReader; import org.apache.ignite.plugin.extensions.communication.MessageSerializer; import org.apache.ignite.plugin.extensions.communication.MessageWriter; @@ -28,7 +29,7 @@ /** Message serializer for compressed message. */ public class CompressedMessageSerializer implements MessageSerializer { /** {@inheritDoc} */ - @Override public boolean writeTo(CompressedMessage msg, MessageWriter writer) { + @Override public boolean writeTo(CompressedMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -74,7 +75,7 @@ public class CompressedMessageSerializer implements MessageSerializer> { + private class ClientMessageWorker extends MessageWorker { /** Node ID. */ private final UUID clientNodeId; - // The code responsible for sending and receiving messages to and from client nodes represents a special case in ServerImpl, - // as it is split into two separate components. - // One part, ClientMessageWorker, handles only message sending to clients and does not process responses. - // The other part, which reads messages from clients, is implemented in SocketReader. - // Due to this separation, we don't require a full TcpDiscoveryIoSession here - // and can instead extract just the message-writing functionality. - // At the same time, we aim to keep both reading and writing logic encapsulated within TcpDiscoveryIoSession. - // As a result, we need to copy some code from TcpDiscoveryIoSession into the new class, TcpDiscoveryMessageSerializer. - /** */ - private final TcpDiscoveryMessageSerializer clientMsgSer; - /** Session shared with the socket reader serving the same client connection. */ private final TcpDiscoveryIoSession ses; @@ -7518,11 +7510,14 @@ private ClientMessageWorker(TcpDiscoveryIoSession ses, UUID clientNodeId, Ignite this.ses = ses; this.clientNodeId = clientNodeId; - clientMsgSer = new TcpDiscoveryMessageSerializer(ctx); - lastMetricsUpdateMsgTimeNanos = System.nanoTime(); } + /** */ + TcpDiscoveryIoSession session() { + return ses; + } + /** * @param clientVer Client version. */ @@ -7546,24 +7541,19 @@ void metrics(ClusterMetrics metrics) { this.metrics = metrics; } - /** - * @param msg Message. - */ + /** @param msg Discovery Message. */ void addMessage(TcpDiscoveryAbstractMessage msg) { - addMessage(msg, null); + addMessage(new ClientMessageHolder(msg)); } - /** - * @param msg Message. - * @param msgBytes Optional message bytes. - */ - void addMessage(TcpDiscoveryAbstractMessage msg, @Nullable byte[] msgBytes) { - T2 t = new T2<>(msg, msgBytes); + /** @param msgHolder Holder of a Discovery Message to send to the client. */ + void addMessage(ClientMessageHolder msgHolder) { + TcpDiscoveryAbstractMessage msg = msgHolder.message(); if (msg.highPriority()) - queue.addFirst(t); + queue.addFirst(msgHolder); else - queue.add(t); + queue.add(msgHolder); DebugLogger log = messageLogger(msg); @@ -7572,10 +7562,10 @@ void addMessage(TcpDiscoveryAbstractMessage msg, @Nullable byte[] msgBytes) { } /** {@inheritDoc} */ - @Override protected void processMessage(T2 msgT) { + @Override protected void processMessage(ClientMessageHolder msgHolder) { boolean success = false; - TcpDiscoveryAbstractMessage msg = msgT.get1(); + TcpDiscoveryAbstractMessage msg = msgHolder.message(); try { assert msg.verified() : msg; @@ -7601,8 +7591,9 @@ else if (msgLog.isDebugEnabled()) { + getLocalNodeId() + ", rmtNodeId=" + clientNodeId + ", msg=" + msg + ']'); } - writeToSocket(msgT, spi.failureDetectionTimeoutEnabled() ? spi.clientFailureDetectionTimeout() : - spi.getSocketTimeout()); + long timeout = spi.failureDetectionTimeoutEnabled() ? spi.clientFailureDetectionTimeout() : spi.getSocketTimeout(); + + writeMessage(msgHolder, timeout); } } else { @@ -7613,7 +7604,7 @@ else if (msgLog.isDebugEnabled()) { assert topologyInitialized(msg) : msg; - writeToSocket(msgT, spi.getEffectiveSocketTimeout(false)); + writeMessage(msgHolder, spi.getEffectiveSocketTimeout(false)); } boolean clientFailed = msg instanceof TcpDiscoveryNodeFailedMessage && @@ -7643,14 +7634,16 @@ else if (msgLog.isDebugEnabled()) { } /** - * @param msgT Message tuple. + * @param msgHolder Message holder. * @param timeout Timeout. */ - private void writeToSocket(T2 msgT, long timeout) - throws IgniteCheckedException, IOException { - byte[] msgBytes = msgT.get2() == null ? clientMsgSer.serializeMessage(msgT.get1()) : msgT.get2(); + private void writeMessage(ClientMessageHolder msgHolder, long timeout) throws IgniteCheckedException, IOException { + byte[] msgBytes = msgHolder.messageBytes(ses.serializationContext()); - spi.write(ses, msgBytes, timeout); + if (msgBytes != null) + spi.write(ses, msgBytes, timeout); + else + spi.writeMessage(ses, msgHolder.message(), timeout); } /** diff --git a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryIoSession.java b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryIoSession.java index 0251712fc95b7..0efdf394c11e4 100644 --- a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryIoSession.java +++ b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryIoSession.java @@ -35,10 +35,12 @@ import org.apache.ignite.IgniteException; import org.apache.ignite.IgniteLogger; import org.apache.ignite.internal.GridKernalContext; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.direct.DirectMessageReader; -import org.apache.ignite.internal.direct.DirectMessageWriter; +import org.apache.ignite.internal.direct.IgniteMessageSerializationContext; import org.apache.ignite.internal.managers.communication.DiscoveryMarshalling; import org.apache.ignite.internal.managers.communication.UnknownMessageException; +import org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteNodeFeatureSet; import org.apache.ignite.internal.util.CommonUtils; import org.apache.ignite.internal.util.nio.MessageSerialization; import org.apache.ignite.internal.util.typedef.X; @@ -47,6 +49,8 @@ import org.apache.ignite.plugin.extensions.communication.Message; import org.apache.ignite.plugin.extensions.communication.MessageFactory; import org.apache.ignite.plugin.extensions.communication.MessageSerializer; +import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryMessageSerializer; +import org.apache.ignite.spi.discovery.tcp.internal.UnsupportedNodeVersionException; import org.apache.ignite.spi.discovery.tcp.messages.TcpDiscoveryAbstractMessage; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -66,8 +70,8 @@ public class TcpDiscoveryIoSession implements AutoCloseable { /** Default size of buffer used for buffering socket in/out. */ private static final int DFLT_SOCK_BUFFER_SIZE = 8192; - /** Size for an intermediate buffer for serializing discovery messages. */ - private static final int MSG_BUFFER_SIZE = 100; + /** Size of the intermediate buffer a message is deserialized through. */ + private static final int READ_BUFFER_SIZE = 100; /** */ private final GridKernalContext ctx; @@ -82,11 +86,14 @@ public class TcpDiscoveryIoSession implements AutoCloseable { private final Socket sock; /** */ - private final DirectMessageWriter msgWriter; + private final TcpDiscoveryMessageSerializer msgSer; /** */ private final DirectMessageReader msgReader; + /** */ + private final ByteBuffer readBuf; + /** Buffered socket output stream. */ private final OutputStream out; @@ -94,10 +101,7 @@ public class TcpDiscoveryIoSession implements AutoCloseable { private final CompositeInputStream in; /** */ - private final ByteBuffer readBuf; - - /** */ - private final ByteBuffer writeBuf; + private volatile MessageSerializationContext serCtx = MessageSerializationContext.UNNEGOTIATED; /** * Creates a new discovery I/O session bound to the given socket. @@ -112,12 +116,11 @@ public class TcpDiscoveryIoSession implements AutoCloseable { this.msgFactory = ctx.messageFactory(); this.log = ctx.log(getClass()); - readBuf = ByteBuffer.allocate(MSG_BUFFER_SIZE); - writeBuf = ByteBuffer.allocate(MSG_BUFFER_SIZE); - - msgWriter = new DirectMessageWriter(msgFactory); + readBuf = ByteBuffer.allocate(READ_BUFFER_SIZE); msgReader = new DirectMessageReader(msgFactory, null); + msgSer = new TcpDiscoveryMessageSerializer(ctx); + try { int sendBufSize = sock.getSendBufferSize() > 0 ? sock.getSendBufferSize() : DFLT_SOCK_BUFFER_SIZE; int rcvBufSize = sock.getReceiveBufferSize() > 0 ? sock.getReceiveBufferSize() : DFLT_SOCK_BUFFER_SIZE; @@ -130,15 +133,25 @@ public class TcpDiscoveryIoSession implements AutoCloseable { } } + /** */ + void applyMessageSerializationContext(@Nullable IgniteNodeFeatureSet rmtFeatures) throws UnsupportedNodeVersionException { + serCtx = IgniteMessageSerializationContext.buildForPeers(ctx.localNodeFeatures(), rmtFeatures); + } + + /** @return Serialization context the two nodes of this session agreed on. */ + public MessageSerializationContext serializationContext() { + return serCtx; + } + /** * Writes a discovery message to the underlying socket output stream. * * @param msg Message to send to the remote node. * @throws IgniteCheckedException If serialization fails. */ - void writeMessage(TcpDiscoveryAbstractMessage msg) throws IgniteCheckedException, IOException { + synchronized void writeMessage(TcpDiscoveryAbstractMessage msg) throws IgniteCheckedException, IOException { try { - serializeMessage((Message)msg, out); + msgSer.writeTo(msg, out, serCtx); out.flush(); } @@ -210,7 +223,7 @@ T readMessage() throws IgniteCheckedException, IOException { readBuf.limit(read); - finished = MessageSerialization.readFrom(msgFactory, msg, msgReader); + finished = MessageSerialization.readFrom(msgFactory, msg, msgReader, serCtx); // Server Discovery only sends next message to next Server upon receiving a receipt for the previous one. // This behaviour guarantees that we never read a next message from the buffer right after the end of @@ -262,39 +275,13 @@ public Socket socket() { return sock; } - /** - * Serializes a discovery message into given output stream. - * - * @param m Discovery message to serialize. - * @param out Output stream to write serialized message. - * @throws IOException If serialization fails. - */ - void serializeMessage(Message m, OutputStream out) throws IOException, IgniteCheckedException { - DiscoveryMarshalling.marshal(m, ctx, null); - - msgWriter.reset(); - msgWriter.setBuffer(writeBuf); - - boolean finished; - - do { - // Should be cleared before first operation. - writeBuf.clear(); - - finished = MessageSerialization.writeTo(msgFactory, m, msgWriter); - - out.write(writeBuf.array(), 0, writeBuf.position()); - } - while (!finished); - } - /** * Writes raw data to the underlying socket output stream. * * @param data Raw data to write. * @throws IOException If failed. */ - void write(byte[] data) throws IOException { + synchronized void write(byte[] data) throws IOException { out.write(data); out.flush(); @@ -306,7 +293,7 @@ void write(byte[] data) throws IOException { * @param b Integer response. * @throws IOException If failed. */ - void write(int b) throws IOException { + synchronized void write(int b) throws IOException { out.write(b); out.flush(); diff --git a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryMessageSerializer.java b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryMessageSerializer.java deleted file mode 100644 index ec7cdc569f0c7..0000000000000 --- a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryMessageSerializer.java +++ /dev/null @@ -1,68 +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.spi.discovery.tcp; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.Socket; -import org.apache.ignite.IgniteCheckedException; -import org.apache.ignite.internal.GridKernalContext; -import org.apache.ignite.plugin.extensions.communication.Message; -import org.apache.ignite.plugin.extensions.communication.MessageSerializer; -import org.apache.ignite.spi.discovery.tcp.messages.TcpDiscoveryAbstractMessage; - -/** - * Class is responsible for serializing discovery messages using RU-ready {@link MessageSerializer} mechanism. - *

- * It is used in a special case: when server wants to send discovery messages to clients, it may not have a {@link TcpDiscoveryIoSession} - * to serialize the messages. - * This class enables server to serialize discovery messages anyway, duplicating serialization code from {@link TcpDiscoveryIoSession}. - */ -class TcpDiscoveryMessageSerializer extends TcpDiscoveryIoSession { - /** - * @param ctx Kernal context. - */ - public TcpDiscoveryMessageSerializer(GridKernalContext ctx) { - super(ctx, new Socket() { - @Override public OutputStream getOutputStream() throws IOException { - return null; - } - - @Override public InputStream getInputStream() throws IOException { - return null; - } - }); - } - - /** - * Serializes a discovery message into a byte array. - * - * @param msg Discovery message to serialize. - * @return Serialized byte array containing the message data. - * @throws IgniteCheckedException If serialization fails. - * @throws IOException If serialization fails. - */ - byte[] serializeMessage(TcpDiscoveryAbstractMessage msg) throws IgniteCheckedException, IOException { - try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { - serializeMessage((Message)msg, out); - - return out.toByteArray(); - } - } -} diff --git a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpi.java b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpi.java index 7b8490ded8385..c2e1f64295681 100644 --- a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpi.java +++ b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpi.java @@ -58,8 +58,6 @@ import org.apache.ignite.internal.managers.communication.UnknownMessageException; import org.apache.ignite.internal.managers.discovery.IgniteDiscoverySpi; import org.apache.ignite.internal.processors.metric.MetricRegistryImpl; -import org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteComponentFeatureSet; -import org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteNodeFeatureSet; import org.apache.ignite.internal.util.tostring.GridToStringExclude; import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.X; @@ -98,7 +96,6 @@ import org.apache.ignite.spi.discovery.tcp.internal.DiscoveryDataPacket; import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryNode; import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryStatistics; -import org.apache.ignite.spi.discovery.tcp.internal.UnsupportedNodeVersionException; import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder; import org.apache.ignite.spi.discovery.tcp.ipfinder.jdbc.TcpDiscoveryJdbcIpFinder; import org.apache.ignite.spi.discovery.tcp.ipfinder.multicast.TcpDiscoveryMulticastIpFinder; @@ -1680,36 +1677,6 @@ Socket createSocket() throws IOException { } } - /** */ - void validateRemoteFeatures(IgniteNodeFeatureSet rmtFeatures) throws IgniteCheckedException { - if (rmtFeatures == null) { - throw new UnsupportedNodeVersionException( - "Failed to obtain remote node features. The remote node may be running an unsupported Ignite version," + - " which may result in unexpected handshake message serialization"); - } - - for (IgniteComponentFeatureSet rmtCmpFeatures : rmtFeatures.values()) { - IgniteComponentFeatureSet locCmpFeatures = locNode.features().componentFeatures(rmtCmpFeatures.componentName()); - - if (locCmpFeatures == null) - continue; - - int c = locCmpFeatures.version().compareTo(rmtCmpFeatures.version()); - - if (c == 0) - continue; - - IgniteComponentFeatureSet src = c > 0 ? rmtCmpFeatures : locCmpFeatures; - IgniteComponentFeatureSet target = c > 0 ? locCmpFeatures : rmtCmpFeatures; - - if (!src.isUpgradableTo(target)) { - throw new UnsupportedNodeVersionException("Remote node component versions are not supported" + - " [locComponents=" + locNode.features() + - ", rmtComponents=" + rmtFeatures + ']'); - } - } - } - /** * Writes raw data to the session socket. * diff --git a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/ClientMessageHolder.java b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/ClientMessageHolder.java new file mode 100644 index 0000000000000..e663ab47795fc --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/ClientMessageHolder.java @@ -0,0 +1,62 @@ +/* + * 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.spi.discovery.tcp.internal; + +import java.util.HashMap; +import java.util.Map; +import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.internal.MessageSerializationContext; +import org.apache.ignite.spi.discovery.tcp.messages.TcpDiscoveryAbstractMessage; +import org.jetbrains.annotations.Nullable; + +/** */ +public class ClientMessageHolder { + /** */ + private final TcpDiscoveryAbstractMessage msg; + + /** */ + private final Map bytesByCtx = new HashMap<>(1); + + /** */ + public ClientMessageHolder(TcpDiscoveryAbstractMessage msg) { + assert msg != null; + + this.msg = msg; + } + + /** */ + public TcpDiscoveryAbstractMessage message() { + return msg; + } + + /** */ + public synchronized byte @Nullable [] messageBytes(MessageSerializationContext ctx) { + return bytesByCtx.get(ctx); + } + + /** */ + public synchronized void serialize(TcpDiscoveryMessageSerializer ser, MessageSerializationContext ctx) throws IgniteCheckedException { + if (!bytesByCtx.containsKey(ctx)) + bytesByCtx.put(ctx, ser.serialize(msg, ctx)); + } + + /** {@inheritDoc} */ + @Override public String toString() { + return msg.toString(); + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/TcpDiscoveryMessageSerializer.java b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/TcpDiscoveryMessageSerializer.java new file mode 100644 index 0000000000000..9102876206bd4 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/TcpDiscoveryMessageSerializer.java @@ -0,0 +1,106 @@ +/* + * 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.spi.discovery.tcp.internal; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.internal.GridKernalContext; +import org.apache.ignite.internal.MessageSerializationContext; +import org.apache.ignite.internal.direct.DirectMessageWriter; +import org.apache.ignite.internal.managers.communication.DiscoveryMarshalling; +import org.apache.ignite.internal.util.io.GridByteArrayOutputStream; +import org.apache.ignite.internal.util.nio.MessageSerialization; +import org.apache.ignite.spi.discovery.tcp.messages.TcpDiscoveryAbstractMessage; + +/** */ +public class TcpDiscoveryMessageSerializer { + /** Size of the intermediate buffer a message is serialized through. */ + private static final int BUFFER_SIZE = 100; + + /** */ + private final GridKernalContext ctx; + + /** */ + private final DirectMessageWriter writer; + + /** */ + private final ByteBuffer buf = ByteBuffer.allocate(BUFFER_SIZE); + + /** @param ctx Kernal context. */ + public TcpDiscoveryMessageSerializer(GridKernalContext ctx) { + this.ctx = ctx; + + writer = new DirectMessageWriter(ctx.messageFactory()); + } + + /** + * Serializes a discovery message into given output stream. + * + * @param msg Discovery message to serialize. + * @param out Output stream to write serialized message. + * @param serCtx Serialization context the recipient agreed on. + * @throws IgniteCheckedException If serialization fails. + * @throws IOException If serialization fails. + */ + public void writeTo( + TcpDiscoveryAbstractMessage msg, + OutputStream out, + MessageSerializationContext serCtx + ) throws IgniteCheckedException, IOException { + DiscoveryMarshalling.marshal(msg, ctx, null); + + writer.reset(); + writer.setBuffer(buf); + + boolean finished; + + do { + // Should be cleared before first operation. + buf.clear(); + + finished = MessageSerialization.writeTo(ctx.messageFactory(), msg, writer, serCtx); + + out.write(buf.array(), 0, buf.position()); + } + while (!finished); + } + + /** + * Serializes a discovery message into a byte array. + * + * @param msg Discovery message to serialize. + * @param serCtx Serialization context the recipient agreed on. + * @return Serialized byte array containing the message data. + * @throws IgniteCheckedException If serialization fails. + */ + public byte[] serialize( + TcpDiscoveryAbstractMessage msg, + MessageSerializationContext serCtx + ) throws IgniteCheckedException { + try (GridByteArrayOutputStream out = new GridByteArrayOutputStream()) { + writeTo(msg, out, serCtx); + + return out.toByteArray(); + } + catch (IOException e) { + throw new IgniteCheckedException("Failed to serialize a discovery message: " + msg, e); + } + } +} 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 1105f85cf3fd9..1f8a288af4d3f 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 @@ -93,6 +93,46 @@ public void testProcessorGeneratesSerializer() { .hasSourceEquivalentTo(javaFile("TestMessageMarshaller.java")); } + /** */ + @Test + public void testRollingUpgradeAwareMessage() { + Compilation compilation = compile("TestFeatureRegistry.java", "TestRollingUpgradeAwareMessage.java"); + + assertThat(compilation).succeeded(); + + assertThat(compilation) + .generatedSourceFile("org.apache.ignite.internal.TestRollingUpgradeAwareMessageSerializer") + .hasSourceEquivalentTo(javaFile("TestRollingUpgradeAwareMessageSerializer.java")); + } + + /** */ + @Test + public void testUnknownFeatureConstantRejected() { + Compilation compilation = compile("TestUnknownFeatureMessage.java"); + + assertThat(compilation).failed(); + assertThat(compilation).hadErrorContaining("Failed to resolve feature in the registry by its name [reg="); + assertThat(compilation).hadErrorContaining(", feature=NO_SUCH_FEATURE]"); + } + + /** */ + @Test + public void testSameFeatureInBothGuardsRejected() { + Compilation compilation = compile("TestFeatureConflictMessage.java"); + + assertThat(compilation).failed(); + assertThat(compilation).hadErrorContaining("must not reference the same feature"); + } + + /** */ + @Test + public void testFeatureConstantOfWrongTypeRejected() { + Compilation compilation = compile("TestInvalidFeatureRegistry.java", "TestInvalidFeatureMessage.java"); + + assertThat(compilation).failed(); + assertThat(compilation).hadErrorContaining("must be of type IgniteFeature [reg="); + } + /** */ @Test public void testCollectionsMessage() { diff --git a/modules/core/src/test/java/org/apache/ignite/internal/direct/DirectMarshallingMessagesTest.java b/modules/core/src/test/java/org/apache/ignite/internal/direct/DirectMarshallingMessagesTest.java index e9874e03055fc..a3e66c91819bd 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/direct/DirectMarshallingMessagesTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/direct/DirectMarshallingMessagesTest.java @@ -31,6 +31,7 @@ import org.apache.ignite.transactions.TransactionIsolation; import org.junit.Test; +import static org.apache.ignite.internal.MessageSerializationContext.IGNORED; import static org.apache.ignite.transactions.TransactionIsolation.READ_COMMITTED; import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_READ; import static org.apache.ignite.transactions.TransactionIsolation.SERIALIZABLE; @@ -139,7 +140,7 @@ private T doMarshalUnmarshalChunked(T srcMsg) { writer.setBuffer(chunk); - fullyWritten = writer.writeMessage(srcMsg, false); + fullyWritten = writer.writeMessage(srcMsg, false, IGNORED); chunk.flip(); @@ -168,7 +169,7 @@ private T doMarshalUnmarshalChunked(T srcMsg) { reader.setBuffer(chunk); - resMsg = reader.readMessage(false); + resMsg = reader.readMessage(false, IGNORED); pos += chunk.position(); } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/managers/AbstractMessageSerializationTest.java b/modules/core/src/test/java/org/apache/ignite/internal/managers/AbstractMessageSerializationTest.java index bfa3c8e314204..48e861cc6c98a 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/managers/AbstractMessageSerializationTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/managers/AbstractMessageSerializationTest.java @@ -25,6 +25,7 @@ import java.util.Map; import java.util.Set; import java.util.UUID; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.managers.communication.IgniteMessageFactoryImpl; import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; import org.apache.ignite.internal.processors.cache.CacheObject; @@ -47,6 +48,7 @@ import org.junit.Test; import static java.lang.Integer.MAX_VALUE; +import static org.apache.ignite.internal.MessageSerializationContext.IGNORED; import static org.apache.ignite.plugin.extensions.communication.CollectionImplementationType.HASH_SET; import static org.junit.Assert.assertEquals; @@ -106,13 +108,13 @@ private void checkSerializationAndDeserializationConsistency( initializeMessage(msg); - while (!MessageSerialization.writeTo(msgFactory, msg, writer)) { + while (!MessageSerialization.writeTo(msgFactory, msg, writer, IGNORED)) { // No-op. } msg = msgFactory.create(msgType); - while (!MessageSerialization.readFrom(msgFactory, msg, reader)) { + while (!MessageSerialization.readFrom(msgFactory, msg, reader, IGNORED)) { // No-op. } @@ -295,22 +297,22 @@ private boolean writeField(Class type) { } /** {@inheritDoc} */ - @Override public boolean writeMessage(Message val, boolean compress) { + @Override public boolean writeMessage(Message val, boolean compress, MessageSerializationContext ctx) { return writeField(Message.class); } /** {@inheritDoc} */ - @Override public boolean writeObjectArray(T[] arr, MessageArrayType type) { + @Override public boolean writeObjectArray(T[] arr, MessageArrayType type, MessageSerializationContext ctx) { return writeField(Object[].class); } /** {@inheritDoc} */ - @Override public boolean writeCollection(Collection col, MessageCollectionType type) { + @Override public boolean writeCollection(Collection col, MessageCollectionType type, MessageSerializationContext ctx) { return writeField(type.collectionImplementationType() == HASH_SET ? Set.class : Collection.class); } /** {@inheritDoc} */ - @Override public boolean writeMap(Map map, MessageMapType type, boolean compress) { + @Override public boolean writeMap(Map map, MessageMapType type, boolean compress, MessageSerializationContext ctx) { return writeField(type.linked() ? LinkedHashMap.class : HashMap.class); } @@ -537,7 +539,7 @@ private void readField(Class type) { } /** {@inheritDoc} */ - @Override public T readMessage(boolean compress) { + @Override public T readMessage(boolean compress, MessageSerializationContext ctx) { readField(Message.class); return null; @@ -565,21 +567,21 @@ private void readField(Class type) { } /** {@inheritDoc} */ - @Override public T[] readObjectArray(MessageArrayType type) { + @Override public T[] readObjectArray(MessageArrayType type, MessageSerializationContext ctx) { readField(Object[].class); return null; } /** {@inheritDoc} */ - @Override public > C readCollection(MessageCollectionType type) { + @Override public > C readCollection(MessageCollectionType type, MessageSerializationContext ctx) { readField(type.collectionImplementationType() == HASH_SET ? Set.class : Collection.class); return null; } /** {@inheritDoc} */ - @Override public > M readMap(MessageMapType type, boolean compress) { + @Override public > M readMap(MessageMapType type, boolean compress, MessageSerializationContext ctx) { readField(type.linked() ? LinkedHashMap.class : HashMap.class); return null; diff --git a/modules/core/src/test/java/org/apache/ignite/internal/managers/communication/CompressedMessageTest.java b/modules/core/src/test/java/org/apache/ignite/internal/managers/communication/CompressedMessageTest.java index 046dbbb6f55b0..28c5b23e347be 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/managers/communication/CompressedMessageTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/managers/communication/CompressedMessageTest.java @@ -42,6 +42,7 @@ import org.apache.ignite.testframework.GridTestUtils; import org.junit.Test; +import static org.apache.ignite.internal.MessageSerializationContext.IGNORED; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -70,7 +71,7 @@ public void testWriteReadHugeMessage() { ByteBuffer msgBuf = ByteBuffer.allocate(40_960); while (!finished) { - finished = writer.writeMessage(fullMsg, true); + finished = writer.writeMessage(fullMsg, true, IGNORED); if (checkChunkCnt) { DirectMessageState state = U.field(writer, "state"); @@ -104,7 +105,7 @@ public void testWriteReadHugeMessage() { reader.setBuffer(msgBuf); - Message readMsg = reader.readMessage(true); + Message readMsg = reader.readMessage(true, IGNORED); assertTrue(readMsg instanceof GridDhtPartitionsFullMessage); @@ -133,7 +134,7 @@ public void testReadFailsOnNullChunk() { reader.setBuffer(buf); GridTestUtils.assertThrows(null, - () -> MessageSerialization.readFrom(MSG_FACTORY, new CompressedMessage(), reader), + () -> MessageSerialization.readFrom(MSG_FACTORY, new CompressedMessage(), reader, IGNORED), IgniteException.class, "unexpected null chunk"); } @@ -156,7 +157,7 @@ public void testReadFailsOnNegativeDataSize() { reader.setBuffer(buf); GridTestUtils.assertThrows(null, - () -> MessageSerialization.readFrom(MSG_FACTORY, new CompressedMessage(), reader), + () -> MessageSerialization.readFrom(MSG_FACTORY, new CompressedMessage(), reader, IGNORED), IgniteException.class, "Invalid compressed message data size"); } @@ -217,7 +218,7 @@ public void testReadFailsOnTruncatedPayload() { writer.setBuffer(tmpBuf); - assertTrue(writer.writeMessage(fullMessage(), false)); + assertTrue(writer.writeMessage(fullMessage(), false, IGNORED)); tmpBuf.flip(); @@ -231,7 +232,7 @@ public void testReadFailsOnTruncatedPayload() { wireWriter.setBuffer(wire); - assertTrue(wireWriter.writeMessage(compressedMsg, false)); + assertTrue(wireWriter.writeMessage(compressedMsg, false, IGNORED)); wire.flip(); @@ -239,7 +240,7 @@ public void testReadFailsOnTruncatedPayload() { reader.setBuffer(wire); - GridTestUtils.assertThrows(null, () -> reader.readMessage(true), IgniteException.class, "ended unexpectedly"); + GridTestUtils.assertThrows(null, () -> reader.readMessage(true, IGNORED), IgniteException.class, "ended unexpectedly"); } /** */ diff --git a/modules/core/src/test/java/org/apache/ignite/internal/managers/communication/GridIoManagerOrderedUnmarshalFailureTest.java b/modules/core/src/test/java/org/apache/ignite/internal/managers/communication/GridIoManagerOrderedUnmarshalFailureTest.java index 4b51337eac099..2a5ce91b96302 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/managers/communication/GridIoManagerOrderedUnmarshalFailureTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/managers/communication/GridIoManagerOrderedUnmarshalFailureTest.java @@ -28,6 +28,7 @@ import org.apache.ignite.internal.CoreMessagesProvider; import org.apache.ignite.internal.GridKernalContext; import org.apache.ignite.internal.IgniteEx; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.processors.cache.CacheObjectContext; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.marshaller.Marshaller; @@ -186,7 +187,7 @@ private static class FailingUnmarshalMessage implements Message { /** Writes the two fields behind the header. */ private static class Serializer implements MessageSerializer { /** {@inheritDoc} */ - @Override public boolean writeTo(FailingUnmarshalMessage msg, MessageWriter writer) { + @Override public boolean writeTo(FailingUnmarshalMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -212,7 +213,7 @@ private static class Serializer implements MessageSerializer @@ -121,7 +123,7 @@ public void testCacheSize() throws Exception { // 2kb should be enough for an empty message even if it is a relatively large metrics message. msgWritter.setBuffer(ByteBuffer.allocate(2048)); - assertTrue(MessageSerialization.writeTo(msgFactory, msg, msgWritter)); + assertTrue(MessageSerialization.writeTo(msgFactory, msg, msgWritter, IGNORED)); assertTrue(msgWritter.getBuffer().hasRemaining()); @@ -133,7 +135,7 @@ public void testCacheSize() throws Exception { TcpDiscoveryMetricsUpdateMessage msg2 = new TcpDiscoveryMetricsUpdateMessage(); - assertTrue(MessageSerialization.readFrom(msgFactory, msg2, msgReader)); + assertTrue(MessageSerialization.readFrom(msgFactory, msg2, msgReader, IGNORED)); Map cacheMetrics2 = msg2.serversFullMetricsMessages().values().iterator().next() .cachesMetricsMessages(); diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/query/continuous/IgniteCacheContinuousQueryImmutableEntryTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/query/continuous/IgniteCacheContinuousQueryImmutableEntryTest.java index 45a5fb3381fd5..4f6851c31a6e6 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/query/continuous/IgniteCacheContinuousQueryImmutableEntryTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/query/continuous/IgniteCacheContinuousQueryImmutableEntryTest.java @@ -45,6 +45,7 @@ import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC; import static org.apache.ignite.cache.CacheMode.PARTITIONED; import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC; +import static org.apache.ignite.internal.MessageSerializationContext.IGNORED; /** * @@ -204,7 +205,7 @@ private CacheContinuousQueryEntry roundTrip(CacheContinuousQueryEntry e) throws // Skip write class header. writer.onHeaderWritten(); - MessageSerialization.writeTo(msgFactory, e, writer); + MessageSerialization.writeTo(msgFactory, e, writer, IGNORED); CacheContinuousQueryEntry res = new CacheContinuousQueryEntry(); @@ -212,7 +213,7 @@ private CacheContinuousQueryEntry roundTrip(CacheContinuousQueryEntry e) throws reader.setBuffer(ByteBuffer.wrap(buf.array())); - MessageSerialization.readFrom(msgFactory, res, reader); + MessageSerialization.readFrom(msgFactory, res, reader, IGNORED); return res; } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/query/schema/message/QueryEntityMessageSerializationTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/query/schema/message/QueryEntityMessageSerializationTest.java index f7fea391425dd..60d05ca3feddf 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/query/schema/message/QueryEntityMessageSerializationTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/query/schema/message/QueryEntityMessageSerializationTest.java @@ -48,6 +48,7 @@ import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; import org.junit.Test; +import static org.apache.ignite.internal.MessageSerializationContext.IGNORED; import static org.apache.ignite.internal.util.CommonUtils.makeMessageType; /** Test for serialization round-trip of {@link QueryEntityMessage} and {@link QueryEntityExMessage}. */ @@ -173,7 +174,7 @@ private T writeAndReadBack(T msg, long expReadsWritesCnt) th DirectMessageWriter writer = new DirectMessageWriter(msgFactory); writer.setBuffer(buf); - assertTrue(MessageSerialization.writeTo(msgFactory, msg, writer)); + assertTrue(MessageSerialization.writeTo(msgFactory, msg, writer, IGNORED)); assertEquals("Writes" + ERROR_SUFFIX, expReadsWritesCnt, writer.state()); @@ -184,7 +185,7 @@ private T writeAndReadBack(T msg, long expReadsWritesCnt) th T res = (T)msgFactory.create(makeMessageType(buf.get(), buf.get())); - assertTrue(MessageSerialization.readFrom(msgFactory, res, reader)); + assertTrue(MessageSerialization.readFrom(msgFactory, res, reader, IGNORED)); assertEquals("Reads" + ERROR_SUFFIX, expReadsWritesCnt, reader.state()); diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/feature/TestPluginReleaseFeatures_2_0_0.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/feature/TestPluginReleaseFeatures_2_0_0.java index d661f5cff66bc..066d0403917df 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/feature/TestPluginReleaseFeatures_2_0_0.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/feature/TestPluginReleaseFeatures_2_0_0.java @@ -19,6 +19,9 @@ /** */ public class TestPluginReleaseFeatures_2_0_0 { + /** */ + public static final IgniteFeature VER_1_0_0_ID_0_FEATURE = TestPluginReleaseFeatures_1_0_0.VER_1_0_0_ID_0_FEATURE; + /** */ public static final IgniteFeature VER_2_0_0_ID_1_FEATURE = new TestPluginFeature(1); } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/feature/TestPluginReleaseFeatures_2_1_0.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/feature/TestPluginReleaseFeatures_2_1_0.java index edb4b3b3646ac..385f7ded6a900 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/feature/TestPluginReleaseFeatures_2_1_0.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/feature/TestPluginReleaseFeatures_2_1_0.java @@ -20,7 +20,10 @@ /** */ public class TestPluginReleaseFeatures_2_1_0 { /** */ - public static final IgniteFeature VER_2_1_0_ID_1_FEATURE = new TestPluginFeature(1); + public static final IgniteFeature VER_1_0_0_ID_0_FEATURE = TestPluginReleaseFeatures_2_0_0.VER_1_0_0_ID_0_FEATURE; + + /** */ + public static final IgniteFeature VER_2_0_0_ID_1_FEATURE = TestPluginReleaseFeatures_2_0_0.VER_2_0_0_ID_1_FEATURE; /** */ public static final IgniteFeature VER_2_1_0_ID_2_FEATURE = new TestPluginFeature(2); diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/RollingUpgradeMessageSerializationTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/RollingUpgradeMessageSerializationTest.java new file mode 100644 index 0000000000000..56e5431d8bb03 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/RollingUpgradeMessageSerializationTest.java @@ -0,0 +1,511 @@ +/* + * 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.processors.rollingupgrade.message; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; +import org.apache.ignite.Ignite; +import org.apache.ignite.Ignition; +import org.apache.ignite.cluster.ClusterNode; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.internal.IgniteEx; +import org.apache.ignite.internal.managers.communication.GridIoPolicy; +import org.apache.ignite.internal.managers.discovery.DiscoveryCustomMessage; +import org.apache.ignite.internal.processors.rollingupgrade.AbstractRollingUpgradeTest; +import org.apache.ignite.plugin.extensions.communication.Message; +import org.apache.ignite.spi.MessagesPluginProvider; +import org.junit.Test; + +import static org.apache.ignite.internal.processors.rollingupgrade.message.TestMessage.A; +import static org.apache.ignite.internal.processors.rollingupgrade.message.TestMessage.B; +import static org.apache.ignite.internal.processors.rollingupgrade.message.TestMessage.C; +import static org.apache.ignite.internal.processors.rollingupgrade.message.TestMessage.D; +import static org.apache.ignite.internal.processors.rollingupgrade.message.TestMessage.E; +import static org.apache.ignite.internal.processors.rollingupgrade.message.TestMessage.F; + +/** */ +public class RollingUpgradeMessageSerializationTest extends AbstractRollingUpgradeTest { + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName, String ver) throws Exception { + IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName, ver); + + cfg.setPluginProviders(org.apache.ignite.internal.util.typedef.F.concat( + cfg.getPluginProviders(), + new MessagesPluginProvider( + TestCoreMessage.class, + TestPluginMessage.class, + TestDefaultRegistryMessage.class)) + ); + + return cfg; + } + + /** */ + @Test + public void testSameOldVersion() throws Exception { + checkMutualCoreMessageSend("2.19.0", "2.19.0", A, B, C, null, null, null); + } + + /** */ + @Test + public void testMixedPair() throws Exception { + checkMutualCoreMessageSend("2.19.0", "2.20.0", A, B, C, null, null, null); + } + + /** */ + @Test + public void testSameNewVersion() throws Exception { + checkMutualCoreMessageSend("2.20.0", "2.20.0", A, B, C, D, E, null); + } + + /** */ + @Test + public void testWindowOpenSameVersion() throws Exception { + checkMutualCoreMessageSend("2.19.2", "2.19.2", A, B, C, D, null, null); + } + + /** */ + @Test + public void testWindowOpenMixedPair() throws Exception { + checkMutualCoreMessageSend("2.19.2", "2.20.0", A, B, C, D, null, null); + } + + /** */ + @Test + public void testWindowClosed() throws Exception { + checkMutualCoreMessageSend("2.20.0", "2.20.1", A, null, C, null, E, null); + } + + /** */ + @Test + public void testDiscoveryNewerClient() throws Exception { + IgniteEx srv = startGrid(0, "2.19.0"); + + ru(srv).enableVersionUpgrade(); + + startClientGrid(1, "2.20.0"); + + checkCoreMessageBroadcast(srv, A, B, C, null, null, null); + } + + /** */ + @Test + public void testDiscoveryClientOriginated() throws Exception { + IgniteEx srv = startGrid(0, "2.19.0"); + + ru(srv).enableVersionUpgrade(); + + IgniteEx cli1 = startClientGrid(1, "2.20.0"); + + startClientGrid(2, "2.19.0"); + + checkCoreMessageBroadcast(cli1, A, B, C, null, null, null); + } + + /** */ + @Test + public void testDiscoveryClientsOnDifferentVersions() throws Exception { + startGrid(0, "2.19.0"); + startGrid(1, "2.19.0"); + + ru(1).enableVersionUpgrade(); + + upgradeNodeVersion(0, "2.20.0"); + upgradeNodeVersion(1, "2.20.0"); + + IgniteEx newVerCli = startClientGrid(2, "2.20.0"); + IgniteEx oldVerCli = startClientGrid(3, "2.19.0"); + + Map receivedMsgs = sendOverDiscovery(grid(1), TestCoreMessage.build()); + + assertFields(A, B, C, D, E, null, receivedMsgs.get(newVerCli.name())); + assertFields(A, B, C, null, null, null, receivedMsgs.get(oldVerCli.name())); + } + + /** */ + @Test + public void testCommunicationWithClient() throws Exception { + IgniteEx srv = startGrid(0, "2.19.0"); + + ru(srv).enableVersionUpgrade(); + + IgniteEx client = startClientGrid(1, "2.20.0"); + + checkMutualCoreMessageSend(srv, client, A, B, C, null, null, null); + } + + /** */ + @Test + public void testDefaultRegistryMixedPair() throws Exception { + startServerNodes("2.19.0", "2.20.0"); + + checkMutualMessageSend(grid(0), grid(1), TestDefaultRegistryMessage::build, A, null, C, D, E, F); + } + + /** */ + @Test + public void testDiscoveryUniformRing() throws Exception { + startGrid(0, "2.20.0"); + startGrid(1, "2.20.0"); + startGrid(2, "2.20.0"); + + checkCoreMessageBroadcast(grid(1), A, B, C, D, E, null); + } + + /** */ + @Test + public void testDiscoveryMixedRing() throws Exception { + startGrid(0, "2.19.0"); + + ru(grid(0)).enableVersionUpgrade(); + + startGrid(1, "2.20.0"); + startGrid(2, "2.20.0"); + + checkCoreMessageBroadcast(grid(1), A, B, C, null, null, null); + } + + + /** */ + @Test + public void testCommunicationUpgradeOpensWindow() throws Exception { + startGrid(0, "2.19.0"); + startGrid(1, "2.19.0"); + + checkMutualCoreMessageSend(grid(0), grid(1), A, B, C, null, null, null); + + ru(1).enableVersionUpgrade(); + + upgradeNodeVersion(0, "2.19.2"); + upgradeNodeVersion(1, "2.19.2"); + + checkMutualCoreMessageSend(grid(0), grid(1), A, B, C, D, null, null); + } + + /** */ + @Test + public void testCommunicationUpgradeAgreesNewFeature() throws Exception { + startGrid(0, "2.19.2"); + startGrid(1, "2.19.2"); + + checkMutualCoreMessageSend(grid(0), grid(1), A, B, C, D, null, null); + + ru(1).enableVersionUpgrade(); + + upgradeNodeVersion(0, "2.19.2", "2.20.0"); + + checkMutualCoreMessageSend(grid(0), grid(1), A, B, C, D, null, null); + + upgradeNodeVersion(1, "2.19.2", "2.20.0"); + + checkMutualCoreMessageSend(grid(0), grid(1), A, B, C, D, E, null); + } + + /** */ + @Test + public void testPluginDiffersCoreMatches() throws Exception { + startServerNodes("2.20.0 | 1.0.0", "2.20.0 | 2.0.0"); + + checkMutualCoreMessageSend(grid(0), grid(1), A, B, C, D, E, null); + + checkMutualMessageSend(grid(0), grid(1), TestPluginMessage::build, A, B, C, D, null, null); + } + + /** */ + @Test + public void testPluginSameVersion() throws Exception { + startServerNodes("2.20.0 | 2.0.0", "2.20.0 | 2.0.0"); + + checkMutualCoreMessageSend(grid(0), grid(1), A, B, C, D, E, null); + + checkMutualMessageSend(grid(0), grid(1), TestPluginMessage::build, A, B, C, D, E, null); + } + + /** */ + @Test + public void testPluginMissingOnClient() throws Exception { + IgniteEx srv = startGrid(0, "2.20.0 | 2.0.0"); + + ru(srv).enableVersionUpgrade(); + + IgniteEx cli = startClientGrid(1, "2.20.0"); + + checkMutualMessageSend(srv, cli, TestPluginMessage::build, A, B, C, null, null, null); + + checkMutualCoreMessageSend(srv, cli, A, B, C, D, E, null); + } + + /** */ + @Test + public void testWholeUpgradeProcess() throws Exception { + startGrid(0, "2.19.0"); + startGrid(1, "2.19.0"); + startClientGrid(2, "2.19.0"); + + checkMessagesTransmissionBetweenAllNodes(A, B, C, null, null, null); + + ru(1).enableVersionUpgrade(); + + checkMessagesTransmissionBetweenAllNodes(A, B, C, null, null, null); + + upgradeNodeVersion(0, "2.19.0", "2.19.2"); + + checkMessagesTransmissionBetweenAllNodes(A, B, C, null, null, null); + + upgradeNodeVersion(1, "2.19.0", "2.19.2"); + + checkMutualCoreMessageSend(grid(0), grid(1), A, B, C, D, null, null); + checkMutualCoreMessageSend(grid(0), grid(2), A, B, C, null, null, null); + checkMutualCoreMessageSend(grid(1), grid(2), A, B, C, null, null, null); + + upgradeNodeVersion(2, "2.19.0", "2.19.2"); + + checkMessagesTransmissionBetweenAllNodes(A, B, C, D, null, null); + + finalizeClusterVersion(0, "2.19.2"); + + checkMessagesTransmissionBetweenAllNodes(A, B, C, D, null, null); + + ru(1).enableVersionUpgrade(); + + upgradeNodeVersion(0, "2.19.2", "2.20.0"); + + checkMessagesTransmissionBetweenAllNodes(A, B, C, D, null, null); + + upgradeNodeVersion(1, "2.19.2", "2.20.0"); + + checkMutualCoreMessageSend(grid(0), grid(1), A, B, C, D, E, null); + checkMutualCoreMessageSend(grid(0), grid(2), A, B, C, D, null, null); + checkMutualCoreMessageSend(grid(1), grid(2), A, B, C, D, null, null); + + upgradeNodeVersion(2, "2.19.2", "2.20.0"); + + checkMessagesTransmissionBetweenAllNodes(A, B, C, D, E, null); + + finalizeClusterVersion(0, "2.20.0"); + + checkMessagesTransmissionBetweenAllNodes(A, B, C, D, E, null); + + ru(1).enableVersionUpgrade(); + + upgradeNodeVersion(0, "2.20.0", "2.20.1"); + + checkMutualCoreMessageSend(grid(0), grid(1), A, null, C, null, E, null); + checkMutualCoreMessageSend(grid(0), grid(2), A, null, C, null, E, null); + checkMutualCoreMessageSend(grid(1), grid(2), A, B, C, D, E, null); + + upgradeNodeVersion(1, "2.20.0", "2.20.1"); + + checkMutualCoreMessageSend(grid(0), grid(1), A, B, C, D, E, F); + checkMutualCoreMessageSend(grid(0), grid(2), A, null, C, null, E, null); + checkMutualCoreMessageSend(grid(1), grid(2), A, null, C, null, E, null); + + upgradeNodeVersion(2, "2.20.0", "2.20.1"); + + checkMessagesTransmissionBetweenAllNodes(A, B, C, D, E, F); + + finalizeClusterVersion(0, "2.20.1"); + + checkMessagesTransmissionBetweenAllNodes(A, B, C, D, E, F); + } + + /** */ + private void checkMessagesTransmissionBetweenAllNodes( + String expA, + String expB, + String expC, + String expD, + String expE, + String expF + ) throws Exception { + List clusterNodes = Ignition.allGrids(); + + for (int i = 0; i < clusterNodes.size(); i++) { + for (int j = i + 1; j < clusterNodes.size(); j++) { + checkMutualCoreMessageSend( + (IgniteEx)clusterNodes.get(i), (IgniteEx)clusterNodes.get(j), expA, expB, expC, expD, expE, expF); + } + } + } + + /** */ + private void checkMutualCoreMessageSend( + String firstVer, + String secondVer, + String expA, + String expB, + String expC, + String expD, + String expE, + String expF + ) throws Exception { + startServerNodes(firstVer, secondVer); + + checkMutualCoreMessageSend(grid(0), grid(1), expA, expB, expC, expD, expE, expF); + } + + /** */ + private void checkMutualCoreMessageSend( + IgniteEx first, + IgniteEx second, + String expA, + String expB, + String expC, + String expD, + String expE, + String expF + ) throws Exception { + checkMutualMessageSend(first, second, TestCoreMessage::build, expA, expB, expC, expD, expE, expF); + } + + /** */ + private void checkCoreMessageBroadcast( + IgniteEx from, + String expA, + String expB, + String expC, + String expD, + String expE, + String expF + ) throws Exception { + Collection receivedMsgs = sendOverDiscovery(from, TestCoreMessage.build()).values(); + + for (TestCoreMessage msg : receivedMsgs) + assertFields(expA, expB, expC, expD, expE, expF, msg); + } + + /** */ + private void checkMutualMessageSend( + IgniteEx first, + IgniteEx second, + Supplier msgFactory, + String expA, + String expB, + String expC, + String expD, + String expE, + String expF + ) throws Exception { + checkReceivedMessageFields(first, second, msgFactory, expA, expB, expC, expD, expE, expF); + checkReceivedMessageFields(second, first, msgFactory, expA, expB, expC, expD, expE, expF); + } + + /** */ + private void checkReceivedMessageFields( + IgniteEx from, + IgniteEx to, + Supplier msgFactory, + String expA, + String expB, + String expC, + String expD, + String expE, + String expF + ) throws Exception { + assertFields(expA, expB, expC, expD, expE, expF, send(from, to, msgFactory.get())); + + assertFields(expA, expB, expC, expD, expE, expF, sendOverDiscovery(from, msgFactory.get()).get(to.name())); + } + + /** */ + private T send(IgniteEx from, IgniteEx to, T msg) throws Exception { + AtomicReference got = new AtomicReference<>(); + CountDownLatch latch = new CountDownLatch(1); + + String topic = msg.getClass().getName(); + + to.context().io().addMessageListener(topic, (nodeId, rcvd, plc) -> { + got.set((T)rcvd); + + latch.countDown(); + }); + + ClusterNode rcvNode = from.context().discovery().node(to.localNode().id()); + + from.context().io().sendToCustomTopic(rcvNode, topic, msg, GridIoPolicy.PUBLIC_POOL); + + assertTrue(latch.await(getTestTimeout(), TimeUnit.MILLISECONDS)); + + return got.get(); + } + + /** */ + private Map sendOverDiscovery( + IgniteEx from, + T msg + ) throws Exception { + List clusterNodes = Ignition.allGrids(); + + Map receivedMsgs = new ConcurrentHashMap<>(); + + CountDownLatch latch = new CountDownLatch(clusterNodes.size()); + + for (Ignite rcv : clusterNodes) { + String name = rcv.name(); + + ((IgniteEx)rcv).context().discovery().setCustomEventListener((Class)msg.getClass(), + (v, n, m) -> { + receivedMsgs.put(name, m); + + latch.countDown(); + }); + } + + from.context().discovery().sendCustomEvent(msg); + + assertTrue(latch.await(getTestTimeout(), TimeUnit.MILLISECONDS)); + + receivedMsgs.remove(from.name()); + + return receivedMsgs; + } + + /** */ + private void startServerNodes(String firstVer, String secondVer) throws Exception { + IgniteEx first = startGrid(0, firstVer); + + if (!firstVer.equals(secondVer)) + ru(first).enableVersionUpgrade(); + + startGrid(1, secondVer); + } + + /** */ + private static void assertFields( + String expA, + String expB, + String expC, + String expD, + String expE, + String expF, + TestMessage msg + ) { + assertEquals(expA, msg.fldA()); + assertEquals(expB, msg.fldB()); + assertEquals(expC, msg.fldC()); + assertEquals(expD, msg.fldD()); + assertEquals(expE, msg.fldE()); + assertEquals(expF, msg.fldF()); + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestCoreMessage.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestCoreMessage.java new file mode 100644 index 0000000000000..843c928ad1ca0 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestCoreMessage.java @@ -0,0 +1,107 @@ +/* + * 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.processors.rollingupgrade.message; + +import org.apache.ignite.internal.FeatureRegistry; +import org.apache.ignite.internal.Order; +import org.apache.ignite.internal.managers.discovery.DiscoveryCustomMessage; +import org.apache.ignite.internal.processors.rollingupgrade.feature.TestIgniteReleaseFeatures_2_20_1; +import org.apache.ignite.lang.IgniteUuid; +import org.jetbrains.annotations.Nullable; + +/** */ +@FeatureRegistry(TestIgniteReleaseFeatures_2_20_1.class) +public class TestCoreMessage extends DiscoveryCustomMessage implements TestMessage { + /** */ + @Order(0) + String fldA; + + /** */ + @Order(value = 1, deprecatedBy = "VER_2_20_0_ID_3_FEATURE") + String fldB; + + /** */ + @Order(2) + String fldC; + + /** */ + @Order(value = 3, introducedBy = "VER_2_19_2_ID_1_FEATURE", deprecatedBy = "VER_2_20_0_ID_3_FEATURE") + String fldD; + + /** */ + @Order(value = 4, introducedBy = "VER_2_20_0_ID_3_FEATURE") + String fldE; + + /** */ + @Order(value = 5, introducedBy = "VER_2_20_1_ID_6_FEATURE") + String fldF; + + /** */ + public TestCoreMessage() { + super(IgniteUuid.randomUuid()); + } + + /** {@inheritDoc} */ + @Nullable @Override public DiscoveryCustomMessage ackMessage() { + return null; + } + + /** */ + public static TestCoreMessage build() { + TestCoreMessage msg = new TestCoreMessage(); + + msg.fldA = A; + msg.fldB = B; + msg.fldC = C; + msg.fldD = D; + msg.fldE = E; + msg.fldF = F; + + return msg; + } + + /** {@inheritDoc} */ + @Override public String fldA() { + return fldA; + } + + /** {@inheritDoc} */ + @Override public String fldB() { + return fldB; + } + + /** {@inheritDoc} */ + @Override public String fldC() { + return fldC; + } + + /** {@inheritDoc} */ + @Override public String fldD() { + return fldD; + } + + /** {@inheritDoc} */ + @Override public String fldE() { + return fldE; + } + + /** {@inheritDoc} */ + @Override public String fldF() { + return fldF; + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestDefaultRegistryMessage.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestDefaultRegistryMessage.java new file mode 100644 index 0000000000000..02e6173990b11 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestDefaultRegistryMessage.java @@ -0,0 +1,104 @@ +/* + * 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.processors.rollingupgrade.message; + +import org.apache.ignite.internal.Order; +import org.apache.ignite.internal.managers.discovery.DiscoveryCustomMessage; +import org.apache.ignite.lang.IgniteUuid; +import org.jetbrains.annotations.Nullable; + +/** */ +public class TestDefaultRegistryMessage extends DiscoveryCustomMessage implements TestMessage { + /** */ + @Order(0) + String fldA; + + /** */ + @Order(value = 1, deprecatedBy = "ROLLING_UPGRADE_FEATURE") + String fldB; + + /** */ + @Order(2) + String fldC; + + /** */ + @Order(3) + String fldD; + + /** */ + @Order(4) + String fldE; + + /** */ + @Order(value = 5, introducedBy = "ROLLING_UPGRADE_FEATURE") + String fldF; + + /** */ + public TestDefaultRegistryMessage() { + super(IgniteUuid.randomUuid()); + } + + /** {@inheritDoc} */ + @Nullable @Override public DiscoveryCustomMessage ackMessage() { + return null; + } + + /** */ + public static TestDefaultRegistryMessage build() { + TestDefaultRegistryMessage msg = new TestDefaultRegistryMessage(); + + msg.fldA = A; + msg.fldB = B; + msg.fldC = C; + msg.fldD = D; + msg.fldE = E; + msg.fldF = F; + + return msg; + } + + /** {@inheritDoc} */ + @Override public String fldA() { + return fldA; + } + + /** {@inheritDoc} */ + @Override public String fldB() { + return fldB; + } + + /** {@inheritDoc} */ + @Override public String fldC() { + return fldC; + } + + /** {@inheritDoc} */ + @Override public String fldD() { + return fldD; + } + + /** {@inheritDoc} */ + @Override public String fldE() { + return fldE; + } + + /** {@inheritDoc} */ + @Override public String fldF() { + return fldF; + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestMessage.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestMessage.java new file mode 100644 index 0000000000000..15c639bf6fdaa --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestMessage.java @@ -0,0 +1,57 @@ +/* + * 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.processors.rollingupgrade.message; + +/** */ +public interface TestMessage { + /** */ + public static final String A = "A"; + + /** */ + public static final String B = "B"; + + /** */ + public static final String C = "C"; + + /** */ + public static final String D = "D"; + + /** */ + public static final String E = "E"; + + /** */ + public static final String F = "F"; + + /** */ + public String fldA(); + + /** */ + public String fldB(); + + /** */ + public String fldC(); + + /** */ + public String fldD(); + + /** */ + public String fldE(); + + /** */ + public String fldF(); +} diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestPluginMessage.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestPluginMessage.java new file mode 100644 index 0000000000000..4bdf8ebe80ac3 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestPluginMessage.java @@ -0,0 +1,107 @@ +/* + * 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.processors.rollingupgrade.message; + +import org.apache.ignite.internal.FeatureRegistry; +import org.apache.ignite.internal.Order; +import org.apache.ignite.internal.managers.discovery.DiscoveryCustomMessage; +import org.apache.ignite.internal.processors.rollingupgrade.feature.TestPluginReleaseFeatures_2_1_0; +import org.apache.ignite.lang.IgniteUuid; +import org.jetbrains.annotations.Nullable; + +/** */ +@FeatureRegistry(TestPluginReleaseFeatures_2_1_0.class) +public class TestPluginMessage extends DiscoveryCustomMessage implements TestMessage { + /** */ + @Order(0) + String fldA; + + /** */ + @Order(value = 1, deprecatedBy = "VER_2_0_0_ID_1_FEATURE") + String fldB; + + /** */ + @Order(2) + String fldC; + + /** */ + @Order(value = 3, introducedBy = "VER_1_0_0_ID_0_FEATURE", deprecatedBy = "VER_2_0_0_ID_1_FEATURE") + String fldD; + + /** */ + @Order(value = 4, introducedBy = "VER_2_0_0_ID_1_FEATURE") + String fldE; + + /** */ + @Order(value = 5, introducedBy = "VER_2_1_0_ID_2_FEATURE") + String fldF; + + /** */ + public TestPluginMessage() { + super(IgniteUuid.randomUuid()); + } + + /** {@inheritDoc} */ + @Nullable @Override public DiscoveryCustomMessage ackMessage() { + return null; + } + + /** */ + public static TestPluginMessage build() { + TestPluginMessage msg = new TestPluginMessage(); + + msg.fldA = A; + msg.fldB = B; + msg.fldC = C; + msg.fldD = D; + msg.fldE = E; + msg.fldF = F; + + return msg; + } + + /** {@inheritDoc} */ + @Override public String fldA() { + return fldA; + } + + /** {@inheritDoc} */ + @Override public String fldB() { + return fldB; + } + + /** {@inheritDoc} */ + @Override public String fldC() { + return fldC; + } + + /** {@inheritDoc} */ + @Override public String fldD() { + return fldD; + } + + /** {@inheritDoc} */ + @Override public String fldE() { + return fldE; + } + + /** {@inheritDoc} */ + @Override public String fldF() { + return fldF; + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/service/LazyServiceConfigurationMessageSerializationTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/service/LazyServiceConfigurationMessageSerializationTest.java index 1536b53e9926d..a978a506abb89 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/service/LazyServiceConfigurationMessageSerializationTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/service/LazyServiceConfigurationMessageSerializationTest.java @@ -37,6 +37,7 @@ import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; import org.junit.Test; +import static org.apache.ignite.internal.MessageSerializationContext.IGNORED; import static org.apache.ignite.internal.util.CommonUtils.makeMessageType; import static org.junit.Assert.assertArrayEquals; @@ -126,7 +127,7 @@ private T writeAndReadBack(T msg, long expReadsWritesCnt) th DirectMessageWriter writer = new DirectMessageWriter(msgFactory); writer.setBuffer(buf); - assertTrue(MessageSerialization.writeTo(msgFactory, msg, writer)); + assertTrue(MessageSerialization.writeTo(msgFactory, msg, writer, IGNORED)); assertEquals("Writes" + ERROR_SUFFIX, expReadsWritesCnt, writer.state()); buf.flip(); @@ -136,7 +137,7 @@ private T writeAndReadBack(T msg, long expReadsWritesCnt) th T res = (T)msgFactory.create(makeMessageType(buf.get(), buf.get())); - assertTrue(MessageSerialization.readFrom(msgFactory, res, reader)); + assertTrue(MessageSerialization.readFrom(msgFactory, res, reader, IGNORED)); assertEquals("Reads" + ERROR_SUFFIX, expReadsWritesCnt, reader.state()); DiscoveryMarshalling.unmarshal(res, kctx); diff --git a/modules/core/src/test/java/org/apache/ignite/internal/util/distributed/DistributedProcessResultMarshallingTest.java b/modules/core/src/test/java/org/apache/ignite/internal/util/distributed/DistributedProcessResultMarshallingTest.java index 0286297db11e4..8a8f0521739c4 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/util/distributed/DistributedProcessResultMarshallingTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/util/distributed/DistributedProcessResultMarshallingTest.java @@ -29,6 +29,7 @@ import org.apache.ignite.internal.CoreMessagesProvider; import org.apache.ignite.internal.GridKernalContext; import org.apache.ignite.internal.IgniteEx; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.TestRecordingCommunicationSpi; import org.apache.ignite.internal.managers.communication.CommunicationMarshalling; import org.apache.ignite.internal.managers.communication.GridIoMessage; @@ -239,7 +240,7 @@ public PayloadMessage() { /** */ private static class PayloadSerializer implements MessageSerializer { /** {@inheritDoc} */ - @Override public boolean writeTo(PayloadMessage msg, MessageWriter writer) { + @Override public boolean writeTo(PayloadMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -255,7 +256,7 @@ private static class PayloadSerializer implements MessageSerializer { /** {@inheritDoc} */ - @Override public boolean writeTo(MarshalOnceCheckMessage msg, MessageWriter writer) { + @Override public boolean writeTo(MarshalOnceCheckMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -169,7 +170,7 @@ private static class Serializer implements MessageSerializer { /** {@inheritDoc} */ - @Override public boolean writeTo(RetryCheckMessage msg, MessageWriter writer) { + @Override public boolean writeTo(RetryCheckMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -221,7 +222,7 @@ private static class RetrySerializer implements MessageSerializer T writeAndReadBack(T msg) throws IgniteCheckedExcept DirectMessageWriter writer = new DirectMessageWriter(msgFactory); writer.setBuffer(buf); - assertTrue(MessageSerialization.writeTo(msgFactory, msg, writer)); + assertTrue(MessageSerialization.writeTo(msgFactory, msg, writer, IGNORED)); buf.flip(); @@ -126,7 +127,7 @@ private T writeAndReadBack(T msg) throws IgniteCheckedExcept T res = (T)msgFactory.create(makeMessageType(buf.get(), buf.get())); - assertTrue(MessageSerialization.readFrom(msgFactory, res, reader)); + assertTrue(MessageSerialization.readFrom(msgFactory, res, reader, IGNORED)); return res; } diff --git a/modules/core/src/test/java/org/apache/ignite/spi/communication/tcp/TestDelayMessageSerializer.java b/modules/core/src/test/java/org/apache/ignite/spi/communication/tcp/TestDelayMessageSerializer.java index ce905b9806f70..1d61f44f57fa7 100644 --- a/modules/core/src/test/java/org/apache/ignite/spi/communication/tcp/TestDelayMessageSerializer.java +++ b/modules/core/src/test/java/org/apache/ignite/spi/communication/tcp/TestDelayMessageSerializer.java @@ -18,6 +18,7 @@ package org.apache.ignite.spi.communication.tcp; import org.apache.ignite.internal.IgniteInterruptedCheckedException; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.plugin.extensions.communication.MessageReader; import org.apache.ignite.plugin.extensions.communication.MessageSerializer; @@ -26,7 +27,7 @@ /** Serializer for {@link TestDelayMessage} that injects an optional write delay for testing. */ public class TestDelayMessageSerializer implements MessageSerializer { /** {@inheritDoc} */ - @Override public boolean writeTo(TestDelayMessage msg, MessageWriter writer) { + @Override public boolean writeTo(TestDelayMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -47,7 +48,7 @@ public class TestDelayMessageSerializer implements MessageSerializer serde; /** {@inheritDoc} */ - @Override public boolean writeTo(ExploitMessage msg, MessageWriter writer) { + @Override public boolean writeTo(ExploitMessage msg, MessageWriter writer, MessageSerializationContext ctx) { initIfNecessary(); - return serde.writeTo(msg, writer); + return serde.writeTo(msg, writer, ctx); } /** {@inheritDoc} */ - @Override public boolean readFrom(ExploitMessage msg, MessageReader reader) { + @Override public boolean readFrom(ExploitMessage msg, MessageReader reader, MessageSerializationContext ctx) { initIfNecessary(); - return serde.readFrom(msg, reader); + return serde.readFrom(msg, reader, ctx); } /** {@inheritDoc} */ diff --git a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TestTcpDiscoverySpi.java b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TestTcpDiscoverySpi.java index feedf6a4c6ed5..99eae878e2df1 100644 --- a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TestTcpDiscoverySpi.java +++ b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TestTcpDiscoverySpi.java @@ -125,7 +125,11 @@ public void discoveryHook(DiscoveryHook discoHook) { }; try (dataSock) { - return new TcpDiscoveryIoSession(ctx, dataSock).readMessage(); + TcpDiscoveryIoSession ses = new TcpDiscoveryIoSession(ctx, dataSock); + + ses.applyMessageSerializationContext(ctx.localNodeFeatures()); + + return ses.readMessage(); } catch (Exception e) { throw new IgniteException("Failed to decode a message", e); diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/GridTestNode.java b/modules/core/src/test/java/org/apache/ignite/testframework/GridTestNode.java index cd76954a1a274..e90cc86847564 100644 --- a/modules/core/src/test/java/org/apache/ignite/testframework/GridTestNode.java +++ b/modules/core/src/test/java/org/apache/ignite/testframework/GridTestNode.java @@ -17,15 +17,19 @@ package org.apache.ignite.testframework; +import java.io.Serializable; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; +import org.apache.ignite.cache.CacheMetrics; import org.apache.ignite.cluster.ClusterMetrics; import org.apache.ignite.cluster.ClusterNode; import org.apache.ignite.internal.IgniteNodeAttributes; +import org.apache.ignite.internal.managers.discovery.IgniteClusterNode; +import org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteNodeFeatureSet; import org.apache.ignite.internal.util.lang.GridMetadataAwareAdapter; import org.apache.ignite.lang.IgniteProductVersion; @@ -34,7 +38,7 @@ /** * Test node. */ -public class GridTestNode extends GridMetadataAwareAdapter implements ClusterNode { +public class GridTestNode extends GridMetadataAwareAdapter implements IgniteClusterNode { /** */ private static final IgniteProductVersion VERSION = fromString("99.99.99"); @@ -103,13 +107,6 @@ public GridTestNode(UUID id, ClusterMetrics metrics) { return id; } - /** - * @param consistentId Consistent ID. - */ - public void consistentId(Object consistentId) { - this.consistentId = consistentId; - } - /** {@inheritDoc} */ @Override public Object consistentId() { return consistentId; @@ -213,6 +210,16 @@ public void order(long order) { return VERSION; } + /** {@inheritDoc} */ + @Override public IgniteNodeFeatureSet features() { + return IgniteNodeFeatureSet.LOCAL_CORE_FEATURES; + } + + /** {@inheritDoc} */ + @Override public void setConsistentId(Serializable consistentId) { + this.consistentId = consistentId; + } + /** * Sets node metrics. * @@ -222,6 +229,16 @@ public void setMetrics(ClusterMetrics metrics) { this.metrics = metrics; } + /** {@inheritDoc} */ + @Override public Map cacheMetrics() { + return Collections.emptyMap(); + } + + /** {@inheritDoc} */ + @Override public void setCacheMetrics(Map cacheMetrics) { + // No-op. + } + /** {@inheritDoc} */ @Override public boolean isLocal() { return false; diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java index 55e53c7d44cb1..4767e5fdc587d 100755 --- a/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java +++ b/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java @@ -1414,7 +1414,7 @@ protected final List movingKeysAfterJoin(Ignite ign, String cacheName, if (nodeInitializer != null) nodeInitializer.apply(fakeNode); - fakeNode.consistentId(joiningNodeConsistentId == null ? getTestIgniteInstanceName(nodes.size()) : + fakeNode.setConsistentId(joiningNodeConsistentId == null ? getTestIgniteInstanceName(nodes.size()) : joiningNodeConsistentId); nodes.add(fakeNode); @@ -1465,7 +1465,7 @@ protected List evictingPartitionsAfterJoin(Ignite ign, IgniteCache { /** */ - @Override public final boolean writeTo(ChildMessage msg, MessageWriter writer) { + @Override public final boolean writeTo(ChildMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -68,7 +69,7 @@ public final class ChildMessageSerializer implements MessageSerializer { /** */ - @Override public final boolean writeTo(CorrectEmptyMessage msg, MessageWriter writer) { + @Override public final boolean writeTo(CorrectEmptyMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -44,7 +45,7 @@ public final class CorrectEmptyMessageSerializer implements MessageSerializer(TransactionIsolation.class, transactionIsolationMapper::encode, transactionIsolationMapper::decode), CollectionImplementationType.ARRAY_LIST), CollectionImplementationType.ARRAY_LIST); /** */ - @Override public final boolean writeTo(CustomMapperEnumFieldsMessage msg, MessageWriter writer) { + @Override public final boolean writeTo(CustomMapperEnumFieldsMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -57,7 +58,7 @@ public final class CustomMapperEnumFieldsMessageSerializer implements MessageSer writer.incrementState(); case 1: - if (!writer.writeCollection(msg.isolations, isolationsCollDesc)) + if (!writer.writeCollection(msg.isolations, isolationsCollDesc, ctx)) return false; writer.incrementState(); @@ -67,7 +68,7 @@ public final class CustomMapperEnumFieldsMessageSerializer implements MessageSer } /** */ - @Override public final boolean readFrom(CustomMapperEnumFieldsMessage msg, MessageReader reader) { + @Override public final boolean readFrom(CustomMapperEnumFieldsMessage msg, MessageReader reader, MessageSerializationContext ctx) { switch (reader.state()) { case 0: msg.txMode = transactionIsolationMapper.decode(reader.readByte()); @@ -78,7 +79,7 @@ public final class CustomMapperEnumFieldsMessageSerializer implements MessageSer reader.incrementState(); case 1: - msg.isolations = reader.readCollection(isolationsCollDesc); + msg.isolations = reader.readCollection(isolationsCollDesc, ctx); if (!reader.isLastRead()) return false; diff --git a/modules/core/src/test/resources/codegen/DefaultMapperEnumFieldsMessageSerializer.java b/modules/core/src/test/resources/codegen/DefaultMapperEnumFieldsMessageSerializer.java index dba15be284691..9e707da48c770 100644 --- a/modules/core/src/test/resources/codegen/DefaultMapperEnumFieldsMessageSerializer.java +++ b/modules/core/src/test/resources/codegen/DefaultMapperEnumFieldsMessageSerializer.java @@ -18,6 +18,7 @@ package org.apache.ignite.internal; import org.apache.ignite.internal.DefaultMapperEnumFieldsMessage; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.processors.cache.GridCacheOperation; import org.apache.ignite.internal.processors.cache.verify.PartitionHashRecord.PartitionState; import org.apache.ignite.plugin.extensions.communication.CollectionImplementationType; @@ -50,7 +51,7 @@ public final class DefaultMapperEnumFieldsMessageSerializer implements MessageSe private static final MessageMapType isolationStringMapCollDesc = new MessageMapType(new MessageCollectionType(new MessageEnumType<>(TransactionIsolation.class, DefaultEnumMapper.INSTANCE::encode, b -> DefaultEnumMapper.INSTANCE.decode(transactionIsolationVals, b)), CollectionImplementationType.ARRAY_LIST), new MessageItemType(MessageCollectionItemType.STRING), false); /** */ - @Override public final boolean writeTo(DefaultMapperEnumFieldsMessage msg, MessageWriter writer) { + @Override public final boolean writeTo(DefaultMapperEnumFieldsMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -72,13 +73,13 @@ public final class DefaultMapperEnumFieldsMessageSerializer implements MessageSe writer.incrementState(); case 2: - if (!writer.writeMap(msg.isolationStringMap, isolationStringMapCollDesc)) + if (!writer.writeMap(msg.isolationStringMap, isolationStringMapCollDesc, ctx)) return false; writer.incrementState(); case 3: - if (!writer.writeCollection(msg.partStates, partStatesCollDesc)) + if (!writer.writeCollection(msg.partStates, partStatesCollDesc, ctx)) return false; writer.incrementState(); @@ -88,7 +89,7 @@ public final class DefaultMapperEnumFieldsMessageSerializer implements MessageSe } /** */ - @Override public final boolean readFrom(DefaultMapperEnumFieldsMessage msg, MessageReader reader) { + @Override public final boolean readFrom(DefaultMapperEnumFieldsMessage msg, MessageReader reader, MessageSerializationContext ctx) { switch (reader.state()) { case 0: msg.publicEnum = DefaultEnumMapper.INSTANCE.decode(transactionIsolationVals, reader.readByte()); @@ -107,7 +108,7 @@ public final class DefaultMapperEnumFieldsMessageSerializer implements MessageSe reader.incrementState(); case 2: - msg.isolationStringMap = reader.readMap(isolationStringMapCollDesc); + msg.isolationStringMap = reader.readMap(isolationStringMapCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -115,7 +116,7 @@ public final class DefaultMapperEnumFieldsMessageSerializer implements MessageSe reader.incrementState(); case 3: - msg.partStates = reader.readCollection(partStatesCollDesc); + msg.partStates = reader.readCollection(partStatesCollDesc, ctx); if (!reader.isLastRead()) return false; diff --git a/modules/core/src/test/resources/codegen/TestCollectionsMessageSerializer.java b/modules/core/src/test/resources/codegen/TestCollectionsMessageSerializer.java index bd79c20a48c26..d1814946151ce 100644 --- a/modules/core/src/test/resources/codegen/TestCollectionsMessageSerializer.java +++ b/modules/core/src/test/resources/codegen/TestCollectionsMessageSerializer.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.TestCollectionsMessage; import org.apache.ignite.plugin.extensions.communication.CollectionImplementationType; import org.apache.ignite.plugin.extensions.communication.MessageCollectionItemType; @@ -86,7 +87,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer private static final MessageCollectionType uuidListCollDesc = new MessageCollectionType(new MessageItemType(MessageCollectionItemType.UUID), CollectionImplementationType.ARRAY_LIST); /** */ - @Override public final boolean writeTo(TestCollectionsMessage msg, MessageWriter writer) { + @Override public final boolean writeTo(TestCollectionsMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -96,157 +97,157 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer switch (writer.state()) { case 0: - if (!writer.writeCollection(msg.booleanArrayList, booleanArrayListCollDesc)) + if (!writer.writeCollection(msg.booleanArrayList, booleanArrayListCollDesc, ctx)) return false; writer.incrementState(); case 1: - if (!writer.writeCollection(msg.byteArrayList, byteArrayListCollDesc)) + if (!writer.writeCollection(msg.byteArrayList, byteArrayListCollDesc, ctx)) return false; writer.incrementState(); case 2: - if (!writer.writeCollection(msg.shortArrayList, shortArrayListCollDesc)) + if (!writer.writeCollection(msg.shortArrayList, shortArrayListCollDesc, ctx)) return false; writer.incrementState(); case 3: - if (!writer.writeCollection(msg.intArrayList, intArrayListCollDesc)) + if (!writer.writeCollection(msg.intArrayList, intArrayListCollDesc, ctx)) return false; writer.incrementState(); case 4: - if (!writer.writeCollection(msg.longArrayList, longArrayListCollDesc)) + if (!writer.writeCollection(msg.longArrayList, longArrayListCollDesc, ctx)) return false; writer.incrementState(); case 5: - if (!writer.writeCollection(msg.charArrayList, charArrayListCollDesc)) + if (!writer.writeCollection(msg.charArrayList, charArrayListCollDesc, ctx)) return false; writer.incrementState(); case 6: - if (!writer.writeCollection(msg.floatArrayList, floatArrayListCollDesc)) + if (!writer.writeCollection(msg.floatArrayList, floatArrayListCollDesc, ctx)) return false; writer.incrementState(); case 7: - if (!writer.writeCollection(msg.doubleArrayList, doubleArrayListCollDesc)) + if (!writer.writeCollection(msg.doubleArrayList, doubleArrayListCollDesc, ctx)) return false; writer.incrementState(); case 8: - if (!writer.writeCollection(msg.stringList, stringListCollDesc)) + if (!writer.writeCollection(msg.stringList, stringListCollDesc, ctx)) return false; writer.incrementState(); case 9: - if (!writer.writeCollection(msg.uuidList, uuidListCollDesc)) + if (!writer.writeCollection(msg.uuidList, uuidListCollDesc, ctx)) return false; writer.incrementState(); case 10: - if (!writer.writeCollection(msg.bitSetList, bitSetListCollDesc)) + if (!writer.writeCollection(msg.bitSetList, bitSetListCollDesc, ctx)) return false; writer.incrementState(); case 11: - if (!writer.writeCollection(msg.igniteUuidList, igniteUuidListCollDesc)) + if (!writer.writeCollection(msg.igniteUuidList, igniteUuidListCollDesc, ctx)) return false; writer.incrementState(); case 12: - if (!writer.writeCollection(msg.affTopVersionList, affTopVersionListCollDesc)) + if (!writer.writeCollection(msg.affTopVersionList, affTopVersionListCollDesc, ctx)) return false; writer.incrementState(); case 13: - if (!writer.writeCollection(msg.boxedBooleanList, boxedBooleanListCollDesc)) + if (!writer.writeCollection(msg.boxedBooleanList, boxedBooleanListCollDesc, ctx)) return false; writer.incrementState(); case 14: - if (!writer.writeCollection(msg.boxedByteList, boxedByteListCollDesc)) + if (!writer.writeCollection(msg.boxedByteList, boxedByteListCollDesc, ctx)) return false; writer.incrementState(); case 15: - if (!writer.writeCollection(msg.boxedShortList, boxedShortListCollDesc)) + if (!writer.writeCollection(msg.boxedShortList, boxedShortListCollDesc, ctx)) return false; writer.incrementState(); case 16: - if (!writer.writeCollection(msg.boxedIntList, boxedIntListCollDesc)) + if (!writer.writeCollection(msg.boxedIntList, boxedIntListCollDesc, ctx)) return false; writer.incrementState(); case 17: - if (!writer.writeCollection(msg.boxedLongList, boxedLongListCollDesc)) + if (!writer.writeCollection(msg.boxedLongList, boxedLongListCollDesc, ctx)) return false; writer.incrementState(); case 18: - if (!writer.writeCollection(msg.boxedCharList, boxedCharListCollDesc)) + if (!writer.writeCollection(msg.boxedCharList, boxedCharListCollDesc, ctx)) return false; writer.incrementState(); case 19: - if (!writer.writeCollection(msg.boxedFloatList, boxedFloatListCollDesc)) + if (!writer.writeCollection(msg.boxedFloatList, boxedFloatListCollDesc, ctx)) return false; writer.incrementState(); case 20: - if (!writer.writeCollection(msg.boxedDoubleList, boxedDoubleListCollDesc)) + if (!writer.writeCollection(msg.boxedDoubleList, boxedDoubleListCollDesc, ctx)) return false; writer.incrementState(); case 21: - if (!writer.writeCollection(msg.messageList, messageListCollDesc)) + if (!writer.writeCollection(msg.messageList, messageListCollDesc, ctx)) return false; writer.incrementState(); case 22: - if (!writer.writeCollection(msg.gridLongListList, gridLongListListCollDesc)) + if (!writer.writeCollection(msg.gridLongListList, gridLongListListCollDesc, ctx)) return false; writer.incrementState(); case 23: - if (!writer.writeCollection(msg.boxedIntegerSet, boxedIntegerSetCollDesc)) + if (!writer.writeCollection(msg.boxedIntegerSet, boxedIntegerSetCollDesc, ctx)) return false; writer.incrementState(); case 24: - if (!writer.writeCollection(msg.bitSetSet, bitSetSetCollDesc)) + if (!writer.writeCollection(msg.bitSetSet, bitSetSetCollDesc, ctx)) return false; writer.incrementState(); case 25: - if (!writer.writeCollection(msg.cacheObjectSet, cacheObjectSetCollDesc)) + if (!writer.writeCollection(msg.cacheObjectSet, cacheObjectSetCollDesc, ctx)) return false; writer.incrementState(); @@ -256,10 +257,10 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer } /** */ - @Override public final boolean readFrom(TestCollectionsMessage msg, MessageReader reader) { + @Override public final boolean readFrom(TestCollectionsMessage msg, MessageReader reader, MessageSerializationContext ctx) { switch (reader.state()) { case 0: - msg.booleanArrayList = reader.readCollection(booleanArrayListCollDesc); + msg.booleanArrayList = reader.readCollection(booleanArrayListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -267,7 +268,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 1: - msg.byteArrayList = reader.readCollection(byteArrayListCollDesc); + msg.byteArrayList = reader.readCollection(byteArrayListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -275,7 +276,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 2: - msg.shortArrayList = reader.readCollection(shortArrayListCollDesc); + msg.shortArrayList = reader.readCollection(shortArrayListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -283,7 +284,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 3: - msg.intArrayList = reader.readCollection(intArrayListCollDesc); + msg.intArrayList = reader.readCollection(intArrayListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -291,7 +292,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 4: - msg.longArrayList = reader.readCollection(longArrayListCollDesc); + msg.longArrayList = reader.readCollection(longArrayListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -299,7 +300,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 5: - msg.charArrayList = reader.readCollection(charArrayListCollDesc); + msg.charArrayList = reader.readCollection(charArrayListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -307,7 +308,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 6: - msg.floatArrayList = reader.readCollection(floatArrayListCollDesc); + msg.floatArrayList = reader.readCollection(floatArrayListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -315,7 +316,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 7: - msg.doubleArrayList = reader.readCollection(doubleArrayListCollDesc); + msg.doubleArrayList = reader.readCollection(doubleArrayListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -323,7 +324,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 8: - msg.stringList = reader.readCollection(stringListCollDesc); + msg.stringList = reader.readCollection(stringListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -331,7 +332,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 9: - msg.uuidList = reader.readCollection(uuidListCollDesc); + msg.uuidList = reader.readCollection(uuidListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -339,7 +340,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 10: - msg.bitSetList = reader.readCollection(bitSetListCollDesc); + msg.bitSetList = reader.readCollection(bitSetListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -347,7 +348,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 11: - msg.igniteUuidList = reader.readCollection(igniteUuidListCollDesc); + msg.igniteUuidList = reader.readCollection(igniteUuidListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -355,7 +356,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 12: - msg.affTopVersionList = reader.readCollection(affTopVersionListCollDesc); + msg.affTopVersionList = reader.readCollection(affTopVersionListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -363,7 +364,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 13: - msg.boxedBooleanList = reader.readCollection(boxedBooleanListCollDesc); + msg.boxedBooleanList = reader.readCollection(boxedBooleanListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -371,7 +372,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 14: - msg.boxedByteList = reader.readCollection(boxedByteListCollDesc); + msg.boxedByteList = reader.readCollection(boxedByteListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -379,7 +380,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 15: - msg.boxedShortList = reader.readCollection(boxedShortListCollDesc); + msg.boxedShortList = reader.readCollection(boxedShortListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -387,7 +388,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 16: - msg.boxedIntList = reader.readCollection(boxedIntListCollDesc); + msg.boxedIntList = reader.readCollection(boxedIntListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -395,7 +396,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 17: - msg.boxedLongList = reader.readCollection(boxedLongListCollDesc); + msg.boxedLongList = reader.readCollection(boxedLongListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -403,7 +404,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 18: - msg.boxedCharList = reader.readCollection(boxedCharListCollDesc); + msg.boxedCharList = reader.readCollection(boxedCharListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -411,7 +412,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 19: - msg.boxedFloatList = reader.readCollection(boxedFloatListCollDesc); + msg.boxedFloatList = reader.readCollection(boxedFloatListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -419,7 +420,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 20: - msg.boxedDoubleList = reader.readCollection(boxedDoubleListCollDesc); + msg.boxedDoubleList = reader.readCollection(boxedDoubleListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -427,7 +428,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 21: - msg.messageList = reader.readCollection(messageListCollDesc); + msg.messageList = reader.readCollection(messageListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -435,7 +436,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 22: - msg.gridLongListList = reader.readCollection(gridLongListListCollDesc); + msg.gridLongListList = reader.readCollection(gridLongListListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -443,7 +444,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 23: - msg.boxedIntegerSet = reader.readCollection(boxedIntegerSetCollDesc); + msg.boxedIntegerSet = reader.readCollection(boxedIntegerSetCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -451,7 +452,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 24: - msg.bitSetSet = reader.readCollection(bitSetSetCollDesc); + msg.bitSetSet = reader.readCollection(bitSetSetCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -459,7 +460,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 25: - msg.cacheObjectSet = reader.readCollection(cacheObjectSetCollDesc); + msg.cacheObjectSet = reader.readCollection(cacheObjectSetCollDesc, ctx); if (!reader.isLastRead()) return false; diff --git a/modules/core/src/test/resources/codegen/TestEnumSetMessageSerializer.java b/modules/core/src/test/resources/codegen/TestEnumSetMessageSerializer.java index fb0afa111fa6f..d5cc64a6abde1 100644 --- a/modules/core/src/test/resources/codegen/TestEnumSetMessageSerializer.java +++ b/modules/core/src/test/resources/codegen/TestEnumSetMessageSerializer.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.TestEnumSetMessage; import org.apache.ignite.plugin.extensions.communication.CollectionImplementationType; import org.apache.ignite.plugin.extensions.communication.MessageCollectionItemType; @@ -46,7 +47,7 @@ public final class TestEnumSetMessageSerializer implements MessageSerializer(TransactionIsolation.class, DefaultEnumMapper.INSTANCE::encode, b -> DefaultEnumMapper.INSTANCE.decode(transactionIsolationVals, b)), CollectionImplementationType.ENUM_SET), false); /** */ - @Override public final boolean writeTo(TestEnumSetMessage msg, MessageWriter writer) { + @Override public final boolean writeTo(TestEnumSetMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -56,19 +57,19 @@ public final class TestEnumSetMessageSerializer implements MessageSerializer { /** */ - @Override public final boolean writeTo(TestMarshallableMessage msg, MessageWriter writer) { + @Override public final boolean writeTo(TestMarshallableMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -61,7 +62,7 @@ public final class TestMarshallableMessageSerializer implements MessageSerialize } /** */ - @Override public final boolean readFrom(TestMarshallableMessage msg, MessageReader reader) { + @Override public final boolean readFrom(TestMarshallableMessage msg, MessageReader reader, MessageSerializationContext ctx) { switch (reader.state()) { case 0: msg.iv = reader.readInt(); diff --git a/modules/core/src/test/resources/codegen/TestMarshalledArrayMapMessageSerializer.java b/modules/core/src/test/resources/codegen/TestMarshalledArrayMapMessageSerializer.java index 4f3a7c0e51fc9..52f8785c633e4 100644 --- a/modules/core/src/test/resources/codegen/TestMarshalledArrayMapMessageSerializer.java +++ b/modules/core/src/test/resources/codegen/TestMarshalledArrayMapMessageSerializer.java @@ -19,6 +19,7 @@ import java.util.List; import org.apache.ignite.internal.GridTopicMessage; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.TestMarshalledArrayMapMessage; import org.apache.ignite.plugin.extensions.communication.CollectionImplementationType; import org.apache.ignite.plugin.extensions.communication.MessageArrayType; @@ -45,7 +46,7 @@ public final class TestMarshalledArrayMapMessageSerializer implements MessageSer private static final MessageArrayType mapValsCollDesc = new MessageArrayType(new MessageCollectionType(new MessageItemType(MessageCollectionItemType.MSG), CollectionImplementationType.ARRAY_LIST), List.class); /** */ - @Override public final boolean writeTo(TestMarshalledArrayMapMessage msg, MessageWriter writer) { + @Override public final boolean writeTo(TestMarshalledArrayMapMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -55,25 +56,25 @@ public final class TestMarshalledArrayMapMessageSerializer implements MessageSer switch (writer.state()) { case 0: - if (!writer.writeObjectArray(msg.mapKeys, mapKeysCollDesc)) + if (!writer.writeObjectArray(msg.mapKeys, mapKeysCollDesc, ctx)) return false; writer.incrementState(); case 1: - if (!writer.writeObjectArray(msg.mapVals, mapValsCollDesc)) + if (!writer.writeObjectArray(msg.mapVals, mapValsCollDesc, ctx)) return false; writer.incrementState(); case 2: - if (!writer.writeObjectArray(msg.fixedMapKeys, fixedMapKeysCollDesc)) + if (!writer.writeObjectArray(msg.fixedMapKeys, fixedMapKeysCollDesc, ctx)) return false; writer.incrementState(); case 3: - if (!writer.writeObjectArray(msg.fixedMapVals, fixedMapValsCollDesc)) + if (!writer.writeObjectArray(msg.fixedMapVals, fixedMapValsCollDesc, ctx)) return false; writer.incrementState(); @@ -83,10 +84,10 @@ public final class TestMarshalledArrayMapMessageSerializer implements MessageSer } /** */ - @Override public final boolean readFrom(TestMarshalledArrayMapMessage msg, MessageReader reader) { + @Override public final boolean readFrom(TestMarshalledArrayMapMessage msg, MessageReader reader, MessageSerializationContext ctx) { switch (reader.state()) { case 0: - msg.mapKeys = reader.readObjectArray(mapKeysCollDesc); + msg.mapKeys = reader.readObjectArray(mapKeysCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -94,7 +95,7 @@ public final class TestMarshalledArrayMapMessageSerializer implements MessageSer reader.incrementState(); case 1: - msg.mapVals = reader.readObjectArray(mapValsCollDesc); + msg.mapVals = reader.readObjectArray(mapValsCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -102,7 +103,7 @@ public final class TestMarshalledArrayMapMessageSerializer implements MessageSer reader.incrementState(); case 2: - msg.fixedMapKeys = reader.readObjectArray(fixedMapKeysCollDesc); + msg.fixedMapKeys = reader.readObjectArray(fixedMapKeysCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -110,7 +111,7 @@ public final class TestMarshalledArrayMapMessageSerializer implements MessageSer reader.incrementState(); case 3: - msg.fixedMapVals = reader.readObjectArray(fixedMapValsCollDesc); + msg.fixedMapVals = reader.readObjectArray(fixedMapValsCollDesc, ctx); if (!reader.isLastRead()) return false; diff --git a/modules/core/src/test/resources/codegen/TestMarshalledCollectionMessageSerializer.java b/modules/core/src/test/resources/codegen/TestMarshalledCollectionMessageSerializer.java index 43f7bf2f2e996..7b831f8ec04e8 100644 --- a/modules/core/src/test/resources/codegen/TestMarshalledCollectionMessageSerializer.java +++ b/modules/core/src/test/resources/codegen/TestMarshalledCollectionMessageSerializer.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.TestMarshalledCollectionMessage; import org.apache.ignite.internal.processors.cache.version.GridCacheVersion; import org.apache.ignite.plugin.extensions.communication.MessageArrayType; @@ -36,7 +37,7 @@ public final class TestMarshalledCollectionMessageSerializer implements MessageS private static final MessageArrayType keysArrCollDesc = new MessageArrayType(new MessageItemType(MessageCollectionItemType.GRID_CACHE_VERSION), GridCacheVersion.class); /** */ - @Override public final boolean writeTo(TestMarshalledCollectionMessage msg, MessageWriter writer) { + @Override public final boolean writeTo(TestMarshalledCollectionMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -46,7 +47,7 @@ public final class TestMarshalledCollectionMessageSerializer implements MessageS switch (writer.state()) { case 0: - if (!writer.writeObjectArray(msg.keysArr, keysArrCollDesc)) + if (!writer.writeObjectArray(msg.keysArr, keysArrCollDesc, ctx)) return false; writer.incrementState(); @@ -56,10 +57,10 @@ public final class TestMarshalledCollectionMessageSerializer implements MessageS } /** */ - @Override public final boolean readFrom(TestMarshalledCollectionMessage msg, MessageReader reader) { + @Override public final boolean readFrom(TestMarshalledCollectionMessage msg, MessageReader reader, MessageSerializationContext ctx) { switch (reader.state()) { case 0: - msg.keysArr = reader.readObjectArray(keysArrCollDesc); + msg.keysArr = reader.readObjectArray(keysArrCollDesc, ctx); if (!reader.isLastRead()) return false; diff --git a/modules/core/src/test/resources/codegen/TestMarshalledMapMessageSerializer.java b/modules/core/src/test/resources/codegen/TestMarshalledMapMessageSerializer.java index dd0fcb2253364..e3aca2bf684f3 100644 --- a/modules/core/src/test/resources/codegen/TestMarshalledMapMessageSerializer.java +++ b/modules/core/src/test/resources/codegen/TestMarshalledMapMessageSerializer.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.TestMarshalledMapMessage; import org.apache.ignite.plugin.extensions.communication.CollectionImplementationType; import org.apache.ignite.plugin.extensions.communication.MessageCollectionItemType; @@ -38,7 +39,7 @@ public final class TestMarshalledMapMessageSerializer implements MessageSerializ private static final MessageCollectionType mapValsCollDesc = new MessageCollectionType(new MessageItemType(MessageCollectionItemType.GRID_CACHE_VERSION), CollectionImplementationType.ARRAY_LIST); /** */ - @Override public final boolean writeTo(TestMarshalledMapMessage msg, MessageWriter writer) { + @Override public final boolean writeTo(TestMarshalledMapMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -48,13 +49,13 @@ public final class TestMarshalledMapMessageSerializer implements MessageSerializ switch (writer.state()) { case 0: - if (!writer.writeCollection(msg.mapKeys, mapKeysCollDesc)) + if (!writer.writeCollection(msg.mapKeys, mapKeysCollDesc, ctx)) return false; writer.incrementState(); case 1: - if (!writer.writeCollection(msg.mapVals, mapValsCollDesc)) + if (!writer.writeCollection(msg.mapVals, mapValsCollDesc, ctx)) return false; writer.incrementState(); @@ -64,10 +65,10 @@ public final class TestMarshalledMapMessageSerializer implements MessageSerializ } /** */ - @Override public final boolean readFrom(TestMarshalledMapMessage msg, MessageReader reader) { + @Override public final boolean readFrom(TestMarshalledMapMessage msg, MessageReader reader, MessageSerializationContext ctx) { switch (reader.state()) { case 0: - msg.mapKeys = reader.readCollection(mapKeysCollDesc); + msg.mapKeys = reader.readCollection(mapKeysCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -75,7 +76,7 @@ public final class TestMarshalledMapMessageSerializer implements MessageSerializ reader.incrementState(); case 1: - msg.mapVals = reader.readCollection(mapValsCollDesc); + msg.mapVals = reader.readCollection(mapValsCollDesc, ctx); if (!reader.isLastRead()) return false; diff --git a/modules/core/src/test/resources/codegen/TestMarshalledMessageSerializer.java b/modules/core/src/test/resources/codegen/TestMarshalledMessageSerializer.java index fa81057b60403..b924687a0d06f 100644 --- a/modules/core/src/test/resources/codegen/TestMarshalledMessageSerializer.java +++ b/modules/core/src/test/resources/codegen/TestMarshalledMessageSerializer.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.TestMarshalledMessage; import org.apache.ignite.plugin.extensions.communication.MessageReader; import org.apache.ignite.plugin.extensions.communication.MessageSerializer; @@ -29,7 +30,7 @@ */ public final class TestMarshalledMessageSerializer implements MessageSerializer { /** */ - @Override public final boolean writeTo(TestMarshalledMessage msg, MessageWriter writer) { + @Override public final boolean writeTo(TestMarshalledMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -49,7 +50,7 @@ public final class TestMarshalledMessageSerializer implements MessageSerializer< } /** */ - @Override public final boolean readFrom(TestMarshalledMessage msg, MessageReader reader) { + @Override public final boolean readFrom(TestMarshalledMessage msg, MessageReader reader, MessageSerializationContext ctx) { switch (reader.state()) { case 0: msg.dataBytes = reader.readByteArray(); diff --git a/modules/core/src/test/resources/codegen/TestMarshalledObjectsMessageSerializer.java b/modules/core/src/test/resources/codegen/TestMarshalledObjectsMessageSerializer.java index b68578b06a615..68d62d8879123 100644 --- a/modules/core/src/test/resources/codegen/TestMarshalledObjectsMessageSerializer.java +++ b/modules/core/src/test/resources/codegen/TestMarshalledObjectsMessageSerializer.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.TestMarshalledObjectsMessage; import org.apache.ignite.plugin.extensions.communication.CollectionImplementationType; import org.apache.ignite.plugin.extensions.communication.MessageCollectionItemType; @@ -36,7 +37,7 @@ public final class TestMarshalledObjectsMessageSerializer implements MessageSeri private static final MessageCollectionType dataBytesCollDesc = new MessageCollectionType(new MessageItemType(MessageCollectionItemType.BYTE_ARR), CollectionImplementationType.ARRAY_LIST); /** */ - @Override public final boolean writeTo(TestMarshalledObjectsMessage msg, MessageWriter writer) { + @Override public final boolean writeTo(TestMarshalledObjectsMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -46,7 +47,7 @@ public final class TestMarshalledObjectsMessageSerializer implements MessageSeri switch (writer.state()) { case 0: - if (!writer.writeCollection(msg.dataBytes, dataBytesCollDesc)) + if (!writer.writeCollection(msg.dataBytes, dataBytesCollDesc, ctx)) return false; writer.incrementState(); @@ -56,10 +57,10 @@ public final class TestMarshalledObjectsMessageSerializer implements MessageSeri } /** */ - @Override public final boolean readFrom(TestMarshalledObjectsMessage msg, MessageReader reader) { + @Override public final boolean readFrom(TestMarshalledObjectsMessage msg, MessageReader reader, MessageSerializationContext ctx) { switch (reader.state()) { case 0: - msg.dataBytes = reader.readCollection(dataBytesCollDesc); + msg.dataBytes = reader.readCollection(dataBytesCollDesc, ctx); if (!reader.isLastRead()) return false; diff --git a/modules/core/src/test/resources/codegen/TestMessageSerializer.java b/modules/core/src/test/resources/codegen/TestMessageSerializer.java index 7aa0a054471aa..1811c09f59037 100644 --- a/modules/core/src/test/resources/codegen/TestMessageSerializer.java +++ b/modules/core/src/test/resources/codegen/TestMessageSerializer.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.TestMessage; import org.apache.ignite.internal.processors.cache.version.GridCacheVersion; import org.apache.ignite.plugin.extensions.communication.MessageArrayType; @@ -40,7 +41,7 @@ public final class TestMessageSerializer implements MessageSerializer { + /** */ + @Override public final boolean writeTo(TestRollingUpgradeAwareMessage msg, MessageWriter writer, MessageSerializationContext ctx) { + if (!writer.isHeaderWritten()) { + if (!writer.writeHeader(msg.directType())) + return false; + + writer.onHeaderWritten(); + } + + switch (writer.state()) { + case 0: + if (!writer.writeInt(msg.plain)) + return false; + + writer.incrementState(); + + case 1: + if (ctx.includeFieldDeprecatedBy(TestFeatureRegistry.FIRST_FEATURE)) { + if (!writer.writeString(msg.oldFld)) + return false; + } + + writer.incrementState(); + + case 2: + if (ctx.includeFieldIntroducedBy(TestFeatureRegistry.FIRST_FEATURE)) { + if (!writer.writeString(msg.newFld)) + return false; + } + + writer.incrementState(); + + case 3: + if (ctx.includeFieldIntroducedBy(TestFeatureRegistry.FIRST_FEATURE) && ctx.includeFieldDeprecatedBy(TestFeatureRegistry.SECOND_FEATURE)) { + if (!writer.writeLong(msg.windowed)) + return false; + } + + writer.incrementState(); + + } + + return true; + } + + /** */ + @Override public final boolean readFrom(TestRollingUpgradeAwareMessage msg, MessageReader reader, MessageSerializationContext ctx) { + switch (reader.state()) { + case 0: + msg.plain = reader.readInt(); + + if (!reader.isLastRead()) + return false; + + reader.incrementState(); + + case 1: + if (ctx.includeFieldDeprecatedBy(TestFeatureRegistry.FIRST_FEATURE)) { + msg.oldFld = reader.readString(); + + if (!reader.isLastRead()) + return false; + } + + reader.incrementState(); + + case 2: + if (ctx.includeFieldIntroducedBy(TestFeatureRegistry.FIRST_FEATURE)) { + msg.newFld = reader.readString(); + + if (!reader.isLastRead()) + return false; + } + + reader.incrementState(); + + case 3: + if (ctx.includeFieldIntroducedBy(TestFeatureRegistry.FIRST_FEATURE) && ctx.includeFieldDeprecatedBy(TestFeatureRegistry.SECOND_FEATURE)) { + msg.windowed = reader.readLong(); + + if (!reader.isLastRead()) + return false; + } + + reader.incrementState(); + + } + + return true; + } + + /** {@inheritDoc} */ + @Override public final TestRollingUpgradeAwareMessage createMessage() { + return new TestRollingUpgradeAwareMessage(); + } +} diff --git a/modules/core/src/test/resources/codegen/TestUnknownFeatureMessage.java b/modules/core/src/test/resources/codegen/TestUnknownFeatureMessage.java new file mode 100644 index 0000000000000..7e1db62bb29e7 --- /dev/null +++ b/modules/core/src/test/resources/codegen/TestUnknownFeatureMessage.java @@ -0,0 +1,27 @@ +/* + * 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 org.apache.ignite.plugin.extensions.communication.Message; + +/** */ +public class TestUnknownFeatureMessage implements Message { + /** */ + @Order(value = 0, introducedBy = "NO_SUCH_FEATURE") + int fld; +} diff --git a/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/GridDirectParser.java b/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/GridDirectParser.java index f30fbc5cfc0ea..43f89a6ab11c8 100644 --- a/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/GridDirectParser.java +++ b/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/GridDirectParser.java @@ -29,6 +29,7 @@ import org.jetbrains.annotations.Nullable; import static org.apache.ignite.internal.util.CommonUtils.makeMessageType; +import static org.apache.ignite.internal.util.nio.MessageSerialization.resolveSerializationContext; /** * Parser for direct messages. @@ -86,7 +87,7 @@ public GridDirectParser(IgniteLogger log, MessageFactory msgFactory, GridNioMess if (msg != null && buf.hasRemaining()) { reader.setBuffer(buf); - finished = MessageSerialization.readFrom(msgFactory, msg, reader); + finished = MessageSerialization.readFrom(msgFactory, msg, reader, resolveSerializationContext(ses)); } if (finished) { diff --git a/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/GridNioServer.java b/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/GridNioServer.java index a067277648d35..40badc6bcf4f6 100644 --- a/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/GridNioServer.java +++ b/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/GridNioServer.java @@ -85,6 +85,7 @@ import static org.apache.ignite.failure.FailureType.SYSTEM_WORKER_TERMINATION; import static org.apache.ignite.internal.util.nio.GridNioSessionMetaKey.MSG_WRITER; import static org.apache.ignite.internal.util.nio.GridNioSessionMetaKey.NIO_OPERATION; +import static org.apache.ignite.internal.util.nio.MessageSerialization.resolveSerializationContext; /** * TCP NIO server. Due to asynchronous nature of connections processing @@ -1506,7 +1507,7 @@ private void processWriteSsl(SelectionKey key) throws IOException { List pendingRequests = new ArrayList<>(2); if (req != null) - finished = writeToBuffer(writer, buf, req, pendingRequests); + finished = writeToBuffer(ses, writer, buf, req, pendingRequests); // Fill up as many messages as possible to write buffer. while (finished) { @@ -1518,7 +1519,7 @@ private void processWriteSsl(SelectionKey key) throws IOException { if (req == null) break; - finished = writeToBuffer(writer, buf, req, pendingRequests); + finished = writeToBuffer(ses, writer, buf, req, pendingRequests); } int sesBufLimit = buf.limit(); @@ -1588,6 +1589,7 @@ private void processWriteSsl(SelectionKey key) throws IOException { } /** + * @param ses Session the message is written to. * @param writer Customizer of writing. * @param buf Buffer to write. * @param req Source of data. @@ -1595,6 +1597,7 @@ private void processWriteSsl(SelectionKey key) throws IOException { * @return {@code true} if message successfully written to buffer and {@code false} otherwise. */ private boolean writeToBuffer( + GridSelectorNioSessionImpl ses, MessageWriter writer, ByteBuffer buf, SessionWriteRequest req, @@ -1614,7 +1617,7 @@ private boolean writeToBuffer( else { writer.setBuffer(buf); - finished = MessageSerialization.writeTo(messageFactory(), msg, writer); + finished = MessageSerialization.writeTo(messageFactory(), msg, writer, resolveSerializationContext(ses)); } if (finished) { @@ -1781,14 +1784,13 @@ private void processWrite0(SelectionKey key) throws IOException { } /** - * @param writer Customizer of writing. + * @param ses Session the message is written to. * @param buf Buffer to write. * @param req Source of data. - * @param ses Session for notification about writting. + * @param writer Customizer of writing. * @return {@code true} if message successfully written to buffer and {@code false} otherwise. */ - private boolean writeToBuffer(GridSelectorNioSessionImpl ses, ByteBuffer buf, SessionWriteRequest req, - MessageWriter writer) { + private boolean writeToBuffer(GridSelectorNioSessionImpl ses, ByteBuffer buf, SessionWriteRequest req, MessageWriter writer) { Message msg; boolean finished; msg = (Message)req.message(); @@ -1803,7 +1805,7 @@ private boolean writeToBuffer(GridSelectorNioSessionImpl ses, ByteBuffer buf, Se else { writer.setBuffer(buf); - finished = MessageSerialization.writeTo(msgFactory, msg, writer); + finished = MessageSerialization.writeTo(msgFactory, msg, writer, resolveSerializationContext(ses)); } if (finished) { diff --git a/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/GridNioSessionMetaKey.java b/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/GridNioSessionMetaKey.java index fbdcc6d5d34f9..9a5836d14cc4c 100644 --- a/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/GridNioSessionMetaKey.java +++ b/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/GridNioSessionMetaKey.java @@ -42,7 +42,10 @@ public enum GridNioSessionMetaKey { MARSHALLER_ID, /** Message writer. */ - MSG_WRITER; + MSG_WRITER, + + /** Message serialization context. */ + MSG_SER_CTX; /** Maximum count of NIO session keys in system. */ public static final int MAX_KEYS_CNT = 64; diff --git a/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/MessageSerialization.java b/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/MessageSerialization.java index b436edb764d49..5f888f042ad3a 100644 --- a/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/MessageSerialization.java +++ b/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/MessageSerialization.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal.util.nio; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.plugin.extensions.communication.Message; import org.apache.ignite.plugin.extensions.communication.MessageFactory; import org.apache.ignite.plugin.extensions.communication.MessageReader; @@ -40,11 +41,17 @@ private MessageSerialization() { * @param factory Message factory. * @param msg Message instance. * @param writer Writer. + * @param ctx Serialization context. * @param Message type. * @return Whether message was fully written. */ - public static boolean writeTo(MessageFactory factory, M msg, MessageWriter writer) { - return resolve(factory, msg).writeTo(msg, writer); + public static boolean writeTo( + MessageFactory factory, + M msg, + MessageWriter writer, + MessageSerializationContext ctx + ) { + return resolveMessageserializer(factory, msg).writeTo(msg, writer, ctx); } /** @@ -53,16 +60,31 @@ public static boolean writeTo(MessageFactory factory, M msg, * @param factory Message factory. * @param msg Message instance. * @param reader Reader. + * @param ctx Serialization context. * @param Message type. * @return Whether message was fully read. */ - public static boolean readFrom(MessageFactory factory, M msg, MessageReader reader) { - return resolve(factory, msg).readFrom(msg, reader); + public static boolean readFrom( + MessageFactory factory, + M msg, + MessageReader reader, + MessageSerializationContext ctx + ) { + return resolveMessageserializer(factory, msg).readFrom(msg, reader, ctx); + } + + /** */ + public static MessageSerializationContext resolveSerializationContext(GridNioSession ses) { + MessageSerializationContext ctx = ses.meta(GridNioSessionMetaKey.MSG_SER_CTX.ordinal()); + + assert ctx != null : "Session has no serialization context: " + ses; + + return ctx; } /** @return the serializer registered for {@code msg}'s direct type. */ @SuppressWarnings("unchecked") - private static MessageSerializer resolve(MessageFactory factory, M msg) { + private static MessageSerializer resolveMessageserializer(MessageFactory factory, M msg) { return (MessageSerializer)factory.serializer(msg.directType()); } } diff --git a/modules/nio/src/main/java/org/apache/ignite/plugin/extensions/communication/MessageReader.java b/modules/nio/src/main/java/org/apache/ignite/plugin/extensions/communication/MessageReader.java index 796acc40a191d..faf50669796dd 100644 --- a/modules/nio/src/main/java/org/apache/ignite/plugin/extensions/communication/MessageReader.java +++ b/modules/nio/src/main/java/org/apache/ignite/plugin/extensions/communication/MessageReader.java @@ -22,6 +22,7 @@ import java.util.Collection; import java.util.Map; import java.util.UUID; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; import org.apache.ignite.internal.processors.cache.CacheObject; import org.apache.ignite.internal.processors.cache.KeyCacheObject; @@ -196,21 +197,23 @@ public default void setBuffer(ByteBuffer buf) { /** * Reads nested message. * + * @param ctx Serialization context. * @param Type of the message. * @return Message. */ - public default T readMessage() { - return readMessage(false); + public default T readMessage(MessageSerializationContext ctx) { + return readMessage(false, ctx); } /** * Reads nested message. * * @param compress Whether message should be decompressed. + * @param ctx Serialization context. * @param Type of the message. * @return Message. */ - public T readMessage(boolean compress); + public T readMessage(boolean compress, MessageSerializationContext ctx); /** * Reads {@link CacheObject}. @@ -237,29 +240,32 @@ public default T readMessage() { * Reads array of objects. * * @param type Array component type. + * @param ctx Serialization context. * @param Type of the read object. * @return Array of objects. */ - public T[] readObjectArray(MessageArrayType type); + public T[] readObjectArray(MessageArrayType type, MessageSerializationContext ctx); /** * Reads any collection. * * @param type Collection item type. + * @param ctx Serialization context. * @param Type of the read collection. * @return Collection. */ - public > C readCollection(MessageCollectionType type); + public > C readCollection(MessageCollectionType type, MessageSerializationContext ctx); /** * Reads map. * * @param type Map type. + * @param ctx Serialization context. * @param Type of the read map. * @return Map. */ - public default > M readMap(MessageMapType type) { - return readMap(type, false); + public default > M readMap(MessageMapType type, MessageSerializationContext ctx) { + return readMap(type, false, ctx); } /** @@ -267,10 +273,11 @@ public default T readMessage() { * * @param type Map type. * @param compress Whether map should be compressed. + * @param ctx Serialization context. * @param Type of the read map. * @return Map. */ - public > M readMap(MessageMapType type, boolean compress); + public > M readMap(MessageMapType type, boolean compress, MessageSerializationContext ctx); /** @return Ignite product version. */ IgniteProductVersion readIgniteProductVersion(); diff --git a/modules/nio/src/main/java/org/apache/ignite/plugin/extensions/communication/MessageSerializer.java b/modules/nio/src/main/java/org/apache/ignite/plugin/extensions/communication/MessageSerializer.java index 86ce0d170a09f..ab66f296ed6b0 100644 --- a/modules/nio/src/main/java/org/apache/ignite/plugin/extensions/communication/MessageSerializer.java +++ b/modules/nio/src/main/java/org/apache/ignite/plugin/extensions/communication/MessageSerializer.java @@ -17,6 +17,8 @@ package org.apache.ignite.plugin.extensions.communication; +import org.apache.ignite.internal.MessageSerializationContext; + /** * Interface for message serialization logic. Resolve-and-dispatch entry points that look the serializer up from the * message factory live in {@code MessageSerialization}. @@ -27,18 +29,20 @@ public interface MessageSerializer { * * @param msg Message instance. * @param writer Writer. + * @param ctx Serialization context. * @return Whether message was fully written. */ - public boolean writeTo(M msg, MessageWriter writer); + public boolean writeTo(M msg, MessageWriter writer, MessageSerializationContext ctx); /** * Reads this message from provided byte buffer. * * @param msg Message instance. * @param reader Reader. + * @param ctx Serialization context. * @return Whether message was fully read. */ - public boolean readFrom(M msg, MessageReader reader); + public boolean readFrom(M msg, MessageReader reader, MessageSerializationContext ctx); /** * @return New instance of message. diff --git a/modules/nio/src/main/java/org/apache/ignite/plugin/extensions/communication/MessageWriter.java b/modules/nio/src/main/java/org/apache/ignite/plugin/extensions/communication/MessageWriter.java index 019bab8fa9db9..4f281cec3a86a 100644 --- a/modules/nio/src/main/java/org/apache/ignite/plugin/extensions/communication/MessageWriter.java +++ b/modules/nio/src/main/java/org/apache/ignite/plugin/extensions/communication/MessageWriter.java @@ -22,6 +22,7 @@ import java.util.Collection; import java.util.Map; import java.util.UUID; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; import org.apache.ignite.internal.processors.cache.CacheObject; import org.apache.ignite.internal.processors.cache.KeyCacheObject; @@ -246,10 +247,11 @@ public default void setBuffer(ByteBuffer buf) { * Writes nested message. * * @param val Message. + * @param ctx Serialization context. * @return Whether value was fully written. */ - public default boolean writeMessage(Message val) { - return writeMessage(val, false); + public default boolean writeMessage(Message val, MessageSerializationContext ctx) { + return writeMessage(val, false, ctx); } /** @@ -257,9 +259,10 @@ public default boolean writeMessage(Message val) { * * @param val Message. * @param compress Whether message should be compressed. + * @param ctx Serialization context. * @return Whether value was fully written. */ - public boolean writeMessage(Message val, boolean compress); + public boolean writeMessage(Message val, boolean compress, MessageSerializationContext ctx); /** * Writes {@link CacheObject}. @@ -290,32 +293,35 @@ public default boolean writeMessage(Message val) { * * @param arr Array of objects. * @param type Array component type. + * @param ctx Serialization context. * @param Type of the objects that array contains. * @return Whether array was fully written. */ - public boolean writeObjectArray(T[] arr, MessageArrayType type); + public boolean writeObjectArray(T[] arr, MessageArrayType type, MessageSerializationContext ctx); /** * Writes collection with its elements order. * * @param col Collection. * @param type Collection item type. + * @param ctx Serialization context. * @param Type of the objects that collection contains. * @return Whether value was fully written. */ - public boolean writeCollection(Collection col, MessageCollectionType type); + public boolean writeCollection(Collection col, MessageCollectionType type, MessageSerializationContext ctx); /** * Writes map. * * @param map Map. * @param type Map type. + * @param ctx Serialization context. * @param Initial key types of the map to write. * @param Initial value types of the map to write. * @return Whether value was fully written. */ - public default boolean writeMap(Map map, MessageMapType type) { - return writeMap(map, type, false); + public default boolean writeMap(Map map, MessageMapType type, MessageSerializationContext ctx) { + return writeMap(map, type, false, ctx); } /** @@ -324,11 +330,12 @@ public default boolean writeMap(Map map, MessageMapType type) { * @param map Map. * @param type Map type. * @param compress Whether map should be compressed. + * @param ctx Serialization context. * @param Initial key types of the map to write. * @param Initial value types of the map to write. * @return Whether value was fully written. */ - public boolean writeMap(Map map, MessageMapType type, boolean compress); + public boolean writeMap(Map map, MessageMapType type, boolean compress, MessageSerializationContext ctx); /** * Writes ignite product version. diff --git a/modules/zookeeper/src/main/java/org/apache/ignite/spi/discovery/zk/internal/DiscoveryMessageParser.java b/modules/zookeeper/src/main/java/org/apache/ignite/spi/discovery/zk/internal/DiscoveryMessageParser.java index aeb7c1be8851b..451fe3d66efb9 100644 --- a/modules/zookeeper/src/main/java/org/apache/ignite/spi/discovery/zk/internal/DiscoveryMessageParser.java +++ b/modules/zookeeper/src/main/java/org/apache/ignite/spi/discovery/zk/internal/DiscoveryMessageParser.java @@ -37,6 +37,8 @@ import org.apache.ignite.plugin.extensions.communication.MessageSerializer; import org.apache.ignite.spi.IgniteSpiException; +import static org.apache.ignite.internal.MessageSerializationContext.IGNORED; + /** * Class is responsible for serializing discovery messages using RU-ready {@link MessageSerializer} mechanism. */ @@ -105,7 +107,7 @@ private void serializeMessage(Message m, OutputStream out) throws IOException { do { msgBuf.clear(); - finished = MessageSerialization.writeTo(msgFactory, m, msgWriter); + finished = MessageSerialization.writeTo(msgFactory, m, msgWriter, IGNORED); out.write(msgBuf.array(), 0, msgBuf.position()); } @@ -131,7 +133,7 @@ private T deserializeMessage(InputStream in) throws IOExcept msgBuf.rewind(); } - finished = MessageSerialization.readFrom(msgFactory, msg, msgReader); + finished = MessageSerialization.readFrom(msgFactory, msg, msgReader, IGNORED); assert read != -1 || finished : "Stream closed before message was fully read.";