How to Autowire conditionally in spring boot? - java

I have created one scheduler class
public class TestSchedulderNew {
#Scheduled(fixedDelay = 3000)
public void fixedRateJob1() {
System.out.println("Job 1 running");
}
#Scheduled(fixedDelay = 3000)
public void fixedRateJob2() {
System.out.println("Job 2 running");
}
}
In configuration i have put #ConditionalOnProperty annotation to enable this on conditional purpose.
#Bean
#ConditionalOnProperty(value = "jobs.enabled")
public TestSchedulderNew testSchedulderNew() {
return new TestSchedulderNew();
}
Now in controller, i have created "stopScheduler" method to stop those scheduler , in this controller i have autowired
TestSchedulderNew class
#RestController
#RequestMapping("/api")
public class TestCont {
private static final String SCHEDULED_TASKS = "testSchedulderNew";
#Autowired
private ScheduledAnnotationBeanPostProcessor postProcessor; /]
#Autowired
private TestSchedulderNew testSchedulderNew;
#GetMapping(value = "/stopScheduler")
public String stopSchedule(){
postProcessor.postProcessBeforeDestruction(testSchedulderNew,
SCHEDULED_TASKS);
return "OK";
}
}
Now the problem is if conditional property is false then i get below exception
Field testSchedulderNew in com.sbill.app.web.rest.TestCont required a bean of type 'com.sbill.app.schedulerJob.TestSchedulderNew
In case of true everything works fine,
Do we have any option to solve this ?

You can use #Autowired(required=false) and null check in stopScheduler method.
#Autowired(required=false)
private TestSchedulderNew testSchedulderNew;
#GetMapping(value = "/stopScheduler")
public String stopSchedule() {
if (testSchedulderNew != null) {
postProcessor.postProcessBeforeDestruction(testSchedulderNew,
SCHEDULED_TASKS);
return "OK";
}
return "NOT_OK";
}

Related

Unit Test could not pass due to constructur error

I am struggling at wrting unit test, when I test my code block, it says : constructor error
my code below;
#Component
public class CodeConfigBuilder {
#Value("${promoConfig.prefix.length}")
private Integer prefixLength;
public void validateRequestAndSetDefaults(PromoRequest promoRequest) {
prefixAndPostFixControlAccordingToLength(promoRequest);
}
private void prefixAndPostFixControlAccordingToLength(PromoRequest promoRequest) {
if (promoRequest.getPostfix() != null) {
int lengthControl = prefixLength + promoRequest.getPostfix().length();
if (lengthControl >= promoRequest.getLength()) {
throw new BadRequestException(Constant.ClientConstants.THE_SUM_OF_PREFIX_AND_POSTFIX_CAN_NOT_BE_GREATER_THAN_LENGHT);
}
}
}
public void validateRequestAndSetDefaults(PromoRequest promoRequest) {
prefixAndPostFixControlAccordingToLength(PromoRequest promoRequest)
}
my yml configuration below;
#========= Promo Config ========== #
promoConfig:
prefix:
length: 3
my service below;
public void validateRequest(PromoRequest promoRequest) {
codeConfigBuilder.validateRequestAndSetDefaults(promoRequest);
}
I have a created PropertySourceResolver class
#Value("${promoGenerationConfig.prefix.length}")
private Integer prefixLength;
and my test class below;
#ExtendWith(SpringExtension.class)
class CodeConfigBuilderTest {
private final PromonRequest promoRequest;
private final PropertySourceResolver propertySourceResolver;
private final PromoService promoService;
private final Request request;
public CodeConfigBuilderTest(PromonRequest promoGenerationRequest, PropertySourceResolver propertySourceResolver, PromoService promoService, Request request) {
this.PromonRequest = PromonRequest ;
this.propertySourceResolver = propertySourceResolver;
this.promoService = promoService;
this.request = request;
}
#Test
void prefixAndPostFixControlAccordingToLength() {
promoService.validateRequest(promoRequest);
int lengthControl = propertySourceResolver.getPrefixLength() + promoRequest.getPostfix().length();
Assertions.assertTrue(true, String.valueOf(lengthControl));
}
I have tried many things but my code does not pass the test it says "org.junit.jupiter.api.extension.ParameterResolutionException: No ParameterResolver registered for parameter"
any help, thank you
I'm not 100% but IMHO you can't use constructor injection in unit tests.
Use this instead:
#SpringBootTest
class CodeConfigBuilderTest {
#Autowired
private PromonRequest promoRequest;
#Autowired
private PropertySourceResolver propertySourceResolver;
#Autowired
private PromoService promoService;
#Autowired
private Request request;

Why is #AfterReturning is never called

I have this method and it does return a list:
public List<ReportReconciliationEntry> getMissingReports(List<ReportReconciliationEntry> expectedReports,
List<GeneratedReportContent> generatedReports){
...
return missingReports;
}
but this method is never called:
#AfterReturning(value = "execution(* com.XXX.YYY.ZZZ.service.ReconciliationService.getMissingReports(..)) && args(expectedReports,generatedReports)", argNames = "expectedReports,generatedReports,missingReports", returning = "missingReports")
public void logReportReconciliationException(List<ReportReconciliationEntry> expectedReports, List<GeneratedReportContent> generatedReports, List<ReportReconciliationEntry> missingReports) {
final String notApplicable = properties.getNotApplicable();
ReportingAlertMarker marker = ReportingAlertMarker.builder()
.eventType(E90217)
.userIdentity(notApplicable)
.destinationIp(properties.getDestinationIp())
.destinationPort(properties.getDestinationPort())
.dataIdentity(notApplicable)
.resourceIdentity(notApplicable)
.responseCode(404)
.build();
MDC.put(SYSTEM_COMPONENT, properties.getBpsReportGenerationService());
System.out.println(missingReports);
logWrapper.logError(marker, "SDGFHDZFHDFR!!");
}
I check the return of the first method with a breakpoint. It does return a list, but the #AfterReturning is never called, although the IDE shows the "Navigate to AOP advices" icon. What am I missing?
This is what my class looks like:
#Component
#Aspect
#Slf4j
public class ReportingAlertAspect {
private final LogWrapper logWrapper;
private final ReportingAlertProperties properties;
public ReportingAlertAspect(final ReportingAlertProperties properties, final LogWrapper logWrapper) {
this.logWrapper = logWrapper;
this.properties = properties;
}
....
}
I have another class with a function in it and this one works fine:
#Component
#Aspect
#Slf4j
public class ReportingInfoAspect {
private final LogWrapper logWrapper;
private final ReportingAlertProperties properties;
#AfterReturning(value = "execution(* com.xxx.yyy.zzz.qqq.ReconciliationService.reconcile(..)) && args(windowId)", argNames = "windowId,check",
returning = "check")
public void logSuccessfulReportReconciliation(ReconciliationEvent windowId, boolean check){
String notApplicable = properties.getNotApplicable();
MDC.put(SYSTEM_COMPONENT, properties.getBpsReportGenerationService());
ReportingAlertMarker marker = ReportingAlertMarker.builder()
.eventType(E90293)
.userIdentity(notApplicable)
.destinationIp(properties.getDestinationIp())
.destinationPort(properties.getDestinationPort())
.dataIdentity(notApplicable)
.resourceIdentity(notApplicable)
.responseCode(200)
.build();
if (check){
logWrapper.logInfo(marker, "All reports for windowId {} were generated successfully", windowId.windowId);
}
}
}
I found the problem.
The getMissingReports method was called from another method inside the same class. This is a case of self-invocation and the method was never called through the proxy.
This is what the class looks like:
#Service
#RequiredArgsConstructor
public class ReconciliationService {
private final ReconciliationRepository reconciliationRepository;
private final ReportSafeStoreClientService reportSafeStoreClientService;
#Handler
public whatever whatever() {
...
getMissingReports()
}
}
You can find more info here

In Spring Boot which class would be initialize first #Component or #Configuration

I have a class annotated with #Component which is use to initialze application.yml config properties. Service classe is using configuration property. But sometime my Service class instance created before the Configuration class and I get null property value in service class, Its random not specific pattern.
Configuration Initializer class..
#Component
public class ConfigInitializer implements InitializingBean {
private static final Logger log = LoggerFactory.getLogger(ConfigInitializer.class);
#Autowired
ProxyConfig proxyConfig;
/*#PostConstruct
public void postConstruct(){
setProperties();
}
*/
#Override
public void afterPropertiesSet() {
setProperties();
}
private void setSystemProperties(){
log.debug("Setting properties...");
Properties props = new Properties();
props.put("PROXY_URL", proxyConfig.getProxyUrl());
props.put("PROXY_PORT", proxyConfig.getProxyPort());
System.getProperties().putAll(props);
}
}
#Component
#ConfigurationProperties(prefix = "proxy-config")
public static class ProxyConfig {
private String proxyUrl;
private String proxyPort;
public String getProxyUrl() {
return proxyUrl;
}
public void setProxyUrl(String proxyUrl) {
this.proxyUrl = proxyUrl;
}
public String getProxyPort() {
return proxyPort;
}
public void setProxyPort(String proxyPort) {
this.proxyPort = proxyPort;
}
}
Service Class..
#Service("receiverService")
public class ReceiverService {
private static final Logger logger = LoggerFactory.getLogger(ReceiverService.class);
private ExecutorService executorService = Executors.newSingleThreadExecutor();
#Autowired
public ReceiverService() {
initClient();
}
private void initClient() {
Future future = executorService.submit(new Callable(){
public Object call() throws Exception {
String value = System.getProperty("PROXY_URL"); **//Here I am getting null**
logger.info("Values : " + value);
}
});
System.out.println("future.get() = " + future.get());
}
}
Above Service class get null values String value = System.getProperty("PROXY_URL")
When I use #DependsOn annotation on Service class, it works fine.
In my little knowledge, I know Spring does not have specific order of bean creation.
I want to know If I use #Configuration instead of #Component on ConfigInitializer class like below, Will spring initialize ConfigInitializer
class before other beans ?.
#Configuration
public class ConfigInitializer implements InitializingBean {
//code here
}

how to resolve Spring boot #EnableAsync and #Async problem?

my service:
#Service
public class ForgetService{
#Async
public CompletableFuture<Object> getTokenService() {
Map<String, Object> map = new HashMap<>(8);
map.put("status", ErrorEnum.TOKEN_SUSSCESS.getStatus());
map.put("message", ErrorEnum.TOKEN_SUSSCESS.getMessage());
return CompletableFuture.completedFuture(map);
}
}
my controller:
#RestController
public class ForgetController {
private final ForgetService forgetService;
#Autowired
private ForgetController(ForgetService forgetService) {
this.forgetService = forgetService;
}
#PostMapping(value = "/forget/token")
#Async
public CompletableFuture<Object> getTokenController() {
return CompletableFuture.completedFuture(forgetService.getTokenService());
}
}
spring boot application class:
#SpringBootApplication
#EnableAsync
public class ApitestApplication {
public static void main(String[] args) {
SpringApplication.run(ApitestApplication.class, args);
}
}
when i run my application, console log :
The bean 'forgetService' could not be injected as a 'com.apitest.service.ForgetService' because it is a JDK dynamic proxy that implements:
com.apitest.inf.ForgetServiceInf
Action:
Consider injecting the bean as one of its interfaces or forcing the use of CGLib-based proxies by setting proxyTargetClass=true on #EnableAsync and/or #EnableCaching.
i tried setting 'spring.aop.proxy-target-class=true' in application.properties and setting '#EnableAsync(proxyTargetClass=true), but it's useless,so what's wrong? how to resolve it?
please use below approach, it might help you to fix this issue.
#Service
public class ForgetService{
#Bean("GetTokenService")
public CompletableFuture<Object> getTokenService() {
Map<String, Object> map = new HashMap<>(8);
map.put("status", ErrorEnum.TOKEN_SUSSCESS.getStatus());
map.put("message", ErrorEnum.TOKEN_SUSSCESS.getMessage());
return CompletableFuture.completedFuture(map);
}
#RestController
public class ForgetController {
private final ForgetService forgetService;
#Autowired
private ForgetController(ForgetService forgetService) {
this.forgetService = forgetService;
}
#PostMapping(value = "/forget/token")
#Async("GetTokenService")
public CompletableFuture<Object> getTokenController() {
return CompletableFuture.completedFuture(forgetService.getTokenService());
}
}

Testing a controller with an auto wired component is null when calling the controller from a test case

I have a controller
#RestController
public class Create {
#Autowired
private ComponentThatDoesSomething something;
#RequestMapping("/greeting")
public String call() {
something.updateCounter();
return "Hello World " + something.getCounter();
}
}
I have a component for that controller
#Component
public class ComponentThatDoesSomething {
private int counter = 0;
public void updateCounter () {
counter++;
}
public int getCounter() {
return counter;
}
}
I also have a test for my controller.
#RunWith(SpringRunner.class)
#SpringBootTest
public class ForumsApplicationTests {
#Test
public void contextLoads() {
Create subject = new Create();
subject.call();
subject.call();
assertEquals(subject.call(), "Hello World 2");
}
}
The test fails when the controller calls something.updateCounter(). I get a NullPointerException. While I understand it's possible to add #Autowired to a constructor I would like to know if there is anyway to do this with an #Autowired field. How do I make sure the #Autowired field annotation works in my test?
Spring doesn't auto wire your component cause you instantiate your Controller with new not with Spring, so Component is not instatntiated
The SpringMockMvc test check it correct:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public class CreateTest {
#Autowired
private WebApplicationContext context;
private MockMvc mvc;
#Before
public void setup() {
mvc = MockMvcBuilders
.webAppContextSetup(context)
.build();
}
#Test
public void testCall() throws Exception {
//increment first time
this.mvc.perform(get("/greeting"))
.andExpect(status().isOk());
//increment secont time and get response to check
String contentAsString = this.mvc.perform(get("/greeting"))
.andExpect(status().isOk()).andReturn()
.getResponse().getContentAsString();
assertEquals("Hello World 2", contentAsString);
}
}
The #Autowired class can be easily mocked and tested with MockitoJUnitRunner with the correct annotations.
With this you can do whatever you need to do with the mock object for the unit test.
Here is a quick example that will test the Create method call with mocked data from ComponentThatDoesSomething.
#RunWith(MockitoJUnitRunner.class)
public class CreateTest {
#InjectMocks
Create create;
#Mock
ComponentThatDoesSomething componentThatDoesSomething;
#Test
public void testCallWithCounterOf4() {
when(componentThatDoesSomething.getCounter()).thenReturn(4);
String result = create.call();
assertEquals("Hello World 4", result);
}
}
Use Mockito and inject a mock that you create. I would prefer constructor injection:
#RestController
public class Create {
private ComponentThatDoesSomething something;
#Autowired
public Create(ComponentThatDoesSomething c) {
this.something = c;
}
}
Don't use Spring in your Junit tests.
public CreateTest {
private Create create;
#Before
public void setUp() {
ComponentThatDoesSomething c = Mockito.mock(ComponentThatDoesSomething .class);
this.create = new Create(c);
}
}

Categories