Enable or disable Spring restful API (endpoint) based on the flag from DB - spring

I have a restful API which has to be enabled or disabled based on the flag value which I would be fetching during application load. But I am unable enable/disable the API using #Conditional Annotation. I can achieve this by #ConditionOnProperty by setting an property in application.properties file. But, I need a dynamic value from DB to enable/disable the API.
Condition class looks like below
#Component
public class CheckCondition implements Condition {
#Autowired
private AppProperties appProp;
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//Get the Flag value from DB which is fetched from AppProperties
String value = appProp.getProperty(AppPropertiesEnum.ENABLE_LOGSTASH);
boolean flag = false;
if(value != null && value.equalsIgnoreCase("YES"))
flag = true;
return flag;
}
}
Controller class which uses CheckCondition.
#RestController
#CrossOrigin
#Conditional(CheckCondition.class)
public class CheckController {
private static final String URL_PUT_CHECKS = "v1/core/checks"; // PUT
#Autowired
private ContextService serviceContext;
#Autowired
private CheckService serviceCheck;
#RequestMapping(value=URL_PUT_CHECKS, method=RequestMethod.PUT)
public void putLogstash(#RequestBody String jsonValue) {
serviceCheck.storeValue(request, serviceContext.getAppNameVerified(request), jsonValue);
}
}
AppProperties is also a component in which I am making a database call to fetch flag to set the condition.
While application is loaded the CheckCondition class gets initiated first and the appProp will be null. Seems it is implementing condition interface spring boot doesnot load the postProcessor methods/beans. I tried using DependsOn and Order for this. I am not sure what am I missing.
Any suggestions appreciated. Thanks in advance.

You can try like this way.
#RestController
public class TestController {
#Autowired
private CheckRepository checkRepository;
#GetMapping("/test")
public ResponseEntity<Object> getData() {
boolean flag = checkRepository.findByName("value");
if (!flag) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
}
}
}

Related

Spring Data Rest: #Autowire in Custom JsonDeserializer

I am trying to autowire a component into a custom JsonDeserializer but cannot get it right even with the following suggestions I found:
Autowiring in JsonDeserializer: SpringBeanAutowiringSupport vs HandlerInstantiator
Right way to write JSON deserializer in Spring or extend it
How to customise the Jackson JSON mapper implicitly used by Spring Boot?
Spring Boot Autowiring of JsonDeserializer in Integration test
My final goal is to accept URLs to resources in different microservices and store only the ID of the resource locally. But I don't want to just extract the ID from the URL but also verify that the rest of the URL is correct.
I have tried many things and lost track a bit of what I tried but I believe I tried everything mentioned in the links above. I created tons of beans for SpringHandlerInstantiator, Jackson2ObjectMapperBuilder, MappingJackson2HttpMessageConverter, RestTemplate and others and also tried with setting the SpringHandlerInstantiator in RepositoryRestConfigurer#configureJacksonObjectMapper.
I am using Spring Boot 2.1.6.RELEASE which makes me think something might have changed since some of the linked threads are quite old.
Here's my last attempt:
#Configuration
public class JacksonConfig {
#Bean
public HandlerInstantiator handlerInstantiator(ApplicationContext applicationContext) {
return new SpringHandlerInstantiator(applicationContext.getAutowireCapableBeanFactory());
}
}
#Configuration
public class RestConfiguration implements RepositoryRestConfigurer {
#Autowired
private Validator validator;
#Autowired
private HandlerInstantiator handlerInstantiator;
#Override
public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
validatingListener.addValidator("beforeCreate", validator);
validatingListener.addValidator("beforeSave", validator);
}
#Override
public void configureJacksonObjectMapper(ObjectMapper objectMapper) {
objectMapper.setHandlerInstantiator(handlerInstantiator);
}
}
#Component
public class RestResourceURLSerializer extends JsonDeserializer<Long> {
#Autowired
private MyConfig config;
#Override
public Long deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
ServiceConfig serviceConfig = config.getServices().get("identity");
URI serviceUri = serviceConfig.getExternalUrl();
String servicePath = serviceUri.getPath();
URL givenUrl = p.readValueAs(URL.class);
String givenPath = givenUrl.getPath();
if (servicePath.equals(givenPath)) {
return Long.parseLong(givenPath.substring(givenPath.lastIndexOf('/') + 1));
}
return null;
}
}
I keep getting a NullPointerException POSTing something to the API endpoint that is deserialized with the JsonDeserializer above.
I was able to solve a similar problem by marking my deserializer constructor accept a parameter (and therefore removing the empty constructor) and marking constructor as #Autowired.
public class MyDeserializer extends JsonDeserializer<MyEntity> {
private final MyBean bean;
// no default constructor
#Autowired
public MyDeserializer(MyBean bean){
this.bean = bean
}
...
}
#JsonDeserialize(using = MyDeserializer.class)
public class MyEntity{...}
My entity is marked with annotation #JsonDeserialize so I don't have to explicitly register it with ObjectMapper.

How to test Spring event listener conditional SpEL?

I have a working annotation driven event listener with a conditional statement. But even though the code works fine, I'm not able to unit test this conditional due to a failure in the test case processing the SpEL condition.
I noticed that this error only occurs for Spring Boot 1.5.x version, as 2.1.x version worked as expected. Unfortunately I need to use the 1.5.x version.
Class handling the event:
#Component
public class MyComponent {
private static final Logger LOGGER = LoggerFactory.getLogger(MyComponent.class);
#EventListener(condition = "#createdEvent.awesome")
public void handleOrderCreatedEvent(OrderCreatedEvent createdEvent) {
LOGGER.info("Awesome event handled");
}
}
The event class:
public class OrderCreatedEvent {
public OrderCreatedEvent(boolean awesome) {
this.awesome = awesome;
}
private boolean awesome;
public boolean isAwesome() {
return awesome;
}
}
My test class:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MyComponent.class)
public class DemoApplicationTests {
#Autowired
private ApplicationEventPublisher publisher;
#MockBean
private MyComponent myComponent;
#Test
public void handleOrderCreatedEvent_shouldExecute_whenAwesome() {
OrderCreatedEvent event = new OrderCreatedEvent(true);
publisher.publishEvent(event);
verify(myComponent).handleOrderCreatedEvent(event);
}
}
Full source code can be found here: https://github.com/crazydevman/spring-event-testing
Running the application everything works as expected. However, when running the test case I keep getting this error:
org.springframework.expression.spel.SpelEvaluationException: EL1007E: Property or field 'awesome' cannot be found on null
Debugging the code, it looks like this is due to SpEL not being able to interpret the method parameter name 'createdEvent' for the mocked bean, but I don't know how to fix it.
Is there a way to unit test the event conditional?
#Component
public class MyComponent {
private static final Logger LOGGER = LoggerFactory.getLogger(MyComponent.class);
#EventListener(condition = "#root.args[0].awesome")
public void handleOrderCreatedEvent(OrderCreatedEvent createdEvent) {
LOGGER.info("Awesome event handled");
}
}

#Cacheable() returning id null

I have a method findAll() that returns all the speciality from BD, in this method i put the annotation #Cacheable to get the data from the cache, the problem is when i execute the /specialities api, the first time i get the correct data, when i execute the api the second time i get the data with null ids
#Service
#CacheConfig(cacheNames = ServiceConstant.SPECIALITY)
public class SpecialityServiceImpl implements SpecialityService {
#Autowired
private SpecialitySearchRepository specialitySearchRepository;
#Autowired
private SpecialtyMapper specialityMapper;
#Override
#Cacheable
public List<SpecialityDTO> findAll() {
return specialitySearchRepository.findAll().stream().map(specialityMapper::toDto)
.collect(Collectors.toCollection(LinkedList::new));
}
}
#RestController
public class SpecialityResource {
#Autowired
private SpecialityService specialityService;
#GetMapping("/specialities")
public List<SpecialityDTO> getAllSpecialitys() {
return specialityService.findAll();
}
}
Config
#Configuration
#EnableCaching
public class CacheConfiguration {
private static final String HAZELCAST_LOGGING_TYPE = "hazelcast.logging.type";
#Bean
public Config hazelCastConfig() {
return new Config().setInstanceName("cache")
.addMapConfig(
new MapConfig().setName(ServiceConstant.SPECIALITY)
.setMaxSizeConfig(new MaxSizeConfig(200, MaxSizeConfig.MaxSizePolicy.FREE_HEAP_SIZE))
.setEvictionPolicy(EvictionPolicy.LRU).setTimeToLiveSeconds(100))
.setProperty(HAZELCAST_LOGGING_TYPE, "none");
}
}
#Aymen Kanzari, please see: https://gist.github.com/gokhanoner/766a1a807744d1a69c6a7799c3f34d73
I tried to replicate the issue but it seems working as expected. I can see inside findAll method for the first call but next ones just hit the Hazelcast cache. Can you describe the issue a bit more?

Is it possible to have a constant valued through a Spring Service?

We have a web service that one of its parameters is called origin and this origin is always validated against a code in the database.
For each one of our services I have to validate this code. This code does not change so I want to keep it in a constant, but I still have to validate it to prevent clients from sending a wrong code.
Basically what I want is this:
#Service
public class Service {
#Autowired
private LogBS logBS;
// I know this cannot be used in a static context.
public static final Long CODE = this.logBS.retrieveLogWebServiceCode("webServiceName");
public void validateOriginCode(final Long origin) {
if (!origin.equals(CODE)) {
throw new ServiceException("Wrong origin code!");
}
}
}
I know something similar can be done with Spring caching, but is it possible to do it with a constant?
I would rather go with this:
#Service
public class CodeValidatorService {
private LogBS logBS;
private Long CODE;
#Autowired
public CodeValidatorService(LogBS logBS){
this.logBS = logBS;
CODE = this.logBS.retrieveLogWebServiceCode("webServiceName");
if (CODE == null){
throw new ServiceException("Code cannot be read from DB!");
}
}
public void validateOriginCode(final Long origin) {
if (!origin.equals(CODE)) {
throw new ServiceException("Wrong origin code!");
}
}
}
Just as a code review, I prefer injecting dependencies in the constructor rather than using #Autowired in the field directly, it makes the service testable. You could also try to read the code in a #PostConstruct method, but I think it's better to do it in the constructor so you always have the service in a ready-to-go state.
For using it in the rest of your services, inject the CodeValidatorService instance on them:
#Service
public class OtherService {
private CodeValidatorService codeValidatorService;
#Autowired
public OtherService(CodeValidatorService codeValidatorService){
this.codeValidatorService = codeValidatorService;
}
public void performAction(final Long origin) {
codeValidatorService.validateOriginCode(origin);
//do the rest of your logic here
}
}
See also:
Spring Beans and dependency injection
Setter injection versus constructor injection
You can have a constantsProvider class
#Component
public class ConstantsProvider {
#Autowired
private LogBS logBS;
private String CODE;
#PostConstruct
public void init() {
CODE = this.logBS.retrieveLogWebServiceCode("webServiceName");
}
public String getCode() {
return CODE;
}
}
Add this snippet of code to Service class
#Autowired
private ConstantsProvider constantsProvider;
You can use constantsProvider.getCode() in your services. This way CODE is going to be immutable and not defined in a static context.
Note: If you have more constants similar to this, there is a better way to define the ConstantsProvider class. If there is only one, I would stick to the above implementation.
Edit 1:
If you need it in all the service classes, make the constantsProvider a spring bean and initialize the CODE there itself. Updated the answer

Events in Spring Boot

Is it possible to use events in Spring Boot? I need to execute one method but without waiting for return. I'm trying to use this:
public class GerarSeloEvent extends ApplicationEvent {
private TbPedido pedido;
private Integer cdCartorio;
public GerarSeloEvent(Object source, TbPedido pedido, Integer cdCartorio) {
super(source);
this.pedido = pedido;
this.cdCartorio = cdCartorio;
}
public TbPedido getPedido() {
return pedido;
}
public Integer getCdCartorio() {
return cdCartorio;
}
}
#Component
public class GerarSeloListener implements ApplicationListener<GerarSeloEvent> {
#Autowired
SeloService seloService;
#Override
public void onApplicationEvent(GerarSeloEvent event) {
seloService.gerarSelos(event.getPedido(), event.getCdCartorio());
}
}
and my call
GerarSeloEvent gerarSelos = new GerarSeloEvent(this, pedido, cdCartorio);
EnviarEmailPedidoEvent enviarEmail = new EnviarEmailPedidoEvent(this, pedido);
publisher.publishEvent(gerarSelos);
But my code waits to return anything to my front-end. I need one async event.
This should work:
#Component
public class GerarSeloListener {
private final SeloService seloService;
#Autowired
public GerarSeloListener(SeloService seloService) { ... }
#EventListener
#Async
public void handleGerarSeloEvent(GerarSeloEvent event event) {
....
}
You need to add #EnableAsync on one of your configuration (the best place is your #SpringBootApplication annotated class). But as Martin already said you don't need event if you want to process a method asynchronously: only add #Async and invoke it the usual way.
You may want to read the documentation

Resources