IGNITE-17451 Provide internal access to BinaryContext in thin client (#10179)

diff --git a/modules/core/src/main/java/org/apache/ignite/internal/client/thin/ClientBinary.java b/modules/core/src/main/java/org/apache/ignite/internal/client/thin/ClientBinary.java
index 05d121a..cc5ac28 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/client/thin/ClientBinary.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/client/thin/ClientBinary.java
@@ -35,7 +35,7 @@
 /**
  * Thin client implementation of {@link IgniteBinary}.
  */
-class ClientBinary implements IgniteBinary {
+public class ClientBinary implements IgniteBinary {
     /** Marshaller. */
     private final ClientBinaryMarshaller marsh;
 
@@ -48,7 +48,7 @@
 
     /** {@inheritDoc} */
     @Override public int typeId(String typeName) {
-        return marsh.context().typeId(typeName);
+        return binaryContext().typeId(typeName);
     }
 
     /** {@inheritDoc} */
@@ -69,7 +69,7 @@
         if (typeName == null || typeName.isEmpty())
             throw new IllegalArgumentException("typeName");
 
-        return new BinaryObjectBuilderImpl(marsh.context(), typeName);
+        return new BinaryObjectBuilderImpl(binaryContext(), typeName);
     }
 
     /** {@inheritDoc} */
@@ -93,19 +93,19 @@
         if (typeName == null || typeName.isEmpty())
             throw new IllegalArgumentException("typeName");
 
-        int typeId = marsh.context().typeId(typeName);
+        int typeId = binaryContext().typeId(typeName);
 
         return type(typeId);
     }
 
     /** {@inheritDoc} */
     @Override public BinaryType type(int typeId) {
-        return marsh.context().metadata(typeId);
+        return binaryContext().metadata(typeId);
     }
 
     /** {@inheritDoc} */
     @Override public Collection<BinaryType> types() {
-        return marsh.context().metadata();
+        return binaryContext().metadata();
     }
 
     /** {@inheritDoc} */
@@ -113,7 +113,7 @@
         if (typeName == null || typeName.isEmpty())
             throw new IllegalArgumentException("typeName");
 
-        BinaryContext ctx = marsh.context();
+        BinaryContext ctx = binaryContext();
 
         int typeId = ctx.typeId(typeName);
 
@@ -128,7 +128,7 @@
         if (name == null || name.isEmpty())
             throw new IllegalArgumentException("name");
 
-        BinaryContext ctx = marsh.context();
+        BinaryContext ctx = binaryContext();
 
         int typeId = ctx.typeId(typeName);
 
@@ -157,7 +157,7 @@
         if (typeName == null || typeName.isEmpty())
             throw new IllegalArgumentException("typeName");
 
-        BinaryContext ctx = marsh.context();
+        BinaryContext ctx = binaryContext();
 
         int typeId = ctx.typeId(typeName);
 
@@ -168,8 +168,13 @@
 
     /** {@inheritDoc} */
     @Override public BinaryType registerClass(Class<?> cls) throws BinaryObjectException {
-        BinaryClassDescriptor clsDesc = marsh.context().registerClass(cls, true, false);
+        BinaryClassDescriptor clsDesc = binaryContext().registerClass(cls, true, false);
 
-        return marsh.context().metadata(clsDesc.typeId());
+        return binaryContext().metadata(clsDesc.typeId());
+    }
+
+    /** @return Binary context. */
+    public BinaryContext binaryContext() {
+        return marsh.context();
     }
 }
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/client/thin/MetadataRegistrationTest.java b/modules/core/src/test/java/org/apache/ignite/internal/client/thin/MetadataRegistrationTest.java
new file mode 100644
index 0000000..6d4ae64
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/client/thin/MetadataRegistrationTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.client.thin;
+
+import org.apache.ignite.IgniteBinary;
+import org.apache.ignite.cdc.CdcConsumer;
+import org.apache.ignite.cdc.TypeMapping;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.MarshallerContextImpl;
+import org.apache.ignite.internal.binary.BinaryContext;
+import org.apache.ignite.internal.binary.BinaryMetadata;
+import org.apache.ignite.internal.cdc.TypeMappingImpl;
+import org.apache.ignite.platform.PlatformType;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.junit.Test;
+
+/**
+ * Metadata registration test.
+ *
+ * @see TypeMapping
+ * @see CdcConsumer
+ */
+public class MetadataRegistrationTest extends AbstractThinClientTest {
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        super.afterTest();
+
+        stopAllGrids();
+    }
+
+    /** @throws Exception If failed. */
+    @Test
+    public void testBinaryMeta() throws Exception {
+        IgniteEx srv = startGrid();
+
+        IgniteBinary srvBinary = srv.binary();
+
+        try (IgniteClient client = startClient(srv)) {
+            BinaryMetadata meta = new BinaryMetadata(123, "newType", null, null, null, false, null);
+
+            assertNull(srvBinary.type(meta.typeId()));
+
+            BinaryContext ctx = ((ClientBinary)client.binary()).binaryContext();
+
+            ctx.updateMetadata(meta.typeId(), meta, false);
+
+            assertNotNull(srvBinary.type(meta.typeId()));
+            assertEquals(meta.typeName(), srvBinary.type(meta.typeId()).typeName());
+        }
+    }
+
+    /** @throws Exception If failed. */
+    @Test
+    @SuppressWarnings("ThrowableNotThrown")
+    public void testMapping() throws Exception {
+        IgniteEx srv = startGrid();
+
+        MarshallerContextImpl marshCtx = srv.context().marshallerContext();
+
+        try (IgniteClient client = startClient(srv)) {
+            TypeMapping mapping = new TypeMappingImpl(123, "newType", PlatformType.JAVA);
+
+            byte platformType = (byte)mapping.platformType().ordinal();
+
+            GridTestUtils.assertThrowsWithCause(() -> marshCtx.getClassName(platformType, mapping.typeId()),
+                ClassNotFoundException.class);
+
+            BinaryContext ctx = ((ClientBinary)client.binary()).binaryContext();
+
+            ctx.registerUserClassName(mapping.typeId(), mapping.typeName(), false, false, platformType);
+
+            assertEquals(mapping.typeName(), marshCtx.getClassName(platformType, mapping.typeId()));
+        }
+    }
+}
diff --git a/modules/indexing/src/test/java/org/apache/ignite/client/ClientTestSuite.java b/modules/indexing/src/test/java/org/apache/ignite/client/ClientTestSuite.java
index 693c09e..4a97cd5 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/client/ClientTestSuite.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/client/ClientTestSuite.java
@@ -25,6 +25,7 @@
 import org.apache.ignite.internal.client.thin.ComputeTaskTest;
 import org.apache.ignite.internal.client.thin.DataReplicationOperationsTest;
 import org.apache.ignite.internal.client.thin.IgniteSetTest;
+import org.apache.ignite.internal.client.thin.MetadataRegistrationTest;
 import org.apache.ignite.internal.client.thin.OptimizedMarshallerClassesCachedTest;
 import org.apache.ignite.internal.client.thin.ReliableChannelTest;
 import org.apache.ignite.internal.client.thin.ServicesBinaryArraysTests;
@@ -75,7 +76,8 @@
     AtomicLongTest.class,
     BinaryConfigurationTest.class,
     IgniteSetTest.class,
-    DataReplicationOperationsTest.class
+    DataReplicationOperationsTest.class,
+    MetadataRegistrationTest.class
 })
 public class ClientTestSuite {
     // No-op.