Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Making Java Objects Queryable by Infinispan Remote Clients

DZone's Guide to

Making Java Objects Queryable by Infinispan Remote Clients

Here's how to make Java objects queryable by remote clients using Infinispan's Protostream technology

· Java Zone ·
Free Resource

Download Microservices for Java Developers: A hands-on introduction to frameworks and containers. Brought to you in partnership with Red Hat.

The following is a common question amongst Infinispan community users, "how do I make my Java objects queryable by remote clients?" In this post, we take a look at how to do just that!

Annotation Method

The simplest way is to take advantage of Infinispan Protostream annotations is to mark your objects queryable and decide how each object field should be indexed. Here is an example:

import org.infinispan.protostream.annotations.ProtoDoc;
import org.infinispan.protostream.annotations.ProtoField;

@ProtoDoc("@Indexed")
public class Pokemon {

   @ProtoDoc("@Field")
   @ProtoField(number = 10)
   String name;

   @ProtoDoc("@Field")
   @ProtoField(number = 20)
   String type1;

   …
}


Then, the ProtoSchemaBuilder can inspect the annotated class and derive a Google Protocol Buffers schema file from it. Here is an example:

RemoteCacheManager remote = …
SerializationContext serialCtx =
         ProtoStreamMarshaller.getSerializationContext(remote);

ProtoSchemaBuilder protoSchemaBuilder = new ProtoSchemaBuilder();
String protoFile = protoSchemaBuilder
   .fileName(fileName)
   .addClass(Pokemon.class)
   .packageName("pokemons")
   .build(serialCtx);


Finally, the schema file needs to be registered in the “___protobuf_metadata” cache:

RemoteCache<String, String> metadataCache =
   remote.getCache(PROTOBUF_METADATA_CACHE_NAME);

metadataCache.put(fileName, protoFile);

String filesWithErrors = metadataCache.get(ERRORS_KEY_SUFFIX);
if (filesWithErrors != null)
   throw new AssertionError("Error in proto file(s): " + filesWithErrors);
else
   System.out.println("Added schema file: " + fileName);


Although this is by far the easiest way to make your Java objects queryable, this method might not always be viable. For example, you might not be able to modify the Java object classes to add the annotations. For such use cases, a more verbose method is available that does not require modifying the source code of the Java object.

Plain Object Method

For example, given this Java object:

public class CryptoCurrency {

   public final String description;

   public final Integer rank;

....
}
view raw


A Protocol Buffers schema must be defined where comments are used to define the object as queryable and decide how each field is indexed:

package crypto;

/**
 * @Indexed
 */
message CryptoCurrency {

    /**
     * @Field
     */
    required string description = 10;

    /**
     * @Field
     */
    required uint32 rank = 20;

}


This method also requires a Protostream message marshaller to be defined . This specifies how each field is serialized/deserialized:

import org.infinispan.protostream.MessageMarshaller;

public class CryptoCurrencyMarshaller implements MessageMarshaller<CryptoCurrency> {

   @Override
   public CryptoCurrency readFrom(ProtoStreamReader reader) throws IOException {
      String description = reader.readString("description");
      Integer rank = reader.readInt("rank");
      return new CryptoCurrency(description, rank);
   }

   @Override
   public void writeTo(ProtoStreamWriter writer, CryptoCurrency obj) throws IOException {
      writer.writeString("description", obj.description);
      writer.writeInt("rank", obj.rank);
   }

   ...
}


This method still requires the Protocol Buffers schema to be registered remotely, but, on top of that, the schema and marshaller need to be registered in the client:

SerializationContext serialCtx = ...

String protoFile = read(CryptoCurrency.class.getResourceAsStream("/crypto.proto"));
metadataCache.put("crypto.proto", protoFile);
...

serialCtx.registerProtoFiles(FileDescriptorSource.fromResources("/crypto.proto"));
serialCtx.registerMarshaller(new CryptoCurrencyMarshaller());


Clearly, this second method is a lot more verbose and laborious when refactoring. If any changes are made to the Java object, the marshaller and Protocol Buffer schema need to be changed accordingly. However, this is done automatically in the first method.

Both methods are demonstrated in full in the queryable-pojos demo.

Download Building Reactive Microservices in Java: Asynchronous and Event-Based Application Design. Brought to you in partnership with Red Hat

Topics:
java ,tutorial ,objects ,remote clients ,infinispan

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}