iPOJO #Requires is null - osgi

When I use the iPOJO and the #Requires annotation it is not injected into the consumer.
Consumer:
#Component
#Provides
#Instantiate
#Content(name = "bean")
public class BeanContentHandler extends ContentHandler
{
#Requires
private IContentMapper mapper;
Provider:
#Component
#Provides
public class JcromContentMapper implements IContentMapper
{
protected static final Jcrom JCROM = new Jcrom(true, false);

I see two potential issues:
What is doing the #Content annotation ?
Your provider is not instantiated (add an #Instantiate annotation on it)
Can you also tell me where is used the mapper field ?

Related

Spring Boot: use autowired constructor with class from configuration file

I have a Spring Boot 2.3 application with a controller:
#RestController
public class StatusController {
private final ServerStatusCheck serverStatusCheck;
private final ServerStatusMapper serverStatusMapper;
#Autowired
public StatusController(AService aService, ServerStatusMapper serverStatusMapper) {
this.serverStatusCheck = aService;
this.serverStatusMapper = serverStatusMapper;
}
// (...)
}
The class AService implements the interface ServerStatusCheck. There is also a BService class, also implementing ServerStatusCheck interface.
What I need to do: the injected AService object should be configurable in a configuration file, so that the service injected is either "AService" or "BService", depending on the configuration file values. What is the best way to achieve this using Spring Boot? If possible, I would like to keep the constructor-based autowiring (instead of field-based autowiring).
You can create the different beans in a configuration class with condition like https://reflectoring.io/spring-boot-conditionals/
#Configuration
public class ServiceConfiguration {
#ConditionalOnProperty(value="service.a.enabled", havingValue = "true", matchIfMissing = true)
public ServerStatusCheck serverStatusCheckA() {
return new AService();
}
#ConditionalOnMissingBean
#ConditionalOnProperty(value="service.b.enabled", havingValue = "true", matchIfMissing = true)
public ServerStatusCheck serverStatusCheckB() {
return new BService();
}
}
and then wire the bean into the constructor

Is it bad to put #Service/#Component along with #Bean?

Suppose I have this service bean:
#Service
public class MyService{
private final HashMap<String,String> values;
...
}
with the values being:
com.foo:
values:
a: world
b: helo
I may want to create it inside of a configuration:
#Configuration
#ConfigurationProperties(prefix="com.foo")
public class MyConf{
private Map<String, String> values;
#Bean
public MyService myService(){
return new MyService(values);
}
}
But I fear that spring could do something strange like creating 2 beans or dunno what...is this a good practice or should I just move #ConfigurationProperties inside of the #Service itself?
You can inject your configuration directly into your Service
#Service
public class MyService{
private final MyConf conf;
public MyService(MyConf conf) {
this.conf = conf;
}
}
And remove the #Bean annotation from MyConf allong with myservice method.
You should not do that, as it will create two beans of the same type.
In your case, you have not mentioned different names for the beans
so it will override if spring.main.allow-bean-definition-overriding=true else it will fail.
PS: For #Service annotation to create a bean, the class package should be configured in the #ComponentScan or in the base scan package
If you want to use your properties values in your Service class (or anywhere else) you should just inject it :
#Service
public class MyService{
#Autowired
private MyConf myConf;
}

ConstraintValidator dependency injection leads to ValidationException when being validated at class level

I've encountered an unexpected behaviour when using dependency injection in a ConstraintValidator which is getting evaluated at class level.
Entity class:
#Entity
#ValidDemoEntity
public class DemoEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}
Validation annotation:
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = {DemoEntityValidator.class})
public #interface ValidDemoEntity {
String message() default "{some.demo.validator.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Validator:
public class DemoEntityValidator implements ConstraintValidator<ValidDemoEntity, DemoEntity> {
private DemoEntityRepository demoEntityRepository;
public DemoEntityValidator(DemoEntityRepository demoEntityRepository) {
this.demoEntityRepository = demoEntityRepository;
}
#Override
public void initialize(ValidDemoEntity constraintAnnotation) {
}
#Override
public boolean isValid(DemoEntity demoEntity, ConstraintValidatorContext constraintValidatorContext) {
return true;
}
}
Test class:
#SpringBootTest
public class ValidatorInstantiationTest {
private Validator validator;
#Before
public void setUp() throws Exception {
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
validator = validatorFactory.getValidator();
}
#Test
public void shouldInitiateAndCallDemoEntityValidator() {
DemoEntity demoEntity = new DemoEntity();
validator.validate(demoEntity);
}
}
Validating the entity leads to:
javax.validation.ValidationException: HV000064: Unable to instantiate ConstraintValidator: com.example.demo.DemoEntityValidator.
and further down the stack trace:
Caused by: java.lang.NoSuchMethodException: com.example.demo.DemoEntityValidator.<init>()
which indicates that Hibernate tried to initiate the the class instead of letting Spring take care of that.
The strange thing about this is that dependency injection works fine for validations applied on field level.
The code is available at GitHub.
The exception says that there is no default constructor because Hibernate Validator tries to instantiate your validator.
You have to use Spring.
1 Make your validator a Spring Bean:
#Component
public class DemoEntityValidator implements ConstraintValidator<ValidDemoEntity, DemoEntity> {
2 Inject the Spring provided validator and use the SpringRunner for executing your tests:
#SpringBootTest
#RunWith(SpringRunner.class)
public class ValidatorInstantiationTest {
#Autowired
private Validator validator;
#Test
public void shouldInitiateAndCallDemoEntityValidator() {
DemoEntity demoEntity = new DemoEntity();
validator.validate(demoEntity);
}
}
1 Make your validator a Spring Bean
This site states:
The Spring framework automatically detects all classes which implement the ConstraintValidator interface. The framework instantiates them and wires all dependencies like the class was a regular Spring bean.
Which clearly works for validations applied on field level.
Nevertheless I've updated the code.
DemoEntityValidator is now a Spring component:
#Component
public class DemoEntityValidator implements ConstraintValidator<ValidDemoEntity, DemoEntity>
I've changed the test to:
#SpringBootTest
#RunWith(SpringRunner.class)
public class ValidatorInstantiationTest {
#Autowired
private DemoEntityRepository demoEntityRepository;
#Test
public void shouldInitiateAndCallDemoEntityValidator() {
DemoEntity demoEntity = new DemoEntity();
demoEntityRepository.save(demoEntity);
}
}
To make the usecase clearer, but the test still leads to the same exception.
Adding an empty constructor to the class DemoEntityValidator disables the error.
I think you answer is here:
https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation-beanvalidation-spring
You need to declare a LocalValidatorFactoryBean in your configuration class and it will just work.
From the documentation:
By default, the LocalValidatorFactoryBean configures a
SpringConstraintValidatorFactory that uses Spring to create
ConstraintValidator instances. This lets your custom
ConstraintValidators benefit from dependency injection like any other
Spring bean.
And an example from the same place:
import javax.validation.ConstraintValidator;
public class MyConstraintValidator implements ConstraintValidator {
#Autowired;
private Foo aDependency;
...
}
And this is how I declared that bean in a #Configuration annotated class:
/**
* Provides auto-wiring capabilities for validators Checkout: https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation-beanvalidation-spring
*/
#Bean
public LocalValidatorFactoryBean validatorFactoryBean() {
LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
bean.setValidationMessageSource(validationMessageSource());
return bean;
}
There's nothing wrong with your validator class. I got this working by making two changes to the test configuration:
1. Run test with Spring
In order to have Spring manage your beans, you need to run your test with a test runner that sets up Spring. You can specify the test runner class using junit's #RunWith-annotation:
#RunWith(SpringRunner.class)
#SpringBootTest
public class ValidatorInstantiationTest { ... }
2. Inject a Spring managed validator bean
Since you're using Spring Boot, you can inject a Spring managed validator – it's already configured. This way, Spring will handle the initiation of your DemoEntityValidator.
#RunWith(SpringRunner.class)
#SpringBootTest
public class ValidatorInstantiationTest {
#Autowired
private Validator validator;
...
}
This is all that is needed. You should not annotate your DemoEntityValidator with #Component or similar.
Note that you need to provide Spring with a data source, since SpringRunner will set up a context based on your Spring Boot setup (I'm guessing it includes spring-boot-starter-data-jpa in your case). The easiest way to get going is just to put an in-memory DB such as h2 on the classpath.

Mapstruct - How can I inject a spring dependency in the Generated Mapper class

I need to inject a spring service class in the generated mapper implementation, so that I can use it via
#Mapping(target="x", expression="java(myservice.findById(id))")"
Is this applicable in Mapstruct-1.0?
As commented by brettanomyces, the service won't be injected if it is not used in mapping operations other than expressions.
The only way I found to this is :
Transform my mapper interface into an abstract class
Inject the service in the abstract class
Make it protected so the "implementation" of the abstract class has access
I'm using CDI but it should be the samel with Spring :
#Mapper(
unmappedTargetPolicy = org.mapstruct.ReportingPolicy.IGNORE,
componentModel = "spring",
uses = {
// My other mappers...
})
public abstract class MyMapper {
#Autowired
protected MyService myService;
#Mappings({
#Mapping(target="x", expression="java(myservice.findById(obj.getId())))")
})
public abstract Dto myMappingMethod(Object obj);
}
It should be possible if you declare Spring as the component model and add a reference to the type of myservice:
#Mapper(componentModel="spring", uses=MyService.class)
public interface MyMapper { ... }
That mechanism is meant for providing access to other mapping methods to be called by generated code, but you should be able to use them in the expression that way, too. Just make sure you use the correct name of the generated field with the service reference.
Since 1.2 this can be solved with a combination of #AfterMapping and #Context.. Like this:
#Mapper(componentModel="spring")
public interface MyMapper {
#Mapping(target="x",ignore = true)
// other mappings
Target map( Source source, #Context MyService service);
#AfterMapping
default void map( #MappingTarget Target.X target, Source.ID source, #Context MyService service) {
target.set( service.findById( source.getId() ) );
}
}
The service can be passed as context.
A nicer solution would be to use an #Context class which wrap MyService in stead of passing MyService directly. An #AfterMapping method can be implemented on this "context" class: void map( #MappingTarget Target.X target, Source.ID source ) keeping the mapping logic clear of lookup logic. Checkout this example in the MapStruct example repository.
What's worth to add in addition to the answers above is that there is more clean way to use spring service in mapstruct mapper, that fits more into "separation of concerns" design concept, called "qualifier". Easy re-usability in other mappers as a bonus.
For sake of simplicity I prefer named qualifier as noted here http://mapstruct.org/documentation/stable/reference/html/#selection-based-on-qualifiers
Example would be:
import org.mapstruct.Mapper;
import org.mapstruct.Named;
import org.springframework.stereotype.Component;
#Component
#Mapper
public class EventTimeQualifier {
private EventTimeFactory eventTimeFactory; // ---> this is the service you want yo use
public EventTimeQualifier(EventTimeFactory eventTimeFactory) {
this.eventTimeFactory = eventTimeFactory;
}
#Named("stringToEventTime")
public EventTime stringToEventTime(String time) {
return eventTimeFactory.fromString(time);
}
}
This is how you use it in your mapper:
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
#Mapper(componentModel = "spring", uses = EventTimeQualifier.class)
public interface EventMapper {
#Mapping(source = "checkpointTime", target = "eventTime", qualifiedByName = "stringToEventTime")
Event map(EventDTO eventDTO);
}
I am using Mapstruct 1.3.1 and I have found this problem is easy to solve using a decorator.
Example:
#Mapper(unmappedTargetPolicy = org.mapstruct.ReportingPolicy.IGNORE,
componentModel = "spring")
#DecoratedWith(FooMapperDecorator.class)
public interface FooMapper {
FooDTO map(Foo foo);
}
public abstract class FooMapperDecorator implements FooMapper{
#Autowired
#Qualifier("delegate")
private FooMapper delegate;
#Autowired
private MyBean myBean;
#Override
public FooDTO map(Foo foo) {
FooDTO fooDTO = delegate.map(foo);
fooDTO.setBar(myBean.getBar(foo.getBarId());
return fooDTO;
}
}
Mapstruct will generate 2 classes and mark the FooMapper that extends FooMapperDecorator as the #Primary bean.
I can't use componentModel="spring" because I work in a large project that doesn't use it. Many mappers includes my mapper with Mappers.getMapper(FamilyBasePersonMapper.class), this instance is not the Spring bean and the #Autowired field in my mapper is null.
I can't modifiy all mappers that use my mapper. And I can't use particular constructor with the injections or the Spring's #Autowired dependency injection.
The solution that I found: Using a Spring bean instance without using Spring directly:
Here is the Spring Component that regist itself first instance (the Spring instance):
#Component
#Mapper
public class PermamentAddressMapper {
#Autowired
private TypeAddressRepository typeRepository;
#Autowired
private PersonAddressRepository personAddressRepository;
static protected PermamentAddressMapper FIRST_INSTANCE;
public PermamentAddressMapper() {
if(FIRST_INSTANCE == null) {
FIRST_INSTANCE = this;
}
}
public static PermamentAddressMapper getFirstInstance(){
return FIRST_INSTANCE;
}
public static AddressDTO idPersonToPermamentAddress(Integer idPerson) {
//...
}
//...
}
Here is the Mapper that use the Spring Bean accross getFirstInstance method:
#Mapper(uses = { NationalityMapper.class, CountryMapper.class, DocumentTypeMapper.class })
public interface FamilyBasePersonMapper {
static FamilyBasePersonMapper INSTANCE = Mappers.getMapper(FamilyBasePersonMapper.class);
#Named("idPersonToPermamentAddress")
default AddressDTO idPersonToPermamentAddress(Integer idPerson) {
return PermamentAddressMapper.getFirstInstance()
.idPersonToPermamentAddress(idPersona);
}
#Mapping(
source = "idPerson",
target="permamentAddres",
qualifiedByName="idPersonToPermamentAddress" )
#Mapping(
source = "idPerson",
target = "idPerson")
FamilyDTO toFamily(PersonBase person);
//...
Maybe this is not the best solution. But it has helped to decrement the impact of changes in the final resolution.

Inject a list of classes that extends an abstract class with Spring

Hy,
I have the next code:
public abstract class MyClass{
protected abstract void method1();
}
Classes that extend the first one:
#Component
public class MyClass1 extends MyClass{
.....
}
#Component
public class MyClass2 extends MyClass{
.....
}
My class where I try to inject list of classes that extends an abstract class
#Component
public class SpringClass{
#Autowired **//It doesnt work, nothing is inyected!**
List<MyClass> classes
}
My problem is it doesnt work, it doesnt inject the list of classes that extend MyClass in property classes. Why?
Thanks
You can remove #Autowired annotation for the instance variable and add it to the setter method. After doing this your spring class would be
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component
public class SpringClass {
List<MyClass> classes;
#Autowired
public void setClasses(List<MyClass> classes) {
this.classes = classes;
}
}
Hope this helps.
#Component
public class SpringClass {
#Autowired
private Map<String, YourInterface> map;
}
String in map will be contain all classes name that implements the YourInterface as String.
if you want to get all instances - use map.values()
if you want to get specific instance - get it by class name.
in additional, you can customize the key by another recognize such as Enum.
in this case - you need to configure this as #Bean
as bellow :
#Configuration
public class CalculationHandlerConfig {
#Bean
public HashMap<OperatorTypeEnum, CalculatorService> CalculationHandlers(Map<String, CalculatorService> beansMap) {
HashMap<OperatorTypeEnum, CalculatorService> map = new HashMap<>();
for (CalculatorService bean : beansMap.values()) {
map.put(bean.getOperatorType(), bean);
}
return map;
}
}
It looks like it could be dependent on the version of Spring being used, however, you may also want to try using an interface that all the desired classes implement, and injecting the list referencing the interface.
In other words, instead of this..
List<AbstractOrConcreteBaseClass>
use...
List<Interface>

Resources