Showing posts with label Chronicle-Wire. Show all posts
Showing posts with label Chronicle-Wire. Show all posts

Friday, 9 October 2015

Chronicle-Wire Tutorial (Part 3): Serialising Code

Before reading this, Part 3 of the Chronicle-Wire tutorial, I'm assuming that you are comfortable with at least Part 1 (the basics) of the tutorial.

So far we've looked at how to serialise data as objects as well as serialising data in the form of documents. Now we're going to look at how we can serialise code.

There are two ways this can be done with lambdas and with enums.

Serialising code with lambdas

One of the coolest features of Java 8 are lambdas which provide a means to pass functions (essentially code) from one part of your application to be run in other parts of your application.

Being able to serialise code is really useful, for example, if you want to write some code on a client which gets executed on the server. Or, for map reduce type problems where you break up a problem and distribute the code so that it can be run on many machines. 

This example demonstrates how this can be done, in this case using the class SerializableFunction. The lambda String::toUpperCase is serialised to Bytes using both TextWire and BinaryWire.  The Bytes are then deserialised into a Function which is applied to a string "hello world".



The output of this program is:

----------Testing with TextWire--------------------
Text Wire representation of serialised function
toUpperCase: !SerializedLambda {
  cc: !type net.openhft.engine.chronicle.demo.WireDemoLambdas,
  fic: net/openhft/chronicle/core/util/SerializableFunction,
  fimn: apply,
  fims: (Ljava/lang/Object;)Ljava/lang/Object;,
  imk: 5,
  ic: java/lang/String,
  imn: toUpperCase,
  ims: ()Ljava/lang/String;,
  imt: (Ljava/lang/String;)Ljava/lang/String;,
  ca: [
  ]
}

hello world -> HELLO WORLD

----------Testing with BinaryWire--------------------
Binary Wire representation of serialised function
00000000 CB 74 6F 55 70 70 65 72  43 61 73 65 B6 10 53 65 ·toUpper Case··Se
00000010 72 69 61 6C 69 7A 65 64  4C 61 6D 62 64 61 82 1E rialized Lambda··
00000020 01 00 00 C2 63 63 BC 31  6E 65 74 2E 6F 70 65 6E ····cc·1 net.open
00000030 68 66 74 2E 65 6E 67 69  6E 65 2E 63 68 72 6F 6E hft.engi ne.chron
00000040 69 63 6C 65 2E 64 65 6D  6F 2E 57 69 72 65 44 65 icle.dem o.WireDe
00000050 6D 6F 4C 61 6D 62 64 61  73 C3 66 69 63 B8 34 6E moLambda s·fic·4n
00000060 65 74 2F 6F 70 65 6E 68  66 74 2F 63 68 72 6F 6E et/openh ft/chron
00000070 69 63 6C 65 2F 63 6F 72  65 2F 75 74 69 6C 2F 53 icle/cor e/util/S
00000080 65 72 69 61 6C 69 7A 61  62 6C 65 46 75 6E 63 74 erializa bleFunct
00000090 69 6F 6E C4 66 69 6D 6E  E5 61 70 70 6C 79 C4 66 ion·fimn ·apply·f
000000a0 69 6D 73 B8 26 28 4C 6A  61 76 61 2F 6C 61 6E 67 ims·&(Lj ava/lang
000000b0 2F 4F 62 6A 65 63 74 3B  29 4C 6A 61 76 61 2F 6C /Object; )Ljava/l
000000c0 61 6E 67 2F 4F 62 6A 65  63 74 3B C3 69 6D 6B 05 ang/Obje ct;·imk·
000000d0 C2 69 63 F0 6A 61 76 61  2F 6C 61 6E 67 2F 53 74 ·ic·java /lang/St
000000e0 72 69 6E 67 C3 69 6D 6E  EB 74 6F 55 70 70 65 72 ring·imn ·toUpper
000000f0 43 61 73 65 C3 69 6D 73  F4 28 29 4C 6A 61 76 61 Case·ims ·()Ljava
00000100 2F 6C 61 6E 67 2F 53 74  72 69 6E 67 3B C3 69 6D /lang/St ring;·im
00000110 74 B8 26 28 4C 6A 61 76  61 2F 6C 61 6E 67 2F 53 t·&(Ljav a/lang/S
00000120 74 72 69 6E 67 3B 29 4C  6A 61 76 61 2F 6C 61 6E tring;)L java/lan
00000130 67 2F 53 74 72 69 6E 67  3B C2 63 61 82 00 00 00 g/String ;·ca····
00000140 00                                               ·                

hello world -> HELLO WORLD

Serialising code with enums

Using lambdas to serialise code give the developer maximum flexibility in terms of what can be serialised but there also a couple drawbacks.
  1. The serialised lambda is very bulky (see the print out of the bytes above)
  2. There is no control over what can be serialised.  If you are using serialisation to send messages across the wire between client and server you might want to have more control over the code that can be executed by the client on the server. 
Using enums addresses these shortcomings.

The code below does exactly the same as the code we saw in the lambda example in the first section. It takes the code to transform a string to upper case and serialises it. The code to transform the string is stored in an enum which implements Function.




This is the output from that program

----------Testing with TextWire--------------------

Text Wire representation of serialised function
toUpperCase: !StringFunctions TO_UPPER_CASE

hello world -> HELLO WORLD

----------Testing with BinaryWire--------------------

Binary Wire representation of serialised function
00000000 CB 74 6F 55 70 70 65 72  43 61 73 65 B6 0F 53 74 ·toUpper Case··St
00000010 72 69 6E 67 46 75 6E 63  74 69 6F 6E 73 ED 54 4F ringFunc tions·TO
00000020 5F 55 50 50 45 52 5F 43  41 53 45                _UPPER_C ASE     


hello world -> HELLO WORLD

Straight away we see the difference between the verbosity of the self describing lambda to the predefined enum.

Enum ->  toUpperCase: !StringFunctions TO_UPPER_CASE
Lambda -> toUpperCase: !SerializedLambda {
  cc: !type net.openhft.engine.chronicle.demo.WireDemoLambdas,
  fic: net/openhft/chronicle/core/util/SerializableFunction,
  fimn: apply,
  fims: (Ljava/lang/Object;)Ljava/lang/Object;,
  imk: 5,
  ic: java/lang/String,
  imn: toUpperCase,
  ims: ()Ljava/lang/String;,
  imt: (Ljava/lang/String;)Ljava/lang/String;,
  ca: [
  ]
}

Since all the enums have been pre-defined there are no nasty surprises as to what code is going to be run.

On the downside of course you lose the flexibility of ad hoc lambdas.

Note the line:

ClassAliasPool.CLASS_ALIASES.addAlias(StringFunctions.class, "StringFunctions");

As explained earlier this means that "StringFunctions" is mapped to the class StringFunctions:

Without this line (without class aliasing) the output:

toUpperCase: !StringFunctions TO_UPPER_CASE

would be

toUpperCase: !net.openhft.engine.chronicle.demo.WireDemoEnums$StringFunctions TO_UPPER_CASE

Apart from being more verbose and harder to read it would also be a problem when passing the message between languages which is one of the reasons to use Chronicle-Wire serialisation.

Summary

Use enums when you know you need maximum efficiency and/or control. Use lambdas when you need flexibility. In practice you will probably mix both for different parts of your application.

Chronicle-Wire Tutorial (Part 2): Working with Documents

In Part 1 we saw the basics of how to use Chronicle-Wire to serialise and deserialise objects and some of the benefits of using Wire.

In this post I want to show you some more advanced features around serialising data in the form of documents.

Serialising documents

Rather than serialising a whole object with Wire (which is what we did in the previous post with Person) it is possible that you might want to group a number of data items together in an ad hoc object and serialise that with Wire.

You can do this with the document feature of Chronicle.

This should be clearer by looking at this example.



The output from this program is:


--------TextWire Demo--------------
Data serialised with TextWire:
!data: {
  name: dan,
  age: 44
}

Data deserialised:
Name:dan
Age:44

---------BinaryWire Demo--------------
Data serialised with BinaryWire:
00000020 34 34 0A 7D 0A 18 00 00  00 C4 64 61 74 61 82 0E 44·}···· ··data··
00000030 00 00 00 C4 6E 61 6D 65  E3 64 61 6E C3 61 67 65 ····name ·dan·age
00000040 2C                                               ,                
Data deserialised:
Name:dan

Age:44

So you have the ability to create ad hoc objects in form of documents.

But you can go a step further with this:

Creating real objects on the fly

When writing a document you have the ability to give it a 'type'.  This is done by calling the method typePrefix() as you can see in the code below.



This is the out put from the program:

---------TextWire Demo--------------
Data serialised with TextWire:
Kdata: !chronicle.demo.Person {
  name: dan,
  age: 44
}

Data deserialised:
Person{name='dan', age=44}

---------BinaryWire Demo--------------
Data serialised with BinaryWire:
00000040 6E 2C 0A 20 20 61 67 65  3A 20 34 34 0A 7D 0A 42 n,·  age : 44·}·B
00000050 00 00 00 C4 64 61 74 61  B6 28 6E 65 74 2E 6F 70 ····data ·(net.op
00000060 65 6E 68 66 74 2E 65 6E  67 69 6E 65 2E 63 68 72 enhft.en gine.chr
00000070 6F 6E 69 63 6C 65 2E 64  65 6D 6F 2E 50 65 72 73 onicle.d emo.Pers
00000080 6F 6E 82 0E 00 00 00 C4  6E 61 6D 65 E3 64 61 6E on······ name·dan
00000090 C3 61 67 65 2C                                   ·age,            
Data deserialised:
Person{name='dan', age=44}


The code for the serialisation is almost the same as the we used in the last example except that this time it sets typePrefix() to the Person class.  This is the same Person we saw in the previous post. Code listing below:




Because we know the type of the object, we are able to deserialise using the method typedMarshallable() into a Java object.  In this example we have created a Person object.

Note: Wire has the concept of a ClassAliasPool which allows you to use shortened names or aliases rather than the fully qualified class name. This is important as it can make your data shorter and easier to read, both of which are goals of Chronicle-Wire.

Deserialising documents without creating objects

One of the goals of Chronicle in general and Chronicle-Wire in particular is to aim for zero object creation.  Reusing objects is key to achieving this goal.

In the code below we see how to deserialise data from the document directly into an existing object. (For the sake of brevity I'm only going to use TextWire).




Working with deserialised data

Along the same lines as we saw above you can also deserialise directly into a lambda that can be used to manipulate or use that data.

Take a look at this example that tests the value of the deserialised data:




The interesting thing to note here is how, when deserialising, the object() method can be employed to use the data. In this case we are asserting to prove we have the correct data but in a real application other more meaningful tasks would be created.

Summary

Hopefully this tutorial has introduced you to the power of using documents within Chronicle-Wire.  Creating ad hoc objects, parts of objects, and real objects from their constituent data parts are features that can make your code easier to write, easier to debug and most of all, make your code faster. 

Thursday, 8 October 2015

Chronicle-Wire Tutorial (Part 1): The Basics

Chronicle-Wire is a library written by Chronicle that allows a Java developer to serialise Java objects. It's a library we developed to support our higher level products like Chronicle Queue and Chronicle Map. However the library has applications in any code that uses serialisation.

At this point you're wondering what's new about serialisation why another library... Serialisation is hardly a novel concept in Java. In fact Serializable has been in Java since jdk1.1 - so almost forever :)

The real innovation behind Wire is that it abstracts away the implementation of the serialisation to a pluggable Wire implementation.  The idea is that your objects need only describe what is to be serialised not how it should be serialised. This is done by the objects (the POJOs that are to be serialised) implementing the Marshallable interface.  

It is only at the point of serialisation that you decide how that serialisation is actually implemented by selecting a particular Wire implementation to provide to the process.

Let's look at an example of this which will hopefully make this concept much clearer:


This is the output from the program:

-----------TEXT WIRE------------

Person to serialise: Person{name='dan', age=44}
Text Wire prints:
name: dan
age: 44
Deserialised person: Person{name='dan', age=44}

-----------BINARY WIRE------------

Person to serialise: Person{name='dan', age=44}
Text Wire prints:
00000000 C4 6E 61 6D 65 E3 64 61  6E C3 61 67 65 2C       ·name·da n·age,  
Deserialised person: Person{name='dan', age=44}

-----------RAW WIRE------------

Person to serialise: Person{name='dan', age=44}
Text Wire prints:
00000000 03 64 61 6E 2C                                   ·dan,            

Deserialised person: Person{name='dan', age=44}

What should be clear here is that the class Person is in no way responsible for how its data is serialised.  That is done by the various implementations of Wire.

  • TextWire - serialises to text for a humanly readable format
  • BinaryWire - serialises to a self describing binary format
  • RawWire - serialises to a compact binary format

Person is only responsible for choosing the data that is to be serialised and describing the type of that data.  You will notice that Wire has a very large list of types which allow for maximum efficiency (e.g. int8(), int16() ) that can achieved by certain Wire implementations.

Whilst in the example above Person is serialised to Bytes (for more information on Bytes see here) you can actually serialise to whatever format you want. All you have to do is to implement the Wire interface.  (It is not necessary to provide an implementation for the methods if not appropriate.)

As well as obvious serialisation formats such as JSON, YAML, csv you can also create some rather bizarre ones as well. For example I created an LDAPWire which serialises objects into Attributes that can be stored into an LDAP database.  In this case I only implemented the text() methods of Wire.

This chart (scroll down) compares a couple of the Wire implementations with the competition. As you can see it compares favourably with SBE and Capt'n Proto.