Add property to entity in spring data REST - spring

I am trying out the ResourceProcessor interface in Spring Data REST. I don't think my Processor ever gets called. No error message either.
Here is my processor (in Groovy):
#Autowired
PersonService personService
#Override
public Resource<Person> process(Resource<Person> resource) {
resource.content.isAdult = personService.isAdult(resource.content)
// sanity check: does this link get added?? (NOPE!!)
resource.add(new Link("http://localhost:8080/people", "added-link"))
log.info "## resource.content.isAdult ==> ${resource.content}"
return resource
}
Any help would be most appreciated!! You can see the entire project here in GitHub: https://github.com/txiasummer/spring-data-rest-examples

Finally got it to work! It turns out to be something completely trivial and I can't believe I missed it. I have a PersonProcessor classes which implements Spring's native ResourceProcessor interface. But PersonProcessor is still just a basic class that must be injected by Spring!! I think I was getting it confused with #Projection, which will be recognized automatically and does not need to be injected explicitly.
I addd #ComponentScan to my Application.groovy and now everything is working swimmingly. Alternatively, you an also manually define the PersonProcessor class and its service PersonService class as #Bean in Application.groovy. Again, you can see the whole project here: https://github.com/txiasummer/spring-data-rest-examples

Related

how to Resolve "could not initialize proxy - no session" error when using Spring repository

I'm working on a mutitenant project it maintains different schema for each tenant, followed Project
As we are dynamically switching the tenants so it looks like some configuration is missed which is closing the session or not keeping the session open to fetch the LAZY loaded objects. Which results in "could not initialize proxy - no session" error.
Please check below link to access the complete project and db schema scripts, please follow the steps given in Readme file.
Project
It will be helpful if someone can point out the issue in the code.
i tried to put service methods in #Transactional annotation but that didn't work.
I'm expecting it to make another call to the LAZY loaded object, This project is simplefied verson of the complex project, actually i have lot more lazy loaded objects.
Issue:-
I'm getting no Session error "could not initialize proxy [com.amran.dynamic.multitenant.tenant.entity.Tenant#1] - no Session"
at line 26 (/dynamicmultitenant/src/main/java/com/amran/dynamic/multitenant/tenant/service/ProductServiceImpl.java)
The issue is that your transaction boundaries are not correct. In TenantDatabaseConfig and MasterDatabaseConfig you've correctly added #EnableTransactionManagement, which will setup transactions when requested.
However - the outermost component that has an (implicit) #Transactional annotation is the ProductRepository (by virtue of it being implemented by the SimpleJpaRepository class - which has the annotation applied to it - https://github.com/spring-projects/spring-data-jpa/blob/864c7c454dac61eb602674c4123d84e63f23d766/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java#L95 )
and so your productRepository.findAll(); call will start a transaction, create a JPA session, run the query, close the session, close the transaction, which means that there is no longer any transaction / session open in which to perform the lazy-loading.
Therefore, your original attempt of
i tried to put service methods in #Transactional annotation but that didn't work.
IS the correct thing to do.
You don't say exactly what you tried to do, and where, but there are a few things that could have gone wrong. Firstly, make sure you're adding a org.springframework.transaction.annotation.Transactional and not a javax.transaction.Transactional annotation.
Secondly (and the more likely problem in this scenario), you'll need to configure the annotation with which transaction manager the transaction should be bound to, otherwise it may use an existing / new transaction created against the master DB connection, not the tenant one.
In this case, I think that:
#Service
#Transactional(transactionManager = "tenantTransactionManager")
public class ProductServiceImpl implements ProductService {
should work for you, and make all the methods of the service be bound to a transaction on the tenant DB connection.
EDIT: Answering a follow-up question:
can you please also suggest a better way to inject my tenantTransactionManager in all my service classes, as I don't want to mention tenantTxnManger in all service classes if there is any better way to do it ?
Yes, sure. You can create a meta-annotation that applies multiple other annotations, so you could create:
/**
* Marks class as being a service operating on a single Tenant
*/
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Service
#Transactional("tenantTransactionManager")
public #interface TenantService {
}
and then you can simply annotate your service classes with #TenantService instead of #Service:
#TenantService
public class ProductServiceImpl implements ProductService {

Spring Load Balancer, RestTemplate, and Microservices issue

Following this post, I configured
(1) a Eureka Server,
(2) a toll rate service which is a client of (1),
(3) a toll rate dashboard which read data from (2).
It went well until I made some changes using #LoadBalanced (maybe it is not exactly due to load balancer, but I'll post related code chunks below)
In bootstrap.properties in (2), the common endpoint name is
spring.application.name = demo-tollrate-service
In DashboardController.java in (3), as you can see the HTTP address is changed to above application name
// import some libraries
#Controller
public class DashboardController {
#Autowired
private RestTemplate restTemplate;
#LoadBalanced
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
#RequestMapping("/dashboard")
public String GetTollRate(#RequestParam int stationId, Model m) {
TollRate tr = restTemplate.getForObject("http://demo-tollrate-service/tollrate/" + stationId, TollRate.class);
m.addAttribute("rate", tr.getCurrentRate());
return "dashboard";
}
}
The pom.xml is roughly the same in this post
When running this app, I got the below errors.
***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
┌──->──┐
| dashboardController (field private org.springframework.web.client.RestTemplate com.example.demo.DashboardController.restTemplate)
└──<-──┘
Action:
Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.
Can anyone please help me here? Thank you.
It has a circular dependency, the DashboardController depends on the RestTemplate, but the RestTemplate is defined inside the DashboardController.
The Java version does not make any difference, but maybe the Spring version. The new Spring version is not allowing circular references by default. You can change that adding a property in the application.properties file, here you can find more details:
https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.6-Release-Notes#circular-references-prohibited-by-default
But I recommend a refactor, there is no good reason to define the RestTemplate inside the controller, it is much better to do it in a #Configuration class.
Spring 101: You can’t define beans in a controller class. Move RestRemplate definition to a #Configuration class or to your application main class.

Is a Spring #Bean method ever called if it's not needed?

Let's say I have a configuration class like so
#Configuration
public class NeverUsedConfig {
#Bean
public UselessObject getUselessObject() {
// execute some crucial state-changing code
}
}
If getUselessObject() is never called and UselessObject is never #Injected anywhere, is the getUselessObject() method ever called?
I have a similar situation, where it seems like it's being called in one environment and not in another. When it's called, I can see the logging inside the method as well as setting up some important AWS SDK Metrics. In another account/region, it doesn't seem to be called. My AWS SDK Metrics are not set up and there is no logging

Spring boot test mutliple projects with same bean name

I have multiple spring projects as part of a single umbrella project. Two of them are AuthServer and BackendApplication. AuthServer, as name suggests is used only for auth purposes and rest is handled by BackendApplication. Now I am trying to write tests inside BackendApplication that also need to use auth related work. For that I have added AuthServer as a test dependency to BackendApplication. Now the problem is that, both projects have beans names Utility because of which I get DuplicateBeanException when I am including both contexts in my test. But I can disable any of them as they are necessary. Is there a way around it?
Could you name your beans, for example:
#Bean(name = "my-utility-1")
public Utility createUtility1() {
return new Utility();
}
// or
#Component(value = "my-utility-2")
public class Utility {
...
}
and refer to them by #Qualified
#Autowired #Qualified("my-utility-1")
private Utility myUtility;
Not related to your question, but i think you can mock AuthServer when testing BackendApplication.

Unable to read values from external property file in Spring Boot

I have a running Spring Boot project. I want to read some environment specific properties from an external properties file.
I mentioned config files names and locations while starting the server as follows:
java -jar AllergiesConditions.jar --spring.config.name=application,metadata --spring.config.location=classpath:/,/APPS/SpringBoot/
The property files loads successfully(because i tried to log one of the external key values inside datasource bean and It printed successfully) But when i try to access a value using #Value annotation - It returns null.
My test Class is as follows:
#Component
public class testclass {
private Logger logger = LogManager.getLogger(testcla.class);
#Value("${sso.server}")
public String sso;
public void test(){
logger.info("sso url is: "+sso); //This sso is logged as null
otherStuff();
}
}
This test() function is called when a particular API is hit after server is running.
The external config file - metadata.properties contains this variable:
sso.server=1234test
Edit: As suggested in this apparently duplicate question I also tried adding #PropertySource(name = "general-properties", value = { "classpath:path to your app.properties"}) in main Application configuration class and It loaded the files, but still I get null value itself.
Can someone please help in what's going wrong here?? Does the testclass need some specific annotation OR it needs to be a bean or something??
Thanks in Advance :)
Thanks to M.Deinum for great input and saving my time
Just posting his comment as answer
Factually ${sso.server} cannot be null. If ${sso.server} couldn't be resolved, my application will break at startup itself.
So the obvious problem was that I was creating a new instance of testclass in my controller using
testclass obj = new testclass(); obj.test();
Rather I should be using spring managed instance by autowiring testclass in my controller.

Resources