From a844a62df97b3fad3b32ec95b4260c98f16ca9c9 Mon Sep 17 00:00:00 2001 From: Nikolay Date: Tue, 8 Dec 2020 20:07:31 +0300 Subject: [PATCH 1/6] IGNITE-10075 .NET Avoid binary configurations of Ignite Java service params (#8509) (cherry picked from commit 35f551c023a12b3570d65c803d10c89480f7d5e4) --- .../binary/PlatformBinaryProcessor.java | 11 +- .../org/apache/ignite/platform/Address.java | 47 +++ .../apache/ignite/platform/Department.java | 34 ++ .../org/apache/ignite/platform/Employee.java | 47 +++ .../java/org/apache/ignite/platform/Key.java | 51 +++ .../platform/PlatformDeployServiceTask.java | 89 +++++ .../org/apache/ignite/platform/Value.java | 51 +++ ...Apache.Ignite.Core.Tests.DotNetCore.csproj | 4 + .../Apache.Ignite.Core.Tests.csproj | 6 +- .../Client/Cache/CacheTestNoMeta.cs | 2 +- .../Services/IJavaService.cs | 183 ++++++++++ .../Services/JavaServiceDynamicProxy.cs | 340 ++++++++++++++++++ .../Services/Model.cs | 86 +++++ .../Services/ServiceTypeAutoResolveTest.cs | 211 +++++++++++ .../TestUtils.Common.cs | 17 + .../Impl/Binary/BinaryProcessor.cs | 4 +- .../Impl/Binary/BinaryProcessorClient.cs | 19 +- .../Impl/Binary/IBinaryProcessor.cs | 3 +- .../Impl/Binary/Marshaller.cs | 6 +- .../Impl/Services/ServiceProxySerializer.cs | 51 ++- 20 files changed, 1235 insertions(+), 27 deletions(-) create mode 100644 modules/core/src/test/java/org/apache/ignite/platform/Address.java create mode 100644 modules/core/src/test/java/org/apache/ignite/platform/Department.java create mode 100644 modules/core/src/test/java/org/apache/ignite/platform/Employee.java create mode 100644 modules/core/src/test/java/org/apache/ignite/platform/Key.java create mode 100644 modules/core/src/test/java/org/apache/ignite/platform/Value.java create mode 100644 modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs create mode 100644 modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/JavaServiceDynamicProxy.cs create mode 100644 modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/Model.cs create mode 100644 modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServiceTypeAutoResolveTest.cs 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..4c5c1226f8af8 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; } } diff --git a/modules/core/src/test/java/org/apache/ignite/platform/Address.java b/modules/core/src/test/java/org/apache/ignite/platform/Address.java new file mode 100644 index 0000000000000..39390e859492d --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/platform/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; + +/** 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/Department.java b/modules/core/src/test/java/org/apache/ignite/platform/Department.java new file mode 100644 index 0000000000000..a1aa9b77f237e --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/platform/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; + +/** 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/Employee.java b/modules/core/src/test/java/org/apache/ignite/platform/Employee.java new file mode 100644 index 0000000000000..03d99a6e6ad32 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/platform/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; + +/** 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/Key.java b/modules/core/src/test/java/org/apache/ignite/platform/Key.java new file mode 100644 index 0000000000000..26c578709bd0c --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/platform/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; + +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/PlatformDeployServiceTask.java b/modules/core/src/test/java/org/apache/ignite/platform/PlatformDeployServiceTask.java index 6986ae4e80bd3..bb00ecb7fa027 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 @@ -21,6 +21,8 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.UUID; @@ -33,6 +35,7 @@ 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.resources.IgniteInstanceResource; import org.apache.ignite.services.Service; import org.apache.ignite.services.ServiceContext; @@ -412,5 +415,91 @@ 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 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 iter = deps.iterator(); + + assert "HR".equals(iter.next().getName()); + assert "IT".equals(iter.next().getName()); + + Collection 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 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/Value.java b/modules/core/src/test/java/org/apache/ignite/platform/Value.java new file mode 100644 index 0000000000000..09502918251b7 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/platform/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; + +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 @@ + + + + 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 @@ + + + @@ -374,6 +377,7 @@ + @@ -587,4 +591,4 @@ --> - \ No newline at end of file + 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 types) } /** */ - 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..8c9e6b6755b3e --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs @@ -0,0 +1,183 @@ +/* + * 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; + + /// + /// Java service proxy interface. + /// + [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); + + /** */ + Employee[] testEmployees(Employee[] emps); + + /** */ + ICollection testDepartments(ICollection deps); + + /** */ + IDictionary testMap(IDictionary dict); + + /** */ + 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..3276b838500ba --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/JavaServiceDynamicProxy.cs @@ -0,0 +1,340 @@ +/* + * 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; + + /// + /// Explicit service proxy over dynamic variable. + /// + public class JavaServiceDynamicProxy : IJavaService + { + /** */ + private readonly dynamic _svc; + + /** */ + public JavaServiceDynamicProxy(dynamic svc) + { + _svc = svc; + } + + /** */ + public bool isCancelled() + { + return _svc.isCancelled(); + } + + /** */ + public bool isInitialized() + { + return _svc.isInitialized(); + } + + /** */ + public bool isExecuted() + { + return _svc.isExecuted(); + } + + /** */ + public byte test(byte x) + { + return _svc.test(x); + } + + /** */ + public short test(short x) + { + return _svc.test(x); + } + + /** */ + public int test(int x) + { + return _svc.test(x); + } + + /** */ + public long test(long x) + { + return _svc.test(x); + } + + /** */ + public float test(float x) + { + return _svc.test(x); + } + + /** */ + public double test(double x) + { + return _svc.test(x); + } + + /** */ + public char test(char x) + { + return _svc.test(x); + } + + /** */ + public string test(string x) + { + return _svc.test(x); + } + + /** */ + public bool test(bool x) + { + return _svc.test(x); + } + + /** */ + public DateTime test(DateTime x) + { + return _svc.test(x); + } + + /** */ + public Guid test(Guid x) + { + return _svc.test(x); + } + + /** */ + public byte? testWrapper(byte? x) + { + return _svc.testWrapper(x); + } + + /** */ + public short? testWrapper(short? x) + { + return _svc.testWrapper(x); + } + + /** */ + public int? testWrapper(int? x) + { + return _svc.testWrapper(x); + } + + /** */ + public long? testWrapper(long? x) + { + return _svc.testWrapper(x); + } + + /** */ + public float? testWrapper(float? x) + { + return _svc.testWrapper(x); + } + + /** */ + public double? testWrapper(double? x) + { + return _svc.testWrapper(x); + } + + /** */ + public char? testWrapper(char? x) + { + return _svc.testWrapper(x); + } + + /** */ + public bool? testWrapper(bool? x) + { + return _svc.testWrapper(x); + } + + /** */ + public byte[] testArray(byte[] x) + { + return _svc.testArray(x); + } + + /** */ + public short[] testArray(short[] x) + { + return _svc.testArray(x); + } + + /** */ + public int[] testArray(int[] x) + { + return _svc.testArray(x); + } + + /** */ + public long[] testArray(long[] x) + { + return _svc.testArray(x); + } + + /** */ + public float[] testArray(float[] x) + { + return _svc.testArray(x); + } + + /** */ + public double[] testArray(double[] x) + { + return _svc.testArray(x); + } + + /** */ + public char[] testArray(char[] x) + { + return _svc.testArray(x); + } + + /** */ + public string[] testArray(string[] x) + { + return _svc.testArray(x); + } + + /** */ + public bool[] testArray(bool[] x) + { + return _svc.testArray(x); + } + + /** */ + public DateTime?[] testArray(DateTime?[] x) + { + return _svc.testArray(x); + } + + /** */ + public Guid?[] testArray(Guid?[] x) + { + return _svc.testArray(x); + } + + /** */ + public int test(int x, string y) + { + return _svc.test(x, y); + } + + /** */ + public int test(string x, int y) + { + return _svc.test(x, y); + } + + /** */ + public int? testNull(int? x) + { + return _svc.testNull(x); + } + + /** */ + public DateTime? testNullTimestamp(DateTime? x) + { + return _svc.testNullTimestamp(x); + } + + /** */ + public Guid? testNullUUID(Guid? x) + { + return _svc.testNullUUID(x); + } + + /** */ + public int testParams(params object[] args) + { + return _svc.testParams(args); + } + + /** */ + public ServicesTest.PlatformComputeBinarizable testBinarizable(ServicesTest.PlatformComputeBinarizable x) + { + return _svc.testBinarizable(x); + } + + /** */ + public object[] testBinarizableArrayOfObjects(object[] x) + { + return _svc.testBinarizableArrayOfObjects(x); + } + + /** */ + public IBinaryObject[] testBinaryObjectArray(IBinaryObject[] x) + { + return _svc.testBinaryObjectArray(x); + } + + /** */ + public ServicesTest.PlatformComputeBinarizable[] testBinarizableArray(ServicesTest.PlatformComputeBinarizable[] x) + { + return _svc.testBinarizableArray(x); + } + + /** */ + public ICollection testBinarizableCollection(ICollection x) + { + return _svc.testBinarizableCollection(x); + } + + /** */ + public IBinaryObject testBinaryObject(IBinaryObject x) + { + return _svc.testBinaryObject(x); + } + + /** */ + public Address testAddress(Address addr) + { + return _svc.testAddress(addr); + } + + /** */ + public Employee[] testEmployees(Employee[] emps) + { + return _svc.testEmployees(emps); + } + + /** */ + public ICollection testDepartments(ICollection deps) + { + return _svc.testDepartments(deps); + } + + /** */ + public IDictionary testMap(IDictionary dict) + { + return _svc.testMap(dict); + } + + /** */ + 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..584511cd06528 --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/Model.cs @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace org.apache.ignite.platform +{ + /// + /// A class is a clone of Java class Address with the same namespace. + /// + public class Address + { + /** */ + public string Zip { get; set; } + + /** */ + public string Addr { get; set; } + } + + /// + /// A class is a clone of Java class Department with the same namespace. + /// + public class Department + { + /** */ + public string Name { get; set; } + } + + /// + /// A class is a clone of Java class Employee with the same namespace. + /// + public class Employee + { + /** */ + public string Fio { get; set; } + + /** */ + public long Salary { get; set; } + } + + /// + /// A class is a clone of Java class Employee with the same namespace. + /// + 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() + { + return Id.GetHashCode(); + } + } + + /// + /// A class is a clone of Java class Employee with the same namespace. + /// + public class Value + { + public string Val { get; set; } + } +} 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..1ae518724652e --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServiceTypeAutoResolveTest.cs @@ -0,0 +1,211 @@ +/* + * 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; + + /// + /// Tests checks ability to execute service method without explicit registration of parameter type. + /// + public class ServicesTypeAutoResolveTest + { + /** */ + private IIgnite _grid1; + + [TestFixtureTearDown] + public void FixtureTearDown() + { + StopGrids(); + } + + /// + /// Executes before each test. + /// + [SetUp] + public void SetUp() + { + StartGrids(); + } + + /// + /// Executes after each test. + /// + [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 + } + } + + /// + /// Tests Java service invocation with dynamic proxy. + /// Types should be resolved implicitly. + /// + [Test] + public void TestCallJavaServiceDynamicProxy() + { + // Deploy Java service + var javaSvcName = TestUtils.DeployJavaService(_grid1); + var svc = _grid1.GetServices().GetDynamicServiceProxy(javaSvcName, true); + + doTestService(new JavaServiceDynamicProxy(svc)); + } + + /// + /// Tests Java service invocation. + /// Types should be resolved implicitly. + /// + [Test] + public void TestCallJavaService() + { + // Deploy Java service + var javaSvcName = TestUtils.DeployJavaService(_grid1); + + var svc = _grid1.GetServices().GetServiceProxy(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().Select(d => d.Name).ToArray()[0]); + + _grid1.GetServices().Cancel(javaSvcName); + } + + /// + /// Tests java service instance. + /// + 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); + + Assert.IsNull(svc.testEmployees(null)); + + Employee[] emps = svc.testEmployees(new[] + { + new Employee { Fio = "Sarah Connor", Salary = 1 }, + new Employee { Fio = "John Connor", Salary = 2 } + }); + + 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(); + + 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); + } + + /// + /// Starts the grids. + /// + private void StartGrids() + { + if (_grid1 != null) + return; + + var path = Path.Combine("Config", "Compute", "compute-grid"); + _grid1 = Ignition.Start(GetConfiguration(path + "1.xml")); + } + + /// + /// Stops the grids. + /// + private void StopGrids() + { + _grid1 = null; + + Ignition.StopAll(true); + } + + /// + /// Gets the Ignite configuration. + /// + private IgniteConfiguration GetConfiguration(string springConfigUrl) + { + springConfigUrl = ReplaceFooterSetting(springConfigUrl); + + return new IgniteConfiguration(TestUtils.GetTestConfiguration()) + { + SpringConfigUrl = springConfigUrl + }; + } + + /// + /// Replaces the footer setting. + /// + 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/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(failoverSocket, "_socket"); return GetPrivateField(socket, "_notificationListeners"); } + + /// + /// Deploys the Java service. + /// + public static string DeployJavaService(IIgnite ignite) + { + const string serviceName = "javaService"; + + ignite.GetCompute() + .ExecuteJavaTask("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/Impl/Binary/BinaryProcessor.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryProcessor.cs index b62977a53a02a..66380d67ccf2a 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryProcessor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryProcessor.cs @@ -103,8 +103,9 @@ public void PutBinaryTypes(ICollection types) /// /// The identifier. /// The type name. + /// True if should register type both for dotnet and java platforms. /// True if registration succeeded; otherwise, false. - 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 +114,7 @@ public bool RegisterType(int id, string typeName) { w.WriteInt(id); w.WriteString(typeName); + w.WriteBoolean(registerSameJavaType); }) == True; } 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..a45f95f66dc9e 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryProcessorClient.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryProcessorClient.cs @@ -28,6 +28,9 @@ namespace Apache.Ignite.Core.Impl.Binary /// internal class BinaryProcessorClient : IBinaryProcessor { + /** Java platform id. See org.apache.ignite.internal.MarshallerPlatformIds in Java. */ + private const byte JavaPlatformId = 0; + /** Type registry platform id. See org.apache.ignite.internal.MarshallerPlatformIds in Java. */ private const byte DotNetPlatformId = 1; @@ -73,14 +76,26 @@ public void PutBinaryTypes(ICollection types) } /** */ - 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.WriteInt(id); ctx.Writer.WriteString(typeName); }, ctx => ctx.Stream.ReadBool()); + + if (registerSameJavaType && res) + { + res = _socket.DoOutInOp(ClientOp.BinaryTypeNamePut, ctx => + { + ctx.Stream.WriteByte(JavaPlatformId); + ctx.Stream.WriteInt(id); + ctx.Writer.WriteString(typeName); + }, ctx => ctx.Stream.ReadBool()); + } + + return res; } /** */ 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 /// /// The identifier. /// The type name. + /// True if should register type both for dotnet and java platforms. /// True if registration succeeded; otherwise, false. - bool RegisterType(int id, string typeName); + bool RegisterType(int id, string typeName, bool registerSameJavaType); /// /// 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..0ed967d623ba8 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 /// internal class Marshaller { + /** Register same java type flag. */ + public static readonly ThreadLocal RegisterSameJavaType = new ThreadLocal(() => false); + /** Binary configuration. */ private readonly BinaryConfiguration _cfg; @@ -556,7 +560,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); } 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..66998cbb114fa 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxySerializer.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxySerializer.cs @@ -46,35 +46,45 @@ public static void WriteProxyMethod(BinaryWriter writer, string methodName, Meth { Debug.Assert(writer != null); - writer.WriteString(methodName); + Marshaller.RegisterSameJavaType.Value = true; - if (arguments != null) + try { - writer.WriteBoolean(true); - writer.WriteInt(arguments.Length); + writer.WriteString(methodName); - if (platformType == PlatformType.DotNet) + if (arguments != null) { - // Write as is for .NET. - foreach (var arg in arguments) + writer.WriteBoolean(true); + writer.WriteInt(arguments.Length); + + if (platformType == PlatformType.DotNet) { - writer.WriteObjectDetached(arg); + // Write as is for .NET. + foreach (var arg in arguments) + { + writer.WriteObjectDetached(arg); + } } - } - else - { - // Other platforms do not support Serializable, need to convert arrays and collections - var mParams = method != null ? method.GetParameters() : null; - Debug.Assert(mParams == null || mParams.Length == arguments.Length); - - for (var i = 0; i < arguments.Length; i++) + else { - WriteArgForPlatforms(writer, mParams != null ? mParams[i].ParameterType : null, arguments[i]); + // Other platforms do not support Serializable, need to convert arrays and collections + var mParams = method != null ? method.GetParameters() : null; + Debug.Assert(mParams == null || mParams.Length == arguments.Length); + + for (var i = 0; i < arguments.Length; i++) + { + WriteArgForPlatforms(writer, mParams != null ? mParams[i].ParameterType : null, + arguments[i]); + } } } + else + writer.WriteBoolean(false); + } + finally + { + Marshaller.RegisterSameJavaType.Value = false; } - else - writer.WriteBoolean(false); } /// @@ -265,6 +275,9 @@ private static Action 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); From 7d6b3d1657a3997efdc673e30898d16933068bf8 Mon Sep 17 00:00:00 2001 From: Nikolay Date: Sat, 19 Dec 2020 00:22:34 +0300 Subject: [PATCH 2/6] IGNITE-13865 Support DateTime as a key or value in .NET and Java (#8580) (cherry picked from commit 685c1b70ca6d48e18991514531f22d789464f62b) --- .../platform/PlatformDeployServiceTask.java | 35 ++++ .../Services/IJavaService.cs | 11 +- .../Services/JavaServiceDynamicProxy.cs | 18 ++ .../Services/Model.cs | 6 +- .../Services/ServicesTest.cs | 179 ++++-------------- .../Binary/BinaryConfiguration.cs | 28 ++- .../Binary/BinaryReflectiveSerializer.cs | 22 +-- .../IgniteClientConfigurationSection.xsd | 5 + .../IgniteConfigurationSection.xsd | 5 + .../Impl/Binary/BinarySystemHandlers.cs | 35 +++- .../Impl/Binary/BinaryWriter.cs | 4 +- .../Impl/Binary/Marshaller.cs | 29 ++- .../Impl/Services/ServiceProxySerializer.cs | 5 +- 13 files changed, 204 insertions(+), 178 deletions(-) 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 bb00ecb7fa027..5197a417c6ea8 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 @@ -19,14 +19,17 @@ import java.sql.Timestamp; 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; @@ -92,6 +95,9 @@ private PlatformDeployServiceJob(String serviceName) { * Test service. */ public static class PlatformTestService implements Service { + @IgniteInstanceResource + private Ignite ignite; + /** */ private boolean isCancelled; @@ -492,6 +498,35 @@ public Map testMap(Map map) { return m; } + /** */ + 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 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 sleep(long delayMs) { try { diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs index 8c9e6b6755b3e..dfcaaff51e38b 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs @@ -176,7 +176,16 @@ public interface IJavaService /** */ IDictionary testMap(IDictionary dict); - + + /** */ + void testDateArray(DateTime?[] dates); + + /** */ + DateTime testDate(DateTime date); + + /** */ + void testUTCDateFromCache(); + /** */ 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 index 3276b838500ba..26c06373a44e0 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/JavaServiceDynamicProxy.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/JavaServiceDynamicProxy.cs @@ -331,6 +331,24 @@ public IDictionary testMap(IDictionary dict) return _svc.testMap(dict); } + /** */ + public void testDateArray(DateTime?[] dates) + { + _svc.testDateArray(dates); + } + + /** */ + public DateTime testDate(DateTime date) + { + return _svc.testDate(date); + } + + /** */ + public void testUTCDateFromCache() + { + _svc.testDateFromCache(); + } + /** */ public void sleep(long 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 index 584511cd06528..0793e874de466 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/Model.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/Model.cs @@ -15,6 +15,7 @@ * limitations under the License. */ +// ReSharper disable once CheckNamespace namespace org.apache.ignite.platform { /// @@ -28,7 +29,7 @@ public class Address /** */ public string Addr { get; set; } } - + /// /// A class is a clone of Java class Department with the same namespace. /// @@ -37,7 +38,7 @@ public class Department /** */ public string Name { get; set; } } - + /// /// A class is a clone of Java class Employee with the same namespace. /// @@ -72,6 +73,7 @@ public override bool Equals(object obj) public override int GetHashCode() { + // ReSharper disable once NonReadonlyMemberInGetHashCode return Id.GetHashCode(); } } 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..00d1c2b964b3f 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; @@ -959,6 +957,23 @@ public void TestCallJavaService() Assert.AreEqual(new[] {11, 12, 13}, binSvc.testBinaryObjectArray(binArr) .Select(x => x.GetField("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("net-dates"); + + cache.Put(1, dt1); + cache.Put(2, dt2); + + svc.testUTCDateFromCache(); + + Assert.AreEqual(dt1, cache.Get(3)); + Assert.AreEqual(dt2, cache.Get(4)); + Services.Cancel(javaSvcName); } @@ -1042,6 +1057,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("net-dates"); + + cache.Put(1, dt1); + cache.Put(2, dt2); + + svc.testUTCDateFromCache(); + + Assert.AreEqual(dt1, cache.Get(3)); + Assert.AreEqual(dt2, cache.Get(4)); } /// @@ -1139,7 +1169,8 @@ private IgniteConfiguration GetConfiguration(string springConfigUrl) typeof (PlatformComputeBinarizable), typeof (BinarizableObject)) { - NameMapper = BinaryBasicNameMapper.SimpleNameInstance + NameMapper = BinaryBasicNameMapper.SimpleNameInstance, + ForceTimestamp = true } }; } @@ -1505,148 +1536,6 @@ private class BinarizableObject public int Val { get; set; } } - /// - /// Java service proxy interface. - /// - [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); - } - /// /// Interop class. /// diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryConfiguration.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryConfiguration.cs index 32d2c9d6f5d15..2a2bb9579512e 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 /// public const bool DefaultKeepDeserialized = true; + /// + /// Default setting. + /// + public const bool DefaultForceTimestamp = false; + /** Footer setting. */ private bool? _compactFooter; @@ -49,6 +54,7 @@ public class BinaryConfiguration public BinaryConfiguration() { KeepDeserialized = DefaultKeepDeserialized; + ForceTimestamp = DefaultForceTimestamp; } /// @@ -72,6 +78,7 @@ internal void CopyLocalProperties(BinaryConfiguration cfg) IdMapper = cfg.IdMapper; NameMapper = cfg.NameMapper; KeepDeserialized = cfg.KeepDeserialized; + ForceTimestamp = cfg.ForceTimestamp; if (cfg.Serializer != null) { @@ -106,7 +113,7 @@ public BinaryConfiguration(params Type[] binaryTypes) public ICollection TypeConfigurations { get; set; } /// - /// Gets or sets a collection of assembly-qualified type names + /// Gets or sets a collection of assembly-qualified type names /// (the result of ) for binarizable types. /// /// Shorthand for creating . @@ -137,12 +144,12 @@ public BinaryConfiguration(params Type[] binaryTypes) /// /// 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. /// /// WARNING! 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. /// [DefaultValue(DefaultCompactFooter)] @@ -152,6 +159,21 @@ public bool CompactFooter set { _compactFooter = value; } } + /// + /// Gets or sets a value indicating whether all DateTime keys, values and object fields + /// should be written as a Timestamp. + /// + /// 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. + /// + /// Normally Ignite serializer uses for DateTime fields, + /// keys and values. + /// This attribute changes the behavior to . + /// + /// See also , . + /// + public bool ForceTimestamp { get; set; } + /// /// Gets the compact footer internal nullable value. /// 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; /// - /// Binary serializer which reflectively writes all fields except of ones with + /// Binary serializer which reflectively writes all fields except of ones with /// . /// - /// 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 ReadTimestamp(..., true/false) 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 ReadTimestamp(..., true/false) methods in /// and . /// 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. /// public sealed class BinaryReflectiveSerializer : IBinarySerializer { @@ -94,7 +94,7 @@ public bool RawMode /// Normally serializer uses for DateTime fields. /// This attribute changes the behavior to . /// - /// See also . + /// See also , . /// public bool ForceTimestamp { diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteClientConfigurationSection.xsd b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteClientConfigurationSection.xsd index ba64aca369216..ada1abfbfd3d9 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteClientConfigurationSection.xsd +++ b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteClientConfigurationSection.xsd @@ -170,6 +170,11 @@ Compact footer flag. + + + Force timestamp flag. + + diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd index 0ab59ec78bea8..160d095086b3c 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd +++ b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd @@ -274,6 +274,11 @@ Compact footer flag. + + + Force timestamp flag. + + 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..3ffb1f978b680 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemHandlers.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemHandlers.cs @@ -122,9 +122,18 @@ static BinarySystemHandlers() /// Try getting write handler for type. /// /// + /// /// - public static IBinarySystemWriteHandler GetWriteHandler(Type type) + public static IBinarySystemWriteHandler GetWriteHandler(Type type, bool forceTimestamp) { + if (forceTimestamp) + { + if (type == typeof(DateTime)) + return new BinarySystemWriteHandler(WriteTimestamp, false); + if (type == typeof(DateTime?[])) + return new BinarySystemWriteHandler(WriteTimestampArray, true); + } + return WriteHandlers.GetOrAdd(type, t => { return FindWriteHandler(t); @@ -293,6 +302,18 @@ private static void WriteGuid(BinaryWriter ctx, Guid obj) BinaryUtils.WriteGuid(obj, ctx.Stream); } + /// + /// Write Date. + /// + /// Context. + /// Value. + private static void WriteTimestamp(BinaryWriter ctx, DateTime obj) + { + ctx.Stream.WriteByte(BinaryTypeId.Timestamp); + + BinaryUtils.WriteTimestamp(obj, ctx.Stream); + } + /// /// Write boolaen array. /// @@ -425,6 +446,18 @@ private static void WriteGuidArray(BinaryWriter ctx, Guid?[] obj) BinaryUtils.WriteGuidArray(obj, ctx.Stream); } + /// + /// Write nullable Date array. + /// + /// Context. + /// Value. + private static void WriteTimestampArray(BinaryWriter ctx, DateTime?[] obj) + { + ctx.Stream.WriteByte(BinaryTypeId.ArrayTimestamp); + + BinaryUtils.WriteTimestampArray(obj, ctx.Stream); + } + /// /// Writes the enum array. /// 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..d10807a5ff768 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs @@ -875,7 +875,7 @@ public void WriteEnum(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 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/Marshaller.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs index 0ed967d623ba8..ba8aac4affa7e 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs @@ -136,6 +136,14 @@ public IIgniteInternal Ignite /// public bool RegistrationDisabled { get; set; } + /// + /// Gets force timestamp flag value. + /// + public bool ForceTimestamp + { + get { return _cfg.ForceTimestamp; } + } + /// /// Marshal object. /// @@ -376,7 +384,7 @@ private BinaryTypeHolder GetBinaryTypeHolder(IBinaryTypeDescriptor desc) { return holder; } - + lock (this) { if (!_metas.TryGetValue(desc.TypeId, out holder)) @@ -394,9 +402,9 @@ private BinaryTypeHolder GetBinaryTypeHolder(IBinaryTypeDescriptor desc) return holder; } - + /// - /// Updates or creates cached binary type holder. + /// Updates or creates cached binary type holder. /// private void UpdateOrCreateBinaryTypeHolder(BinaryType meta) { @@ -406,7 +414,7 @@ private void UpdateOrCreateBinaryTypeHolder(BinaryType meta) holder.Merge(meta); return; } - + lock (this) { if (_metas.TryGetValue(meta.TypeId, out holder)) @@ -414,7 +422,7 @@ private void UpdateOrCreateBinaryTypeHolder(BinaryType meta) holder.Merge(meta); return; } - + var metas0 = new Dictionary(_metas); holder = new BinaryTypeHolder(meta.TypeId, meta.TypeName, meta.AffinityKeyFieldName, meta.IsEnum, this); @@ -696,7 +704,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; @@ -871,16 +882,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(); 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 66998cbb114fa..16e3c4794712f 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxySerializer.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxySerializer.cs @@ -267,7 +267,7 @@ private static Action 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; @@ -281,9 +281,6 @@ private static Action GetPlatformArgWriter(Type paramType, if (arg is ICollection) return (writer, o) => writer.WriteCollection((ICollection) o); - if (arg is DateTime) - return (writer, o) => writer.WriteTimestamp((DateTime) o); - return null; } } From 33cf17ee698fef1a68717abf80934faa59755116 Mon Sep 17 00:00:00 2001 From: Nikolay Date: Thu, 24 Dec 2020 15:00:16 +0300 Subject: [PATCH 3/6] IGNITE-12824 .NET: Add BinaryConfiguration.TimestampConverter (#8568) Co-authored-by: Pavel Tupitsyn (cherry picked from commit 76eda8dba406754635599b0cea55bbffb4137efe) --- .../platform/PlatformDeployServiceTask.java | 20 ++ .../Binary/BinaryDateTimeTest.cs | 213 +++++++++++++++++- .../Services/IJavaService.cs | 3 + .../Services/JavaServiceDynamicProxy.cs | 6 + .../Services/ServicesTest.cs | 49 ++++ .../Apache.Ignite.Core.csproj | 3 + .../Binary/BinaryConfiguration.cs | 12 +- .../Binary/ITimestampConverter.cs | 38 ++++ .../IgniteClientConfigurationSection.xsd | 12 + .../IgniteConfigurationSection.xsd | 12 + .../Impl/Binary/BinaryReader.cs | 8 +- .../Impl/Binary/BinarySystemHandlers.cs | 25 +- .../Impl/Binary/BinaryUtils.cs | 30 ++- .../Impl/Binary/BinaryWriter.cs | 8 +- .../Impl/Binary/Marshaller.cs | 8 + 15 files changed, 414 insertions(+), 33 deletions(-) create mode 100644 modules/platforms/dotnet/Apache.Ignite.Core/Binary/ITimestampConverter.cs 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 5197a417c6ea8..a0d20fe0092ae 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,6 +18,8 @@ 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; @@ -527,6 +529,24 @@ public void testUTCDateFromCache() { assert new Timestamp(new Date(91, Calendar.OCTOBER, 1, 0, 0, 0).getTime()).equals(cache.get(2)); } + /** */ + public void testLocalDateFromCache() { + IgniteCache 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 { 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; /// @@ -28,6 +29,16 @@ namespace Apache.Ignite.Core.Tests.Binary /// 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."; + /// /// Sets up the test fixture. /// @@ -80,7 +91,7 @@ public void TestSerializerForceTimestamp() .Select(x => x.Serializer) .OfType() .Single(); - + Assert.IsTrue(ser.ForceTimestamp); AssertTimestampField((o, d) => o.Value = d, o => o.Value, "Value"); @@ -110,6 +121,152 @@ public void TestClassAttributes() AssertTimestampField((o, d) => o.Value = d, o => o.Value, "Value"); } + /// + /// 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. + /// + [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(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( + 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(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()); + } + + /// + /// Tests custom timestamp converter. + /// + [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((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(() => binary.ToBinary(dt1)); + Assert.AreEqual(ToErrMsg, ex.Message); + + ex = Assert.Throws(() => binary.ToBinary(new DateTime?[] {dt1})); + Assert.AreEqual(ToErrMsg, ex.Message); + + var dt2 = new DateTime(1997, 8, 4, 0, 0, 0, DateTimeKind.Utc); + + ex = Assert.Throws(() => binary.ToBinary(dt2)); + Assert.AreEqual(FromErrMsg, ex.Message); + + ex = Assert.Throws(() => binary.ToBinary(new DateTime?[] {dt2})); + Assert.AreEqual(FromErrMsg, ex.Message); + + var datesCache = ignite.CreateCache("dates"); + + var check = new Action((date1, date2, errMsg) => + { + ex = Assert.Throws(() => 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(() => 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("datesObj"); + + check = (date1, date2, errMsg) => + { + ex = Assert.Throws(() => 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(() => 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); + } + /// /// Asserts that specified field is serialized as DateTime object. /// @@ -136,10 +293,10 @@ private static void AssertDateTimeField(Action setValue, /// Asserts that specified field is serialized as Timestamp. /// private static void AssertTimestampField(Action setValue, - Func getValue, string fieldName) where T : new() + Func 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(Action setValue, var ex = Assert.Throws(() => binary.ToBinary(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 + { + /** */ + 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); + } + + /** */ + 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 + { + /** */ + public void ToJavaTicks(DateTime date, out long high, out int low) + { + BinaryUtils.ToJavaDate(date.AddYears(1), out high, out low); + } + + /** */ + 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/Services/IJavaService.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs index dfcaaff51e38b..07db779b2c8c4 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs @@ -186,6 +186,9 @@ public interface IJavaService /** */ 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 index 26c06373a44e0..41566020355e9 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/JavaServiceDynamicProxy.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/JavaServiceDynamicProxy.cs @@ -349,6 +349,12 @@ public void testUTCDateFromCache() _svc.testDateFromCache(); } + /** */ + public void testLocalDateFromCache() + { + _svc.testLocalDateFromCache(); + } + /** */ public void sleep(long delayMs) { 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 00d1c2b964b3f..e978dc7275856 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs @@ -27,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; @@ -974,6 +975,28 @@ public void TestCallJavaService() 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); } @@ -1171,6 +1194,9 @@ private IgniteConfiguration GetConfiguration(string springConfigUrl) { NameMapper = BinaryBasicNameMapper.SimpleNameInstance, ForceTimestamp = true +#if NETCOREAPP + , TimestampConverter = new TimestampConverter() +#endif } }; } @@ -1553,5 +1579,28 @@ public class PlatformComputeBinarizable2 /** */ public int Field { get; set; } } + +#if NETCOREAPP + /// + /// Adds support of the local dates to the Ignite timestamp serialization. + /// + class TimestampConverter : ITimestampConverter + { + /** */ + 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); + } + + /** */ + 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/Apache.Ignite.Core.csproj b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj index 96d17d7fff9a2..110e849a8e423 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,9 @@ + + + diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryConfiguration.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryConfiguration.cs index 2a2bb9579512e..b25a9f58420aa 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryConfiguration.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryConfiguration.cs @@ -79,6 +79,7 @@ internal void CopyLocalProperties(BinaryConfiguration cfg) NameMapper = cfg.NameMapper; KeepDeserialized = cfg.KeepDeserialized; ForceTimestamp = cfg.ForceTimestamp; + TimestampConverter = cfg.TimestampConverter; if (cfg.Serializer != null) { @@ -136,6 +137,15 @@ public BinaryConfiguration(params Type[] binaryTypes) /// public IBinarySerializer Serializer { get; set; } + /// + /// Gets or sets a converter between and Java Timestamp. + /// Called from , , + /// , . + /// + /// See also . + /// + public ITimestampConverter TimestampConverter { get; set; } + /// /// Default keep deserialized flag. /// @@ -164,7 +174,7 @@ public bool CompactFooter /// should be written as a Timestamp. /// /// 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. + /// Only UTC values are supported in Timestamp format. Other values will cause an exception on write, unless is provided. /// /// Normally Ignite serializer uses for DateTime fields, /// keys and values. 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; + + /// + /// Converts values to Java Timestamp and back. + /// + public interface ITimestampConverter + { + /// Converts date to Java ticks. + /// Date + /// High part (milliseconds). + /// Low part (nanoseconds) + void ToJavaTicks(DateTime date, out long high, out int low); + + /// Converts date from Java ticks. + /// High part (milliseconds). + /// Low part (nanoseconds) + 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 ada1abfbfd3d9..c0314b3488609 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteClientConfigurationSection.xsd +++ b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteClientConfigurationSection.xsd @@ -159,6 +159,18 @@ + + + Default date time converter. + + + + + Assembly-qualified type name. + + + + diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd index 160d095086b3c..7794c21ce234e 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd +++ b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd @@ -263,6 +263,18 @@ + + + Default date time converter. + + + + + Assembly-qualified type name. + + + + 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() /** */ public DateTime? ReadTimestamp(string fieldName) { - return ReadField(fieldName, BinaryUtils.ReadTimestamp, BinaryTypeId.Timestamp); + return ReadField(fieldName, stream => BinaryUtils.ReadTimestamp(stream, _marsh.TimestampConverter), BinaryTypeId.Timestamp); } /** */ public DateTime? ReadTimestamp() { - return Read(BinaryUtils.ReadTimestamp, BinaryTypeId.Timestamp); + return Read(stream => BinaryUtils.ReadTimestamp(stream, _marsh.TimestampConverter), BinaryTypeId.Timestamp); } /** */ public DateTime?[] ReadTimestampArray(string fieldName) { - return ReadField(fieldName, BinaryUtils.ReadTimestampArray, BinaryTypeId.ArrayTimestamp); + return ReadField(fieldName, stream => BinaryUtils.ReadTimestampArray(stream, _marsh.TimestampConverter), BinaryTypeId.ArrayTimestamp); } /** */ public DateTime?[] ReadTimestampArray() { - return Read(BinaryUtils.ReadTimestampArray, BinaryTypeId.ArrayTimestamp); + return Read(stream => BinaryUtils.ReadTimestampArray(stream, _marsh.TimestampConverter), BinaryTypeId.ArrayTimestamp); } /** */ 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 3ffb1f978b680..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(s => s.ReadDouble()); ReadHandlers[BinaryTypeId.Decimal] = new BinarySystemReader(BinaryUtils.ReadDecimal); - // 2. Date. - ReadHandlers[BinaryTypeId.Timestamp] = new BinarySystemReader(BinaryUtils.ReadTimestamp); - // 3. String. ReadHandlers[BinaryTypeId.String] = new BinarySystemReader(BinaryUtils.ReadString); @@ -92,10 +89,6 @@ static BinarySystemHandlers() ReadHandlers[BinaryTypeId.ArrayDecimal] = new BinarySystemReader(BinaryUtils.ReadDecimalArray); - // 6. Date array. - ReadHandlers[BinaryTypeId.ArrayTimestamp] = - new BinarySystemReader(BinaryUtils.ReadTimestampArray); - // 7. String array. ReadHandlers[BinaryTypeId.ArrayString] = new BinarySystemTypedArrayReader(); @@ -258,6 +251,20 @@ public static bool TryReadSystemType(byte typeId, BinaryReader ctx, out T res if (handler == null) { + if (typeId == BinaryTypeId.Timestamp) + { + // Date. + res = TypeCaster.Cast(BinaryUtils.ReadTimestamp(ctx.Stream, ctx.Marshaller.TimestampConverter)); + return true; + } + + if (typeId == BinaryTypeId.ArrayTimestamp) + { + // Date array. + res = TypeCaster.Cast(BinaryUtils.ReadTimestampArray(ctx.Stream, ctx.Marshaller.TimestampConverter)); + return true; + } + res = default(T); return false; } @@ -311,7 +318,7 @@ private static void WriteTimestamp(BinaryWriter ctx, DateTime obj) { ctx.Stream.WriteByte(BinaryTypeId.Timestamp); - BinaryUtils.WriteTimestamp(obj, ctx.Stream); + BinaryUtils.WriteTimestamp(obj, ctx.Stream, ctx.Marshaller.TimestampConverter); } /// @@ -455,7 +462,7 @@ private static void WriteTimestampArray(BinaryWriter ctx, DateTime?[] obj) { ctx.Stream.WriteByte(BinaryTypeId.ArrayTimestamp); - BinaryUtils.WriteTimestampArray(obj, ctx.Stream); + BinaryUtils.WriteTimestampArray(obj, ctx.Stream, ctx.Marshaller.TimestampConverter); } /// 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) * Write date. * Date. * Stream. + * Timestamp Converter. */ - 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) /** * Read date. * Stream. + * Timestamp Converter. * Date */ - 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); } /// @@ -434,7 +442,8 @@ public static long DateTimeToJavaTicks(DateTime dateTime) /// /// Values. /// Stream. - public static void WriteTimestampArray(DateTime?[] vals, IBinaryStream stream) + /// Timestamp Converter. + 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(BinaryReader ctx, bool typed) /// Read timestamp array. /// /// Stream. + /// Timestamp Converter. /// Timestamp array. - 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) * High part (milliseconds). * Low part (nanoseconds) */ - 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 d10807a5ff768..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); } } 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 ba8aac4affa7e..51c6a2151cb7b 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs @@ -144,6 +144,14 @@ public bool ForceTimestamp get { return _cfg.ForceTimestamp; } } + /// + /// Gets date time converter. + /// + public ITimestampConverter TimestampConverter + { + get { return _cfg.TimestampConverter; } + } + /// /// Marshal object. /// From 4e9a396e685cc60df6db9ff153858767ccca63cb Mon Sep 17 00:00:00 2001 From: Nikolay Date: Fri, 25 Dec 2020 22:52:13 +0300 Subject: [PATCH 4/6] IGNITE-13734 .NET: Register service return type on method invocation (#8602) (cherry picked from commit 6d02e32e7f049e4f78f7abd37f4ff91d77f738c2) --- .../binary/PlatformBinaryProcessor.java | 3 +- .../platform/PlatformDeployServiceTask.java | 25 +++++++ .../org/apache/ignite/platform/model/ACL.java | 23 ++++++ .../apache/ignite/platform/model/Account.java | 71 +++++++++++++++++++ .../ignite/platform/{ => model}/Address.java | 2 +- .../platform/{ => model}/Department.java | 2 +- .../ignite/platform/{ => model}/Employee.java | 2 +- .../ignite/platform/{ => model}/Key.java | 2 +- .../apache/ignite/platform/model/Role.java | 39 ++++++++++ .../apache/ignite/platform/model/User.java | 67 +++++++++++++++++ .../ignite/platform/{ => model}/Value.java | 2 +- .../Services/IJavaService.cs | 8 ++- .../Services/JavaServiceDynamicProxy.cs | 12 +++- .../Services/Model.cs | 61 +++++++++++++++- .../Services/ServiceProxyTest.cs | 41 ++++++----- .../Services/ServiceTypeAutoResolveTest.cs | 26 ++++++- .../Impl/Binary/BinaryProcessor.cs | 39 +++++++++- .../Impl/Binary/BinaryProcessorClient.cs | 12 +--- .../Impl/Binary/Marshaller.cs | 2 +- .../Impl/Services/ServiceProxySerializer.cs | 49 ++++++------- .../Impl/Services/Services.cs | 18 +++-- 21 files changed, 434 insertions(+), 72 deletions(-) create mode 100644 modules/core/src/test/java/org/apache/ignite/platform/model/ACL.java create mode 100644 modules/core/src/test/java/org/apache/ignite/platform/model/Account.java rename modules/core/src/test/java/org/apache/ignite/platform/{ => model}/Address.java (96%) rename modules/core/src/test/java/org/apache/ignite/platform/{ => model}/Department.java (96%) rename modules/core/src/test/java/org/apache/ignite/platform/{ => model}/Employee.java (96%) rename modules/core/src/test/java/org/apache/ignite/platform/{ => model}/Key.java (97%) create mode 100644 modules/core/src/test/java/org/apache/ignite/platform/model/Role.java create mode 100644 modules/core/src/test/java/org/apache/ignite/platform/model/User.java rename modules/core/src/test/java/org/apache/ignite/platform/{ => model}/Value.java (97%) 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 4c5c1226f8af8..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 @@ -134,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/test/java/org/apache/ignite/platform/PlatformDeployServiceTask.java b/modules/core/src/test/java/org/apache/ignite/platform/PlatformDeployServiceTask.java index a0d20fe0092ae..3df5acd6c8b4e 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 @@ -41,6 +41,15 @@ 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; @@ -500,6 +509,22 @@ public Map testMap(Map map) { 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; 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/Address.java b/modules/core/src/test/java/org/apache/ignite/platform/model/Address.java similarity index 96% rename from modules/core/src/test/java/org/apache/ignite/platform/Address.java rename to modules/core/src/test/java/org/apache/ignite/platform/model/Address.java index 39390e859492d..42442cfcebd5d 100644 --- a/modules/core/src/test/java/org/apache/ignite/platform/Address.java +++ b/modules/core/src/test/java/org/apache/ignite/platform/model/Address.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.ignite.platform; +package org.apache.ignite.platform.model; /** Test value object. */ public class Address { diff --git a/modules/core/src/test/java/org/apache/ignite/platform/Department.java b/modules/core/src/test/java/org/apache/ignite/platform/model/Department.java similarity index 96% rename from modules/core/src/test/java/org/apache/ignite/platform/Department.java rename to modules/core/src/test/java/org/apache/ignite/platform/model/Department.java index a1aa9b77f237e..b1f74f3731e0a 100644 --- a/modules/core/src/test/java/org/apache/ignite/platform/Department.java +++ b/modules/core/src/test/java/org/apache/ignite/platform/model/Department.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.ignite.platform; +package org.apache.ignite.platform.model; /** Test value object. */ public class Department { diff --git a/modules/core/src/test/java/org/apache/ignite/platform/Employee.java b/modules/core/src/test/java/org/apache/ignite/platform/model/Employee.java similarity index 96% rename from modules/core/src/test/java/org/apache/ignite/platform/Employee.java rename to modules/core/src/test/java/org/apache/ignite/platform/model/Employee.java index 03d99a6e6ad32..6c63c29d684b1 100644 --- a/modules/core/src/test/java/org/apache/ignite/platform/Employee.java +++ b/modules/core/src/test/java/org/apache/ignite/platform/model/Employee.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.ignite.platform; +package org.apache.ignite.platform.model; /** Test value object. */ public class Employee { diff --git a/modules/core/src/test/java/org/apache/ignite/platform/Key.java b/modules/core/src/test/java/org/apache/ignite/platform/model/Key.java similarity index 97% rename from modules/core/src/test/java/org/apache/ignite/platform/Key.java rename to modules/core/src/test/java/org/apache/ignite/platform/model/Key.java index 26c578709bd0c..218d9c0b1e1e8 100644 --- a/modules/core/src/test/java/org/apache/ignite/platform/Key.java +++ b/modules/core/src/test/java/org/apache/ignite/platform/model/Key.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.ignite.platform; +package org.apache.ignite.platform.model; import java.util.Objects; 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/Value.java b/modules/core/src/test/java/org/apache/ignite/platform/model/Value.java similarity index 97% rename from modules/core/src/test/java/org/apache/ignite/platform/Value.java rename to modules/core/src/test/java/org/apache/ignite/platform/model/Value.java index 09502918251b7..7143bff97cc48 100644 --- a/modules/core/src/test/java/org/apache/ignite/platform/Value.java +++ b/modules/core/src/test/java/org/apache/ignite/platform/model/Value.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.ignite.platform; +package org.apache.ignite.platform.model; import java.util.Objects; diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs index 07db779b2c8c4..42b1cfe4e5422 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs @@ -22,7 +22,7 @@ namespace Apache.Ignite.Core.Tests.Services using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using Apache.Ignite.Core.Binary; - using org.apache.ignite.platform; + using org.apache.ignite.platform.model; /// /// Java service proxy interface. @@ -170,6 +170,12 @@ public interface IJavaService /** */ Employee[] testEmployees(Employee[] emps); + + /** */ + Account[] testAccounts(); + + /** */ + User[] testUsers(); /** */ ICollection testDepartments(ICollection deps); diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/JavaServiceDynamicProxy.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/JavaServiceDynamicProxy.cs index 41566020355e9..723e67cfb2244 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/JavaServiceDynamicProxy.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/JavaServiceDynamicProxy.cs @@ -21,7 +21,7 @@ namespace Apache.Ignite.Core.Tests.Services using System.Collections; using System.Collections.Generic; using Apache.Ignite.Core.Binary; - using org.apache.ignite.platform; + using org.apache.ignite.platform.model; /// /// Explicit service proxy over dynamic variable. @@ -319,6 +319,16 @@ public Employee[] testEmployees(Employee[] emps) return _svc.testEmployees(emps); } + public Account[] testAccounts() + { + return _svc.testAccounts(); + } + + public User[] testUsers() + { + return _svc.testUsers(); + } + /** */ public ICollection testDepartments(ICollection deps) { diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/Model.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/Model.cs index 0793e874de466..275dc7dace2ec 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/Model.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/Model.cs @@ -16,8 +16,10 @@ */ // ReSharper disable once CheckNamespace -namespace org.apache.ignite.platform +namespace org.apache.ignite.platform.model { + using System; + /// /// A class is a clone of Java class Address with the same namespace. /// @@ -85,4 +87,61 @@ public class Value { public string Val { get; set; } } + + /// + /// A class is a clone of Java class Account with the same namespace. + /// + 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(); + } + } + + /// + /// A enum is a clone of Java class User with the same namespace. + /// + public enum ACL + { + Allow, Deny + } + + /// + /// A class is a clone of Java class Role with the same namespace. + /// + public class Role + { + public String Name { get; set; } + } + + /// + /// A class is a clone of Java class User with the same namespace. + /// + 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 index 1ae518724652e..f36b72d8696d5 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServiceTypeAutoResolveTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServiceTypeAutoResolveTest.cs @@ -23,7 +23,7 @@ namespace Apache.Ignite.Core.Tests.Services using System.IO; using System.Linq; using NUnit.Framework; - using org.apache.ignite.platform; + using org.apache.ignite.platform.model; /// /// Tests checks ability to execute service method without explicit registration of parameter type. @@ -97,11 +97,11 @@ public void TestCallJavaService() { // Deploy Java service var javaSvcName = TestUtils.DeployJavaService(_grid1); - + var svc = _grid1.GetServices().GetServiceProxy(javaSvcName, false); doTestService(svc); - + Assert.IsNull(svc.testDepartments(null)); var arr = new[] {"HR", "IT"}.Select(x => new Department() {Name = x}).ToArray(); @@ -153,6 +153,26 @@ private void doTestService(IJavaService svc) 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); } /// 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 66380d67ccf2a..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 /// 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; + /// /// Op codes. /// @@ -162,7 +168,38 @@ public BinaryType RegisterEnum(string typeName, IEnumerableType or null. 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; + } + + /// + /// Gets the type name by id for specific platform. + /// + /// The identifier. + /// Platform identifier. + /// Type or null. + private string GetTypeName(int id, byte platformId) + { + return DoOutInOp((int) Op.GetType, w => + { + w.WriteInt(id); + w.WriteByte(platformId); + }, r => Marshaller.StartUnmarshal(r).ReadString()); } /// 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 a45f95f66dc9e..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,12 +28,6 @@ namespace Apache.Ignite.Core.Impl.Binary /// internal class BinaryProcessorClient : IBinaryProcessor { - /** Java platform id. See org.apache.ignite.internal.MarshallerPlatformIds in Java. */ - private const byte JavaPlatformId = 0; - - /** Type registry platform id. See org.apache.ignite.internal.MarshallerPlatformIds in Java. */ - private const byte DotNetPlatformId = 1; - /** Socket. */ private readonly ClientFailoverSocket _socket; @@ -80,7 +74,7 @@ public bool RegisterType(int id, string typeName, bool registerSameJavaType) { 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()); @@ -89,7 +83,7 @@ public bool RegisterType(int id, string typeName, bool registerSameJavaType) { res = _socket.DoOutInOp(ClientOp.BinaryTypeNamePut, ctx => { - ctx.Stream.WriteByte(JavaPlatformId); + ctx.Stream.WriteByte(BinaryProcessor.JavaPlatformId); ctx.Stream.WriteInt(id); ctx.Writer.WriteString(typeName); }, ctx => ctx.Stream.ReadBool()); @@ -109,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/Marshaller.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs index 51c6a2151cb7b..d09efbed5fcce 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs @@ -909,7 +909,7 @@ public void OnClientReconnected(bool clusterRestarted) /// /// Gets the name of the type. /// - 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 16e3c4794712f..e28f4d78ccc65 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxySerializer.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxySerializer.cs @@ -46,45 +46,36 @@ public static void WriteProxyMethod(BinaryWriter writer, string methodName, Meth { Debug.Assert(writer != null); - Marshaller.RegisterSameJavaType.Value = true; + writer.WriteString(methodName); - try + if (arguments != null) { - writer.WriteString(methodName); + writer.WriteBoolean(true); + writer.WriteInt(arguments.Length); - if (arguments != null) + if (platformType == PlatformType.DotNet) { - writer.WriteBoolean(true); - writer.WriteInt(arguments.Length); - - if (platformType == PlatformType.DotNet) + // Write as is for .NET. + foreach (var arg in arguments) { - // Write as is for .NET. - foreach (var arg in arguments) - { - writer.WriteObjectDetached(arg); - } + writer.WriteObjectDetached(arg); } - else + } + else + { + // Other platforms do not support Serializable, need to convert arrays and collections + var mParams = method != null ? method.GetParameters() : null; + Debug.Assert(mParams == null || mParams.Length == arguments.Length); + + for (var i = 0; i < arguments.Length; i++) { - // Other platforms do not support Serializable, need to convert arrays and collections - var mParams = method != null ? method.GetParameters() : null; - Debug.Assert(mParams == null || mParams.Length == arguments.Length); - - 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]); } } - else - writer.WriteBoolean(false); - } - finally - { - Marshaller.RegisterSameJavaType.Value = false; } + else + writer.WriteBoolean(false); } /// 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; + } + } /// From 50baf165c3811436dcadc870c33f7b1a388d5a5a Mon Sep 17 00:00:00 2001 From: Nikolay Date: Tue, 29 Dec 2020 13:39:48 +0300 Subject: [PATCH 5/6] IGNITE-13897 .NET: Service can't assign correct type to passed array parameters (#8614) (cherry picked from commit c2204cda29e70294cc93756eabc844b64e07a42e) --- .../internal/binary/BinaryRawReaderEx.java | 7 ++ .../internal/binary/BinaryReaderExImpl.java | 7 +- .../ignite/internal/binary/BinaryUtils.java | 19 +++-- .../platform/services/PlatformServices.java | 13 ++- .../platform/utils/PlatformUtils.java | 3 + .../binary/BinaryMarshallerSelfTest.java | 84 +++++++++++-------- .../platform/PlatformDeployServiceTask.java | 19 +++++ .../Services/IJavaService.cs | 6 ++ .../Services/JavaServiceDynamicProxy.cs | 12 +++ .../Services/ServiceTypeAutoResolveTest.cs | 16 ++-- 10 files changed, 136 insertions(+), 50 deletions(-) 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/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 unwrapKnownCollection(Collection 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 action) throws Exception { + private void testReadDetachObjectProperly(Object obj, IgniteThrowableConsumer action, boolean deserialize) throws Exception { BinaryMarshaller marsh = binaryMarshaller(); BinaryHeapOutputStream os = new BinaryHeapOutputStream(1024); @@ -3032,7 +3065,7 @@ private void testReadDetachObjectProperly(Object obj, IgniteThrowableConsumer */ + public int testOverload(int count, Employee[] emps) + { + return _svc.testOverload(count, emps); + } + + /** */ + public int testOverload(int first, int second) + { + return _svc.testOverload(first, second); + } + /** */ public Employee[] testEmployees(Employee[] emps) { diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServiceTypeAutoResolveTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServiceTypeAutoResolveTest.cs index f36b72d8696d5..5b78a63f3bc96 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServiceTypeAutoResolveTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServiceTypeAutoResolveTest.cs @@ -127,13 +127,19 @@ private void doTestService(IJavaService svc) 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)); - Employee[] emps = svc.testEmployees(new[] - { - new Employee { Fio = "Sarah Connor", Salary = 1 }, - new Employee { Fio = "John Connor", Salary = 2 } - }); + emps = svc.testEmployees(emps); Assert.NotNull(emps); Assert.AreEqual(1, emps.Length); From d6a200fed13ae8834ef1f84f275d7ff7ba71d60a Mon Sep 17 00:00:00 2001 From: Nikolay Izhikov Date: Mon, 4 Jan 2021 13:03:13 +0300 Subject: [PATCH 6/6] IGN-2322: Compilation fix. --- .../dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj | 2 -- 1 file changed, 2 deletions(-) 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 110e849a8e423..2a42a70d68f78 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj @@ -53,8 +53,6 @@ - -