1

I have a domain object called MyRequest with upper case variable names that is PDX'd to a region called myRequest. Here is the domain object:

package region;

import org.apache.geode.pdx.PdxReader;
import org.apache.geode.pdx.PdxSerializable;
import org.apache.geode.pdx.PdxSerializer;
import org.apache.geode.pdx.PdxWriter;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.data.gemfire.mapping.annotation.Region;

import com.google.gson.Gson;

@Region("myRequest")
public class MyRequest implements PdxSerializer {
private static final Logger log = LogManager.getLogger(MyRequest.class);

private String Client;
private String Provider;
private String Msg;

public String getClient() { return Client; }
public void setClient(String client) { Client = client; }
public String getProvider() { return Provider; }
public void setProvider(String provider) { Provider = provider; }
public String getMsg() { return Msg; }
public void setMsg(String msg) { Msg = msg; }

public CmQuoteRequest()
{
    this.Msg = "";
}

public String ToJson(MyRequest qr)
{
    Gson gson = new Gson();
    return gson.toJson(qr);
}

public MyRequest ToMyRequest(String json)
{
    Gson gson = new Gson();
    return gson.fromJson(json, MyRequest.class);
}

public static PdxSerializable CreateDeserializable()
{
    return (PdxSerializable)new MyRequest();
}

public MyRequest(String Client, String Provider, String Msg) {
    log.traceEntry();

    this.Client = Client;
    this.Provider = Provider;
    this.Msg = Msg;

    log.traceExit();
}

@Override
public boolean toData(Object o, PdxWriter writer) {
    log.traceEntry();

    writer.writeString("Client", this.Client);
    writer.writeString("Provider", this.Provider);
    writer.writeString("Msg", this.Msg);

    log.traceExit();
    return true;
}

@Override
public Object fromData(Class<?> clazz, PdxReader reader) {
    log.traceEntry();

    this.Client = reader.readString("Client");
    this.Provider = reader.readString("Provider");
    this.Msg = reader.readString("Msg");

    log.traceExit();
        return null;
}
}

Using spring boot geode with webflux I set up access to the region for these domain objects like this:

@Configuration
public class GeodeConfig {
private static final Logger log = LogManager.getLogger(GeodeConfig.class);

public static Region<String, MyRequest> myRequest;

@Bean("myRequestRegion")
public Region<String, MyRequest> myRequestRegion(GemFireCache cache) {
    log.traceEntry();

    Region<String, MyRequest> r = cache.getRegion("myRequest");
    myRequest = r;

    return r;
}

I can then statically refer to this region from rest controller endpoints to put object data there. When this happens, like this:

requests.stream().forEach(request -> {
    try {
        GeodeConfig.myRequest.put(id, request);
    } catch (Exception e) {
        new PutError("requestQueue", e);
    }
});

I am seeing in the logs that the MyRequest object variables are... duplicated in lower case:

2020-01-31T17:32:43,019 DEBUG [http-nio-8082-exec-1] org.sprin.data.gemfi.mappi.MappingPdxSerializer 588 lambda$doToData$2: Serializing entity [region.MyRequest] property [Client] value [Client1] of type [java.lang.String] to PDX
2020-01-31T17:32:43,254 DEBUG [http-nio-8082-exec-1] org.sprin.data.gemfi.mappi.MappingPdxSerializer 588 lambda$doToData$2: Serializing entity [region.MyRequest] property [Provider] value [Provider1] of type [java.lang.String] to PDX
2020-01-31T17:32:43,270 DEBUG [http-nio-8082-exec-1] org.sprin.data.gemfi.mappi.MappingPdxSerializer 588 lambda$doToData$2: Serializing entity [region.MyRequest] property [Msg] value [R] of type [java.lang.String] to PDX
2020-01-31T17:32:43,644 DEBUG [http-nio-8082-exec-1] org.sprin.data.gemfi.mappi.MappingPdxSerializer 588 lambda$doToData$2: Serializing entity [region.MyRequest] property [provider] value [Provider1] of type [java.lang.String] to PDX
2020-01-31T17:32:43,673 DEBUG [http-nio-8082-exec-1] org.sprin.data.gemfi.mappi.MappingPdxSerializer 588 lambda$doToData$2: Serializing entity [region.MyRequest] property [client] value [Client1] of type [java.lang.String] to PDX

2020-01-31T17:32:43,904 INFO  [http-nio-8082-exec-1] org.apach.geode.pdx.inter.TypeRegistry 209 defineType: Caching PdxType[dsid=0, typenum=14943844
name=region.MyRequest
fields=[
Client:String:0:idx0(relativeOffset)=0:idx1(vlfOffsetIndex)=-1
Provider:String:1:1:idx0(relativeOffset)=0:idx1(vlfOffsetIndex)=1
Msg:String:2:2:idx0(relativeOffset)=0:idx1(vlfOffsetIndex)=2
provider:String:3:3:idx0(relativeOffset)=0:idx1(vlfOffsetIndex)=3
client:String:4:4:idx0(relativeOffset)=0:idx1(vlfOffsetIndex)=-1]

When I look at the region in GFSH it's got duplicate columns with upper and lower case names!

Whereas domain object variable names are all in upper case... now endemic. Any ideas what is going on with the pdx instances repeated in lower case? It's sort of making a mess for downstream.

rupweb
  • 3,052
  • 1
  • 30
  • 57

1 Answers1

4

First of all, I'm not sure why your domain class implements PdxSerializer. A PdxSerializer should be a separate class that you register with the cache, though in this case, you don't even need one because Spring will automatically use MappingPdxSerializer. While this is incorrect, it isn't the cause of your problem, as the same behavior occurs even if you removed all the PdxSerializer stuff from your class.

The problem is that you're not adhering to the JavaBeans Specification. By convention, in Java, fields should start with a lower case character. When the object is being serialized, the framework looks at the getters and setters to determine what to persist. Following convention, the methods getClient() and setClient() imply the existence of a property named "client". Since Java is case-sensitive, "client" and "Client" are NOT the same.

The best solution to your duplicate field issue is to change your field names to follow Java's naming convention.

If for some reason you can't change your field names, you can implement your own PdxSerializer (which you've already done inside of your MyRequest class). Below is an example PdxSerializer implementation that should do what you want:

public class CustomSerializer implements PdxSerializer {
    @Override
    public boolean toData(Object o, PdxWriter pdxWriter) {
        if(o instanceof MyRequest) {
            MyRequest request = (MyRequest)o;
            pdxWriter.writeString("Client", request.getClient());
            pdxWriter.writeString("Msg", request.getMsg());
            pdxWriter.writeString("Provider", request.getProvider());
            return true;
        }
        return false;
    }

    @Override
    public Object fromData(Class<?> aClass, PdxReader pdxReader) {
        MyRequest request;
        try {
            request = (MyRequest) aClass.getConstructor().newInstance();
            request.setClient(pdxReader.readString("Client"));
            request.setMsg(pdxReader.readString("Msg"));
            request.setProvider(pdxReader.readString("Provider"));
            return request;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

To register this serializer, you need to define a bean of type CustomSerializer in your configuration, like below:

@Bean("customSerializer")
public CustomSerializer customSerializer() {
    return new CustomSerializer();
}

and then annotate your configuration class with @EnablePdx(serializerBeanName = "customSerializer"). This should map your fields correctly without duplication.

Patrick
  • 63
  • 3
  • Thanks a lot Patrick I shall try it. The thing is we use the .Net client as well which as you will know by convention uses upper case variable names! – rupweb Feb 04 '20 at 19:57
  • 1
    @rupweb using Spring Data Geode's `MappingPdxSerializer` you would not need to implement `PdxSerializer` or even `PdxSerializable`. That in conjunction with the renaming of your fields to lowercase should also work. UNLESS you really require a customer `PdxSerializer` because you have decided you want to encode the data in a different manner, different to the standard encoding provided by the existing Pdx serialization build into Geode. – Udo Feb 04 '20 at 20:50