HierarchicalMap Tutorial - Intermediate Level
HierarchicalMap is an interface. So anyone can implement it. This tutorial is based on BasicHierarchicalmap, a reference implementation of HierarchicalMap.
Following subjects are covered in this tutorial:
(Javascript must be enabled to allow syntax highlighting for source code snippets in this tutorial)
Advanced Level
12. .NET InteroperabilityWorking with HierarchicalMap in .Net is very simple, thanks to Jeroen Frijters with his IKVM project! Basically, all HierarchicalMap java classes were converted to .Net classes using ikvmc, a tool that compiles JVM's byte-code into CLR's byte-code. Some new classes were written in .Net to have more Microsoft fashioned behavior like ICollection, IDictionary, DictionaryEntry, Item Property and so on.
Following assemblies must be referenced to start using HierarchicalMap in .Net projects:
Note that there is no need of jar files during .Net execution. IKVM solution is not a wrapper for java classes. All the algorithms implemented in java is converted to CLR's byte-code and actually run as it was written in .Net native compiler.
Although converted classes can be used inside .Net project, like
org.dhmp.util.HierarchicalMap =
new org.dhmp.util.BasicHierarchicalMap()
It is preferable to use .Net wrapper classes. Some of the differences between java implementation and .Net are listed below:
Original (dhmp.dll) | .Net (dhmpms.dll) | Description |
---|---|---|
org.dhmp.util | Dhmp.Util | package org.dhmp is wrapped using namespace Dhmp |
HierarchicalMap | IHierarchicalMap | IHierarchicalMap implements IDictionary |
entrySet() | GetEnumerator() | .Net Enumerator is slightly different from java Iterator |
MapInputStream | MapStreamReader | InputStream was converted to StreamReader |
MapOutputStream | MapStreamWriter | OutputStream was converted to StreamWriter |
BasicHierarchicalMap | BasicHierarchicalMap | Some classes name was unchanged |
The following snippet of code in C#, will create a data structure as in "2. Creating Structure" earlier presented in this tutorial. "using Dhmp.Util" must be declared in the source code:
//create a HierachicalMap containing first address IHierarchicalMap address1 = new BasicHierarchicalMap(); address1.Put("type", "Billing"); address1.Put("City", "Sta Clara"); address1.Put("State", "CA"); address1.Put("ZIP Code", "95054"); //add the address to newly created hmap IHierarchicalMap hmap = new BasicHierarchicalMap(); hmap.Add("Order/Address", address1); //create another HierachicalMap containing second address IHierarchicalMap address2 = hmap.Add("Order/Address"); address2["type"] = "Shipping"; address2["City"] = "São Paulo"; address2["State"] = "SP"; address2["ZIP Code"] = "04717-004";
Note that method name begins with capital letter and Item
Property, as in address2["City"] = "São Paulo";
, is equivalent to
address2.Put("City", "São Paulo");
.
Iteration with map is also different from java.
//first, get all the addresses ICollection addresses = hmap.GetAll("Order/Address"); //for each address in addresses IEnumerator adritr = addresses.GetEnumerator(); while(adritr.MoveNext()) { System.Console.WriteLine("Address"); //get the first address wich is a HierarchicalMap IHierarchicalMap address = (IHierarchicalMap)adritr.Current; //get the entrySet to iterate with its children, //i.e. City, State and ZIP code IDictionaryEnumerator entryset = address.GetEnumerator(); while(entryset.MoveNext()) { //printout the pair key value for each entry DictionaryEntry entry = entryset.Entry; System.Console.WriteLine(entry.Key + " : " + entry.Value); } System.Console.WriteLine(); }
IEnumerator is used instead of Iterator which is based on
MoveNext()
method and Current
property
In .Net, there is a simpler way to iterate with entrySet:
//get the entrySet to iterate with its children, //i.e. City, State and ZIP code foreach(DictionaryEntry entry in address) { //printout the pair key value for each entry System.Console.WriteLine(entry.Key + " : " + entry.Value); }
Streaming HierarchicalMap from .Net to Java
Now, the most interesting part. Streaming HierarchicalMap between Java application and .Net application. Prepare the following code at java side:
Step1. Writing Server in Java
First, write a code for filling a HierarchicalMap:
public static HierarchicalMap fillCatalog() { HierarchicalMap catalog = new BasicHierarchicalMap(); HierarchicalMap item = catalog.add("Item"); item.put("model", "iBook G4"); item.put("brand", "Apple"); item.put("processor", "PowerPC G4"); item.put("price", new BigDecimal("999.99")); item = catalog.add("Item"); item.put("model", "VAIO FS920"); item.put("brand", "Sony"); item.put("processor", "Intel Pentium M 740"); item.put("price", new BigDecimal("1049.99")); item = catalog.add("Item"); item.put("model", "Aspire 5672WLMi"); item.put("brand", "Acer"); item.put("processor", "Intel Core Duo T2300"); item.put("price", new BigDecimal("1399.99")); item = catalog.add("Item"); item.put("model", "Pavilion dv5150us"); item.put("brand", "HP"); item.put("processor", "Intel Core Solo T1300"); item.put("price", new BigDecimal("1049.99")); return catalog; }
Write a Main method to start a socket server:
public static void main(String[] args) { try { ServerSocket ssock = new ServerSocket(1999); while(true) { Socket sock = ssock.accept(); new MapThread(sock).start(); } } catch(Exception e) { e.printStackTrace(); } }
Then, write a thread for processing accepted socket. This part is quite large and written as a single code just for showing how to stream HierarchicalMap.
public static class MapThread extends Thread { Socket sock; HierarchicalMap catalog; MapThread(Socket sock) { this.sock = sock; catalog = fillCatalog(); denormalize(catalog,"Item","model"); } public void run() { try { //get inputstream from socket MapInputStream min = new MapInputStream(sock.getInputStream()); //get outputstream from socket MapOutputStream mout = new MapOutputStream(sock.getOutputStream()); min.init("<Param compress='false'/>"); mout.init("<Param compress='false'/>"); HierarchicalMap request; //repeat while map is available while((request = min.readMap()) != null) { //prepare response HierarchicalMap response = new BasicHierarchicalMap(); //get model and qty from request Object model = request.get("model"); int qty = ((Integer)request.get("qty")). intValue(); //fetch model from catalog HierarchicalMap spec = (HierarchicalMap)catalog. get("Item/" + model); if(spec != null) { //if found a spec, compute the total amount BigDecimal total = (new BigDecimal(qty)). multiply((BigDecimal)spec.get("price")); //10% discount for total over 5000 if(total.compareTo(new BigDecimal("5000")) > 0) { total = total.subtract(total. multiply(new BigDecimal("0.10"))); spec.add("Special discount", "10%"); } //add spec and total on response response.add("spec", spec); response.add("TotalAmount", total); } //send response back mout.writeMap(response); mout.flush(); } } catch(Exception e) { e.printStackTrace(); } } }
The constructor of this class keeps the socket and fills a catalog, actually a HierarchicalMap, invoking the method "fillCatalog()" shown before. Then denormalize it using the algorithm presented at "5. Restructuring the Map".
The "run()" method implements the following algorithm:
Step2. Writing Client in .Net
Following snippet of code must be written in C#:
public static void StreamTest() { Stream st = new TcpClient("localhost", 1999) .GetStream(); Dhmp.IO.MapStreamWriter mout = new Dhmp.IO.MapStreamWriter(st); Dhmp.IO.MapStreamReader min = new Dhmp.IO.MapStreamReader(st); mout.Init("<Param compress='false'/>"); min.Init("<Param compress='false'/>"); IHierarchicalMap request; IHierarchicalMap response; System.Console.WriteLine ("Quoting 5 \"Aspire 5672WLMi\""); request = new BasicHierarchicalMap(); request["model"] = "Aspire 5672WLMi"; request["qty"] = 5; mout.WriteMap(request); mout.Flush(); response = min.ReadMap(); System.Console.WriteLine("TotalAmount: {0:C}", response["TotalAmount"]); System.Console.WriteLine("Spec:"); foreach(DictionaryEntry e in (IHierarchicalMap)response["spec"]) { System.Console.WriteLine(e.Key + ": " + e.Value); } System.Console.WriteLine(); System.Console.WriteLine("Quoting 3 \"iBook G4\""); request = new BasicHierarchicalMap(); request["model"] = "iBook G4"; request["qty"] = 3; mout.WriteMap(request); mout.Flush(); response = min.ReadMap(); System.Console.WriteLine("TotalAmount: {0:C}", response["TotalAmount"]); System.Console.WriteLine("Spec:"); foreach(DictionaryEntry e in (IHierarchicalMap)response["spec"]) { System.Console.WriteLine(e.Key + ": " + e.Value); } }
Now, start the server side and then run the client. The result will be:
Quoting 5 "Aspire 5672WLMi" TotalAmount: $6,299.96 Spec: model: Aspire 5672WLMi brand: Acer processor: Intel Core Duo T2300 price: 1399.99 Special discount: 10% Quoting 3 "iBook G4" TotalAmount: $2,999.97 Spec: model: iBook G4 brand: Apple processor: PowerPC G4 price: 999.99
Converting .Net object to and from Java
Some objects like string and byte are automatically converted inside IKVM but other objects like DateTime, Decimal and even an integer (represented using System.Int32) must be converted to corresponding java object before streaming it. The assembly "dhmpdefaultcvt.dll" contains default conversor for most of primitive data types.
.Net class | java class |
---|---|
bool (System.Boolean) | java.lang.Boolean |
byte (System.Byte) | java.lang.Byte |
char (System.Char) | java.lang.Character |
int (System.Int32) | java.lang.Integer |
long (System.Int64) | java.lang.Long |
float (System.Single) | java.lang.Float |
double (System.Double) | java.lang.Double |
System.Decimal | java.math.BigDecimal |
System.DateTime | java.util.Date |
Writing custom converter is simple. Just implement the Dhmp.Util.IConverter interface under Dhmp.Util.Converter namespace. See below an example:
/// <summary> /// Convert .Net bool to java Boolean. /// </summary> public class System_BooleanCvt : Dhmp.Util.IConverter { Object Dhmp.Util.IConverter.toJava(Object obj) { return new java.lang.Boolean((bool)obj); } Object Dhmp.Util.IConverter.toNet(Object obj) { return obj; } } /// <summary> /// Convert java Boolean to .Net bool. /// </summary> public class java_lang_BooleanCvt : Dhmp.Util.IConverter { Object Dhmp.Util.IConverter.toJava(Object obj) { return obj; } Object Dhmp.Util.IConverter.toNet(Object obj) { return ((java.lang.Boolean)obj).booleanValue(); } }
Note that class name must be the full Type name replacing "." by "_" with a literal "Cvt" as suffix. The resulting assembly name must finish with "cvt" like "myconvertercvt.dll" and must be placed within same directory where "dhmpdefaultcvt.dll" is located. The default converter is loaded as last converter inside internal cache. So any custom converter can override the converter class inside "dhmpdefaultcvt.dll". Though we do not encourage doing so nor writing complex object converter. Instead just exchange primitive types among different systems.
Continue to Advanced Level - Runtime schema validation