Injecting all mappers in a list and calling a convert method polymorphically - mapstruct

Is there a way to autowire all mappers written with Mapstruct in Spring just like we used to do with the Spring Converter interface and calling one toEntity(or convert or any other name)? In spring, it is easy because they all implement the same functional interface and by making it inherit from another interface we can determine the right converter in the runtime like below:
import org.springframework.core.convert.converter.Converter;
public interface CustomConverter<S extends ..., T extends ...> extends Covnerter<S,T>{
boolean supports(Class clazz);
}
And then injecting it would be easy:
#Autowire
private final List<CustomConverter> myConverters;
and by calling supports we would determine the right kind of converter and then call convert against it.
I had something like this in mind:
#Mapper
public interface MyMapper extends CustomMapper<MyEntity, MyDto>{
MyMapper INSTANCE = Mappers.getMapper(MyMapper.class);
MyEntity toEntity(MyDto dto);
default boolean supports(Class clazz) {
return MyDto.class.isAssignableFrom(clazz);
}
And
public interface CustomMapper<T extends ..., S extends ...> {
boolean supports(Class clazz);
T toEntity(S dto);
}
This does not work though.
Do you have any suggestions here? I might have misunderstood this all together...Thanks.

Checkout: https://github.com/mapstruct/mapstruct-spring-extensions
The author made and adapter based on a discusion in this SO issue.
A non spring based solution can be found here. Although you need to write your own annotation processor.

Related

Mapping Hierarchy of Classes with Mapstruct

I have a hierarchy of classes: VehicleDTO is a base abstract class.
CarDTO, TruckDTO, VanDTO extend from it.
I have the same hierarchy on the other side of a mapper:
VehicleBO <- CarBO, TruckBO, VanBO.
I want to have all the mapping logic consolidated in one mapper. Period.
I have defined mappings for common attributes, but here is when it becomes interesting, I get this exception during compilation:
The return type ... is an abstract class or interface.
Provide a non abstract / non interface result type or a factory method.
So, how do I specify a factory method, that based on a value of a particular attribute or a class of the pojo, would create a target object for me? I would appreciate a good code snippet that actually does the trick.
Thanks!
You can use a method annotated with #ObjectFactory receiving a source parameter for what you need.
Let's assume that you have a mapper that looks like:
#Mapper
public interface VehicleMapper {
VehicleDTO map(VehicleBO vehicle);
// more
}
If you add a method looking like:
#ObjectFactory
default VehicleDTO createVehicleDto(VehicleBO vehicle) {
// your creation logic
}
Then MapStruct will use the createVehicleDto to create the VehicleDTO object.
NOTE when mapping hierarchies and when the mapping looks like the one in the answer then MapStruct will only map the properties which are in the VehicleDTO class and not in possible implementations of the class. The reason for that is that MapStruct generates the mapping code during compilation and not during runtime.
For mapping hierarchies like what you explained you can do something like the following:
public interface VehicleMapper {
default VehicleDTO map(VehicleBO vehicle) {
if (vehicle instanceOf CarBO) {
return map((CarBO) vehicle);
} else if (vehicle instanceOf TruckBO) {
return map((TruckBO) vehicle);
} else if (vehicle instanceOf VanBO) {
return map((VanBO) vehicle);
} else {
//TODO decide what you want to do
}
}
#Named("car")
CarDTO map(CarBO car);
#Named("truck")
TruckDTO map(TruckBO truck);
#Named("car")
VanDTO map(VanBO van);
// more
}
There is mapstruct/mapstruct#131 requesting for generating code like my example out of the box
Nowadays, maybe using Visitor pattern could be better choice instead of the instanceOf way, check below:
https://techlab.bol.com/en/blog/mapstruct-object-hierarchies
You need to set the subclassExhaustiveStrategy property in your #Mapper annotation to RUNTIME_EXCEPTION.
See Mapstruct documentation:
...
To allow mappings for abstract classes or interfaces you need to set the subclassExhaustiveStrategy to RUNTIME_EXCEPTION, you can do this at the #MapperConfig, #Mapper or #BeanMapping annotations. If you then pass a GrapeDto an IllegalArgumentException will be thrown because it is unknown how to map a GrapeDto. Adding the missing (#SubclassMapping) for it will fix that.
...

EclipseContext get beans of type

I have classes:
#Creatable
#Singleton
public class Sample1 implements ISample {
}
#Creatable
#Singleton
public class Sample2 implements ISample {
}
How can i get all beans of type ISample from EclipseContext?
The Eclipse context does not support anything like this. All you can do is look for objects with a specific name (or specific class which is just converted to a name).
Additionally objects declared using #Creatable are not even created and added to the context until something actually uses them.

Abstract components via org.osgi.service.component annotations

I am migrating from org.apache.felix.scr annotations to org.osgi.service.component annotations. I have a set of Components that inherit from a common abstract class. In the felix case, I can use a #Component annotation with the option componentAbstract=true on the super class, and then use #Reference annotation in the super class. I cannot find how to migrate this to osgi annotations.
Is it possible to use Component annotations in a super class of a Component? And if so, what is then the appropriate way to handle the properties and metatype generation?
So, what I am looking for, is something like this
/* No component definition should be generated for the parent, as it is
abstract and cannot be instantiated */
#Component(property="parent.property=parentValue")
public abstract class Parent {
#Reference
protected Service aService;
protected activate(Map<String,Object> props) {
System.out.println("I have my parent property: "+props.get("parent.property"));
#Override
public abstract void doSomething();
}
/* For this class, the proper Component definition should be generated, also
including the information coming from the annotations in the parent */
#Component(property="child.property=childValue")
public class Child extends Parent {
#Activate
public activate(Map<String,Object> props) {
super.activate(props);
System.out.println("I have my child property: "+props.get("child.property"));
}
public void doSomething() {
aService.doSomething();
}
}
By default BND will not process DS annotations in parent classes. You can change that with -dsannotations-options: inherit but please see http://enroute.osgi.org/faq/ds-inheritance.html why you shouldn't!
2021-02-23 UPDATE: It seems like the page mentioned above is no longer available. I don't know if it was moved elsewhere or simply removed but its content (in Markdown format) is still available on GitHub: https://github.com/osgi/osgi.enroute.site/blob/pre-R7/_faq/ds-inheritance.md

Wrapping JavaScriptObjects in case of multiple subclasses?

I have a bunch of data entities that all implement Entity. Now I want to expose some of these entities to JavaScript code, but I can't just make a bunch of JavaScriptObject subclasses because of the one-implementation rule.
So, I'm using this kind of thing:
public class JsStandardScale3 implements StandardScale3 {
private JavaScriptObject wrapped;
public JsStandardScale3(JavaScriptObject wrapped) {
this.wrapped = wrapped;
}
#Override
public native Long getLicenseId() /*-{
this.#com.activegrade.client.exported.JsStandardScale3::wrapped.getLicenseId();
}-*/;
This works, it's just a lot of work. The overlay type structure is so much nicer. Any suggestions?
It turns out that you CAN extend JavaScriptObject with multiple subclasses of an interface as long as all of your extensions are from a single "root" extension of JSO.
For example, I have the structure Standard extends Entity and Course extends Entity. I could NOT do:
JsStandard extends JavaScriptObject...
JsCourse extends JavaScriptObject...
but I could do:
JsEntity extends JavaScriptObject...
JsStandard extends JsEntity...
JsCourse extends JsEntity...
fantastic!
The only limitation is that every method must be marked final, which works fine for a simple overlay scenario.

How to test annotation object in the class that implement AbstractModule

I got a question regarding binding and annotation.
I have the following class:
public class MailFacadeImpl implements MailFacade {
private final PersonDao personDao;
#Inject
public MailFacadeImpl(#Mail PersonDao personDao) {
super();
this.personDao = personDao;
}
The PersonDao is annotated with a custom annotation.
I would like to be able to test this annotation inside the class that implement AbstractModule.
here is a piece of code:
bind(new TypeLiteral<SecurityRulesFactory<Person>>(){}).toProvider(FactoryProvider.newFactory(
new TypeLiteral<SecurityRulesFactory<Person>>(){}, new TypeLiteral<MailSecurityRulesCrdb>(){}));
I would like to have somthing similar to :
if(PersonDAO is annotated with(Mail.class) ){
bind(new TypeLiteral<SecurityRulesFactory<Person>>(){}).toProvider(FactoryProvider.newFactory(
new TypeLiteral<SecurityRulesFactory<Person>>(){}, new TypeLiteral<MailSecurityRulesCrdb>(){}));
}
Do you think it's possible?
thx for your help :-)
Have a nice friday!
It's not clear why you want your module to do this test. Instead, your module can specify how to get or create an instance of PersonDao for injection points annotated with Mail:
bind(PersonDao.class).annotatedWith(Mail.class).to(EmailAwarePersonDao.class);
Note that your PersonDao.class.isAnnotationPresent(Mail.class) won't help here, since the PersonDao class itself isn't annotated with Mail; the parameter to the MailFacadeImpl constructor has that annotation. There are ways to test for that, but if you are trying to do that from a Guice module, you're probably doing something wrong.