PadoGrid | Catalogs | Manual | FAQ | Releases | Templates | Pods | Kubernetes | Docker | Apps | Quick Start
This bundle provides step-by-step instructions for generating and deploying Avro and KryoSerializer
in Hazelcast. Using PadoGrid's code generator, you can on the fly generate and deploy Avro wrapper classes and the correspoinding Kryo serializer.
install_bundle -download bundle-hazelcast-4n5-app-kryo_codegen
Kryo allows you to ingest POJO objects to Hazelcast without addding any Hazelcast dependencies. In this bundle, we introduce PadoGrid's code generator that generates KryoSerializer
required by Hazelcast for registering custom serializers. To register POJO classes in Hazelcast, you must register each POJO class individually, making the Hazelcast configuration process difficult and error prone. PadoGrid's Kryo code generator simplifies the registration process by generating the KryoSerializer
class that automatically groups all the POJO classes in a given package. Instead of registering each POJO class individually, you would just register KryoSerializer
.
The Kryo code generator also includes a wrapper class generator which extends POJO classes to allow you to override class methods as needed. This is particularly useful if you generate domain classes using IDL-based serialization tools such as Avro. Avro creates a compact binary data format and is ideal for storing POJO objects in Hazelcast. Avro, however, only supports primitive types and leaves the chore of marshalling and unmarshalling non-primitive types to the application. To close this gap, the wrapper class generator is provided to generate wrapper classes in which you can add your custom marshalling and unmarshalling code without affecting the Avro-generated code.
For example, Avro does not support the Date
class. To store a Date
object, you would need to store its long
value, i.e., Date.getTime()
as the long
type and the logicalType
of timestamp-millis
as follows.
{
"fields": [
{"name": "orderDateLong", "type": "long", "logicalType": "timestamp-millis"}
]
}
To get the Date
object back, the application must explicitly create a Date
object, i.e., new Date(long)
. With the wrapper class, this can be done by adding a method that returns the Date
object. For example,
public class Order extends org.hazelcast.demo.nw.data.avro.generated.__Order {
public Order() {
super();
}
public void setOrderDate(Date date) {
super.setOrderDateLong(date.getTime());
}
public Date getOrderDate() {
return new Date(super.getOrderDateLong());
}
...
}
- Maven 3.x
First, make sure you are switched into a Hazelcast cluster. You can create the default cluster by executing the following.
# Hazelcast cluster - creates 'myhz' cluster
create_cluster -product hazelcast
switch_cluster myhz
If you want to quickly test the bundle, you can execute the following and jump to Step 9. The build_app
carries out the setps 1 to 8 in sequence. It is recommended, however, that you go through the entire steps to get familiar with the code generation and deployment process.
cd_app kryo_codegen/bin_sh
./build_app
If you have a schema registry running, then you can use the -registry
option to retrieve the schemas instead. Please see the usage by running the following.
./build_app -?
This bundle includes the following example schema files.
cd_app kryo_codegen
mkdir -p src/main/resources
cp etc/avro/* src/main/resources/
tree src/main
Output:
src/main
βββ resources
βββ category.avsc
βββ customer.avsc
βββ employee.avsc
βββ order.avsc
Note that we do not have any Java code in the source directory. We start with a set of only Avro schema files and end up with all the necessary Java code for ingesting data into Hazelcast. The following shows the customer.asvc
schema file contents.
cat src/main/resources/order.avsc
Output:
{
"namespace": "org.hazelcast.demo.nw.data.avro.generated",
"type": "record",
"name": "__Order",
"fields": [
{"name": "orderId", "type": "string"},
{"name": "customerId", "type": "string"},
{"name": "employeeId", "type": "string"},
{"name": "freight", "type": "double"},
{"name": "orderDate", "type": "long", "logicalType": "timestamp-millis"},
{"name": "requiredDate", "type": "long", "logicalType": "timestamp-millis"},
{"name": "shipAddress", "type": "string"},
{"name": "shipCity", "type": "string"},
{"name": "shipCountry", "type": "string"},
{"name": "shipName", "type": "string"},
{"name": "shipPostalCode", "type": "string"},
{"name": "shipRegion", "type": ["string", "null"]},
{"name": "shipVia", "type": "string"},
{"name": "shippedDate", "type": "long", "logicalType": "timestamp-millis"}
]
}
cd_app kryo_codegen
mvn package
The above Maven command generates the corresponding Java classes as follows.
tree src/main
Output:
src/main
βββ java
βΒ Β βββ org
βΒ Β βββ hazelcast
βΒ Β βββ demo
βΒ Β βββ nw
βΒ Β βββ data
βΒ Β βββ avro
βΒ Β βββ generated
βΒ Β βββ __Category.java
βΒ Β βββ __Customer.java
βΒ Β βββ __Employee.java
βΒ Β βββ __Order.java
βββ resources
βββ category.avsc
βββ customer.avsc
βββ employee.avsc
βββ order.avsc
Run the PadoGrid's t_generate_wrappers
command to generate the wrapper classes that extend the generated Avro classes. You can use the Avro classes that were generated in the previous setp as they are but it is recommended that you generate the wrapper classes so that you can override the Avro class methods as needed. Let's generate wrapper classes by executing the following:
t_generate_wrappers -sp org.hazelcast.demo.nw.data.avro.generated \
-tp org.hazelcast.demo.nw.data.avro \
-dir src/main/java \
-jar lib/app-kryo-codegen-hazelcast-4-1.0.0.jar \
-classpath lib
The above command creates the wrapper classes in the org.hazelcast.demo.nw.data.avro
package as follows.
tree src/main
Output:
src/main
βββ java
βΒ Β βββ org
βΒ Β βββ hazelcast
βΒ Β βββ demo
βΒ Β βββ nw
βΒ Β βββ data
βΒ Β βββ avro
βΒ Β βββ Category.java
βΒ Β βββ Customer.java
βΒ Β βββ Employee.java
βΒ Β βββ Order.java
βΒ Β βββ generated
βΒ Β βββ __Category.java
βΒ Β βββ __Customer.java
βΒ Β βββ __Employee.java
βΒ Β βββ __Order.java
βββ resources
βββ category.avsc
βββ customer.avsc
βββ employee.avsc
βββ order.avsc
The generated wrapper classes need to be compiled and packaged into a jar file. Run Maven again to generate the lib/app-kryo-codegen-hazelcast-4-1.0.0.jar
file that includes the wrapper classes.
mvn package
With the wrapper classes in the jar file, we can now generate the KyroSerializer
class that properly registers all the wrapper classes in Hazelcast. Execute the following command to generate KryoSerializer
.
t_generate_kryo_serializer -id 1200 \
-package org.hazelcast.demo.nw.data.avro \
-dir src/main/java \
-jar lib/app-kryo-codegen-hazelcast-4-1.0.0.jar \
-classpath lib
Output:
The above command outputs the following (Note the registration information. We'll be entering the ouputted serializer settings in the Hazelcast configuration file later.)
New class count: 4
Total class count: 4
[0] org.hazelcast.demo.nw.data.avro.Category
[1] org.hazelcast.demo.nw.data.avro.Customer
[2] org.hazelcast.demo.nw.data.avro.Employee
[3] org.hazelcast.demo.nw.data.avro.Order
KryoSerializer generated:
C:\Users\dpark\Work\git\padogrid-bundles\hazelcast-bundles\bundle-hazelcast-4-app-kryo_codegen\src\main\java\org\hazel
cast\demo\nw\data\avro\KryoSerializer.java
To register KryoSerializer, add the following lines in the Hazelcast configuration file.
<serialization>
<serializers>
<global-serializer override-java-serialization="true">
org.hazelcast.demo.nw.data.avro.KryoSerializer
</global-serializer>
</serializers>
</serialization>
The source directory now has KryoSerializer.java
as shown below.
tree src/main
Output:
src/main
βββ java
βΒ Β βββ org
βΒ Β βββ hazelcast
βΒ Β βββ demo
βΒ Β βββ nw
βΒ Β βββ data
βΒ Β βββ avro
βΒ Β βββ Category.java
βΒ Β βββ Customer.java
βΒ Β βββ Employee.java
βΒ Β βββ KryoSerializer.java
βΒ Β βββ Order.java
βΒ Β βββ generated
βΒ Β βββ __Category.java
βΒ Β βββ __Customer.java
βΒ Β βββ __Employee.java
βΒ Β βββ __Order.java
βββ resources
βββ category.avsc
βββ customer.avsc
βββ employee.avsc
βββ order.avsc
Once again, repackage the lib/app-kryo-codegen-hazelcast-4-1.0.0.jar
file by running Maven. At this time, the jar file also includes the generated classes including KryoSerializer
which we need to register with Hazelcast.
mvn package
This bundle includes data ingestion clients that use the generated wrapper classes to ingest data into Hazelcast. The source code is located in the src_provided
directory. Let's copy it to the src
directory and rebuild the jar file.
# Copy client code
cp -r src_provided/* src/
# Rebuild
mvn package
βΉοΈ Take a look at src_provided/org/apache/geode/demo/nw/data/avro/Order.java
. It extends the Avro generated class __Order
to include Date objects.
The lib/app-kryo-codegen-hazelcast-4-1.0.0.jar
is now ready to be deployed to the Hazelcast cluster. Since we are using Kryo and Avro, we also need to deploy their jar files along with all the dependencies. The previous Maven build step also generated a tarball, app-kryo-codegen-hazelcast-4-1.0.0.tar.gz
, that contains all the jar files. We need to untar the tarball in the workspace's plugins
directory so that the jar files can be picked up by all the apps and clusters running in the same workspace.
# Deploy the generated tarball in the workspace plugins directory.
tar -C $PADOGRID_WORKSPACE/plugins/ -xzf target/assembly/app-kryo-codegen-hazelcast-4-1.0.0.tar.gz
βΉοΈ Note that you would also need to deploy the tarball to external apps that connect to your Hazelcast cluster. For example, to deploy it to Kafka Connect, untar it in the connector's plugin directory.
Place the serialization information in the current cluster's Hazelcast configuration file.
# Create a Hazelcast cluster if you have not done so already
create_cluster -product hazelcast -cluster myhz
# Switch into your Hazelcast cluster and edit hazelcast.xml
switch_cluster myhz
vi etc/hazelcast.xml
Copy the serializer configuration output from Step 5 and enter it in the hazelcast.xml
file as follows.
<hazelcast>
...
<serialization>
<serializers>
<global-serializer override-java-serialization="true">
org.hazelcast.demo.nw.data.avro.KryoSerializer
</global-serializer>
</serializers>
</serialization>
...
</hazelcast>
start_cluster
The scripts for running the client code are in the bin_sh
directory. The ingestion scripts use the etc/hazelcast-client.xml
file which already includes the KyroSerializer
class. Let's execute them.
# Change dir to the app's bin_sh to run 'ingest' scripts
cd_app kryo_codegen/bin_sh
# Ingest data into the 'nw/customers' map.
./ingest_customers
# Ingest data into the 'nw/orders' map.
./ingest_orders
ingest_customers
Output:
Data Class: org.hazelcast.demo.nw.data.avro.Customer
Ingested: 100
Map: nw/customers
ingest_orders
Output:
Data Class: org.hazelcast.demo.nw.data.avro.Order
Ingested: 100
Map: nw/orders
If you dont' see the outputs shown above then check the log files in the log/
directory.
You can use the read_cache
script to read the ingested data as follows. We have ingested data into the 'nw/customers' and 'nw/orders' maps.
./read_cache nw/customers
./read_cache nw/orders
You can also execute the code generators programmatically. The source code of MyWrapperGenerator
and MyKryoGenerator
is located in the src_provided/main/java
directory and shown below.
package org.hazelcast.addon.demo.kryo;
import com.netcrest.padogrid.tools.WrapperGenerator;
/**
* This class shows you how to run WrapperGenerator programmatically.
*
* @author dpark
*
*/
public class MyWrapperGenerator {
public static void main(String[] args) throws Exception {
// All paths are relative to the working directory.
String sp = "org.hazelcast.demo.nw.data.avro.generated";
String tp = " org.hazelcast.demo.nw.data.avro";
String dir = "src/main/java";
String jar = "lib/app-kryo-codegen-hazelcast-4-1.0.0.jar";
boolean overwrite = false;
WrapperGenerator generator = new WrapperGenerator(sp, tp, jar, dir, WrapperGenerator.WrapperType.simple, overwrite);
generator.generateWrappers();
}
}
package org.hazelcast.addon.demo.kryo;
import com.netcrest.padogrid.tools.HazelcastKryoGenerator;
/**
* This class shows you how to run HazelcastKryoGenerator programmatically.
*
* @author dpark
*
*/
public class MyKryoGenerator {
public static void main(String[] args) throws Exception {
// All paths are relative to the working directory.
String packageName = " org.hazelcast.demo.nw.data.avro";
String jarPath = "lib/app-kryo-codegen-hazelcast-4-1.0.0.jar";
int typeId = 1200;
String srcDir = "src/main/java";
HazelcastKryoGenerator generator = new HazelcastKryoGenerator(packageName, jarPath, typeId, srcDir);
generator.generateKryoSerializer();
}
}
stop_cluster
PadoGrid | Catalogs | Manual | FAQ | Releases | Templates | Pods | Kubernetes | Docker | Apps | Quick Start