
This article will focus on how to create an Apache Ignite cluster that can support the reading and writing of user-defined objects in a common storage format. This is particularly useful in situations where applications need to work with objects but these objects will be accessed by different programming languages and frameworks. Apache Ignite supports a binary format that is particularly useful for this task. We will look at how to achieve the goal of interoperability using some short programming examples.
Background
An Apache Ignite cluster may consist of nodes from a number of different supported platforms. These supported platforms include Java, .NET and C++. In this article, we will look at some code examples using Java and .NET.
In many organizations, different departments and teams may be working with different programming languages and frameworks. However, there may be a need for a common storage format to allow various tools to access the same data. Apache Ignite provides the flexibility for development teams to continue working with their favorite programming languages and tools and have the ability to work with the same data in the cluster.
My colleague Pavel Tupitsyn has a nice write-up of how to create a multi-platform Ignite cluster using Java and .NET. I followed the same steps to set-up and configure IntelliJ IDEA and Microsoft Visual Studio for the projects in the two IDEs.
For my examples, I chose to use a person object with two fields called name and city_id of type string and integer, respectively. These fields represent the name of the person and the code of the city where they live. More fields could be added to represent other data. For example, in an airline application, the person may represent a customer and additional data such as frequent-flyer status could be stored. In a company application, the person may represent an employee and additional data such as employee number could be stored. There are many more fields that could be defined. For demo purposes, however, we will keep the examples quite simple.
Figure 1 shows the Java code for the Person class.
public class Person { private String name; private Integer city_id; public Person(String name, Integer city_id) { this.name = name; this.city_id = city_id; } public String name() { return name; } public Integer city_id() { return city_id; } }
Figure 1. Java Person class
Figure 2 shows the same in .NET.
internal class Person { private string name; private int city_id; public Person(string name, int city_id) { this.name = name; this.city_id = city_id; } public string Name() { return name; } public int City_id() { return city_id; } }
Figure 2. .NET Person class
We can see the use of the same names and data types for the classes and their variables in both Java and .NET.
For the demos, there are four short applications. There is a read and write application for both Java and .NET. The goal is to show writing to the cache using Java and reading from the cache using .NET. Then repeat this by writing to the cache using .NET and reading from the cache using Java.
Modify the Java node configuration to co-exist with .NET nodes
The following configuration parameter has to be added to all Java cluster nodes so that they can co-exist with .NET counterparts:
// Configure Ignite to connect with .NET nodes IgniteConfiguration cfg = new IgniteConfiguration().setBinaryConfiguration( new BinaryConfiguration().setNameMapper(new BinaryBasicNameMapper(true)));
Every object, stored in the Ignite cluster, keeps information about its class for the purpose of deserialization. Instead of keeping a class name in the serialized form, an Ignite node takes the name and converts it to a unique integer ID that is written in an object’s result byte array. By default, Java uses the full class name (package name + simple name) for the ID calculation while .NET uses the simple name only (omitting the package name).
The configuration above ensures that Java nodes will use the simple name for the ID calculation. If we don’t provide this code, the two different nodes will be unable to work together.
Java code
The outline for our main program is shown in Figure 3.
public static void main(String[] args) { // Configure Ignite to connect with .NET nodes IgniteConfiguration cfg = new IgniteConfiguration().setBinaryConfiguration( new BinaryConfiguration().setNameMapper(new BinaryBasicNameMapper(true))); // Start Ignite and retrieve cache Ignite ignite = Ignition.start(cfg); IgniteCache cache = ignite.getOrCreateCache("person"); // Code to call cache put or get here Ignition.stop(false); }
Figure 3. Main program
The Java code in Figure 4 will put data into the cache.
private static void putCache(IgniteCache<Integer, Person> cache) { System.out.println(); System.out.println("> Cache put example started."); // Create some data and put it in the cache cache.put(1, new Person("John Doe", 3)); cache.put(2, new Person("Jane Roe", 2)); cache.put(3, new Person("Mary Major", 1)); cache.put(4, new Person("Richard Miles", 2)); System.out.println("> Stored values in cache."); }
Figure 4. Cache put
The following Java code in Figure 5 will get data from the cache.
private static void getCache(IgniteCache cache) { System.out.println(); System.out.println("> Cache get example started."); System.out.println("> Retrieved person instance from cache: " + cache.get(1).name()); System.out.println("> Retrieved person instance from cache: " + cache.get(2).name()); System.out.println("> Retrieved person instance from cache: " + cache.get(3).name()); System.out.println("> Retrieved person instance from cache: " + cache.get(4).name()); }
Figure 5. Cache get
.NET code
The outline for our main program is shown in Figure 6.
static void Main(string[] args) { // Register Person type var cfg = new IgniteConfiguration { BinaryConfiguration = new BinaryConfiguration { NameMapper = new BinaryBasicNameMapper { IsSimpleName = true } } }; // Start Ignite and retrieve cache var ignite = Ignition.Start(cfg); var cache = ignite.GetOrCreateCache("person"); // Code to call cache put or get here }
Figure 6. Main program
The following .NET code in Figure 7 will put data into the cache.
private static void PutCache(ICache cache) { Console.WriteLine(); Console.WriteLine("> Cache put example started."); // Create some data and put it in the cache cache.Put(4, new Person("John Doe", 3)); cache.Put(3, new Person("Jane Roe", 2)); cache.Put(2, new Person("Mary Major", 1)); cache.Put(1, new Person("Richard Miles", 2)); Console.WriteLine("> Stored values in cache."); }
Figure 7. Cache put
The following .NET code in Figure 8 will get data from the cache.
private static void GetCache(ICache cache) { Console.WriteLine(); Console.WriteLine("> Cache get example started."); Console.WriteLine("> Retrieved person instance from cache: " + cache.Get(1).Name()); Console.WriteLine("> Retrieved person instance from cache: " + cache.Get(2).Name()); Console.WriteLine("> Retrieved person instance from cache: " + cache.Get(3).Name()); Console.WriteLine("> Retrieved person instance from cache: " + cache.Get(4).Name()); }
Figure 8. Cache get
The code appears very similar. However, closer inspection of the code shows that Java uses the following:
// Create some data and put it in the cache cache.put(1, new Person("John Doe", 3)); cache.put(2, new Person("Jane Roe", 2)); cache.put(3, new Person("Mary Major", 1)); cache.put(4, new Person("Richard Miles", 2));
and .NET uses the following:
// Create some data and put it in the cache cache.Put(4, new Person("John Doe", 3)); cache.Put(3, new Person("Jane Roe", 2)); cache.Put(2, new Person("Mary Major", 1)); cache.Put(1, new Person("Richard Miles", 2));
The order of the keys is reversed for .NET. This allows us to verify that data are indeed different when stored from Java and .NET.
Since these are client programs, we need to ensure that there is at least another node running in the cluster that continues to operate and maintain the cache that we are writing to and reading from. This additional node also needs to be configured for binary data.
Write to cache using Java, read from cache using .NET
Let’s run the Java program to write into the cache. The output in IntelliJ IDEA is as follows:
> Cache put example started. > Stored values in cache. [18:09:35] Ignite node stopped OK [uptime=00:00:00:391] Process finished with exit code 0
Now let’s run the .NET program to read from the cache. The output in Microsoft Visual Studio is as follows:
Write to cache using .NET, read from cache using Java
Let’s run the .NET program to write into the cache. The output in Microsoft Visual Studio is as follows:
Now let’s run the Java program to read from the cache. The output in IntelliJ IDEA is as follows:
> Cache get example started. > Retrieved person instance from cache: Richard Miles > Retrieved person instance from cache: Mary Major > Retrieved person instance from cache: Jane Roe > Retrieved person instance from cache: John Doe [18:15:04] Ignite node stopped OK [uptime=00:00:00:266] Process finished with exit code 0
Summary
Departments and development teams in organizations may use different programming languages and frameworks. It can sometimes be difficult to use the same business objects across these different development environments. However, Apache Ignite provides the flexibility to use user-defined objects across a number of popular programming languages, such as Java, .NET and C++. These objects can also be quite complex. By using Apache Ignite’s internal binary representation, it is possible to easily map these objects between these multiple programming languages.


Archive
- January 2021(35)
- December 2020(53)
- November 2020(59)
- October 2020(79)
- September 2020(72)
- August 2020(64)
- July 2020(71)
- June 2020(74)
- May 2020(50)
- April 2020(71)
- March 2020(71)
- February 2020(58)
- January 2020(62)
- December 2019(57)
- November 2019(64)
- October 2019(26)
- September 2019(24)
- August 2019(15)
- July 2019(24)
- June 2019(55)
- May 2019(82)
- April 2019(77)
- March 2019(71)
- February 2019(67)
- January 2019(77)
- December 2018(46)
- November 2018(48)
- October 2018(76)
- September 2018(55)
- August 2018(63)
- July 2018(74)
- June 2018(64)
- May 2018(65)
- April 2018(76)
- March 2018(82)
- February 2018(65)
- January 2018(80)
- December 2017(71)
- November 2017(72)
- October 2017(75)
- September 2017(65)
- August 2017(97)
- July 2017(111)
- June 2017(87)
- May 2017(105)
- April 2017(113)
- March 2017(108)
- February 2017(112)
- January 2017(109)
- December 2016(110)
- November 2016(121)
- October 2016(111)
- September 2016(123)
- August 2016(169)
- July 2016(142)
- June 2016(152)
- May 2016(118)
- April 2016(60)
- March 2016(86)
- February 2016(154)
- January 2016(3)
- December 2015(150)