Thursday 30 December 2021

Standard RAID levels

I needed to put this information somewhere for easy access.

striping
different parts are on different harddisks, advantage: faster, as accessing several disks at once to gather all, disadvantage: a failure causes the entire data to be unrecoverable.
mirroring
an exact copy over multiple harddisks
parity
parity bits help check that the data is unlikely to have faults, see checksum. Can even be used to in cases to recover from faults.

Different RAID levels

RAID 0
striping, increased performance without parity information, redundancy, or fault tolerance
RAID 1
mirroring, no striping or parity
RAID 2
mostly unused, academic
RAID 3
byte-level striping with a parity disk
RAID 4
block-level striping with a dedicated parity disk
RAID 5
block-level striping with distributed parity
RAID 6
block-level striping with two parity blocks distributed across all member disks
RAID 10
raid 1 and 0 together

The Wikipedia article in the references provides more in depth information.

It is obvious for my use that I am solely interested in RAID 1, the mirroring solution.

My primary reason is the fault tolerance of data. The fact that reads perform better is not a big thing, and the disadvantage of space inefficiency I'll accept.

References

Wikipedia - Standard RAID levels
https://en.wikipedia.org/wiki/Standard_RAID_levels

Thursday 23 December 2021

Assing JsonIgnore on generated files in openapi

I am used to dabbling a bit with .xjb files, in order to get the generated Java files from the XSDs that I want.

I am not soo familiar with how this works when dealing with REST Services, YAML files and openapi.

But I needed it at work, because somehow the FAIL_ON_UNKNOWN_PROPERTIES was set to true, when it shouldn't be.

So a colleague gave me this little solution for in the pom.xml:

<additionalModelTypeAnnotations><![CDATA[ @com.fasterxml.jackson.annotation.JsonIgnoreProperties(ignoreUnknown \u003D true)]]></additionalModelTypeAnnotations>

And I could add this to the "configOptions" of the "configuration" of the "openapi-generator-maven-plugin".

And this worked.

However!

This completely failed to explain why the property was set to true. From what I could gather the generated ObjectMapper provider sets this explicitly to false.

public class JSON implements ContextResolver<ObjectMapper> {
  private ObjectMapper mapper = new ObjectMapper();

  public JSON() {
    this.mapper.setSerializationInclusion(Include.NON_NULL);
    this.mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    this.mapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false);
    this.mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    this.mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
    this.mapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
    this.mapper.setDateFormat(new RFC3339DateFormat());
    JsonNullableModule jnm = new JsonNullableModule();
    this.mapper.registerModule(jnm);
    this.mapper.registerModule(new JavaTimeModule());
  }

  public void setDateFormat(DateFormat dateFormat) {
    this.mapper.setDateFormat(dateFormat);
  }

  public ObjectMapper getContext(Class<?> type) {
    return this.mapper;
  }
}

So what was going on?

Luckily a colleague of mine put on his debugging gloves, and started looking into it.

Well, it turns out there was a ContextResolver (like the one above) defined as a @Provider, causing it to be discovered first by the JAX-RS runtime.

Once one is discovered, it will use that one.

And of course it didn't have this setting set to false.

It took a while to find a solution. Neither @Priority nor anything else seemed to have any effect.

In the end, I saw no other option, than to put extra logic in the ContextResolver with the @Provider on it, to not provide an ObjectMapper, if it was any of the objects that concerned my neck of the woods.

Conclusion

CDI is great, and I'm all for it.

The Advantage is that your components in your software are loosely coupled, and this makes things easier.

The Disadvantage, as found out now, is that sometimes things get Injected that you do not expect, and you have a Dickens of a time, finding out where they come from.

References

Openapi Generator Docs - Templating
https://openapi-generator.tech/docs/templating

Thursday 2 December 2021

RESTEASY004590: You must define a @Consumes type on your client method or interface, or supply a default

Just a small note on the above error message.

I was a bit at a loss what I had done wrong.

For example:

@GET
@Path("/{category}")
@Produces("application/json")
public Response getItemDefinition(@PathParam("category") String category, String filterOn);

And I got the following error message:

RESTEASY004590: "You must define a @Consumes type on your client method or interface, or supply a default

It took me a couple of hours to find the problem, but when it hit me, it hit me hard.

Can you spot it?

Solution: I forgot to put in a @QueryParam("filterOn") annotation on the "filterOn" argument. This causes the rest client to interpret the "filterOn" field as being where to put the Body of the response. And for that, it needs a @Consumes annotation, to find out what to put there.

It seems obvious afterwards, but sometimes little mistakes take a lot of time to find.