#Cacheable() returning id null - spring

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?

Related

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

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();
}
}
}

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.

MongoTemplate in MultiTenant Spring Data Mongo Application

This is a follow up to the question Making spring-data-mongodb multi-tenant
Oliver Gierke explained options how to set-up multi-tenancy for a SpringDataMongo application. I followed his "collection approach" and was quite successful. So far. Problems arise, when I want to customise the MongoTemplate used. Have a look on this example:
#SpringBootApplication
public class MultiTenantMongoApplication {
public static void main(String[] args) {
SpringApplication.run(MultiTenantMongoApplication.class, args);
}
#Bean
public MongoTemplate mongoTemplate(Mongo mongo, #Value("${random.name}") String randomName) throws Exception {
String dbname = "db_" + randomName;
MongoTemplate mongoTemplate = new MongoTemplate(mongo, dbname) {
#SuppressWarnings("unused")
public void shutdown() {
mongo.dropDatabase(dbname);
}
};
return mongoTemplate;
}
}
#Document(collection="#{tenantProvider.getTenantCollectionName('Metric')}")
public class Metric {
}
#Repository
public interface MetricRepository extends MongoRepository<Metric, ObjectId>{}
#Component
public class TenantProvider {
public String getTenantCollectionName(String collectionName) {
...
}
}
This yields the following error:
SpelEvaluationException: EL1007E: Property or field 'tenantProvider'
cannot be found on null
When I remove the definition of the MongoTemplate bean in the application class everything is fine and runs as desired.
Obviously the property provider gets not configured appropriately, when the MongoTemplate is customised. Why is this happening? And what can I do, to get the property in place?
I think the above error is because of the SpEL expression. You can try this way to access the TenantProvider class using the below SpEL expression.
#{T(TenantProvider).getTenantCollectionName('Metric')}
or you can add a fully qualified class name for TenantProvider in the above expression.

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

Cannot configure #Transaction to work with Spring Data Neo4j

I'm trying to move away from manually-managed transactions to annotation based transactions in my Neo4j application.
I've prepared annotation-based Spring configuration file:
#Configuration
#EnableNeo4jRepositories("xxx.yyy.neo4jplanetspersistence.repositories")
#ComponentScan(basePackages = "xxx.yyy")
#EnableTransactionManagement
public class SpringDataConfiguration extends Neo4jConfiguration
implements TransactionManagementConfigurer{
public SpringDataConfiguration() {
super();
setBasePackage(new String[] {"xxx.yyy.neo4jplanetspojos"});
}
#Bean
public GraphDBFactory graphDBFactory(){
GraphDBFactory graphDBFactory = new GraphDBFactory();
return graphDBFactory;
}
#Bean
public GraphDatabaseService graphDatabaseService() {
return graphDBFactory().getTestGraphDB(); //new GraphDatabaseFactory().newEmbeddedDatabase inside
}
#Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
return neo4jTransactionManager(graphDatabaseService());
}
}
I've marked my repositories with #Transactional:
#Transactional
public interface AstronomicalObjectRepo extends
GraphRepository<AstronomicalObject>{
}
I've marked my unit test classes and test methods with #Transactional and commented old code that used to manually manage transactions:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {SpringDataConfiguration.class},
loader = AnnotationConfigContextLoader.class)
#Transactional
public class AstronomicalObjectRepoTest {
#Autowired
private AstronomicalObjectRepo repo;
#Autowired
private Neo4jTemplate neo4jTemplate;
(...)
#Test #Transactional
public void testSaveAndGet() {
//try (Transaction tx =
//neo4jTemplate.getGraphDatabaseService().beginTx()) {
AstronomicalObject ceres = new AstronomicalObject("Ceres",
1.8986e27, 142984000, 9.925);
repo.save(ceres); //<- BANG! Exception here
(...)
//tx.success();
//}
}
After that change the tests do not pass.
I receive:
org.springframework.dao.InvalidDataAccessApiUsageException: nested exception is org.neo4j.graphdb.NotInTransactionException
I have tried many different things (explicitly naming transaction manager in #Transactional annotation, changing mode in #EnableTransactionManagment...), nothing helped.
Will be very grateful for a clue about what I'm doing wrong.
Thanks in advance!
I found the reason...
SDN does not support newest Neo4j in the terms of transaction.
I believe it is because SpringTransactionManager in neo4j-kernel has gone in 2.2+ releases, but not 100% sure.
On github we can see that 7 hours ago the change was made to fix it:
https://github.com/spring-projects/spring-data-neo4j/blob/master/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/config/JtaTransactionManagerFactoryBean.java
A quick fix that worked for me was to override neo4jTransactionManager method from Neo4jConfiguration in my configuration, using Neo4jEmbeddedTransactionManager class:
#Override
public PlatformTransactionManager neo4jTransactionManager(GraphDatabaseService graphDatabaseService) {
Neo4jEmbeddedTransactionManager newTxMgr = new Neo4jEmbeddedTransactionManager(graphDatabaseService());
UserTransaction userTransaction = new UserTransactionAdapter( newTxMgr );
return new JtaTransactionManager( userTransaction, newTxMgr );
}

Resources