diff --git a/modules/core/src/main/java/org/apache/ignite/internal/CoreMessagesProvider.java b/modules/core/src/main/java/org/apache/ignite/internal/CoreMessagesProvider.java index 50ba117039d84..83cfc507f6000 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/CoreMessagesProvider.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/CoreMessagesProvider.java @@ -246,13 +246,18 @@ import org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteFeatureSet; import org.apache.ignite.internal.processors.rollingupgrade.feature.IgnitePluginFeatureSet; import org.apache.ignite.internal.processors.security.SecurityContextWrapper; +import org.apache.ignite.internal.processors.service.LazyServiceConfigurationMessage; import org.apache.ignite.internal.processors.service.ServiceChangeBatchRequest; import org.apache.ignite.internal.processors.service.ServiceClusterDeploymentResult; import org.apache.ignite.internal.processors.service.ServiceClusterDeploymentResultBatch; import org.apache.ignite.internal.processors.service.ServiceDeploymentProcessId; import org.apache.ignite.internal.processors.service.ServiceDeploymentRequest; +import org.apache.ignite.internal.processors.service.ServiceInfo; +import org.apache.ignite.internal.processors.service.ServiceProcessorCommonDiscoveryData; +import org.apache.ignite.internal.processors.service.ServiceProcessorJoinNodeDiscoveryData; import org.apache.ignite.internal.processors.service.ServiceSingleNodeDeploymentResult; import org.apache.ignite.internal.processors.service.ServiceSingleNodeDeploymentResultBatch; +import org.apache.ignite.internal.processors.service.ServiceTopology; import org.apache.ignite.internal.processors.service.ServiceUndeploymentRequest; import org.apache.ignite.internal.util.GridByteArrayList; import org.apache.ignite.internal.util.GridIntList; @@ -431,6 +436,11 @@ public CoreMessagesProvider(Marshaller dfltMarsh, Marshaller schemaAwareMarsh, C withNoSchema(ServiceClusterDeploymentResultBatch.class); withNoSchema(ServiceChangeBatchRequest.class); withNoSchema(ServiceSingleNodeDeploymentResultBatch.class); + withNoSchema(ServiceProcessorCommonDiscoveryData.class); + withNoSchema(ServiceProcessorJoinNodeDiscoveryData.class); + withNoSchema(ServiceInfo.class); + withNoSchema(ServiceTopology.class); + withNoSchema(LazyServiceConfigurationMessage.class); // [6500 - 6700]: DiscoveryCustomMessage msgIdx = 6500; diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/IgniteServiceProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/IgniteServiceProcessor.java index 34118541ece6d..274edbd795d74 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/IgniteServiceProcessor.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/IgniteServiceProcessor.java @@ -1805,7 +1805,7 @@ else if (req instanceof ServiceUndeploymentRequest) LazyServiceConfiguration cfg = ((ServiceDeploymentRequest)req).configuration(); if (ctx.security().enabled()) - err = checkPermissions(((ServiceDeploymentRequest)req).configuration().getName(), SERVICE_DEPLOY); + err = checkPermissions(cfg.getName(), SERVICE_DEPLOY); if (err == null) { try { diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/LazyServiceConfiguration.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/LazyServiceConfiguration.java index 859106ee73b56..c769ab4da9e38 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/LazyServiceConfiguration.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/LazyServiceConfiguration.java @@ -105,6 +105,13 @@ public byte[] nodeFilterBytes() { return nodeFilterBytes; } + /** */ + public LazyServiceConfiguration nodeFilterBytes(byte[] nodeFilterBytes) { + this.nodeFilterBytes = nodeFilterBytes; + + return this; + } + /** * @return Service bytes. */ @@ -112,6 +119,13 @@ public byte[] serviceBytes() { return srvcBytes; } + /** */ + public LazyServiceConfiguration serviceBytes(byte[] srvcBytes) { + this.srvcBytes = srvcBytes; + + return this; + } + /** * @return Service class name. */ @@ -119,6 +133,13 @@ public String serviceClassName() { return srvcClsName; } + /** */ + public LazyServiceConfiguration serviceClassName(String srvcClsName) { + this.srvcClsName = srvcClsName; + + return this; + } + /** {@inheritDoc} */ @Override public Service getService() { assert srvc != null : this; @@ -150,6 +171,13 @@ public byte[] interceptorBytes() { return interceptorsBytes; } + /** */ + public LazyServiceConfiguration interceptorBytes(byte[] interceptorsBytes) { + this.interceptorsBytes = interceptorsBytes; + + return this; + } + /** {@inheritDoc} */ @Override public boolean equals(Object o) { if (!(o instanceof LazyServiceConfiguration)) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/LazyServiceConfigurationMessage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/LazyServiceConfigurationMessage.java new file mode 100644 index 0000000000000..1d003f0bc00a8 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/LazyServiceConfigurationMessage.java @@ -0,0 +1,144 @@ +/* + * 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.service; + +import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.internal.MarshallableMessage; +import org.apache.ignite.internal.Order; +import org.apache.ignite.internal.util.tostring.GridToStringExclude; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.S; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.marshaller.Marshaller; +import org.apache.ignite.plugin.extensions.communication.MessageFactory; + +/** Message for {@link LazyServiceConfiguration}. */ +public class LazyServiceConfigurationMessage implements MarshallableMessage { + /** Service name. */ + @Order(0) + String name; + + /** Total count. */ + @Order(1) + int totalCnt; + + /** Max per-node count. */ + @Order(2) + int maxPerNodeCnt; + + /** Cache name. */ + @Order(3) + String cacheName; + + /** Affinity key. */ + private Object affKey; + + /** Serialized {@link #affKey}. */ + @Order(4) + byte[] affKeyBytes; + + /** Enables or disables service statistics. */ + @Order(5) + boolean isStatisticsEnabled; + + /** Node local start order. */ + @Order(6) + int locStartOrder; + + /** Service class name. */ + @Order(7) + String srvcClsName; + + /** Serialized service. */ + @Order(8) + byte[] srvcBytes; + + /** Serialized node filter. */ + @Order(9) + byte[] nodeFilterBytes; + + /** Serialized interceptors. */ + @Order(10) + byte[] interceptorsBytes; + + /** Names of platform service methods to build service statistics. */ + @Order(11) + @GridToStringExclude + String[] platformMtdNames; + + /** Default constructor for {@link MessageFactory}. */ + public LazyServiceConfigurationMessage() { + // No-op. + } + + /** @param cfg Service config. */ + public LazyServiceConfigurationMessage(LazyServiceConfiguration cfg) { + assert cfg != null : "LazyServiceConfiguration is null"; + + name = cfg.getName(); + totalCnt = cfg.getTotalCount(); + maxPerNodeCnt = cfg.getMaxPerNodeCount(); + cacheName = cfg.getCacheName(); + affKey = cfg.getAffinityKey(); + isStatisticsEnabled = cfg.isStatisticsEnabled(); + locStartOrder = cfg.getLocalStartOrder(); + srvcClsName = cfg.serviceClassName(); + srvcBytes = cfg.serviceBytes(); + nodeFilterBytes = cfg.nodeFilterBytes(); + interceptorsBytes = cfg.interceptorBytes(); + platformMtdNames = cfg.platformMtdNames(); + } + + /** @return Service configuration. */ + public LazyServiceConfiguration toConfiguration() { + LazyServiceConfiguration cfg = (LazyServiceConfiguration)new LazyServiceConfiguration() + .setName(name) + .setTotalCount(totalCnt) + .setMaxPerNodeCount(maxPerNodeCnt) + .setCacheName(cacheName) + .setAffinityKey(affKey) + .setStatisticsEnabled(isStatisticsEnabled) + .setLocalStartOrder(locStartOrder); + + return cfg.serviceClassName(srvcClsName) + .serviceBytes(srvcBytes) + .nodeFilterBytes(nodeFilterBytes) + .interceptorBytes(interceptorsBytes) + .platformMtdNames(platformMtdNames); + } + + /** {@inheritDoc} */ + @Override public void prepareMarshal(Marshaller marsh) throws IgniteCheckedException { + if (affKey != null) + affKeyBytes = U.marshal(marsh, affKey); + } + + /** {@inheritDoc} */ + @Override public void finishUnmarshal(Marshaller marsh, ClassLoader clsLdr) throws IgniteCheckedException { + if (!F.isEmpty(affKeyBytes)) { + affKey = U.unmarshal(marsh, affKeyBytes, clsLdr); + + affKeyBytes = null; + } + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(LazyServiceConfigurationMessage.class, this); + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/ServiceDeploymentRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/ServiceDeploymentRequest.java index 7d67164f10be0..f76d9353a73a6 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/ServiceDeploymentRequest.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/ServiceDeploymentRequest.java @@ -17,29 +17,24 @@ package org.apache.ignite.internal.processors.service; -import org.apache.ignite.IgniteCheckedException; -import org.apache.ignite.internal.MarshallableMessage; import org.apache.ignite.internal.Order; import org.apache.ignite.internal.util.typedef.internal.S; -import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.lang.IgniteUuid; -import org.apache.ignite.marshaller.Marshaller; import org.apache.ignite.plugin.extensions.communication.MessageFactory; import org.jetbrains.annotations.NotNull; -/** - * Service deployment request. - */ -public class ServiceDeploymentRequest extends ServiceChangeAbstractRequest implements MarshallableMessage { +/** Service deployment request. */ +public class ServiceDeploymentRequest extends ServiceChangeAbstractRequest { /** Service configuration. */ private LazyServiceConfiguration cfg; - /** JDK serialization for {@link #cfg}. */ + /** Service configuration message. */ @Order(0) - byte[] cfgBytes; + LazyServiceConfigurationMessage cfgMsg; /** Default constructor for {@link MessageFactory}. */ public ServiceDeploymentRequest() { + // No-op. } /** @@ -49,25 +44,16 @@ public ServiceDeploymentRequest() { public ServiceDeploymentRequest(@NotNull IgniteUuid srvcId, @NotNull LazyServiceConfiguration cfg) { this.srvcId = srvcId; this.cfg = cfg; - } - /** - * @return Service configuration. - */ - public LazyServiceConfiguration configuration() { - return cfg; + cfgMsg = new LazyServiceConfigurationMessage(cfg); } - /** {@inheritDoc} */ - @Override public void prepareMarshal(Marshaller marsh) throws IgniteCheckedException { - if (cfg != null) - cfgBytes = U.marshal(marsh, cfg); - } + /** @return Service configuration. */ + public LazyServiceConfiguration configuration() { + if (cfg == null && cfgMsg != null) + cfg = cfgMsg.toConfiguration(); - /** {@inheritDoc} */ - @Override public void finishUnmarshal(Marshaller marsh, ClassLoader clsLdr) throws IgniteCheckedException { - if (cfgBytes != null) - cfg = U.unmarshal(marsh, cfgBytes, clsLdr); + return cfg; } /** {@inheritDoc} */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/ServiceInfo.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/ServiceInfo.java index 59b1f0903f88e..2e6e26d89e10e 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/ServiceInfo.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/ServiceInfo.java @@ -21,10 +21,13 @@ import java.util.UUID; import org.apache.ignite.IgniteException; import org.apache.ignite.internal.GridKernalContext; +import org.apache.ignite.internal.Order; import org.apache.ignite.internal.managers.deployment.GridDeployment; import org.apache.ignite.internal.util.tostring.GridToStringInclude; import org.apache.ignite.internal.util.typedef.internal.S; import org.apache.ignite.lang.IgniteUuid; +import org.apache.ignite.plugin.extensions.communication.Message; +import org.apache.ignite.plugin.extensions.communication.MessageFactory; import org.apache.ignite.services.Service; import org.apache.ignite.services.ServiceDescriptor; import org.jetbrains.annotations.NotNull; @@ -32,10 +35,8 @@ import static org.apache.ignite.internal.processors.service.ServiceTopology.EMPTY; -/** - * Service's information container. - */ -public class ServiceInfo implements ServiceDescriptor { +/** Service's information container. */ +public class ServiceInfo implements ServiceDescriptor, Message { /** */ private static final long serialVersionUID = 0L; @@ -43,24 +44,37 @@ public class ServiceInfo implements ServiceDescriptor { private transient volatile GridKernalContext ctx; /** Origin node ID. */ - private final UUID originNodeId; + @Order(0) + UUID originNodeId; /** Service id. */ - private final IgniteUuid srvcId; + @Order(1) + IgniteUuid srvcId; /** Service configuration. */ - private final LazyServiceConfiguration cfg; + private LazyServiceConfiguration cfg; + + /** Service configuration message. */ + @Order(2) + transient LazyServiceConfigurationMessage cfgMsg; /** Statically configured flag. */ - private final boolean staticCfg; + @Order(3) + boolean staticCfg; /** Topology snapshot. */ + @Order(4) @GridToStringInclude - private volatile ServiceTopology top = EMPTY; + volatile ServiceTopology top = EMPTY; /** Service class. */ private transient volatile Class srvcCls; + /** Default constructor for {@link MessageFactory}. */ + public ServiceInfo() { + // No-op. + } + /** * @param originNodeId Initiating node id. * @param srvcId Service id. @@ -82,6 +96,8 @@ public ServiceInfo(@NotNull UUID originNodeId, @NotNull IgniteUuid srvcId, @NotN this.srvcId = srvcId; this.cfg = cfg; this.staticCfg = staticCfg; + + cfgMsg = new LazyServiceConfigurationMessage(cfg); } /** @@ -107,34 +123,27 @@ public ServiceTopology serviceTopology() { return top; } - /** - * Returns service's configuration. - * - * @return Service configuration. - */ + /** @return Service configuration. */ public LazyServiceConfiguration configuration() { + if (cfg == null && cfgMsg != null) + cfg = cfgMsg.toConfiguration(); + return cfg; } - /** - * @return {@code true} if statically configured. - */ + /** @return {@code true} if statically configured. */ public boolean staticallyConfigured() { return staticCfg; } - /** - * Returns services id. - * - * @return Service id. - */ + /** @return Service id. */ public IgniteUuid serviceId() { return srvcId; } /** {@inheritDoc} */ @Override public String name() { - return cfg.getName(); + return configuration().getName(); } /** {@inheritDoc} */ @@ -142,7 +151,7 @@ public IgniteUuid serviceId() { if (srvcCls != null) return srvcCls; - String clsName = cfg.serviceClassName(); + String clsName = configuration().serviceClassName(); try { srvcCls = (Class)Class.forName(clsName); @@ -167,22 +176,22 @@ public IgniteUuid serviceId() { /** {@inheritDoc} */ @Override public int totalCount() { - return cfg.getTotalCount(); + return configuration().getTotalCount(); } /** {@inheritDoc} */ @Override public int maxPerNodeCount() { - return cfg.getMaxPerNodeCount(); + return configuration().getMaxPerNodeCount(); } /** {@inheritDoc} */ @Nullable @Override public String cacheName() { - return cfg.getCacheName(); + return configuration().getCacheName(); } /** {@inheritDoc} */ @Nullable @Override public K affinityKey() { - return (K)cfg.getAffinityKey(); + return (K)configuration().getAffinityKey(); } /** {@inheritDoc} */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/ServiceProcessorCommonDiscoveryData.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/ServiceProcessorCommonDiscoveryData.java index 7f9fc8388c7d8..03f386d1645d5 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/ServiceProcessorCommonDiscoveryData.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/ServiceProcessorCommonDiscoveryData.java @@ -17,25 +17,26 @@ package org.apache.ignite.internal.processors.service; -import java.io.Serializable; -import java.util.ArrayList; +import java.util.List; +import org.apache.ignite.internal.Order; import org.apache.ignite.internal.util.typedef.internal.S; +import org.apache.ignite.plugin.extensions.communication.Message; +import org.apache.ignite.plugin.extensions.communication.MessageFactory; import org.jetbrains.annotations.NotNull; -/** - * Initial data container to be sent to newly joining node for initialization of {@link IgniteServiceProcessor}. - */ -class ServiceProcessorCommonDiscoveryData implements Serializable { - /** */ - private static final long serialVersionUID = 0L; - +/** Initial data container to be sent to newly joining node for initialization of {@link IgniteServiceProcessor}. */ +public class ServiceProcessorCommonDiscoveryData implements Message { /** Clusters registered services descriptors. */ - private final ArrayList registeredServices; + @Order(0) + List registeredServices; - /** - * @param registeredServices Clusters registered services descriptors. - */ - public ServiceProcessorCommonDiscoveryData(@NotNull ArrayList registeredServices) { + /** Default constructor for {@link MessageFactory}. */ + public ServiceProcessorCommonDiscoveryData() { + // No-op. + } + + /** @param registeredServices Clusters registered services descriptors. */ + public ServiceProcessorCommonDiscoveryData(@NotNull List registeredServices) { this.registeredServices = registeredServices; } @@ -44,7 +45,7 @@ public ServiceProcessorCommonDiscoveryData(@NotNull ArrayList regis * * @return Registered services descriptors. */ - public ArrayList registeredServices() { + public List registeredServices() { return registeredServices; } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/ServiceProcessorJoinNodeDiscoveryData.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/ServiceProcessorJoinNodeDiscoveryData.java index 1d6ed6bc0ca0a..ed8f734b3e8cb 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/ServiceProcessorJoinNodeDiscoveryData.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/ServiceProcessorJoinNodeDiscoveryData.java @@ -17,32 +17,31 @@ package org.apache.ignite.internal.processors.service; -import java.io.Serializable; -import java.util.ArrayList; +import java.util.List; +import org.apache.ignite.internal.Order; import org.apache.ignite.internal.util.typedef.internal.S; +import org.apache.ignite.plugin.extensions.communication.Message; +import org.apache.ignite.plugin.extensions.communication.MessageFactory; import org.jetbrains.annotations.NotNull; -/** - * Initial data of {@link IgniteServiceProcessor} to send in cluster on joining node. - */ -public class ServiceProcessorJoinNodeDiscoveryData implements Serializable { - /** */ - private static final long serialVersionUID = 0L; - +/** Initial data of {@link IgniteServiceProcessor} to send in cluster on joining node. */ +public class ServiceProcessorJoinNodeDiscoveryData implements Message { /** Static services configurations info. */ - public final ArrayList staticServicesInfo; + @Order(0) + List staticServicesInfo; + + /** Default constructor for {@link MessageFactory}. */ + public ServiceProcessorJoinNodeDiscoveryData() { + // No-op. + } - /** - * @param staticServicesInfo Static services configurations info. - */ - public ServiceProcessorJoinNodeDiscoveryData(@NotNull ArrayList staticServicesInfo) { + /** @param staticServicesInfo Static services configurations info. */ + public ServiceProcessorJoinNodeDiscoveryData(@NotNull List staticServicesInfo) { this.staticServicesInfo = staticServicesInfo; } - /** - * @return Static services configurations info. - */ - public ArrayList services() { + /** @return Static services configurations info. */ + public List services() { return staticServicesInfo; } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/ServiceTopology.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/ServiceTopology.java index a4e9b9c229fc4..78c9ac0b6914a 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/ServiceTopology.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/ServiceTopology.java @@ -21,33 +21,37 @@ import java.util.Collections; import java.util.Map; import java.util.UUID; +import org.apache.ignite.internal.Order; import org.apache.ignite.internal.util.tostring.GridToStringInclude; import org.apache.ignite.internal.util.typedef.internal.S; +import org.apache.ignite.plugin.extensions.communication.Message; +import org.apache.ignite.plugin.extensions.communication.MessageFactory; /** */ -public class ServiceTopology implements Serializable { +public class ServiceTopology implements Serializable, Message { /** Serial version uid. */ private static final long serialVersionUID = 0L; /** Empty service topology instance. */ - public static final ServiceTopology EMPTY = new ServiceTopology(); + public static final ServiceTopology EMPTY = empty(); /** Topology snapshot. */ + @Order(0) @GridToStringInclude - private final Map snapshot; + Map snapshot; /** * Whether topology is transitional. Nodes may leave the cluster while the service topology is being recalculated. * In this case, the resulting service topology may be incomplete. We consider the mentioned service topology * transitional and expect it to be recalculated soon. */ + @Order(1) @GridToStringInclude - private final boolean isTransitional; + boolean isTransitional; - /** */ - private ServiceTopology() { - snapshot = Collections.emptyMap(); - isTransitional = true; + /** Default constructor for {@link MessageFactory}. */ + public ServiceTopology() { + // No-op. } /** */ @@ -85,4 +89,14 @@ public boolean containsNode(UUID nodeId) { @Override public String toString() { return S.toString(ServiceTopology.class, this); } + + /** */ + private static ServiceTopology empty() { + ServiceTopology top = new ServiceTopology(); + + top.snapshot = Collections.emptyMap(); + top.isTransitional = true; + + return top; + } } 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 new file mode 100644 index 0000000000000..2039f60e763ed --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/service/LazyServiceConfigurationMessageSerializationTest.java @@ -0,0 +1,168 @@ +/* + * 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.service; + +import java.io.Externalizable; +import java.io.Serializable; +import java.lang.reflect.Modifier; +import java.nio.ByteBuffer; +import java.util.Arrays; +import org.apache.ignite.internal.CoreMessagesProvider; +import org.apache.ignite.internal.direct.DirectMessageReader; +import org.apache.ignite.internal.direct.DirectMessageWriter; +import org.apache.ignite.internal.managers.communication.IgniteMessageFactoryImpl; +import org.apache.ignite.plugin.extensions.communication.Message; +import org.apache.ignite.plugin.extensions.communication.MessageFactory; +import org.apache.ignite.plugin.extensions.communication.MessageFactoryProvider; +import org.apache.ignite.plugin.extensions.communication.MessageSerializer; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; +import org.junit.Test; + +import static org.apache.ignite.internal.util.CommonUtils.gridClassLoader; +import static org.apache.ignite.internal.util.CommonUtils.makeMessageType; +import static org.apache.ignite.marshaller.Marshallers.jdk; +import static org.junit.Assert.assertArrayEquals; + +/** Test for serialization round-trip of {@link LazyServiceConfigurationMessage}. */ +public class LazyServiceConfigurationMessageSerializationTest extends GridCommonAbstractTest { + /** Error suffix. */ + public static final String ERROR_SUFFIX = " count is not equal to the expected fields count. " + + "Has the number of fields in the `LazyServiceConfiguration` class changed?"; + + /** */ + private final MessageFactory msgFactory = new IgniteMessageFactoryImpl( + new MessageFactoryProvider[] {new CoreMessagesProvider(jdk(), jdk(), gridClassLoader())}); + + /** + * ServiceConfiguration declares {@code svc}, {@code nodeFilter}, {@code interceptors} as non-transient, + * so serializableFieldsCount includes them. Yet they are not serialized via MessageSerializer — + * LazyServiceConfiguration replaces them with byte[] counterparts (srvcBytes, nodeFilterBytes, interceptorsBytes). + */ + private static final long SHADOWED_FIELD_COUNT = 3; + + /** */ + @Test + public void testLazyServiceConfiguration() { + LazyServiceConfiguration cfg = lazyServiceConfiguration(); + + assertEquals(cfg, serializeAndDeserialize(cfg)); + } + + /** */ + @Test + public void testLazyServiceConfigurationWithBytes() { + LazyServiceConfiguration cfg = lazyServiceConfigurationWithBytes(); + + LazyServiceConfiguration res = serializeAndDeserialize(cfg); + + assertEquals(cfg, res); + + // Explicitly verify byte[] and array fields survived round-trip + // (equalsIgnoreNodeFilter doesn't cover platformMtdNames, srvcClsName). + assertArrayEquals(cfg.serviceBytes(), res.serviceBytes()); + assertArrayEquals(cfg.nodeFilterBytes(), res.nodeFilterBytes()); + assertArrayEquals(cfg.interceptorBytes(), res.interceptorBytes()); + assertArrayEquals(cfg.platformMtdNames(), res.platformMtdNames()); + assertEquals(cfg.serviceClassName(), res.serviceClassName()); + } + + /** + * @param src Source configuration. + * + * @return Configuration read during a full serde round-trip. + */ + private LazyServiceConfiguration serializeAndDeserialize(LazyServiceConfiguration src) { + long expReadsWritesCnt = serializableFieldsCount(LazyServiceConfiguration.class) - SHADOWED_FIELD_COUNT; + + return writeAndReadBack(new LazyServiceConfigurationMessage(src), expReadsWritesCnt).toConfiguration(); + } + + /** @param cls Class of an object. */ + private long serializableFieldsCount(Class cls) { + if (cls == Object.class) + return 0; + + assertTrue("Not a serializable class: " + cls, Serializable.class.isAssignableFrom(cls)); + assertFalse("Should not be Externalizable:" + cls, Externalizable.class.isAssignableFrom(cls)); + + return serializableFieldsCount(cls.getSuperclass()) + Arrays.stream(cls.getDeclaredFields()) + .filter(f -> !Modifier.isStatic(f.getModifiers()) && !Modifier.isTransient(f.getModifiers())) + .count(); + } + + /** + * @param msg Message to write and read back through {@link DirectMessageWriter}/{@link DirectMessageReader}. + * @param expReadsWritesCnt Expected count of field reads and writes. + * @param Type of Message. + * + * @return Restored message. + */ + private T writeAndReadBack(T msg, long expReadsWritesCnt) { + ByteBuffer buf = ByteBuffer.allocate(64 * 1024); + + MessageSerializer serde = (MessageSerializer)msgFactory.serializer(msg.directType()); + + DirectMessageWriter writer = new DirectMessageWriter(msgFactory); + writer.setBuffer(buf); + + assertTrue(serde.writeTo(msg, writer)); + assertEquals("Writes" + ERROR_SUFFIX, expReadsWritesCnt, writer.state()); + + buf.flip(); + + DirectMessageReader reader = new DirectMessageReader(msgFactory, null); + reader.setBuffer(buf); + + T res = (T)msgFactory.create(makeMessageType(buf.get(), buf.get())); + + assertTrue(serde.readFrom(res, reader)); + assertEquals("Reads" + ERROR_SUFFIX, expReadsWritesCnt, reader.state()); + + return res; + } + + /** @return Lazy service configuration with scalar fields only (null byte arrays). */ + private LazyServiceConfiguration lazyServiceConfiguration() { + return (LazyServiceConfiguration)new LazyServiceConfiguration() + .setName("testService") + .setTotalCount(5) + .setMaxPerNodeCount(1) + .setCacheName("testCache") + .setAffinityKey("affKey") + .setStatisticsEnabled(true) + .setLocalStartOrder(10); + } + + /** @return Lazy service configuration with all fields populated, including serialized byte arrays. */ + private LazyServiceConfiguration lazyServiceConfigurationWithBytes() { + LazyServiceConfiguration cfg = (LazyServiceConfiguration)new LazyServiceConfiguration() + .setName("testService") + .setTotalCount(5) + .setMaxPerNodeCount(1) + .setCacheName("testCache") + .setAffinityKey("affKey") + .setStatisticsEnabled(true) + .setLocalStartOrder(10); + + return cfg.serviceClassName("org.apache.ignite.TestService") + .serviceBytes("service_bytes".getBytes()) + .nodeFilterBytes("nodeFilter".getBytes()) + .interceptorBytes("interceptors".getBytes()) + .platformMtdNames(new String[]{"method1", "method2"}); + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteServiceGridTestSuite.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteServiceGridTestSuite.java index 63f16fbbf7067..f825c91260f23 100644 --- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteServiceGridTestSuite.java +++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteServiceGridTestSuite.java @@ -47,6 +47,7 @@ import org.apache.ignite.internal.processors.service.IgniteServiceDynamicCachesSelfTest; import org.apache.ignite.internal.processors.service.IgniteServiceProxyTimeoutInitializedTest; import org.apache.ignite.internal.processors.service.IgniteServiceReassignmentTest; +import org.apache.ignite.internal.processors.service.LazyServiceConfigurationMessageSerializationTest; import org.apache.ignite.internal.processors.service.ServiceConcurrentUndeployTest; import org.apache.ignite.internal.processors.service.ServiceDeploymentDiscoveryListenerNotificationOrderTest; import org.apache.ignite.internal.processors.service.ServiceDeploymentNonSerializableStaticConfigurationTest; @@ -126,7 +127,8 @@ GridServiceMetricsTest.class, IgniteServiceCallInterceptorTest.class, ServiceRedeploymentOnNodeLeftTest.class, - ServiceLocalStartOrderTest.class + ServiceLocalStartOrderTest.class, + LazyServiceConfigurationMessageSerializationTest.class }) public class IgniteServiceGridTestSuite { /** */