Mapstruct not selecting overloaded method for optional properties generated by Immutables - java

I'm using Immutables to generate some classes DTO classes, and Mapstruct to map JPA entities to DTOs. This works great, as long as the DTO has only required properties. If a property is not required (by making the accessor method return an Optional, as per the docs), the builder generated by Immutables will have a setter method with an Optional argument, which Mapstruct will fail on:
error: Can't map property "java.lang.Integer id" to "java.util.Optional<java.lang.Integer> id". Consider to declare/implement a mapping method: "java.util.Optional<java.lang.Integer> map(java.lang.Integer value)".
Makes sense, thankfully there's an option available to also generate methods that take nullable arguments instead. This option generates an additional method that takes a nullable argument. However, Mapstruct seems to fail regardless of the presence of this method.
As a workaround, I implemented this abomination (but at this point I'd rather implement the mapping methods myself):
#Mapper
public class OptionalMapper {
public <T> T unwrapOptional(final Optional<T> optional) {
return optional.orElse(null);
}
public <T> Optional<T> wrapIntoOptional(final T value) {
return Optional.ofNullable(value);
}
Is there any way to make Mapstruct look for overloaded methods (or see the "correct" one first)? Am I going about this the wrong way or simply missing something? Thanks!

Currently writing that custom OptionalMapper is the way to perform the unwrapping of the optionals.
I don't think that doing that is a bad thing. There is an open issue for supporting Optional

Related

Overload lombok setter

We can use lombok to generate setter like this:
#Data //or #Setter
public class Test {
int a;
}
Say for instance I also want an overloaded setter that would take a String:
public void setA(String aStr){
//parseInt and set 'a'
}
But when I add this overloaded method, lombok thinks that I have manually added a setter and so it chooses not to add one itself.
Apparently it looks only at the method name and not the parameters.
Is there a way I can force it to add the normal (that takes an int as parameter) setter?
Or the only way is to add that normal setter myself (using IDE setter generator of course)? I have a lot of fields and a lot of classes.
Adding the #Tolerate annotation on my overloaded method solved the issue.
Documentation:
Put on any method or constructor to make lombok pretend it doesn't
exist, i.e., to generate a method which would otherwise be skipped due
to possible conflicts.
It has been experimental though, since 2014.
The documentation states that "No method is generated if any method already exists with the same name (case insensitive) and same parameter count.".
This is the case that you've described. Instead, you should define an additional custom setter method with a new name like
setAFromString(String aStr)

Different effect when using plural method on Collection field with and without #Singular annotation

I want to have immutable myClass objects. Good solution seams to be using #Singular annotation.
The problem is when I use this annotation the method elements() appends elements to existing list, instead of creating the new one:
Let's assume that that we have:
#Value
#Builder(toBuilder = true)
public class MyClass {
#Singular
private List<String> elemets;
}
and
MyClass.builder()
.elemets(Arrays.asList("elem1"))
.elemets(Arrays.asList("elem2"))
.build();
Without #Singular annotation we have elem2 on the list
with #Singular annotation we have both elem1 and elem2, if I want to have elem2 only I have to use clearElements() before.
Why implementation is different? Is it possible to use #Singular with my own implementation?
With #Singular annotation I cannot implement elemets(List elemets) method in MyClassBuilder class because I get: "Manually adding a method that #Singular #Builder would generate is not supported. If you want to manually manage the builder aspect for this field/parameter, don't use #Singular."
First let me say that using #Singular isn't necessarily the best solution -- it depends on your use case.
However, in most cases where you want to ensure immutability on classes that use collections, it is a good choice.
#Singular behaves the way it does because the Lombok designers decided that it's a good choice to do so. And I agree: It makes the two setter methods behave similarly; and in those rare cases where you want to reset the elements in a builder, you have the clear method.
#Singular generates pretty complex code (see https://projectlombok.org/features/BuilderSingular for an example). This is to ensure properties like efficiency and immutability (also when reusing builders to produce several objects). When you mess around with that code, you can easily violate these properties. Lombok prevents you from doing that.
If you really want to modify this aspect, you have three choices:
delombok the builder code, copy it into your class, and modify it.
Add another differently named method, like clearAndSetElements(List<String>). But that's probably even more confusing.
Remove #Singular and implement the setter methods on your own. You will have to put some effort in the implementation if you want the properties of Lombok's implementation; you can use the delomboked code as inspiration.

Custom `NonNull` annotation in Lombok

Here's the java-doc of NonNull annotation of Lombok:
If put on a parameter, lombok will insert a null-check at the start of
the method / constructor's body, throwing a {#code
NullPointerException} with the parameter's name as message. If put on
a field, any generated method assigning a value to this field will
also produce these nullchecks. Note that any annotation named {#code
NonNull} with any casing and any package will result in nullchecks
produced for generated methods (and the annotation will be copied to
the getter return type and any parameters of generated methods), but
only this annotation, if present on a parameter, will result
in a null check inserted into your otherwise handwritten method.
WARNING: If the java community ever does decide on supporting a single
{#code #NonNull} annotation (for example via JSR305), then this
annotation will be deleted from the lombok package.
If the need to update an import statement scares you, you should use
your own annotation named {#code #NonNull} instead of this one.
What is the simplest way to have my own annotation, let's say NonNullNonnull, and Lombok to inject null-check based on my annotation?
Update: my question is hot to have an annotation to use for method arguments.
First, you need to name it nonNull (casing is irrelevant). NotNull will not be recognized by Lombok. Additionally you need to set another Lombok annotation (e.g. #Data, #Setter, ...), so that your type gets processed by Lombok.
Summarizing your custom annotation isn't probably as valuable as the #lombok.NonNull-annotation itself. An example where you benefit from the #lombok.NonNull-annotation, where your custom annotation wouldn't even be processed, is, when your type doesn't contain any other Lombok annotation, e.g.:
class NoLombokAnnotationsAnywhere {
void testMe(#lombok.NonNull String nonNull) { /* .. */ }
}
will produce a NullPointerException as soon as you call new NoLombokAnnotationsAnywhere().testMe(null). Whereas this wouldn't throw anything with your custom annotation. Of course this only applies as long as you don't have any other Lombok annotations there. As soon as the type gets processed by Lombok, your annotation gets processed too.
If you have your own NonNull-annotation, then you can add just another Lombok-annotation that seems appropriate and Lombok adds a null-check for you, e.g.:
#Data
class NonNullData {
#mycustom.Nonnull
String value;
}
// Calling the following throws a NullPointerException as expected
new NonNullData(null);
You may also find the following issue relevant: Support annotations named #NotNull as well as #NonNull

Guice multiple annotations

I have an interface called StatsStore. I have 2 implementations of this store. An in-memory and an SQL implementation called InMemoryStatsStore and SqlStatsStore. In order to inject them I've create 2 annotations #InMemoryStore and #SqlStore. the injections are:
bind(StatsStore.class)
.annotatedWith(InMemoryStore.class)
.to(InMemoryStatsStore.class);
bind(StatsStore.class)
.annotatedWith(SqlStore.class)
.to(SqlStatsStore.class);
Now I want to add a new layer of annotation to separate between InMemoryStringStore and InMemoryNumberStore but I can't add more than one annotation to the binding lines e.g. the following does not compile:
bind(StatsStore.class)
.annotatedWith(InMemoryStore.class)
.annotatedWith(NumberStoreAnnotation.class) // using named doesn't work as well
.to(InMemoryNumberStore.class);
How can I add more than one annotation without using a single named one which would be quite complicated the more layers I add to it?
The other solution I had in mind is Injecting twice:
bind(StatsStore.class)
.annotatedWith(InMemoryStore.class)
.to(InMemoryStatsStore.class);
bind(InMemoryStatsStore.class)
.annotatedWith(NumberStoreAnnotation.class)
.to(InMemoryNumberStore.class);
Thanks all.
As Amit said, you can't have more than one #BindingAnnotation apply to any given injection. Internally, Guice works like a Map<Key, Provider> where a Key is a possibly-parameterized class with an optional single annotation instance. However, because these are instances, you're welcome to create your own instantiable annotation that works the way Named works.
#Inject #InMemoryStore(NUMBER) StatsStore inMemoryNumberStore;
#Inject #SqlStore(STRING) StatsStore sqlStringStore;
// or
#Inject #Store(dataType=NUMBER, backend=SQL) sqlNumberStore;
The annotation must have the fields defined like so. (If you have one element named value, you can omit the property name per JLS 9.7.3.) Equal annotations are defined as in the Annotation.equals docs.
public enum DataType { NUMBER, STRING; }
public enum Backend { SQL, IN_MEMORY; }
#BindingAnnotation #Retention(SOURCE) #Target({ FIELD, PARAMETER, METHOD })
public #interface Store {
DataType dataType();
Backend backend();
}
That works nicely for #Provides, when you can invoke the annotation the same way you inject it, but how can you create a factory method for instances like Names.named? For that, you'll need to do one of the following:
Create an anonymous implementation, with accessors for each attribute as well as correct implementations of equals and hashCode. Note that the hashCode contract is much stricter than for Object, but you can get compatible implementations from Apache annotation utils or similar libraries.
Use AnnotationLiteral, which provides equals and hashCode implementations for arbitrary subclasses.
Use Google Auto or a similar code generator to generate code for a compatible implementation for you. Familiarity with this type of solution is particularly useful for Android and other memory-constrained environments for which reflection is slow, though such environments usually preclude you from using Guice. (#Qualifier annotations work the same way in other JSR-330 compatible dependency injection frameworks, though, including Dagger.)
If the above seems a little complicated, or if you want more complex logic than Guice's map-based implementation can accomplish, one alternative is to add a layer of indirection that you control:
public class StoreStore {
#Inject Provider<InMemoryNumberStore> inMemoryNumberStoreProvider;
// ...
// You can also inject the Injector to call getInstance with a class literal.
public StatsStore getStore(DataType dataType, Backend backend) {
// This can also be a switch or any other sort of lookup, of course.
if (dataType == NUMBER && backend == IN_MEMORY) {
return inMemoryNumberStoreProvider.get();
} // ...
}
}
You can't do that:
#BindingAnnotation tells Guice that this is a binding annotation. Guice will produce an error if ever multiple binding annotations apply to the same member.
You could use named bindings instead, or you should consider redesigning your solution.

Java Builder generator problem

In a project of mine I have two packages full of DTOs, POJOs with just getters and setters. While it's important that they are simple java beans (e.g. because Apache CXF uses them to create Web Service XSDs etc.), it's also awful and error-prone to program like that.
Foo foo = new Foo();
foo.setBar("baz");
foo.setPhleem(123);
return foo;
I prefer fluent interfaces and builder objects, so I use maven / gmaven to automatically create builders for the DTOs. So for the above code, a FooBuilder is automatically generated, which I can use like this:
Foo foo = new FooBuilder()
.bar("baz")
.phleem(123)
.build();
I also automatically generates Unit tests for the generated Builders. A unit test would generate both of the above codes (builder version and non builder version) and assert that both versions are equivalent in terms of equals() and hashcode(). The way I can achieve that is to have a globally accessible Map with defaults for every property type. Something like this:
public final class Defaults{
private Defaults(){}
private static final Map<Class<?>, Object> DEFAULT_VALUES =
new HashMap<Class<?>, Object>();
static{
DEFAULT_VALUES.put(String.class, "baz");
// argh, autoboxing is necessary :-)
DEFAULT_VALUES.put(int.class, 123);
// etc. etc.
}
public static getPropertyValue(Class<?> type){
return DEFAULT_VALUES.get(type);
}
}
Another non-trivial aspect is that the pojos sometimes have collection members. e.g.:
foo.setBings(List<Bing> bings)
but in my builder I would like this to generate two methods from this case: a set method and an add method:
fooBuilder.bings(List<Bing> bings); // set method
fooBuilder.addBing(Bing bing); // add method
I have solved this by adding a custom annotation to the property fields in Foo
#ComponentType(Bing.class)
private List<Bing> bings;
The builder builder (sic) reads the annotation and uses the value as the generic type of the methods to generate.
We are now getting closer to the question (sorry, brevity is not one of my merits :-)).
I have realized that this builder approach could be used in more than one project, so I am thinking of turning it into a maven plugin. I am perfectly clear about how to generate a maven plugin, so that's not part of the question (nor is how to generate valid Java source code). My problem is: how can I deal with the two above problems without introducing any common dependencies (between Project and Plugin):
<Question>
I need a Defaults class (or a similar mechanism) for getting default values for generated unit tests (this is a key part of the concept, I would not trust automatically generated builders if they weren't fully tested). Please help me come up with a good and generic way to solve this problem, given that each project will have it's own domain objects.
I need a common way of communicating generic types to the builder generator. The current annotation based version I am using is not satisfactory, as both project and plugin need to be aware of the same annotation.
</Question>
Any Ideas?
BTW: I know that the real key point of using builders is making objects immutable. I can't make mine immutable, because standard java beans are necessary, but I use AspectJ to enforce that neither set-methods nor constructors are called anywhere in my code base except in the builders, so for practical purposes, the resulting objects are immutable.
Also: Yes, I am aware of existing Builder-generator IDE plugins. That doesn't fit my purpose, I want an automated solution, that's always up to date whenever the underlying code has changed.
Matt B requested some info about how I generate my builders. Here's what I do:
I read a class per reflection, use Introspector.getBeanInfo(clazz).getPropertyDescriptors() to get an array of property descriptors. All my builders have a base class AbstractBuilder<T> where T would be Foo in the above case. Here's the code of the Abstract Builder class. For every property in the PropertyDescriptor array, a method is generated with the name of the property. This would be the implementation of FooBuilder.bar(String):
public FooBuilder bar(String bar){
setProperty("bar", bar);
return this;
}
the build() method in AbstractBuilder instantiates the object and assigns all properties in it's property map.
A POJO is an object which doesn't follow the Java Bean spoec. ie. it doesn't have setters/getters.
JavaBeans are not required to have setters, if you don't want them to be called, don't generate them. (Your builder can call a package local or private constructor to create your immutable objects)
Have you looked at Diezel ?
It's a Builder generator.
It handles generic types, so it might be helpful here for the question 2
It generates all the interfaces, and implementation boiler plate based on a description XML file. You might be able, through introspection to generate this XML (or even goes directly into lower API )
It is bundled as a maven plugin.

Categories

Resources