diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryRawReaderEx.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryRawReaderEx.java index 603275ecc6809..c1785497c00b9 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryRawReaderEx.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryRawReaderEx.java @@ -30,4 +30,11 @@ public interface BinaryRawReaderEx extends BinaryRawReader { * @throws org.apache.ignite.binary.BinaryObjectException In case of error. */ @Nullable public Object readObjectDetached() throws BinaryObjectException; + + /** + * @param deserialize {@code True} if object should be deserialized during reading. + * @return Object. + * @throws org.apache.ignite.binary.BinaryObjectException In case of error. + */ + @Nullable public Object readObjectDetached(boolean deserialize) throws BinaryObjectException; } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryReaderExImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryReaderExImpl.java index 2f0ff231059a7..8c36d34958531 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryReaderExImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryReaderExImpl.java @@ -1333,7 +1333,12 @@ float readFloat(int fieldId) throws BinaryObjectException { /** {@inheritDoc} */ @Nullable @Override public Object readObjectDetached() throws BinaryObjectException { - return BinaryUtils.unmarshal(in, ctx, ldr, this, true); + return readObjectDetached(false); + } + + /** {@inheritDoc} */ + @Nullable @Override public Object readObjectDetached(boolean deserialize) throws BinaryObjectException { + return BinaryUtils.unmarshal(in, ctx, ldr, this, true, deserialize); } /** {@inheritDoc} */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java index 84919435501b8..46267e20813e2 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java @@ -1825,6 +1825,15 @@ public static Object doReadOptimized(BinaryInputStream in, BinaryContext ctx, @N */ @Nullable public static Object unmarshal(BinaryInputStream in, BinaryContext ctx, ClassLoader ldr, BinaryReaderHandlesHolder handles, boolean detach) throws BinaryObjectException { + return unmarshal(in, ctx, ldr, handles, detach, false); + } + + /** + * @return Unmarshalled value. + * @throws BinaryObjectException In case of error. + */ + @Nullable public static Object unmarshal(BinaryInputStream in, BinaryContext ctx, ClassLoader ldr, + BinaryReaderHandlesHolder handles, boolean detach, boolean deserialize) throws BinaryObjectException { int start = in.position(); byte flag = in.readByte(); @@ -1843,7 +1852,7 @@ public static Object doReadOptimized(BinaryInputStream in, BinaryContext ctx, @N in.position(handlePos); - obj = unmarshal(in, ctx, ldr, handles, detach); + obj = unmarshal(in, ctx, ldr, handles, detach, deserialize); in.position(retPos); } @@ -1964,13 +1973,13 @@ public static Object doReadOptimized(BinaryInputStream in, BinaryContext ctx, @N return doReadTimeArray(in); case GridBinaryMarshaller.OBJ_ARR: - return doReadObjectArray(in, ctx, ldr, handles, detach, false); + return doReadObjectArray(in, ctx, ldr, handles, detach, deserialize); case GridBinaryMarshaller.COL: - return doReadCollection(in, ctx, ldr, handles, detach, false, null); + return doReadCollection(in, ctx, ldr, handles, detach, deserialize, null); case GridBinaryMarshaller.MAP: - return doReadMap(in, ctx, ldr, handles, detach, false, null); + return doReadMap(in, ctx, ldr, handles, detach, deserialize, null); case GridBinaryMarshaller.BINARY_OBJ: return doReadBinaryObject(in, ctx, detach); @@ -2162,7 +2171,7 @@ public static Collection<?> doReadCollection(BinaryInputStream in, BinaryContext */ private static Object deserializeOrUnmarshal(BinaryInputStream in, BinaryContext ctx, ClassLoader ldr, BinaryReaderHandlesHolder handles, boolean detach, boolean deserialize) { - return deserialize ? doReadObject(in, ctx, ldr, handles) : unmarshal(in, ctx, ldr, handles, detach); + return deserialize ? doReadObject(in, ctx, ldr, handles) : unmarshal(in, ctx, ldr, handles, detach, deserialize); } /** diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/binary/PlatformBinaryProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/binary/PlatformBinaryProcessor.java index 649e7742d2771..425c29934f91d 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/binary/PlatformBinaryProcessor.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/binary/PlatformBinaryProcessor.java @@ -76,10 +76,19 @@ public PlatformBinaryProcessor(PlatformContext platformCtx) { case OP_REGISTER_TYPE: { int typeId = reader.readInt(); String typeName = reader.readString(); + boolean registerSameJavaType = reader.readBoolean(); - return platformContext().kernalContext().marshallerContext() + int res = platformContext().kernalContext().marshallerContext() .registerClassName(MarshallerPlatformIds.DOTNET_ID, typeId, typeName, false) ? TRUE : FALSE; + + if (registerSameJavaType && res == TRUE) { + res = platformContext().kernalContext().marshallerContext() + .registerClassName(MarshallerPlatformIds.JAVA_ID, typeId, typeName, false) + ? TRUE : FALSE; + } + + return res; } } @@ -125,10 +134,11 @@ public PlatformBinaryProcessor(PlatformContext platformCtx) { case OP_GET_TYPE: { int typeId = reader.readInt(); + byte platformId = reader.readByte(); try { String typeName = platformContext().kernalContext().marshallerContext() - .getClassName(MarshallerPlatformIds.DOTNET_ID, typeId); + .getClassName(platformId, typeId); writer.writeString(typeName); } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/services/PlatformServices.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/services/PlatformServices.java index 8e67cab2d0393..d5067180d60b1 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/services/PlatformServices.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/services/PlatformServices.java @@ -271,6 +271,8 @@ private ServiceDescriptor findDescriptor(String name) { assert arg != null; assert arg instanceof ServiceProxyHolder; + ServiceProxyHolder svc = (ServiceProxyHolder)arg; + String mthdName = reader.readString(); Object[] args; @@ -279,13 +281,13 @@ private ServiceDescriptor findDescriptor(String name) { args = new Object[reader.readInt()]; for (int i = 0; i < args.length; i++) - args[i] = reader.readObjectDetached(); + args[i] = reader.readObjectDetached(!srvKeepBinary && !svc.isPlatformService()); } else args = null; try { - Object result = ((ServiceProxyHolder)arg).invoke(mthdName, srvKeepBinary, args); + Object result = svc.invoke(mthdName, srvKeepBinary, args); PlatformUtils.writeInvocationResult(writer, result, null); } @@ -583,7 +585,7 @@ private ServiceProxyHolder(Object proxy, Class clazz, PlatformContext ctx) { */ public Object invoke(String mthdName, boolean srvKeepBinary, Object[] args) throws IgniteCheckedException, NoSuchMethodException { - if (proxy instanceof PlatformService) + if (isPlatformService()) return ((PlatformService)proxy).invokeMethod(mthdName, srvKeepBinary, args); else { assert proxy instanceof GridServiceProxy; @@ -707,6 +709,11 @@ private static boolean areMethodArgsCompatible(Class[] argTypes, Object[] args) private static Class wrap(Class c) { return c.isPrimitive() ? PRIMITIVES_TO_WRAPPERS.get(c) : c; } + + /** @return {@code True} if service is platform service. */ + public boolean isPlatformService() { + return proxy instanceof PlatformService; + } } /** diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformUtils.java index 38308229bc85b..a97934fc12466 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformUtils.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformUtils.java @@ -989,6 +989,9 @@ private static Collection<Object> unwrapKnownCollection(Collection<Object> col) * @return Result. */ public static Object[] unwrapBinariesInArray(Object[] arr) { + if (arr.getClass().getComponentType() != Object.class) + return arr; + Object[] res = new Object[arr.length]; for (int i = 0; i < arr.length; i++) diff --git a/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryMarshallerSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryMarshallerSelfTest.java index 68279385e50d0..3d476b4c485bd 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryMarshallerSelfTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryMarshallerSelfTest.java @@ -84,6 +84,7 @@ import org.apache.ignite.internal.managers.discovery.GridDiscoveryManager; import org.apache.ignite.internal.managers.systemview.GridSystemViewManager; import org.apache.ignite.internal.processors.cache.CacheObjectContext; +import org.apache.ignite.internal.processors.platform.utils.PlatformUtils; import org.apache.ignite.internal.util.GridUnsafe; import org.apache.ignite.internal.util.IgniteUtils; import org.apache.ignite.internal.util.lang.GridMapEntry; @@ -2969,7 +2970,7 @@ public void testReadDetachedMap() throws Exception { assertTrue(map.containsKey(key)); assertEquals(val, map.get(key)); }); - }); + }, false); } /** @@ -2989,27 +2990,59 @@ public void testReadDetachedCollection() throws Exception { assertTrue(col.contains(val)); }); - }); + }, false); } - /** - * @throws Exception If failed. - */ + /** @throws Exception If failed. */ @Test - public void testReadDetachedArray() throws Exception { + public void testReadDetachedTypedArray() throws Exception { Value[] arr = IntStream.range(0, 1000).mapToObj(Value::new).toArray(Value[]::new); testReadDetachObjectProperly(arr, obj -> { - Object[] desArr = (Object[])obj; + assertArrayEquals(arr, (Value[])obj); - assertEquals(arr.length, desArr.length); + Object[] args = new Object[] {obj}; - for (int i = 0; i < arr.length; i++) { - BinaryObject val = (BinaryObject)desArr[i]; + assertTrue(args[0] instanceof Value[]); - assertEquals(arr[i], new Value(val.field("val"))); - } - }); + args = PlatformUtils.unwrapBinariesInArray(args); + + assertTrue(args[0] instanceof Value[]); + assertArrayEquals(arr, (Value[])args[0]); + }, true); + } + + /** @throws Exception If failed. */ + @Test + public void testReadArrayOfCollections() throws Exception { + Collection[] arr = new Collection[] { Arrays.asList(new Value(1), new Value(2), new Value(3)) }; + testReadDetachObjectProperly(arr, obj -> { + assertArrayEquals(arr, (Collection[])obj); + + Object[] args = new Object[] {obj}; + + assertTrue(args[0] instanceof Collection[]); + + args = PlatformUtils.unwrapBinariesInArray(args); + + assertTrue(args[0] instanceof Collection[]); + assertArrayEquals(arr, (Collection[])args[0]); + }, true); + } + + + /** @throws Exception If failed. */ + @Test + public void testReadArrayOfBinaryCollections() throws Exception { + Collection[] arr = new Collection[] { new ArrayList<>(Arrays.asList(new Value(1), new Value(2), new Value(3))) }; + + testReadDetachObjectProperly(arr, obj -> { + Object[] args = PlatformUtils.unwrapBinariesInArray(new Object[] {obj}); + + Collection deserVals = (Collection)((Object[])args[0])[0]; + + assertEqualsCollections(arr[0], deserVals); + }, false); } /** @@ -3019,7 +3052,7 @@ public void testReadDetachedArray() throws Exception { * @param action Action to perform on object. * @throws Exception If failed. */ - private void testReadDetachObjectProperly(Object obj, IgniteThrowableConsumer<Object> action) throws Exception { + private void testReadDetachObjectProperly(Object obj, IgniteThrowableConsumer<Object> action, boolean deserialize) throws Exception { BinaryMarshaller marsh = binaryMarshaller(); BinaryHeapOutputStream os = new BinaryHeapOutputStream(1024); @@ -3032,7 +3065,7 @@ private void testReadDetachObjectProperly(Object obj, IgniteThrowableConsumer<Ob BinaryReaderExImpl reader = marsh.binaryMarshaller().reader(is); - Object bObj = reader.readObjectDetached(); + Object bObj = reader.readObjectDetached(deserialize); Arrays.fill(os.array(), (byte)0); @@ -5507,27 +5540,6 @@ private static class DecimalMarshalAware extends DecimalReflective implements Bi } } - /** - * Wrapper object. - */ - private static class Wrapper { - - /** Value. */ - private final Object value; - - /** Constructor. */ - public Wrapper(Object value) { - this.value = value; - } - - /** - * @return Value. - */ - public Object getValue() { - return value; - } - } - /** */ private static class SingleHandleA { diff --git a/modules/core/src/test/java/org/apache/ignite/platform/PlatformDeployServiceTask.java b/modules/core/src/test/java/org/apache/ignite/platform/PlatformDeployServiceTask.java index 6986ae4e80bd3..c79f8c8ff9710 100644 --- a/modules/core/src/test/java/org/apache/ignite/platform/PlatformDeployServiceTask.java +++ b/modules/core/src/test/java/org/apache/ignite/platform/PlatformDeployServiceTask.java @@ -18,13 +18,20 @@ package org.apache.ignite.platform; import java.sql.Timestamp; +import java.time.ZoneId; +import java.time.ZonedDateTime; import java.util.ArrayList; +import java.util.Calendar; import java.util.Collection; import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.UUID; import org.apache.ignite.Ignite; +import org.apache.ignite.IgniteCache; import org.apache.ignite.IgniteException; import org.apache.ignite.binary.BinaryObject; import org.apache.ignite.cluster.ClusterNode; @@ -33,6 +40,16 @@ import org.apache.ignite.compute.ComputeJobResult; import org.apache.ignite.compute.ComputeTaskAdapter; import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.platform.model.ACL; +import org.apache.ignite.platform.model.Account; +import org.apache.ignite.platform.model.Address; +import org.apache.ignite.platform.model.Department; +import org.apache.ignite.platform.model.Employee; +import org.apache.ignite.platform.model.Key; +import org.apache.ignite.platform.model.Role; +import org.apache.ignite.platform.model.User; +import org.apache.ignite.platform.model.Value; import org.apache.ignite.resources.IgniteInstanceResource; import org.apache.ignite.services.Service; import org.apache.ignite.services.ServiceContext; @@ -89,6 +106,9 @@ private PlatformDeployServiceJob(String serviceName) { * Test service. */ public static class PlatformTestService implements Service { + @IgniteInstanceResource + private Ignite ignite; + /** */ private boolean isCancelled; @@ -412,5 +432,173 @@ public BinaryObject testBinaryObject(BinaryObject o) { return o.toBuilder().setField("field", 15).build(); } + + /** */ + public Address testAddress(Address addr) { + if (addr == null) + return null; + + assert "000".equals(addr.getZip()); + assert "Moscow".equals(addr.getAddr()); + + addr.setZip("127000"); + addr.setAddr("Moscow Akademika Koroleva 12"); + + return addr; + } + + /** */ + public int testOverload(Integer count, Employee[] emps) { + assert emps != null; + assert count == emps.length; + + assert "Sarah Connor".equals(emps[0].getFio()); + assert 1 == emps[0].getSalary(); + + assert "John Connor".equals(emps[1].getFio()); + assert 2 == emps[1].getSalary(); + + return 42; + } + + /** */ + public int testOverload(int first, int second) { + return first + second; + } + + /** */ + public Employee[] testEmployees(Employee[] emps) { + if (emps == null) + return null; + + assert 2 == emps.length; + + assert "Sarah Connor".equals(emps[0].getFio()); + assert 1 == emps[0].getSalary(); + + assert "John Connor".equals(emps[1].getFio()); + assert 2 == emps[1].getSalary(); + + Employee kyle = new Employee(); + + kyle.setFio("Kyle Reese"); + kyle.setSalary(3); + + return new Employee[] { kyle }; + } + + /** */ + public Collection testDepartments(Collection deps) { + if (deps == null) + return null; + + assert 2 == deps.size(); + + Iterator<Department> iter = deps.iterator(); + + assert "HR".equals(iter.next().getName()); + assert "IT".equals(iter.next().getName()); + + Collection<Department> res = new ArrayList<>(); + + Department d = new Department(); + + d.setName("Executive"); + + res.add(d); + + return res; + } + + /** */ + public Map testMap(Map map) { + if (map == null) + return null; + + assert map.containsKey(new Key(1)); + assert map.containsKey(new Key(2)); + + assert "value1".equals(((Value)map.get(new Key(1))).getVal()); + assert "value2".equals(((Value)map.get(new Key(2))).getVal()); + + Map m = new HashMap(); + + m.put(new Key(3), new Value("value3")); + + return m; + } + + /** */ + public Account[] testAccounts() { + return new Account[] { + new Account("123", 42), + new Account("321", 0) + }; + } + + /** */ + public User[] testUsers() { + return new User[] { + new User(1, ACL.ALLOW, new Role("admin")), + new User(2, ACL.DENY, new Role("user")) + }; + } + + /** */ + public void testDateArray(Timestamp[] dates) { + assert dates != null; + assert 2 == dates.length; + assert new Timestamp(new Date(82, Calendar.APRIL, 1, 0, 0, 0).getTime()).equals(dates[0]); + assert new Timestamp(new Date(91, Calendar.OCTOBER, 1, 0, 0, 0).getTime()).equals(dates[1]); + } + + /** */ + public Timestamp testDate(Timestamp date) { + if (date == null) + return null; + + assert new Timestamp(new Date(82, Calendar.APRIL, 1, 0, 0, 0).getTime()).equals(date); + + return new Timestamp(new Date(91, Calendar.OCTOBER, 1, 0, 0, 0).getTime()); + } + + /** */ + public void testUTCDateFromCache() { + IgniteCache<Integer, Timestamp> cache = ignite.cache("net-dates"); + + cache.put(3, new Timestamp(new Date(82, Calendar.APRIL, 1, 0, 0, 0).getTime())); + cache.put(4, new Timestamp(new Date(91, Calendar.OCTOBER, 1, 0, 0, 0).getTime())); + + assert new Timestamp(new Date(82, Calendar.APRIL, 1, 0, 0, 0).getTime()).equals(cache.get(1)); + assert new Timestamp(new Date(91, Calendar.OCTOBER, 1, 0, 0, 0).getTime()).equals(cache.get(2)); + } + + /** */ + public void testLocalDateFromCache() { + IgniteCache<Integer, Timestamp> cache = ignite.cache("net-dates"); + + ZoneId msk = ZoneId.of("Europe/Moscow"); + + //This Date in Europe/Moscow have offset +4. + Timestamp ts1 = new Timestamp(ZonedDateTime.of(1982, 4, 1, 1, 0, 0, 0, msk).toInstant().toEpochMilli()); + //This Date in Europe/Moscow have offset +3. + Timestamp ts2 = new Timestamp(ZonedDateTime.of(1982, 3, 31, 22, 0, 0, 0, msk).toInstant().toEpochMilli()); + + assert ts1.equals(cache.get(5)); + assert ts2.equals(cache.get(6)); + + cache.put(7, ts1); + cache.put(8, ts2); + } + + /** */ + public void sleep(long delayMs) { + try { + U.sleep(delayMs); + } + catch (Exception e) { + throw new IgniteException(e); + } + } } } diff --git a/modules/core/src/test/java/org/apache/ignite/platform/model/ACL.java b/modules/core/src/test/java/org/apache/ignite/platform/model/ACL.java new file mode 100644 index 0000000000000..6f75846186ba2 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/platform/model/ACL.java @@ -0,0 +1,23 @@ +/* + * 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.platform.model; + +/** Test enum. */ +public enum ACL { + ALLOW, DENY +} diff --git a/modules/core/src/test/java/org/apache/ignite/platform/model/Account.java b/modules/core/src/test/java/org/apache/ignite/platform/model/Account.java new file mode 100644 index 0000000000000..d192ce77035e8 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/platform/model/Account.java @@ -0,0 +1,71 @@ +/* + * 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.platform.model; + +import java.util.Objects; + +/** */ +public class Account { + /** */ + private String id; + + /** */ + private int amount; + + /** */ + public Account() { + } + + public Account(String id, int amount) { + this.id = id; + this.amount = amount; + } + + /** */ + public String getId() { + return id; + } + + /** */ + public void setId(String id) { + this.id = id; + } + + /** */ + public int getAmount() { + return amount; + } + + /** */ + public void setAmount(int amount) { + this.amount = amount; + } + + @Override public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Account account = (Account)o; + return Objects.equals(id, account.id); + } + + @Override public int hashCode() { + return Objects.hash(id); + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/platform/model/Address.java b/modules/core/src/test/java/org/apache/ignite/platform/model/Address.java new file mode 100644 index 0000000000000..42442cfcebd5d --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/platform/model/Address.java @@ -0,0 +1,47 @@ +/* + * 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.platform.model; + +/** Test value object. */ +public class Address { + /** */ + private String zip; + + /** */ + private String addr; + + /** */ + public String getZip() { + return zip; + } + + /** */ + public void setZip(String zip) { + this.zip = zip; + } + + /** */ + public String getAddr() { + return addr; + } + + /** */ + public void setAddr(String addr) { + this.addr = addr; + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/platform/model/Department.java b/modules/core/src/test/java/org/apache/ignite/platform/model/Department.java new file mode 100644 index 0000000000000..b1f74f3731e0a --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/platform/model/Department.java @@ -0,0 +1,34 @@ +/* + * 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.platform.model; + +/** Test value object. */ +public class Department { + /** */ + private String name; + + /** */ + public String getName() { + return name; + } + + /** */ + public void setName(String name) { + this.name = name; + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/platform/model/Employee.java b/modules/core/src/test/java/org/apache/ignite/platform/model/Employee.java new file mode 100644 index 0000000000000..6c63c29d684b1 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/platform/model/Employee.java @@ -0,0 +1,47 @@ +/* + * 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.platform.model; + +/** Test value object. */ +public class Employee { + /** */ + private String fio; + + /** */ + private long salary; + + /** */ + public String getFio() { + return fio; + } + + /** */ + public void setFio(String fio) { + this.fio = fio; + } + + /** */ + public long getSalary() { + return salary; + } + + /** */ + public void setSalary(long salary) { + this.salary = salary; + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/platform/model/Key.java b/modules/core/src/test/java/org/apache/ignite/platform/model/Key.java new file mode 100644 index 0000000000000..218d9c0b1e1e8 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/platform/model/Key.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.platform.model; + +import java.util.Objects; + +/** Test key object. */ +public class Key { + /** */ + private long id; + + /** */ + public Key(long id) { + this.id = id; + } + + /** */ + public long getId() { + return id; + } + + /** {@inheritDoc} */ + @Override public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Key key = (Key)o; + return id == key.id; + } + + /** {@inheritDoc} */ + @Override public int hashCode() { + return Objects.hash(id); + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/platform/model/Role.java b/modules/core/src/test/java/org/apache/ignite/platform/model/Role.java new file mode 100644 index 0000000000000..f47eddb2b6fa6 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/platform/model/Role.java @@ -0,0 +1,39 @@ +/* + * 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.platform.model; + +/** Test value object. */ +public class Role { + /** */ + String name; + + /** */ + public Role(String name) { + this.name = name; + } + + /** */ + public String getName() { + return name; + } + + /** */ + public void setName(String name) { + this.name = name; + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/platform/model/User.java b/modules/core/src/test/java/org/apache/ignite/platform/model/User.java new file mode 100644 index 0000000000000..88849d16d658c --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/platform/model/User.java @@ -0,0 +1,67 @@ +/* + * 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.platform.model; + +/** Test value object. */ +public class User { + /** */ + private int id; + + /** */ + private ACL acl; + + /** */ + private Role role; + + /** */ + public User(int id, ACL acl, Role role) { + this.id = id; + this.acl = acl; + this.role = role; + } + + /** */ + public int getId() { + return id; + } + + /** */ + public void setId(int id) { + this.id = id; + } + + /** */ + public ACL getAcl() { + return acl; + } + + /** */ + public void setAcl(ACL acl) { + this.acl = acl; + } + + /** */ + public Role getRole() { + return role; + } + + /** */ + public void setRole(Role role) { + this.role = role; + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/platform/model/Value.java b/modules/core/src/test/java/org/apache/ignite/platform/model/Value.java new file mode 100644 index 0000000000000..7143bff97cc48 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/platform/model/Value.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.platform.model; + +import java.util.Objects; + +/** Test value object. */ +public class Value { + /** */ + private String val; + + /** */ + public Value(String val) { + this.val = val; + } + + /** */ + public String getVal() { + return val; + } + + /** {@inheritDoc} */ + @Override public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Value value = (Value)o; + return Objects.equals(val, value.val); + } + + /** {@inheritDoc} */ + @Override public int hashCode() { + return Objects.hash(val); + } +} diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.DotNetCore/Apache.Ignite.Core.Tests.DotNetCore.csproj b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.DotNetCore/Apache.Ignite.Core.Tests.DotNetCore.csproj index e745e90d924be..10dfa37ccfa31 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.DotNetCore/Apache.Ignite.Core.Tests.DotNetCore.csproj +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.DotNetCore/Apache.Ignite.Core.Tests.DotNetCore.csproj @@ -343,11 +343,15 @@ <Compile Include="..\Apache.Ignite.Core.Tests\Plugin\TestIgnitePluginException.cs" Link="Plugin\TestIgnitePluginException.cs" /> <Compile Include="..\Apache.Ignite.Core.Tests\Plugin\TestIgnitePluginProvider.cs" Link="Plugin\TestIgnitePluginProvider.cs" /> <Compile Include="..\Apache.Ignite.Core.Tests\Query\BinarizablePerson.cs" Link="Cache\Query\BinarizablePerson.cs" /> + <Compile Include="..\Apache.Ignite.Core.Tests\Services\Model.cs" Link="Services\Model.cs" /> + <Compile Include="..\Apache.Ignite.Core.Tests\Services\IJavaService.cs" Link="Services\IJavaService.cs" /> + <Compile Include="..\Apache.Ignite.Core.Tests\Services\JavaServiceDynamicProxy.cs" Link="Services\JavaServiceDynamicProxy.cs" /> <Compile Include="..\Apache.Ignite.Core.Tests\Services\CallPlatformServiceTest.cs" Link="Services\CallPlatformServiceTest.cs" /> <Compile Include="..\Apache.Ignite.Core.Tests\Services\ServiceProxyTest.cs" Link="Services\ServiceProxyTest.cs" /> <Compile Include="..\Apache.Ignite.Core.Tests\Services\ServicesAsyncWrapper.cs" Link="Services\ServicesAsyncWrapper.cs" /> <Compile Include="..\Apache.Ignite.Core.Tests\Services\ServicesTest.cs" Link="Services\ServicesTest.cs" /> <Compile Include="..\Apache.Ignite.Core.Tests\Services\ServicesTestAsync.cs" Link="Services\ServicesTestAsync.cs" /> + <Compile Include="..\Apache.Ignite.Core.Tests\Services\ServiceTypeAutoResolveTest.cs" Link="Services\ServiceTypeAutoResolveTest.cs" /> <Compile Include="..\Apache.Ignite.Core.Tests\TaskExtensions.cs" Link="Common\TaskExtensions.cs" /> <Compile Include="..\Apache.Ignite.Core.Tests\TestUtils.Common.cs" Link="Common\TestUtils.Common.cs" /> <Compile Include="..\Apache.Ignite.Core.Tests\EnvVar.cs" Link="Common\EnvVar.cs" /> diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj index 9779ddaddd028..4fccb7503174d 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj @@ -262,6 +262,9 @@ <Compile Include="Plugin\TestIgnitePluginConfiguration.cs" /> <Compile Include="Plugin\TestIgnitePluginException.cs" /> <Compile Include="Plugin\TestIgnitePluginProvider.cs" /> + <Compile Include="Services\Model.cs" /> + <Compile Include="Services\IJavaService.cs" /> + <Compile Include="Services\JavaServiceDynamicProxy.cs" /> <Compile Include="Ssl\SslConfigurationTest.cs" /> <Compile Include="TaskExtensions.cs" /> <Compile Include="TestAppConfig.cs" /> @@ -374,6 +377,7 @@ <Compile Include="Services\ServicesTestAsync.cs" /> <Compile Include="Services\ServiceProxyTest.cs" /> <Compile Include="Services\ServicesAsyncWrapper.cs" /> + <Compile Include="Services\ServiceTypeAutoResolveTest.cs" /> <Compile Include="TestRunner.cs" /> <Compile Include="Unmanaged\JniThreadDetachTest.cs" /> <Compile Include="Unmanaged\UnmanagedThreadTest.cs" /> @@ -587,4 +591,4 @@ <Target Name="AfterBuild"> </Target> --> -</Project> \ No newline at end of file +</Project> diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryDateTimeTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryDateTimeTest.cs index e971eeac04b3c..9eae89307d24e 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryDateTimeTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryDateTimeTest.cs @@ -21,6 +21,7 @@ namespace Apache.Ignite.Core.Tests.Binary using System.Linq; using Apache.Ignite.Core.Binary; using Apache.Ignite.Core.Cache.Configuration; + using Apache.Ignite.Core.Impl.Binary; using NUnit.Framework; /// <summary> @@ -28,6 +29,16 @@ namespace Apache.Ignite.Core.Tests.Binary /// </summary> public class BinaryDateTimeTest { + /** */ + internal const String FromErrMsg = "FromJavaTicks Error!"; + + /** */ + internal const String ToErrMsg = "ToJavaTicks Error!"; + + /** */ + private const String NotUtcDate = + "DateTime is not UTC. Only UTC DateTime can be used for interop with other platforms."; + /// <summary> /// Sets up the test fixture. /// </summary> @@ -80,7 +91,7 @@ public void TestSerializerForceTimestamp() .Select(x => x.Serializer) .OfType<BinaryReflectiveSerializer>() .Single(); - + Assert.IsTrue(ser.ForceTimestamp); AssertTimestampField<DateTimeObj2>((o, d) => o.Value = d, o => o.Value, "Value"); @@ -110,6 +121,152 @@ public void TestClassAttributes() AssertTimestampField<DateTimeClassAttribute2>((o, d) => o.Value = d, o => o.Value, "Value"); } + /// <summary> + /// Tests custom timestamp converter that modifies the values by adding one year on write and read. + /// This test verifies that actual converted values are used by Ignite. + /// </summary> + [Test] + public void TestAddYearTimestampConverter() + { + var cfg = new IgniteConfiguration(TestUtils.GetTestConfiguration()) + { + AutoGenerateIgniteInstanceName = true, + BinaryConfiguration = new BinaryConfiguration + { + ForceTimestamp = true, + TimestampConverter = new AddYearTimestampConverter() + } + }; + + var ignite = Ignition.Start(cfg); + + var dt = DateTime.UtcNow; + var expected = dt.AddYears(2); + + // Key & value. + var cache = ignite.GetOrCreateCache<DateTime, DateTime>(TestUtils.TestName); + cache[dt] = dt; + + var resEntry = cache.Single(); + + Assert.AreEqual(expected, resEntry.Key); + Assert.AreEqual(expected, resEntry.Value); + + // Key & value array. + + // Object field. + var cache2 = ignite.GetOrCreateCache<DateTimePropertyAttribute, DateTimePropertyAttribute>( + TestUtils.TestName); + + cache2.RemoveAll(); + + var obj = new DateTimePropertyAttribute {Value = dt}; + cache2[obj] = obj; + + var resEntry2 = cache2.Single(); + Assert.AreEqual(expected, resEntry2.Key.Value); + Assert.AreEqual(expected, resEntry2.Value.Value); + + // Object array field. + var cache3 = ignite.GetOrCreateCache<DateTimeArr, DateTimeArr>(TestUtils.TestName); + cache3.RemoveAll(); + + var obj2 = new DateTimeArr {Value = new[]{dt}}; + cache3[obj2] = obj2; + cache3[obj2] = obj2; + + var resEntry3 = cache3.Single(); + Assert.AreEqual(expected, resEntry3.Key.Value.Single()); + Assert.AreEqual(expected, resEntry3.Value.Value.Single()); + } + + /// <summary> + /// Tests custom timestamp converter. + /// </summary> + [Test] + public void TestCustomTimestampConverter() + { + var cfg = new IgniteConfiguration(TestUtils.GetTestConfiguration(name: "ignite-1")) + { + BinaryConfiguration = new BinaryConfiguration + { + ForceTimestamp = true, + TimestampConverter = new TimestampConverter() + } + }; + + var ignite = Ignition.Start(cfg); + var binary = ignite.GetBinary(); + + // Check config. + Assert.NotNull(ignite.GetConfiguration().BinaryConfiguration.TimestampConverter); + + AssertTimestampField<DateTimeObj2>((o, d) => o.Value = d, o => o.Value, "Value", ignite); + + var dt1 = new DateTime(1997, 8, 29, 0, 0, 0, DateTimeKind.Utc); + + var ex = Assert.Throws<BinaryObjectException>(() => binary.ToBinary<DateTime>(dt1)); + Assert.AreEqual(ToErrMsg, ex.Message); + + ex = Assert.Throws<BinaryObjectException>(() => binary.ToBinary<DateTime?[]>(new DateTime?[] {dt1})); + Assert.AreEqual(ToErrMsg, ex.Message); + + var dt2 = new DateTime(1997, 8, 4, 0, 0, 0, DateTimeKind.Utc); + + ex = Assert.Throws<BinaryObjectException>(() => binary.ToBinary<DateTime>(dt2)); + Assert.AreEqual(FromErrMsg, ex.Message); + + ex = Assert.Throws<BinaryObjectException>(() => binary.ToBinary<DateTime?[]>(new DateTime?[] {dt2})); + Assert.AreEqual(FromErrMsg, ex.Message); + + var datesCache = ignite.CreateCache<DateTime, DateTime>("dates"); + + var check = new Action<DateTime, DateTime, String>((date1, date2, errMsg) => + { + ex = Assert.Throws<BinaryObjectException>(() => datesCache.Put(date1, date2), "Timestamp fields should throw an error on non-UTC values"); + + Assert.AreEqual(errMsg, ex.Message); + }); + + check.Invoke(DateTime.Now, DateTime.UtcNow, NotUtcDate); + check.Invoke(DateTime.UtcNow, DateTime.Now, NotUtcDate); + check.Invoke(dt1, DateTime.UtcNow, ToErrMsg); + check.Invoke(DateTime.UtcNow, dt1, ToErrMsg); + + var now = DateTime.UtcNow; + + datesCache.Put(now, dt2); + ex = Assert.Throws<BinaryObjectException>(() => datesCache.Get(now), "Timestamp fields should throw an error on non-UTC values"); + Assert.AreEqual(FromErrMsg, ex.Message); + + datesCache.Put(now, now); + Assert.AreEqual(now, datesCache.Get(now)); + + var datesObjCache = ignite.CreateCache<DateTimeObj, DateTimeObj>("datesObj"); + + check = (date1, date2, errMsg) => + { + ex = Assert.Throws<BinaryObjectException>(() => datesObjCache.Put(new DateTimeObj {Value = date1}, new DateTimeObj {Value = date2}), + "Timestamp fields should throw an error on non-UTC values"); + + Assert.AreEqual(errMsg, ex.Message); + }; + + check.Invoke(DateTime.Now, DateTime.UtcNow, NotUtcDate); + check.Invoke(DateTime.UtcNow, DateTime.Now, NotUtcDate); + check.Invoke(dt1, DateTime.UtcNow, ToErrMsg); + check.Invoke(DateTime.UtcNow, dt1, ToErrMsg); + + var nowObj = new DateTimeObj {Value = now}; + + datesObjCache.Put(nowObj, new DateTimeObj {Value = dt2}); + ex = Assert.Throws<BinaryObjectException>(() => datesObjCache.Get(nowObj), "Timestamp fields should throw an error on non-UTC values"); + Assert.AreEqual(FromErrMsg, ex.Message); + + datesObjCache.Put(nowObj, nowObj); + Assert.AreEqual(nowObj.Value, datesObjCache.Get(nowObj).Value); + } + /// <summary> /// Asserts that specified field is serialized as DateTime object. /// </summary> @@ -136,10 +293,10 @@ private static void AssertDateTimeField<T>(Action<T, DateTime> setValue, /// Asserts that specified field is serialized as Timestamp. /// </summary> private static void AssertTimestampField<T>(Action<T, DateTime> setValue, - Func<T, DateTime> getValue, string fieldName) where T : new() + Func<T, DateTime> getValue, string fieldName, IIgnite ignite = null) where T : new() { // Non-UTC DateTime throws. - var binary = Ignition.GetIgnite().GetBinary(); + var binary = ignite != null ? ignite.GetBinary() : Ignition.GetIgnite().GetBinary(); var obj = new T(); @@ -148,8 +305,7 @@ private static void AssertTimestampField<T>(Action<T, DateTime> setValue, var ex = Assert.Throws<BinaryObjectException>(() => binary.ToBinary<IBinaryObject>(obj), "Timestamp fields should throw an error on non-UTC values"); - Assert.AreEqual("DateTime is not UTC. Only UTC DateTime can be used for interop with other platforms.", - ex.Message); + Assert.AreEqual(NotUtcDate, ex.Message); // UTC DateTime works. setValue(obj, DateTime.UtcNow); @@ -171,6 +327,11 @@ private class DateTimeObj2 public DateTime Value { get; set; } } + private class DateTimeArr + { + public DateTime[] Value { get; set; } + } + private class DateTimePropertyAttribute { [Timestamp] @@ -200,5 +361,47 @@ private class DateTimeClassAttribute2 { public DateTime Value; } + + private class TimestampConverter : ITimestampConverter + { + /** <inheritdoc /> */ + public void ToJavaTicks(DateTime date, out long high, out int low) + { + if (date.Year == 1997 && date.Month == 8 && date.Day == 29) + throw new BinaryObjectException(BinaryDateTimeTest.ToErrMsg); + + BinaryUtils.ToJavaDate(date, out high, out low); + } + + /** <inheritdoc /> */ + public DateTime FromJavaTicks(long high, int low) + { + var date = new DateTime(BinaryUtils.JavaDateTicks + high * TimeSpan.TicksPerMillisecond + low / 100, + DateTimeKind.Utc); + + if (date.Year == 1997 && date.Month == 8 && date.Day == 4) + throw new BinaryObjectException(BinaryDateTimeTest.FromErrMsg); + + return date; + } + } + + private class AddYearTimestampConverter : ITimestampConverter + { + /** <inheritdoc /> */ + public void ToJavaTicks(DateTime date, out long high, out int low) + { + BinaryUtils.ToJavaDate(date.AddYears(1), out high, out low); + } + + /** <inheritdoc /> */ + public DateTime FromJavaTicks(long high, int low) + { + var date = new DateTime(BinaryUtils.JavaDateTicks + high * TimeSpan.TicksPerMillisecond + low / 100, + DateTimeKind.Utc); + + return date.AddYears(1); + } + } } } diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTestNoMeta.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTestNoMeta.cs index 98c69f607d20a..e152c6b94c5c3 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTestNoMeta.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTestNoMeta.cs @@ -104,7 +104,7 @@ public void PutBinaryTypes(ICollection<BinaryType> types) } /** <inheritdoc /> */ - public bool RegisterType(int id, string typeName) + public bool RegisterType(int id, string typeName, bool registerSameJavaType) { return false; } diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs new file mode 100644 index 0000000000000..11a13b88ad86a --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs @@ -0,0 +1,207 @@ +/* + * 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. + */ + +namespace Apache.Ignite.Core.Tests.Services +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using Apache.Ignite.Core.Binary; + using org.apache.ignite.platform.model; + + /// <summary> + /// Java service proxy interface. + /// </summary> + [SuppressMessage("ReSharper", "InconsistentNaming")] + public interface IJavaService + { + /** */ + bool isCancelled(); + + /** */ + bool isInitialized(); + + /** */ + bool isExecuted(); + + /** */ + byte test(byte x); + + /** */ + short test(short x); + + /** */ + int test(int x); + + /** */ + long test(long x); + + /** */ + float test(float x); + + /** */ + double test(double x); + + /** */ + char test(char x); + + /** */ + string test(string x); + + /** */ + bool test(bool x); + + /** */ + DateTime test(DateTime x); + + /** */ + Guid test(Guid x); + + /** */ + byte? testWrapper(byte? x); + + /** */ + short? testWrapper(short? x); + + /** */ + int? testWrapper(int? x); + + /** */ + long? testWrapper(long? x); + + /** */ + float? testWrapper(float? x); + + /** */ + double? testWrapper(double? x); + + /** */ + char? testWrapper(char? x); + + /** */ + bool? testWrapper(bool? x); + + /** */ + byte[] testArray(byte[] x); + + /** */ + short[] testArray(short[] x); + + /** */ + int[] testArray(int[] x); + + /** */ + long[] testArray(long[] x); + + /** */ + float[] testArray(float[] x); + + /** */ + double[] testArray(double[] x); + + /** */ + char[] testArray(char[] x); + + /** */ + string[] testArray(string[] x); + + /** */ + bool[] testArray(bool[] x); + + /** */ + DateTime?[] testArray(DateTime?[] x); + + /** */ + Guid?[] testArray(Guid?[] x); + + /** */ + int test(int x, string y); + + /** */ + int test(string x, int y); + + /** */ + int? testNull(int? x); + + /** */ + DateTime? testNullTimestamp(DateTime? x); + + /** */ + Guid? testNullUUID(Guid? x); + + /** */ + int testParams(params object[] args); + + /** */ + ServicesTest.PlatformComputeBinarizable testBinarizable(ServicesTest.PlatformComputeBinarizable x); + + /** */ + object[] testBinarizableArrayOfObjects(object[] x); + + /** */ + IBinaryObject[] testBinaryObjectArray(IBinaryObject[] x); + + /** */ + ServicesTest.PlatformComputeBinarizable[] testBinarizableArray(ServicesTest.PlatformComputeBinarizable[] x); + + /** */ + ICollection testBinarizableCollection(ICollection x); + + /** */ + IBinaryObject testBinaryObject(IBinaryObject x); + + /** */ + Address testAddress(Address addr); + + /** */ + int testOverload(int count, Employee[] emps); + + /** */ + int testOverload(int first, int second); + + /** */ + Employee[] testEmployees(Employee[] emps); + + /** */ + Account[] testAccounts(); + + /** */ + User[] testUsers(); + + /** */ + ICollection testDepartments(ICollection deps); + + /** */ + IDictionary testMap(IDictionary<Key, Value> dict); + + /** */ + void testDateArray(DateTime?[] dates); + + /** */ + DateTime testDate(DateTime date); + + /** */ + void testUTCDateFromCache(); + + /** */ + void testLocalDateFromCache(); + + /** */ + void sleep(long delayMs); + } +} diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/JavaServiceDynamicProxy.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/JavaServiceDynamicProxy.cs new file mode 100644 index 0000000000000..3f34366b6e00a --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/JavaServiceDynamicProxy.cs @@ -0,0 +1,386 @@ +/* + * 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. + */ + +namespace Apache.Ignite.Core.Tests.Services +{ + using System; + using System.Collections; + using System.Collections.Generic; + using Apache.Ignite.Core.Binary; + using org.apache.ignite.platform.model; + + /// <summary> + /// Explicit service proxy over dynamic variable. + /// </summary> + public class JavaServiceDynamicProxy : IJavaService + { + /** */ + private readonly dynamic _svc; + + /** */ + public JavaServiceDynamicProxy(dynamic svc) + { + _svc = svc; + } + + /** <inheritDoc /> */ + public bool isCancelled() + { + return _svc.isCancelled(); + } + + /** <inheritDoc /> */ + public bool isInitialized() + { + return _svc.isInitialized(); + } + + /** <inheritDoc /> */ + public bool isExecuted() + { + return _svc.isExecuted(); + } + + /** <inheritDoc /> */ + public byte test(byte x) + { + return _svc.test(x); + } + + /** <inheritDoc /> */ + public short test(short x) + { + return _svc.test(x); + } + + /** <inheritDoc /> */ + public int test(int x) + { + return _svc.test(x); + } + + /** <inheritDoc /> */ + public long test(long x) + { + return _svc.test(x); + } + + /** <inheritDoc /> */ + public float test(float x) + { + return _svc.test(x); + } + + /** <inheritDoc /> */ + public double test(double x) + { + return _svc.test(x); + } + + /** <inheritDoc /> */ + public char test(char x) + { + return _svc.test(x); + } + + /** <inheritDoc /> */ + public string test(string x) + { + return _svc.test(x); + } + + /** <inheritDoc /> */ + public bool test(bool x) + { + return _svc.test(x); + } + + /** <inheritDoc /> */ + public DateTime test(DateTime x) + { + return _svc.test(x); + } + + /** <inheritDoc /> */ + public Guid test(Guid x) + { + return _svc.test(x); + } + + /** <inheritDoc /> */ + public byte? testWrapper(byte? x) + { + return _svc.testWrapper(x); + } + + /** <inheritDoc /> */ + public short? testWrapper(short? x) + { + return _svc.testWrapper(x); + } + + /** <inheritDoc /> */ + public int? testWrapper(int? x) + { + return _svc.testWrapper(x); + } + + /** <inheritDoc /> */ + public long? testWrapper(long? x) + { + return _svc.testWrapper(x); + } + + /** <inheritDoc /> */ + public float? testWrapper(float? x) + { + return _svc.testWrapper(x); + } + + /** <inheritDoc /> */ + public double? testWrapper(double? x) + { + return _svc.testWrapper(x); + } + + /** <inheritDoc /> */ + public char? testWrapper(char? x) + { + return _svc.testWrapper(x); + } + + /** <inheritDoc /> */ + public bool? testWrapper(bool? x) + { + return _svc.testWrapper(x); + } + + /** <inheritDoc /> */ + public byte[] testArray(byte[] x) + { + return _svc.testArray(x); + } + + /** <inheritDoc /> */ + public short[] testArray(short[] x) + { + return _svc.testArray(x); + } + + /** <inheritDoc /> */ + public int[] testArray(int[] x) + { + return _svc.testArray(x); + } + + /** <inheritDoc /> */ + public long[] testArray(long[] x) + { + return _svc.testArray(x); + } + + /** <inheritDoc /> */ + public float[] testArray(float[] x) + { + return _svc.testArray(x); + } + + /** <inheritDoc /> */ + public double[] testArray(double[] x) + { + return _svc.testArray(x); + } + + /** <inheritDoc /> */ + public char[] testArray(char[] x) + { + return _svc.testArray(x); + } + + /** <inheritDoc /> */ + public string[] testArray(string[] x) + { + return _svc.testArray(x); + } + + /** <inheritDoc /> */ + public bool[] testArray(bool[] x) + { + return _svc.testArray(x); + } + + /** <inheritDoc /> */ + public DateTime?[] testArray(DateTime?[] x) + { + return _svc.testArray(x); + } + + /** <inheritDoc /> */ + public Guid?[] testArray(Guid?[] x) + { + return _svc.testArray(x); + } + + /** <inheritDoc /> */ + public int test(int x, string y) + { + return _svc.test(x, y); + } + + /** <inheritDoc /> */ + public int test(string x, int y) + { + return _svc.test(x, y); + } + + /** <inheritDoc /> */ + public int? testNull(int? x) + { + return _svc.testNull(x); + } + + /** <inheritDoc /> */ + public DateTime? testNullTimestamp(DateTime? x) + { + return _svc.testNullTimestamp(x); + } + + /** <inheritDoc /> */ + public Guid? testNullUUID(Guid? x) + { + return _svc.testNullUUID(x); + } + + /** <inheritDoc /> */ + public int testParams(params object[] args) + { + return _svc.testParams(args); + } + + /** <inheritDoc /> */ + public ServicesTest.PlatformComputeBinarizable testBinarizable(ServicesTest.PlatformComputeBinarizable x) + { + return _svc.testBinarizable(x); + } + + /** <inheritDoc /> */ + public object[] testBinarizableArrayOfObjects(object[] x) + { + return _svc.testBinarizableArrayOfObjects(x); + } + + /** <inheritDoc /> */ + public IBinaryObject[] testBinaryObjectArray(IBinaryObject[] x) + { + return _svc.testBinaryObjectArray(x); + } + + /** <inheritDoc /> */ + public ServicesTest.PlatformComputeBinarizable[] testBinarizableArray(ServicesTest.PlatformComputeBinarizable[] x) + { + return _svc.testBinarizableArray(x); + } + + /** <inheritDoc /> */ + public ICollection testBinarizableCollection(ICollection x) + { + return _svc.testBinarizableCollection(x); + } + + /** <inheritDoc /> */ + public IBinaryObject testBinaryObject(IBinaryObject x) + { + return _svc.testBinaryObject(x); + } + + /** <inheritDoc /> */ + public Address testAddress(Address addr) + { + return _svc.testAddress(addr); + } + + /** <inheritDoc /> */ + public int testOverload(int count, Employee[] emps) + { + return _svc.testOverload(count, emps); + } + + /** <inheritDoc /> */ + public int testOverload(int first, int second) + { + return _svc.testOverload(first, second); + } + + /** <inheritDoc /> */ + public Employee[] testEmployees(Employee[] emps) + { + return _svc.testEmployees(emps); + } + + public Account[] testAccounts() + { + return _svc.testAccounts(); + } + + public User[] testUsers() + { + return _svc.testUsers(); + } + + /** <inheritDoc /> */ + public ICollection testDepartments(ICollection deps) + { + return _svc.testDepartments(deps); + } + + /** <inheritDoc /> */ + public IDictionary testMap(IDictionary<Key, Value> dict) + { + return _svc.testMap(dict); + } + + /** <inheritDoc /> */ + public void testDateArray(DateTime?[] dates) + { + _svc.testDateArray(dates); + } + + /** <inheritDoc /> */ + public DateTime testDate(DateTime date) + { + return _svc.testDate(date); + } + + /** <inheritDoc /> */ + public void testUTCDateFromCache() + { + _svc.testDateFromCache(); + } + + /** <inheritDoc /> */ + public void testLocalDateFromCache() + { + _svc.testLocalDateFromCache(); + } + + /** <inheritDoc /> */ + public void sleep(long delayMs) + { + _svc.sleep(delayMs); + } + } +} diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/Model.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/Model.cs new file mode 100644 index 0000000000000..275dc7dace2ec --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/Model.cs @@ -0,0 +1,147 @@ +/* + * 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. + */ + +// ReSharper disable once CheckNamespace +namespace org.apache.ignite.platform.model +{ + using System; + + /// <summary> + /// A class is a clone of Java class Address with the same namespace. + /// </summary> + public class Address + { + /** */ + public string Zip { get; set; } + + /** */ + public string Addr { get; set; } + } + + /// <summary> + /// A class is a clone of Java class Department with the same namespace. + /// </summary> + public class Department + { + /** */ + public string Name { get; set; } + } + + /// <summary> + /// A class is a clone of Java class Employee with the same namespace. + /// </summary> + public class Employee + { + /** */ + public string Fio { get; set; } + + /** */ + public long Salary { get; set; } + } + + /// <summary> + /// A class is a clone of Java class Employee with the same namespace. + /// </summary> + public class Key + { + public long Id { get; set; } + + protected bool Equals(Key other) + { + return Id == other.Id; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((Key) obj); + } + + public override int GetHashCode() + { + // ReSharper disable once NonReadonlyMemberInGetHashCode + return Id.GetHashCode(); + } + } + + /// <summary> + /// A class is a clone of Java class Employee with the same namespace. + /// </summary> + public class Value + { + public string Val { get; set; } + } + + /// <summary> + /// A class is a clone of Java class Account with the same namespace. + /// </summary> + public class Account + { + public String Id { get; set; } + + public int Amount { get; set; } + + protected bool Equals(Account other) + { + return Id == other.Id; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((Account) obj); + } + + public override int GetHashCode() + { + // ReSharper disable once NonReadonlyMemberInGetHashCode + return Id.GetHashCode(); + } + } + + /// <summary> + /// A enum is a clone of Java class User with the same namespace. + /// </summary> + public enum ACL + { + Allow, Deny + } + + /// <summary> + /// A class is a clone of Java class Role with the same namespace. + /// </summary> + public class Role + { + public String Name { get; set; } + } + + /// <summary> + /// A class is a clone of Java class User with the same namespace. + /// </summary> + public class User + { + public int Id { get; set; } + + public ACL Acl { get; set; } + + public Role Role { get; set; } + } +} diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServiceProxyTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServiceProxyTest.cs index 53bfbbcc25f3e..82e099323721f 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServiceProxyTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServiceProxyTest.cs @@ -283,32 +283,41 @@ private object InvokeProxyMethod(MethodBase method, object[] args) // 1) Write to a stream inStream.WriteBool(SrvKeepBinary); // WriteProxyMethod does not do this, but Java does - ServiceProxySerializer.WriteProxyMethod(_marsh.StartMarshal(inStream), method.Name, - method, args, PlatformType.DotNet); + Marshaller.RegisterSameJavaType.Value = true; - inStream.SynchronizeOutput(); + try + { + ServiceProxySerializer.WriteProxyMethod(_marsh.StartMarshal(inStream), method.Name, + method, args, PlatformType.DotNet); - inStream.Seek(0, SeekOrigin.Begin); + inStream.SynchronizeOutput(); - // 2) call InvokeServiceMethod - string mthdName; - object[] mthdArgs; + inStream.Seek(0, SeekOrigin.Begin); - ServiceProxySerializer.ReadProxyMethod(inStream, _marsh, out mthdName, out mthdArgs); + // 2) call InvokeServiceMethod + string mthdName; + object[] mthdArgs; - var result = ServiceProxyInvoker.InvokeServiceMethod(_svc, mthdName, mthdArgs); + ServiceProxySerializer.ReadProxyMethod(inStream, _marsh, out mthdName, out mthdArgs); - ServiceProxySerializer.WriteInvocationResult(outStream, _marsh, result.Key, result.Value); + var result = ServiceProxyInvoker.InvokeServiceMethod(_svc, mthdName, mthdArgs); - var writer = _marsh.StartMarshal(outStream); - writer.WriteString("unused"); // fake Java exception details - writer.WriteString("unused"); // fake Java exception details + ServiceProxySerializer.WriteInvocationResult(outStream, _marsh, result.Key, result.Value); - outStream.SynchronizeOutput(); + var writer = _marsh.StartMarshal(outStream); + writer.WriteString("unused"); // fake Java exception details + writer.WriteString("unused"); // fake Java exception details - outStream.Seek(0, SeekOrigin.Begin); + outStream.SynchronizeOutput(); - return ServiceProxySerializer.ReadInvocationResult(outStream, _marsh, KeepBinary); + outStream.Seek(0, SeekOrigin.Begin); + + return ServiceProxySerializer.ReadInvocationResult(outStream, _marsh, KeepBinary); + } + finally + { + Marshaller.RegisterSameJavaType.Value = true; + } } } diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServiceTypeAutoResolveTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServiceTypeAutoResolveTest.cs new file mode 100644 index 0000000000000..5b78a63f3bc96 --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServiceTypeAutoResolveTest.cs @@ -0,0 +1,237 @@ +/* + * 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. + */ + +namespace Apache.Ignite.Core.Tests.Services +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using NUnit.Framework; + using org.apache.ignite.platform.model; + + /// <summary> + /// Tests checks ability to execute service method without explicit registration of parameter type. + /// </summary> + public class ServicesTypeAutoResolveTest + { + /** */ + private IIgnite _grid1; + + [TestFixtureTearDown] + public void FixtureTearDown() + { + StopGrids(); + } + + /// <summary> + /// Executes before each test. + /// </summary> + [SetUp] + public void SetUp() + { + StartGrids(); + } + + /// <summary> + /// Executes after each test. + /// </summary> + [TearDown] + public void TearDown() + { + try + { + _grid1.GetServices(); + + TestUtils.AssertHandleRegistryIsEmpty(1000, _grid1); + } + catch (Exception) + { + // Restart grids to cleanup + StopGrids(); + + throw; + } + finally + { + if (TestContext.CurrentContext.Test.Name.StartsWith("TestEventTypes")) + StopGrids(); // clean events for other tests + } + } + + /// <summary> + /// Tests Java service invocation with dynamic proxy. + /// Types should be resolved implicitly. + /// </summary> + [Test] + public void TestCallJavaServiceDynamicProxy() + { + // Deploy Java service + var javaSvcName = TestUtils.DeployJavaService(_grid1); + var svc = _grid1.GetServices().GetDynamicServiceProxy(javaSvcName, true); + + doTestService(new JavaServiceDynamicProxy(svc)); + } + + /// <summary> + /// Tests Java service invocation. + /// Types should be resolved implicitly. + /// </summary> + [Test] + public void TestCallJavaService() + { + // Deploy Java service + var javaSvcName = TestUtils.DeployJavaService(_grid1); + + var svc = _grid1.GetServices().GetServiceProxy<IJavaService>(javaSvcName, false); + + doTestService(svc); + + Assert.IsNull(svc.testDepartments(null)); + + var arr = new[] {"HR", "IT"}.Select(x => new Department() {Name = x}).ToArray(); + + ICollection deps = svc.testDepartments(arr); + + Assert.NotNull(deps); + Assert.AreEqual(1, deps.Count); + Assert.AreEqual("Executive", deps.OfType<Department>().Select(d => d.Name).ToArray()[0]); + + _grid1.GetServices().Cancel(javaSvcName); + } + + /// <summary> + /// Tests java service instance. + /// </summary> + private void doTestService(IJavaService svc) + { + Assert.IsNull(svc.testAddress(null)); + + Address addr = svc.testAddress(new Address {Zip = "000", Addr = "Moscow"}); + + Assert.AreEqual("127000", addr.Zip); + Assert.AreEqual("Moscow Akademika Koroleva 12", addr.Addr); + + Employee[] emps = new[] + { + new Employee {Fio = "Sarah Connor", Salary = 1}, + new Employee {Fio = "John Connor", Salary = 2} + }; + + Assert.AreEqual(42, svc.testOverload(2, emps)); + Assert.AreEqual(3, svc.testOverload(1, 2)); + Assert.AreEqual(5, svc.testOverload(3, 2)); + + Assert.IsNull(svc.testEmployees(null)); + + emps = svc.testEmployees(emps); + + Assert.NotNull(emps); + Assert.AreEqual(1, emps.Length); + + Assert.AreEqual("Kyle Reese", emps[0].Fio); + Assert.AreEqual(3, emps[0].Salary); + + Assert.IsNull(svc.testMap(null)); + + var map = new Dictionary<Key, Value>(); + + map.Add(new Key() {Id = 1}, new Value() {Val = "value1"}); + map.Add(new Key() {Id = 2}, new Value() {Val = "value2"}); + + var res = svc.testMap(map); + + Assert.NotNull(res); + Assert.AreEqual(1, res.Count); + Assert.AreEqual("value3", ((Value)res[new Key() {Id = 3}]).Val); + + var accs = svc.testAccounts(); + + Assert.NotNull(accs); + Assert.AreEqual(2, accs.Length); + Assert.AreEqual("123", accs[0].Id); + Assert.AreEqual("321", accs[1].Id); + Assert.AreEqual(42, accs[0].Amount); + Assert.AreEqual(0, accs[1].Amount); + + var users = svc.testUsers(); + + Assert.NotNull(users); + Assert.AreEqual(2, users.Length); + Assert.AreEqual(1, users[0].Id); + Assert.AreEqual(ACL.Allow, users[0].Acl); + Assert.AreEqual("admin", users[0].Role.Name); + Assert.AreEqual(2, users[1].Id); + Assert.AreEqual(ACL.Deny, users[1].Acl); + Assert.AreEqual("user", users[1].Role.Name); + } + + /// <summary> + /// Starts the grids. + /// </summary> + private void StartGrids() + { + if (_grid1 != null) + return; + + var path = Path.Combine("Config", "Compute", "compute-grid"); + _grid1 = Ignition.Start(GetConfiguration(path + "1.xml")); + } + + /// <summary> + /// Stops the grids. + /// </summary> + private void StopGrids() + { + _grid1 = null; + + Ignition.StopAll(true); + } + + /// <summary> + /// Gets the Ignite configuration. + /// </summary> + private IgniteConfiguration GetConfiguration(string springConfigUrl) + { + springConfigUrl = ReplaceFooterSetting(springConfigUrl); + + return new IgniteConfiguration(TestUtils.GetTestConfiguration()) + { + SpringConfigUrl = springConfigUrl + }; + } + + /// <summary> + /// Replaces the footer setting. + /// </summary> + internal static string ReplaceFooterSetting(string path) + { + var text = File.ReadAllText(path).Replace( + "property name=\"compactFooter\" value=\"true\"", + "property name=\"compactFooter\" value=\"false\""); + + path += "_fullFooter"; + + File.WriteAllText(path, text); + + Assert.IsTrue(File.Exists(path)); + + return path; + } + } +} diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs index aa8f8ea2c9df7..e978dc7275856 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs @@ -18,9 +18,7 @@ namespace Apache.Ignite.Core.Tests.Services { using System; - using System.Collections; using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Runtime.Serialization.Formatters.Binary; @@ -29,6 +27,7 @@ namespace Apache.Ignite.Core.Tests.Services using Apache.Ignite.Core.Cluster; using Apache.Ignite.Core.Common; using Apache.Ignite.Core.Impl; + using Apache.Ignite.Core.Impl.Binary; using Apache.Ignite.Core.Resource; using Apache.Ignite.Core.Services; using NUnit.Framework; @@ -959,6 +958,45 @@ public void TestCallJavaService() Assert.AreEqual(new[] {11, 12, 13}, binSvc.testBinaryObjectArray(binArr) .Select(x => x.GetField<int>("Field"))); + DateTime dt1 = new DateTime(1982, 4, 1, 0, 0, 0, 0, DateTimeKind.Utc); + DateTime dt2 = new DateTime(1991, 10, 1, 0, 0, 0, 0, DateTimeKind.Utc); + + Assert.AreEqual(dt2, svc.testDate(dt1)); + + svc.testDateArray(new DateTime?[] {dt1, dt2}); + + var cache = Grid1.GetOrCreateCache<int, DateTime>("net-dates"); + + cache.Put(1, dt1); + cache.Put(2, dt2); + + svc.testUTCDateFromCache(); + + Assert.AreEqual(dt1, cache.Get(3)); + Assert.AreEqual(dt2, cache.Get(4)); + +#if NETCOREAPP + //This Date in Europe/Moscow have offset +4. + DateTime dt3 = new DateTime(1982, 4, 1, 1, 0, 0, 0, DateTimeKind.Local); + //This Date in Europe/Moscow have offset +3. + DateTime dt4 = new DateTime(1982, 3, 31, 22, 0, 0, 0, DateTimeKind.Local); + + cache.Put(5, dt3); + cache.Put(6, dt4); + + Assert.AreEqual(dt3.ToUniversalTime(), cache.Get(5).ToUniversalTime()); + Assert.AreEqual(dt4.ToUniversalTime(), cache.Get(6).ToUniversalTime()); + + svc.testLocalDateFromCache(); + + Assert.AreEqual(dt3, cache.Get(7).ToLocalTime()); + Assert.AreEqual(dt4, cache.Get(8).ToLocalTime()); + + var now = DateTime.Now; + cache.Put(9, now); + Assert.AreEqual(now.ToUniversalTime(), cache.Get(9).ToUniversalTime()); +#endif + Services.Cancel(javaSvcName); } @@ -1042,6 +1080,21 @@ public void TestCallJavaServiceDynamicProxy() Assert.AreEqual(guid, svc.testNullUUID(guid)); Assert.IsNull(svc.testNullUUID(null)); Assert.AreEqual(guid, svc.testArray(new Guid?[] { guid })[0]); + + DateTime dt1 = new DateTime(1982, 4, 1, 0, 0, 0, 0, DateTimeKind.Utc); + DateTime dt2 = new DateTime(1991, 10, 1, 0, 0, 0, 0, DateTimeKind.Utc); + + Assert.AreEqual(dt2, svc.testDate(dt1)); + + var cache = Grid1.GetOrCreateCache<int, DateTime>("net-dates"); + + cache.Put(1, dt1); + cache.Put(2, dt2); + + svc.testUTCDateFromCache(); + + Assert.AreEqual(dt1, cache.Get(3)); + Assert.AreEqual(dt2, cache.Get(4)); } /// <summary> @@ -1139,7 +1192,11 @@ private IgniteConfiguration GetConfiguration(string springConfigUrl) typeof (PlatformComputeBinarizable), typeof (BinarizableObject)) { - NameMapper = BinaryBasicNameMapper.SimpleNameInstance + NameMapper = BinaryBasicNameMapper.SimpleNameInstance, + ForceTimestamp = true +#if NETCOREAPP + , TimestampConverter = new TimestampConverter() +#endif } }; } @@ -1505,148 +1562,6 @@ private class BinarizableObject public int Val { get; set; } } - /// <summary> - /// Java service proxy interface. - /// </summary> - [SuppressMessage("ReSharper", "InconsistentNaming")] - public interface IJavaService - { - /** */ - bool isCancelled(); - - /** */ - bool isInitialized(); - - /** */ - bool isExecuted(); - - /** */ - byte test(byte x); - - /** */ - short test(short x); - - /** */ - int test(int x); - - /** */ - long test(long x); - - /** */ - float test(float x); - - /** */ - double test(double x); - - /** */ - char test(char x); - - /** */ - string test(string x); - - /** */ - bool test(bool x); - - /** */ - DateTime test(DateTime x); - - /** */ - Guid test(Guid x); - - /** */ - byte? testWrapper(byte? x); - - /** */ - short? testWrapper(short? x); - - /** */ - int? testWrapper(int? x); - - /** */ - long? testWrapper(long? x); - - /** */ - float? testWrapper(float? x); - - /** */ - double? testWrapper(double? x); - - /** */ - char? testWrapper(char? x); - - /** */ - bool? testWrapper(bool? x); - - /** */ - byte[] testArray(byte[] x); - - /** */ - short[] testArray(short[] x); - - /** */ - int[] testArray(int[] x); - - /** */ - long[] testArray(long[] x); - - /** */ - float[] testArray(float[] x); - - /** */ - double[] testArray(double[] x); - - /** */ - char[] testArray(char[] x); - - /** */ - string[] testArray(string[] x); - - /** */ - bool[] testArray(bool[] x); - - /** */ - DateTime?[] testArray(DateTime?[] x); - - /** */ - Guid?[] testArray(Guid?[] x); - - /** */ - int test(int x, string y); - - /** */ - int test(string x, int y); - - /** */ - int? testNull(int? x); - - /** */ - DateTime? testNullTimestamp(DateTime? x); - - /** */ - Guid? testNullUUID(Guid? x); - - /** */ - int testParams(params object[] args); - - /** */ - PlatformComputeBinarizable testBinarizable(PlatformComputeBinarizable x); - - /** */ - object[] testBinarizableArrayOfObjects(object[] x); - - /** */ - IBinaryObject[] testBinaryObjectArray(IBinaryObject[] x); - - /** */ - PlatformComputeBinarizable[] testBinarizableArray(PlatformComputeBinarizable[] x); - - /** */ - ICollection testBinarizableCollection(ICollection x); - - /** */ - IBinaryObject testBinaryObject(IBinaryObject x); - } - /// <summary> /// Interop class. /// </summary> @@ -1664,5 +1579,28 @@ public class PlatformComputeBinarizable2 /** */ public int Field { get; set; } } + +#if NETCOREAPP + /// <summary> + /// Adds support of the local dates to the Ignite timestamp serialization. + /// </summary> + class TimestampConverter : ITimestampConverter + { + /** <inheritdoc /> */ + public void ToJavaTicks(DateTime date, out long high, out int low) + { + if (date.Kind == DateTimeKind.Local) + date = date.ToUniversalTime(); + + BinaryUtils.ToJavaDate(date, out high, out low); + } + + /** <inheritdoc /> */ + public DateTime FromJavaTicks(long high, int low) + { + return new DateTime(BinaryUtils.JavaDateTicks + high * TimeSpan.TicksPerMillisecond + low / 100, DateTimeKind.Utc); + } + } +#endif } } diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestUtils.Common.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestUtils.Common.cs index f8b14d29d078f..c2fb90da2d369 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestUtils.Common.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestUtils.Common.cs @@ -544,5 +544,22 @@ public static ICollection GetActiveNotificationListeners(this IIgniteClient clie var socket = GetPrivateField<ClientSocket>(failoverSocket, "_socket"); return GetPrivateField<ICollection>(socket, "_notificationListeners"); } + + /// <summary> + /// Deploys the Java service. + /// </summary> + public static string DeployJavaService(IIgnite ignite) + { + const string serviceName = "javaService"; + + ignite.GetCompute() + .ExecuteJavaTask<object>("org.apache.ignite.platform.PlatformDeployServiceTask", serviceName); + + var services = ignite.GetServices(); + + WaitForCondition(() => services.GetServiceDescriptors().Any(x => x.Name == serviceName), 1000); + + return serviceName; + } } } diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj index 96d17d7fff9a2..2a42a70d68f78 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj @@ -52,6 +52,7 @@ <ItemGroup> <Compile Include="Binary\BinaryBasicNameMapper.cs" /> <Compile Include="Binary\TimestampAttribute.cs" /> + <Compile Include="Binary\ITimestampConverter.cs" /> <Compile Include="Cache\Configuration\CacheKeyConfiguration.cs" /> <Compile Include="Cache\Configuration\DataPageEvictionMode.cs" /> <Compile Include="Cache\Configuration\PlatformCacheConfiguration.cs" /> diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryConfiguration.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryConfiguration.cs index 32d2c9d6f5d15..b25a9f58420aa 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryConfiguration.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryConfiguration.cs @@ -40,6 +40,11 @@ public class BinaryConfiguration /// </summary> public const bool DefaultKeepDeserialized = true; + /// <summary> + /// Default <see cref="ForceTimestamp"/> setting. + /// </summary> + public const bool DefaultForceTimestamp = false; + /** Footer setting. */ private bool? _compactFooter; @@ -49,6 +54,7 @@ public class BinaryConfiguration public BinaryConfiguration() { KeepDeserialized = DefaultKeepDeserialized; + ForceTimestamp = DefaultForceTimestamp; } /// <summary> @@ -72,6 +78,8 @@ internal void CopyLocalProperties(BinaryConfiguration cfg) IdMapper = cfg.IdMapper; NameMapper = cfg.NameMapper; KeepDeserialized = cfg.KeepDeserialized; + ForceTimestamp = cfg.ForceTimestamp; + TimestampConverter = cfg.TimestampConverter; if (cfg.Serializer != null) { @@ -106,7 +114,7 @@ public BinaryConfiguration(params Type[] binaryTypes) public ICollection<BinaryTypeConfiguration> TypeConfigurations { get; set; } /// <summary> - /// Gets or sets a collection of assembly-qualified type names + /// Gets or sets a collection of assembly-qualified type names /// (the result of <see cref="Type.AssemblyQualifiedName"/>) for binarizable types. /// <para /> /// Shorthand for creating <see cref="BinaryTypeConfiguration"/>. @@ -129,6 +137,15 @@ public BinaryConfiguration(params Type[] binaryTypes) /// </summary> public IBinarySerializer Serializer { get; set; } + /// <summary> + /// Gets or sets a converter between <see cref="DateTime"/> and Java Timestamp. + /// Called from <see cref="IBinaryWriter.WriteTimestamp"/>, <see cref="IBinaryWriter.WriteTimestampArray"/>, + /// <see cref="IBinaryReader.ReadTimestamp"/>, <see cref="IBinaryReader.ReadTimestampArray"/>. + /// <para /> + /// See also <see cref="ForceTimestamp"/>. + /// </summary> + public ITimestampConverter TimestampConverter { get; set; } + /// <summary> /// Default keep deserialized flag. /// </summary> @@ -137,12 +154,12 @@ public BinaryConfiguration(params Type[] binaryTypes) /// <summary> /// Gets or sets a value indicating whether to write footers in compact form. - /// When enabled, Ignite will not write fields metadata when serializing objects, + /// When enabled, Ignite will not write fields metadata when serializing objects, /// because internally metadata is distributed inside cluster. /// This increases serialization performance. /// <para/> /// <b>WARNING!</b> This mode should be disabled when already serialized data can be taken from some external - /// sources (e.g.cache store which stores data in binary form, data center replication, etc.). + /// sources (e.g.cache store which stores data in binary form, data center replication, etc.). /// Otherwise binary objects without any associated metadata could could not be deserialized. /// </summary> [DefaultValue(DefaultCompactFooter)] @@ -152,6 +169,21 @@ public bool CompactFooter set { _compactFooter = value; } } + /// <summary> + /// Gets or sets a value indicating whether all DateTime keys, values and object fields + /// should be written as a Timestamp. + /// <para /> + /// Timestamp format is required for values used in SQL and for interoperation with other platforms. + /// Only UTC values are supported in Timestamp format. Other values will cause an exception on write, unless <see cref="TimestampConverter"/> is provided. + /// <para /> + /// Normally Ignite serializer uses <see cref="IBinaryWriter.WriteObject{T}"/> for DateTime fields, + /// keys and values. + /// This attribute changes the behavior to <see cref="IBinaryWriter.WriteTimestamp"/>. + /// <para /> + /// See also <see cref="TimestampAttribute"/>, <see cref="BinaryReflectiveSerializer.ForceTimestamp"/>. + /// </summary> + public bool ForceTimestamp { get; set; } + /// <summary> /// Gets the compact footer internal nullable value. /// </summary> diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryReflectiveSerializer.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryReflectiveSerializer.cs index f9874ba23484d..c97b0e61e0168 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryReflectiveSerializer.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryReflectiveSerializer.cs @@ -21,21 +21,21 @@ namespace Apache.Ignite.Core.Binary using Apache.Ignite.Core.Impl.Binary; /// <summary> - /// Binary serializer which reflectively writes all fields except of ones with + /// Binary serializer which reflectively writes all fields except of ones with /// <see cref="System.NonSerializedAttribute"/>. /// <para /> - /// Note that Java platform stores dates as a difference between current time - /// and predefined absolute UTC date. Therefore, this difference is always the - /// same for all time zones. .NET, in contrast, stores dates as a difference - /// between current time and some predefined date relative to the current time - /// zone. It means that this difference will be different as you change time zones. - /// To overcome this discrepancy Ignite always converts .Net date to UTC form - /// before serializing and allows user to decide whether to deserialize them - /// in UTC or local form using <c>ReadTimestamp(..., true/false)</c> methods in + /// Note that Java platform stores dates as a difference between current time + /// and predefined absolute UTC date. Therefore, this difference is always the + /// same for all time zones. .NET, in contrast, stores dates as a difference + /// between current time and some predefined date relative to the current time + /// zone. It means that this difference will be different as you change time zones. + /// To overcome this discrepancy Ignite always converts .Net date to UTC form + /// before serializing and allows user to decide whether to deserialize them + /// in UTC or local form using <c>ReadTimestamp(..., true/false)</c> methods in /// <see cref="IBinaryReader"/> and <see cref="IBinaryRawReader"/>. /// This serializer always read dates in UTC form. It means that if you have /// local date in any field/property, it will be implicitly converted to UTC - /// form after the first serialization-deserialization cycle. + /// form after the first serialization-deserialization cycle. /// </summary> public sealed class BinaryReflectiveSerializer : IBinarySerializer { @@ -94,7 +94,7 @@ public bool RawMode /// Normally serializer uses <see cref="IBinaryWriter.WriteObject{T}"/> for DateTime fields. /// This attribute changes the behavior to <see cref="IBinaryWriter.WriteTimestamp"/>. /// <para /> - /// See also <see cref="TimestampAttribute"/>. + /// See also <see cref="TimestampAttribute"/>, <see cref="BinaryConfiguration.ForceTimestamp"/>. /// </summary> public bool ForceTimestamp { diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Binary/ITimestampConverter.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/ITimestampConverter.cs new file mode 100644 index 0000000000000..c7604793b9360 --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/ITimestampConverter.cs @@ -0,0 +1,38 @@ +/* + * 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. + */ + +namespace Apache.Ignite.Core.Binary +{ + using System; + + /// <summary> + /// Converts <see cref="DateTime"/> values to Java Timestamp and back. + /// </summary> + public interface ITimestampConverter + { + /// <summary>Converts date to Java ticks.</summary> + /// <param name="date">Date</param> + /// <param name="high">High part (milliseconds).</param> + /// <param name="low">Low part (nanoseconds)</param> + void ToJavaTicks(DateTime date, out long high, out int low); + + /// <summary>Converts date from Java ticks.</summary> + /// <param name="high">High part (milliseconds).</param> + /// <param name="low">Low part (nanoseconds)</param> + DateTime FromJavaTicks(long high, int low); + } +} diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteClientConfigurationSection.xsd b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteClientConfigurationSection.xsd index ba64aca369216..c0314b3488609 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteClientConfigurationSection.xsd +++ b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteClientConfigurationSection.xsd @@ -159,6 +159,18 @@ </xs:attribute> </xs:complexType> </xs:element> + <xs:element name="timestampConverter" minOccurs="0"> + <xs:annotation> + <xs:documentation>Default date time converter.</xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:attribute name="type" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation>Assembly-qualified type name.</xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> </xs:all> <xs:attribute name="keepDeserialized" type="xs:boolean"> <xs:annotation> @@ -170,6 +182,11 @@ <xs:documentation>Compact footer flag.</xs:documentation> </xs:annotation> </xs:attribute> + <xs:attribute name="forceTimestamp" type="xs:boolean"> + <xs:annotation> + <xs:documentation>Force timestamp flag.</xs:documentation> + </xs:annotation> + </xs:attribute> </xs:complexType> </xs:element> <xs:element name="endpoints" minOccurs="0" maxOccurs="1"> diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd index 0ab59ec78bea8..7794c21ce234e 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd +++ b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd @@ -263,6 +263,18 @@ </xs:attribute> </xs:complexType> </xs:element> + <xs:element name="timestampConverter" minOccurs="0"> + <xs:annotation> + <xs:documentation>Default date time converter.</xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:attribute name="type" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation>Assembly-qualified type name.</xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> </xs:all> <xs:attribute name="keepDeserialized" type="xs:boolean"> <xs:annotation> @@ -274,6 +286,11 @@ <xs:documentation>Compact footer flag.</xs:documentation> </xs:annotation> </xs:attribute> + <xs:attribute name="forceTimestamp" type="xs:boolean"> + <xs:annotation> + <xs:documentation>Force timestamp flag.</xs:documentation> + </xs:annotation> + </xs:attribute> </xs:complexType> </xs:element> <xs:element name="cacheConfiguration" minOccurs="0"> diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryProcessor.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryProcessor.cs index b62977a53a02a..05deb5ba18b37 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryProcessor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryProcessor.cs @@ -27,6 +27,12 @@ namespace Apache.Ignite.Core.Impl.Binary /// </summary> internal class BinaryProcessor : PlatformTargetAdapter, IBinaryProcessor { + /** Java platform id. See org.apache.ignite.internal.MarshallerPlatformIds in Java. */ + public const byte JavaPlatformId = 0; + + /** Type registry platform id. See org.apache.ignite.internal.MarshallerPlatformIds in Java. */ + public const byte DotNetPlatformId = 1; + /// <summary> /// Op codes. /// </summary> @@ -103,8 +109,9 @@ public void PutBinaryTypes(ICollection<BinaryType> types) /// </summary> /// <param name="id">The identifier.</param> /// <param name="typeName">The type name.</param> + /// <param name="registerSameJavaType">True if should register type both for dotnet and java platforms.</param> /// <returns>True if registration succeeded; otherwise, false.</returns> - public bool RegisterType(int id, string typeName) + public bool RegisterType(int id, string typeName, bool registerSameJavaType) { Debug.Assert(typeName != null); Debug.Assert(id != BinaryTypeId.Unregistered); @@ -113,6 +120,7 @@ public bool RegisterType(int id, string typeName) { w.WriteInt(id); w.WriteString(typeName); + w.WriteBoolean(registerSameJavaType); }) == True; } @@ -160,7 +168,38 @@ public BinaryType RegisterEnum(string typeName, IEnumerable<KeyValuePair<string, /// <returns>Type or null.</returns> public string GetTypeName(int id) { - return DoOutInOp((int) Op.GetType, w => w.WriteInt(id), r => Marshaller.StartUnmarshal(r).ReadString()); + try + { + return GetTypeName(id, DotNetPlatformId); + } + catch (BinaryObjectException) + { + if (!Marshaller.RegisterSameJavaType.Value) + throw; + } + + // Try to get java type name and register corresponding DotNet type. + var javaTypeName = GetTypeName(id, JavaPlatformId); + var netTypeName = Marshaller.GetTypeName(javaTypeName); + + RegisterType(id, netTypeName, false); + + return netTypeName; + } + + /// <summary> + /// Gets the type name by id for specific platform. + /// </summary> + /// <param name="id">The identifier.</param> + /// <param name="platformId">Platform identifier.</param> + /// <returns>Type or null.</returns> + private string GetTypeName(int id, byte platformId) + { + return DoOutInOp((int) Op.GetType, w => + { + w.WriteInt(id); + w.WriteByte(platformId); + }, r => Marshaller.StartUnmarshal(r).ReadString()); } /// <summary> diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryProcessorClient.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryProcessorClient.cs index 8ef84c1fe82f3..4f67c085aab1c 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryProcessorClient.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryProcessorClient.cs @@ -28,9 +28,6 @@ namespace Apache.Ignite.Core.Impl.Binary /// </summary> internal class BinaryProcessorClient : IBinaryProcessor { - /** Type registry platform id. See org.apache.ignite.internal.MarshallerPlatformIds in Java. */ - private const byte DotNetPlatformId = 1; - /** Socket. */ private readonly ClientFailoverSocket _socket; @@ -73,14 +70,26 @@ public void PutBinaryTypes(ICollection<BinaryType> types) } /** <inheritdoc /> */ - public bool RegisterType(int id, string typeName) + public bool RegisterType(int id, string typeName, bool registerSameJavaType) { - return _socket.DoOutInOp(ClientOp.BinaryTypeNamePut, ctx => + var res = _socket.DoOutInOp(ClientOp.BinaryTypeNamePut, ctx => { - ctx.Stream.WriteByte(DotNetPlatformId); + ctx.Stream.WriteByte(BinaryProcessor.DotNetPlatformId); ctx.Stream.WriteInt(id); ctx.Writer.WriteString(typeName); }, ctx => ctx.Stream.ReadBool()); + + if (registerSameJavaType && res) + { + res = _socket.DoOutInOp(ClientOp.BinaryTypeNamePut, ctx => + { + ctx.Stream.WriteByte(BinaryProcessor.JavaPlatformId); + ctx.Stream.WriteInt(id); + ctx.Writer.WriteString(typeName); + }, ctx => ctx.Stream.ReadBool()); + } + + return res; } /** <inheritdoc /> */ @@ -94,7 +103,7 @@ public string GetTypeName(int id) { return _socket.DoOutInOp(ClientOp.BinaryTypeNameGet, ctx => { - ctx.Stream.WriteByte(DotNetPlatformId); + ctx.Stream.WriteByte(BinaryProcessor.DotNetPlatformId); ctx.Stream.WriteInt(id); }, ctx => ctx.Reader.ReadString()); diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReader.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReader.cs index 9153046ece52b..c1a6fed02ddd2 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReader.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReader.cs @@ -315,25 +315,25 @@ public double[] ReadDoubleArray() /** <inheritdoc /> */ public DateTime? ReadTimestamp(string fieldName) { - return ReadField(fieldName, BinaryUtils.ReadTimestamp, BinaryTypeId.Timestamp); + return ReadField(fieldName, stream => BinaryUtils.ReadTimestamp(stream, _marsh.TimestampConverter), BinaryTypeId.Timestamp); } /** <inheritdoc /> */ public DateTime? ReadTimestamp() { - return Read(BinaryUtils.ReadTimestamp, BinaryTypeId.Timestamp); + return Read(stream => BinaryUtils.ReadTimestamp(stream, _marsh.TimestampConverter), BinaryTypeId.Timestamp); } /** <inheritdoc /> */ public DateTime?[] ReadTimestampArray(string fieldName) { - return ReadField(fieldName, BinaryUtils.ReadTimestampArray, BinaryTypeId.ArrayTimestamp); + return ReadField(fieldName, stream => BinaryUtils.ReadTimestampArray(stream, _marsh.TimestampConverter), BinaryTypeId.ArrayTimestamp); } /** <inheritdoc /> */ public DateTime?[] ReadTimestampArray() { - return Read(BinaryUtils.ReadTimestampArray, BinaryTypeId.ArrayTimestamp); + return Read(stream => BinaryUtils.ReadTimestampArray(stream, _marsh.TimestampConverter), BinaryTypeId.ArrayTimestamp); } /** <inheritdoc /> */ diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemHandlers.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemHandlers.cs index cae0f5fcf9a50..8fdf3716fe623 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemHandlers.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemHandlers.cs @@ -54,9 +54,6 @@ static BinarySystemHandlers() ReadHandlers[BinaryTypeId.Double] = new BinarySystemReader<double>(s => s.ReadDouble()); ReadHandlers[BinaryTypeId.Decimal] = new BinarySystemReader<decimal?>(BinaryUtils.ReadDecimal); - // 2. Date. - ReadHandlers[BinaryTypeId.Timestamp] = new BinarySystemReader<DateTime?>(BinaryUtils.ReadTimestamp); - // 3. String. ReadHandlers[BinaryTypeId.String] = new BinarySystemReader<string>(BinaryUtils.ReadString); @@ -92,10 +89,6 @@ static BinarySystemHandlers() ReadHandlers[BinaryTypeId.ArrayDecimal] = new BinarySystemReader<decimal?[]>(BinaryUtils.ReadDecimalArray); - // 6. Date array. - ReadHandlers[BinaryTypeId.ArrayTimestamp] = - new BinarySystemReader<DateTime?[]>(BinaryUtils.ReadTimestampArray); - // 7. String array. ReadHandlers[BinaryTypeId.ArrayString] = new BinarySystemTypedArrayReader<string>(); @@ -122,9 +115,18 @@ static BinarySystemHandlers() /// Try getting write handler for type. /// </summary> /// <param name="type"></param> + /// <param name="forceTimestamp"></param> /// <returns></returns> - public static IBinarySystemWriteHandler GetWriteHandler(Type type) + public static IBinarySystemWriteHandler GetWriteHandler(Type type, bool forceTimestamp) { + if (forceTimestamp) + { + if (type == typeof(DateTime)) + return new BinarySystemWriteHandler<DateTime>(WriteTimestamp, false); + if (type == typeof(DateTime?[])) + return new BinarySystemWriteHandler<DateTime?[]>(WriteTimestampArray, true); + } + return WriteHandlers.GetOrAdd(type, t => { return FindWriteHandler(t); @@ -249,6 +251,20 @@ public static bool TryReadSystemType<T>(byte typeId, BinaryReader ctx, out T res if (handler == null) { + if (typeId == BinaryTypeId.Timestamp) + { + // Date. + res = TypeCaster<T>.Cast(BinaryUtils.ReadTimestamp(ctx.Stream, ctx.Marshaller.TimestampConverter)); + return true; + } + + if (typeId == BinaryTypeId.ArrayTimestamp) + { + // Date array. + res = TypeCaster<T>.Cast(BinaryUtils.ReadTimestampArray(ctx.Stream, ctx.Marshaller.TimestampConverter)); + return true; + } + res = default(T); return false; } @@ -293,6 +309,18 @@ private static void WriteGuid(BinaryWriter ctx, Guid obj) BinaryUtils.WriteGuid(obj, ctx.Stream); } + /// <summary> + /// Write Date. + /// </summary> + /// <param name="ctx">Context.</param> + /// <param name="obj">Value.</param> + private static void WriteTimestamp(BinaryWriter ctx, DateTime obj) + { + ctx.Stream.WriteByte(BinaryTypeId.Timestamp); + + BinaryUtils.WriteTimestamp(obj, ctx.Stream, ctx.Marshaller.TimestampConverter); + } + /// <summary> /// Write boolaen array. /// </summary> @@ -425,6 +453,18 @@ private static void WriteGuidArray(BinaryWriter ctx, Guid?[] obj) BinaryUtils.WriteGuidArray(obj, ctx.Stream); } + /// <summary> + /// Write nullable Date array. + /// </summary> + /// <param name="ctx">Context.</param> + /// <param name="obj">Value.</param> + private static void WriteTimestampArray(BinaryWriter ctx, DateTime?[] obj) + { + ctx.Stream.WriteByte(BinaryTypeId.ArrayTimestamp); + + BinaryUtils.WriteTimestampArray(obj, ctx.Stream, ctx.Marshaller.TimestampConverter); + } + /// <summary> /// Writes the enum array. /// </summary> diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryUtils.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryUtils.cs index 445d42162f4a8..7a1eb9ecb99b3 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryUtils.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryUtils.cs @@ -69,7 +69,7 @@ internal static class BinaryUtils public const int ObjTypeId = -1; /** Ticks for Java epoch. */ - private static readonly long JavaDateTicks = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).Ticks; + public static readonly long JavaDateTicks = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).Ticks; /** Binding flags for static search. */ private const BindingFlags BindFlagsStatic = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; @@ -384,13 +384,17 @@ public static double[] ReadDoubleArray(IBinaryStream stream) * <summary>Write date.</summary> * <param name="val">Date.</param> * <param name="stream">Stream.</param> + * <param name="converter">Timestamp Converter.</param> */ - public static void WriteTimestamp(DateTime val, IBinaryStream stream) + public static void WriteTimestamp(DateTime val, IBinaryStream stream, ITimestampConverter converter) { long high; int low; - ToJavaDate(val, out high, out low); + if (converter != null) + converter.ToJavaTicks(val, out high, out low); + else + ToJavaDate(val, out high, out low); stream.WriteLong(high); stream.WriteInt(low); @@ -399,14 +403,18 @@ public static void WriteTimestamp(DateTime val, IBinaryStream stream) /** * <summary>Read date.</summary> * <param name="stream">Stream.</param> + * <param name="converter">Timestamp Converter.</param> * <returns>Date</returns> */ - public static DateTime? ReadTimestamp(IBinaryStream stream) + public static DateTime? ReadTimestamp(IBinaryStream stream, ITimestampConverter converter) { long high = stream.ReadLong(); int low = stream.ReadInt(); - return new DateTime(JavaDateTicks + high * TimeSpan.TicksPerMillisecond + low / 100, DateTimeKind.Utc); + if (converter != null) + return converter.FromJavaTicks(high, low); + else + return new DateTime(JavaDateTicks + high * TimeSpan.TicksPerMillisecond + low / 100, DateTimeKind.Utc); } /// <summary> @@ -434,7 +442,8 @@ public static long DateTimeToJavaTicks(DateTime dateTime) /// </summary> /// <param name="vals">Values.</param> /// <param name="stream">Stream.</param> - public static void WriteTimestampArray(DateTime?[] vals, IBinaryStream stream) + /// <param name="converter">Timestamp Converter.</param> + public static void WriteTimestampArray(DateTime?[] vals, IBinaryStream stream, ITimestampConverter converter) { stream.WriteInt(vals.Length); @@ -444,7 +453,7 @@ public static void WriteTimestampArray(DateTime?[] vals, IBinaryStream stream) { stream.WriteByte(BinaryTypeId.Timestamp); - WriteTimestamp(val.Value, stream); + WriteTimestamp(val.Value, stream, converter); } else stream.WriteByte(HdrNull); @@ -1161,15 +1170,16 @@ public static T[] ReadArray<T>(BinaryReader ctx, bool typed) /// Read timestamp array. /// </summary> /// <param name="stream">Stream.</param> + /// <param name="converter">Timestamp Converter.</param> /// <returns>Timestamp array.</returns> - public static DateTime?[] ReadTimestampArray(IBinaryStream stream) + public static DateTime?[] ReadTimestampArray(IBinaryStream stream, ITimestampConverter converter) { int len = stream.ReadInt(); DateTime?[] vals = new DateTime?[len]; for (int i = 0; i < len; i++) - vals[i] = stream.ReadByte() == HdrNull ? null : ReadTimestamp(stream); + vals[i] = stream.ReadByte() == HdrNull ? null : ReadTimestamp(stream, converter); return vals; } @@ -1608,7 +1618,7 @@ public static void ValidateProtocolVersion(byte version) * <param name="high">High part (milliseconds).</param> * <param name="low">Low part (nanoseconds)</param> */ - private static void ToJavaDate(DateTime date, out long high, out int low) + public static void ToJavaDate(DateTime date, out long high, out int low) { if (date.Kind != DateTimeKind.Utc) { diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs index 4abac0a63445c..3054bc0554aab 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs @@ -657,7 +657,7 @@ public void WriteTimestamp(string fieldName, DateTime? val) else { _stream.WriteByte(BinaryTypeId.Timestamp); - BinaryUtils.WriteTimestamp(val.Value, _stream); + BinaryUtils.WriteTimestamp(val.Value, _stream, _marsh.TimestampConverter); } } @@ -672,7 +672,7 @@ public void WriteTimestamp(DateTime? val) else { _stream.WriteByte(BinaryTypeId.Timestamp); - BinaryUtils.WriteTimestamp(val.Value, _stream); + BinaryUtils.WriteTimestamp(val.Value, _stream, _marsh.TimestampConverter); } } @@ -690,7 +690,7 @@ public void WriteTimestampArray(string fieldName, DateTime?[] val) else { _stream.WriteByte(BinaryTypeId.ArrayTimestamp); - BinaryUtils.WriteTimestampArray(val, _stream); + BinaryUtils.WriteTimestampArray(val, _stream, _marsh.TimestampConverter); } } @@ -705,7 +705,7 @@ public void WriteTimestampArray(DateTime?[] val) else { _stream.WriteByte(BinaryTypeId.ArrayTimestamp); - BinaryUtils.WriteTimestampArray(val, _stream); + BinaryUtils.WriteTimestampArray(val, _stream, _marsh.TimestampConverter); } } @@ -875,7 +875,7 @@ public void WriteEnum<T>(T val) throw new BinaryObjectException("Type is not an enum: " + type); } - var handler = BinarySystemHandlers.GetWriteHandler(type); + var handler = BinarySystemHandlers.GetWriteHandler(type, _marsh.ForceTimestamp); if (handler != null) { @@ -1163,7 +1163,7 @@ public void Write<T>(T obj) return; // Are we dealing with a well-known type? - var handler = BinarySystemHandlers.GetWriteHandler(type); + var handler = BinarySystemHandlers.GetWriteHandler(type, _marsh.ForceTimestamp); if (handler != null) { diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinaryProcessor.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinaryProcessor.cs index fed78c3f4eb9f..6dfefb1354b95 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinaryProcessor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinaryProcessor.cs @@ -47,8 +47,9 @@ internal interface IBinaryProcessor /// </summary> /// <param name="id">The identifier.</param> /// <param name="typeName">The type name.</param> + /// <param name="registerSameJavaType">True if should register type both for dotnet and java platforms.</param> /// <returns>True if registration succeeded; otherwise, false.</returns> - bool RegisterType(int id, string typeName); + bool RegisterType(int id, string typeName, bool registerSameJavaType); /// <summary> /// Registers the enum. diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs index 056a713451f3f..d09efbed5fcce 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs @@ -22,6 +22,7 @@ namespace Apache.Ignite.Core.Impl.Binary using System.Diagnostics; using System.Linq; using System.Runtime.Serialization; + using System.Threading; using Apache.Ignite.Core.Binary; using Apache.Ignite.Core.Cache.Affinity; using Apache.Ignite.Core.Common; @@ -42,6 +43,9 @@ namespace Apache.Ignite.Core.Impl.Binary /// </summary> internal class Marshaller { + /** Register same java type flag. */ + public static readonly ThreadLocal<Boolean> RegisterSameJavaType = new ThreadLocal<Boolean>(() => false); + /** Binary configuration. */ private readonly BinaryConfiguration _cfg; @@ -132,6 +136,22 @@ public IIgniteInternal Ignite /// </summary> public bool RegistrationDisabled { get; set; } + /// <summary> + /// Gets force timestamp flag value. + /// </summary> + public bool ForceTimestamp + { + get { return _cfg.ForceTimestamp; } + } + + /// <summary> + /// Gets date time converter. + /// </summary> + public ITimestampConverter TimestampConverter + { + get { return _cfg.TimestampConverter; } + } + /// <summary> /// Marshal object. /// </summary> @@ -372,7 +392,7 @@ private BinaryTypeHolder GetBinaryTypeHolder(IBinaryTypeDescriptor desc) { return holder; } - + lock (this) { if (!_metas.TryGetValue(desc.TypeId, out holder)) @@ -390,9 +410,9 @@ private BinaryTypeHolder GetBinaryTypeHolder(IBinaryTypeDescriptor desc) return holder; } - + /// <summary> - /// Updates or creates cached binary type holder. + /// Updates or creates cached binary type holder. /// </summary> private void UpdateOrCreateBinaryTypeHolder(BinaryType meta) { @@ -402,7 +422,7 @@ private void UpdateOrCreateBinaryTypeHolder(BinaryType meta) holder.Merge(meta); return; } - + lock (this) { if (_metas.TryGetValue(meta.TypeId, out holder)) @@ -410,7 +430,7 @@ private void UpdateOrCreateBinaryTypeHolder(BinaryType meta) holder.Merge(meta); return; } - + var metas0 = new Dictionary<int, BinaryTypeHolder>(_metas); holder = new BinaryTypeHolder(meta.TypeId, meta.TypeName, meta.AffinityKeyFieldName, meta.IsEnum, this); @@ -556,7 +576,7 @@ private BinaryFullTypeDescriptor RegisterType(Type type, BinaryFullTypeDescripto var typeName = GetTypeName(type); var typeId = GetTypeId(typeName, _cfg.IdMapper); - var registered = _ignite != null && _ignite.BinaryProcessor.RegisterType(typeId, typeName); + var registered = _ignite != null && _ignite.BinaryProcessor.RegisterType(typeId, typeName, RegisterSameJavaType.Value); return AddUserType(type, typeId, typeName, registered, desc); } @@ -692,7 +712,10 @@ private static IBinarySerializerInternal GetSerializer(BinaryConfiguration cfg, return new SerializableSerializer(type); } - serializer = new BinaryReflectiveSerializer(); + serializer = new BinaryReflectiveSerializer + { + ForceTimestamp = cfg != null && cfg.ForceTimestamp + }; } var refSerializer = serializer as BinaryReflectiveSerializer; @@ -867,16 +890,16 @@ public void OnClientReconnected(bool clusterRestarted) { if (!clusterRestarted) return; - + // Reset all binary structures. Metadata must be sent again. // _idToDesc enumerator is thread-safe (returns a snapshot). // If there are new descriptors added concurrently, they are fine (we are already connected). - + // Race is possible when serialization is started before reconnect (or even before disconnect) // and finished after reconnect, meta won't be sent to cluster because it is assumed to be known, // but operation will succeed. // We don't support this use case. Users should handle reconnect events properly when cluster is restarted. - // Supporting this very rare use case will complicate the code a lot with little benefit. + // Supporting this very rare use case will complicate the code a lot with little benefit. foreach (var desc in _idToDesc) { desc.Value.ResetWriteStructure(); @@ -886,7 +909,7 @@ public void OnClientReconnected(bool clusterRestarted) /// <summary> /// Gets the name of the type. /// </summary> - private string GetTypeName(string fullTypeName, IBinaryNameMapper mapper = null) + public string GetTypeName(string fullTypeName, IBinaryNameMapper mapper = null) { mapper = mapper ?? _cfg.NameMapper ?? GetDefaultNameMapper(); diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxySerializer.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxySerializer.cs index 37453705d145f..e28f4d78ccc65 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxySerializer.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxySerializer.cs @@ -69,7 +69,8 @@ public static void WriteProxyMethod(BinaryWriter writer, string methodName, Meth for (var i = 0; i < arguments.Length; i++) { - WriteArgForPlatforms(writer, mParams != null ? mParams[i].ParameterType : null, arguments[i]); + WriteArgForPlatforms(writer, mParams != null ? mParams[i].ParameterType : null, + arguments[i]); } } } @@ -257,7 +258,7 @@ private static Action<BinaryWriter, object> GetPlatformArgWriter(Type paramType, return (writer, o) => writer.WriteTimestampArray((DateTime?[]) o); } - var handler = BinarySystemHandlers.GetWriteHandler(type); + var handler = BinarySystemHandlers.GetWriteHandler(type, true); if (handler != null) return null; @@ -265,12 +266,12 @@ private static Action<BinaryWriter, object> GetPlatformArgWriter(Type paramType, if (type.IsArray) return (writer, o) => writer.WriteArrayInternal((Array) o); + if (arg is IDictionary) + return (writer, o) => writer.WriteDictionary((IDictionary) o); + if (arg is ICollection) return (writer, o) => writer.WriteCollection((ICollection) o); - if (arg is DateTime) - return (writer, o) => writer.WriteTimestamp((DateTime) o); - return null; } } diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/Services.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/Services.cs index 204d3aeeee78c..3afe34bfc8060 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/Services.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/Services.cs @@ -434,10 +434,20 @@ public dynamic GetDynamicServiceProxy(string name, bool sticky) private object InvokeProxyMethod(IPlatformTargetInternal proxy, string methodName, MethodBase method, object[] args, PlatformType platformType) { - return DoOutInOp(OpInvokeMethod, - writer => ServiceProxySerializer.WriteProxyMethod(writer, methodName, method, args, platformType), - (stream, res) => ServiceProxySerializer.ReadInvocationResult(stream, Marshaller, _keepBinary), - proxy); + Marshaller.RegisterSameJavaType.Value = true; + + try + { + return DoOutInOp(OpInvokeMethod, + writer => ServiceProxySerializer.WriteProxyMethod(writer, methodName, method, args, platformType), + (stream, res) => ServiceProxySerializer.ReadInvocationResult(stream, Marshaller, _keepBinary), + proxy); + } + finally + { + Marshaller.RegisterSameJavaType.Value = false; + } + } /// <summary>