How to access value cached using spring #cacheable in other controllers? - spring

I have a frequently accessed but bulky value - valueA which is obtained by a method - methodA of rest controller - ControllerA. So I have cached this value using #Cacheable annotation as follows.
#CacheConfig(cacheNames={"abc_cache"})
public class ControllerA
{
#Cacheable
#RequestMapping(value = "/value/" , method=RequestMethod.GET)
public ResponseEntity<Value> fetchValue()
{
// some logic
return new ResponseEntity<Value>(value, HttpStatus.OK);
}
}
I want to access this value in another method - methodB of another controller - controllerB.
How can I access this value?

You can have some other class/bean to supply that value. Then you can inject that bean in all the controllers that you want.
Something like this:
#Component
public class MyValueService {
#Cacheable
public Value getValue() {
return ...;
}
}
and then in the controllers
public class ControllerA
{
#Autowired
private MyValueService valServ;
#RequestMapping(value = "/value/" , method=RequestMethod.GET)
public ResponseEntity<Value> fetchValue()
{
return new ResponseEntity<Value>(valServ.getValue(), HttpStatus.OK);
}
}
Are you aware of the Controller -> Service -> Repository pattern?
Basically:
Controllers are the web layer. They handle http requests. They use Services.
Services are responsible for the business logic of the application. They use Repositories.
Repositories are responsible for the data access - databases access, read/write from the file system, etc.
You should structure your application that way.
In general I would use caching on the Repository layer. Usually the bottle neck are the I/O operations (read/write to the file system, DB call, calling external service over the network) and that are the things that you want to cache if the possible.

I think you should just encapsulate your cacheable logic into a method in another class and then just call it from both controllers.
So
public class ControllerA
{
#Resource
private Service service;
#RequestMapping(value = "/value/" , method=RequestMethod.GET)
public ResponseEntity<Value> fetchValue()
{
// some logic
Object v = service.cachedMethod();
return new ResponseEntity<Object>(v, HttpStatus.OK);
}
}
and
#Component
public class Service {
#Cacheable
public Object cachedMethod() {... }
}

Related

Authenticate Operation Before Each Action In Spring Boot Effectively

I have a code block like below. For each method, I call action authentication. Is it possible to do with annotation or other thing effectively?
#GetMapping
public ResponseEntity getAction(#PrincipalUser user, Long actionId)
{
repository.checkUserForAction(user.getId(), actionId);
implement actions...
return service call;
}
#PostMapping
public ResponseEntity addAction(#PrincipalUser user)
{
repository.checkUserForAction(user.getId());
implement actions...
return service call;
}
#DeleteMapping
public ResponseEntity addAction(#PrincipalUser user, Long actionId)
{
repository.checkUserForAction(user.getId(), actionId);
implement actions...
return service call;
}
Actually, in here my other problem is that I call repository method each time and I know this is not an effective way.
You could use Spring Security and #PreAuthorize annotation.
Example:
#PreAuthorize("#authorizationService.check(#actionId)")
#DeleteMapping
public ResponseEntity performAction(Long actionId) {
implement actions...
}
And encapsulate authorization logic inside authorizationService, moreover authorizationService must be a bean and #EnableGlobalMethodSecurity(prePostEnabled = true) must be set.

calling a method by class level annotated with #RequestMapping that includes an autowired class

I am trying to call a method that is annotated with #RequestMapping(signIn) through a class level (from method: authentication) like so:
#RequestMapping(value = /authenticate, method = RequestMethod.POST)
public #ResponseBody Response authentication(HttpServletRequest request)
{
UserController user = new UserController();
return user.signIn(request, null);
}
and my controller looks like:
#Autowired
private UserManager userManager;
#RequestMapping(value = /signin, method = RequestMethod.POST)
public #ResponseBody Response signIn(HttpServletRequest request) {
JsonObject json = Misc.parseJson(request);
String lang = Misc.getLang(request);
user.setEmail(Misc.getEmail(json));
user.setPassword(Misc.getEncryptedPassword(json));
return ResponseUtils.success(userManager.auth(user, lang));
}
user manager is annotated with #component:
#Component
public class UserManager {
public User auth(User user, String lang) {
....
return user;
}
}
Problem is when I call the method "signIn" and just new-up a UserController instance through "/authenticate" mapping, the UserManager becomes NULL. So now I'm assuming that autowiring doesn't work when it's done this way.
Is there any other way to call the signIn method? I would hate to copy paste an already existing code to another class just to get this to work...
Autowiering only works in spring managed bean. If you create a class with new keyword, it is not a spring managed bean and autowiering would not work.
You can try to autowire the class which contains the method which is annotated or better put the code in a service class which can be used by both methods.
It's not problem with #Autowired .There are two type of Annotation
firstly method base annotation and field level annotation. You just used field level annotation.Check your import class with "org.springframework.beans.factory.annotation.Autowired" or it can be problem with initiation of "UserManager"
I don't know why you not moving logic into separate Service classs, but try this:
UserController.java
public UserController(UserManager userManager) {
this.userManager = userManager;
}
and then inside controller where authentication resource method is located:
#Autowired UserManager userManager;
#RequestMapping(value = /authenticate, method = RequestMethod.POST)
public #ResponseBody Response authentication(HttpServletRequest request) {
UserController user = new UserController(userManager);
return user.signIn(request);
}
So in the end I just separated the logic instead. Though one solution that I tried and I could have used was to just add another mapping to the signIn method instead of adding a new method in the other class since the logic was similar. Still I opted for a separate logic instead since there were a lot of unnecessary code in the signIn method for my purpose.

Where should we use #Transactional and where is Service layer?

I have rest style controller in Spring. In controller I have injected dao interfaces. From controller I persist data. In the other words, I have like REST web service. people sends me data, and I persits it.
/**
* Payment rest controller which receives
* JSON of data
*/
#Controller
#RequestMapping("/data")
public class PaymentTransaction {
#Autowired
private TestDao dao;
#RequestMapping(value = "/test", method = RequestMethod.POST)
#ResponseBody()
public String test(HttpServletRequest request) {
...
}
At the moment I have #transaction annotation in Dao classes. For instance:
import org.springframework.transaction.annotation.Transactional;
#Component
#Transactional
public interface TestDao {
#Transactional(propagation = Propagation.REQUIRED)
public void first();
}
I have read that this is very bad style. Using this answer at stackoverflow , here is explain and examples why is this bad - we must not add this annotation in DAO and in controller too. We must add it in service layer.
But I don't understand what is the service layer? Or where is it? I do not have anything like this.
where should I write #Transactional annotation?
Best regards,
According to the cited post, you should design your classes somehow like this (rather pseudocode):
controller (responsible for handling clients' requests/responses)
#Controller
#RequestMapping("/data")
public class TestREST {
#Autowired
private TestService service;
public void storePayment(PaymentDTO dto) {
service.storePayment(dto); //request from a client
}
public PaymentDTO getPayment(int paymentId) {
return service.getPayment(paymentId); //response to a client
}
}
service layer (also called business layer, responsible for business logic - knows what to do with incoming messages, but does not know where they come from).
public class TestServiceImpl {
#Autowired
private TestDao dao;
#Transactional(propagation=Propagation.REQUIRED) //force transaction
public void storePayment(PaymentDTO paymentDto) {
// transform dto -> entity
dao.storePayment(paymentEntity); //read-write hence transaction is on
}
#Transactional(propagation=Propagation.NOT_SUPPORTED) //avoid transaction
public Payment getPayment(int paymentId) {
return dao.findPayment(paymentId); //read-only hence no transaction
}
}
data access layer (also called persistence layer, responsible for accessing database - knows how to use entity model / ORM, does not know anything about the upper service layer)
public class TestDAOImpl {
#PersistenceContext
private EntityManager em;
public void storePayment(PaymentEntity paymentEntity) {
em.persist(paymentEntity);
}
public PaymentEntity getPayment(int paymentId) {
return em.find(PaymentEntity.class, paymentId);
}
}
By this approach you get separation of concerns mentioned in the post. From the other hand such an approach (business layer vs data access layer) got a little dose of criticism from Adam Bien's on his blog ("JPA/EJB3 killed the DAO"). As you can see there is no a single solution for the problem, but I encourage to read some other opinions and apply the solution you find the most suitable for your needs.
When you call two Dao methods first & second from controller, 2 transactions will be done, one with starts before first method and ends after it's execution and the second one starts before second method starts and ends after it's execution. Whereas you create an additional class in between controller and dao (usually this is called service layer) and annotate it with #Transactional and call multiple Dao methods in it, a transaction is started at the start of service method and all the dao calls will be executed and transaction will be closed, which is what you require. And inject the Service into Controller.
Controller -> Service -> Dao
#Controller
#RequestMapping("/data")
public class PaymentTransaction {
#Autowired
private TestService service;
#RequestMapping(value = "/test", method = RequestMethod.POST)
#ResponseBody()
public String test(HttpServletRequest request) {
...
}
}
#Service
#Transactional
public class TestService {
#Autowired
private TestDao dao;
#Transactional
public void serviceCall(){
dao.first();
dao.second();
}
}

Spring dependency injection depending on request object

I am working on a web application with spring. The application is configured with a properties file.
There are multiple instances of the application in different servers, each instance has a different configuration file (each instance is customized for a different customer) I am using controllers and services. Something like this:
public class Controller1 {
#Autowired
Service1 service1;
#RequestMapping(value = "/page.htm", method = { RequestMethod.GET, RequestMethod.POST })
public ModelAndView serve(HttpServletRequest request, HttpServletResponse response) {
service1.doSomething();
return new ModelAndView("/something");
}
}
#Service
public class Service1 {
#Autowired
Service2 service2;
public void doSomething () {
…
service2.doAnotherThing();
…
}
}
#Service
public class Service2 {
#Value("${propertyValue}")
private String propertyValue;
//doAnotherThing() will use propertyValue
public void doAnotherThing () {
…
//Do something with propertyValue
…
}
}
Now I have a new requirement. There won’t be multiple instances for each customer, but only one instance with multiple domains for all the customers.
The application must decide the configuration depending on the host name of the request object in the controller. So if the customer points the browser to www.app1.com I have to use configuration file 1 but if the customer uses www.app2.com I have to use configuration 2 and so on.
I moved the configuration files to the database, but then I realized that I do not know how to make the dependency injection. The services are linked, service1 uses service2 and service2 is the one who must use the value which depends on the configuration. Service 2 has no knowledge of the request object.
Is there a clean way to solve this?
Thanks,
One approach is to create configuration object for all customer as a singleton on spring config:
<bean id="customerAConfig"../>
<bean id="customerBConfig"../>
<bean id="customerCConfig"../>
And have a session scoped ConfigurationService that acts as a pointer to which configuartion is active
public class ConfigurationService {
private CustomerConfig activeConfig;
// getters & setters..
}
Configure a singleton proxy for this service on your spring config so it can be injected into singleton components. You need to have cglib in your classpath for Spring to create the proxy:
<bean class="com.mycompany.ConfigurationService" scope="session">
<aop:scoped-proxy/>
</bean>
And on your login controller, select which configuration should be used by virtual host name and store it into ConfigurationService for later retrieval (remember ConfigurationService is session scoped)
public class LoginController {
#Autowired private CustomerConfig[] custConfigs;
#Autowired private ConfigurationService configService;
#RequestMapping(method = POST)
public String login(HttpServletRequest request, ..) {
...
String host = request.getServerName();
CustomerConfig activeConfig = // decide which one based on host..
configService.setActiveConfig(activeConfig);
...
}
}
Below is a sample FooController that reads customer specific configuration
#Controller
#RequestMapping("/foo")
public class FooController {
#Autowired private ConfigurationService configService;
#RequestMapping(method = "GET")
public String get() {
...
CustomerConfig config = configService.getActiveConfig();
...
}
...
}
If your program does not have a single entry point like a login page, you can code similar logic as a filter. Check if an active configuration is set on session, if not look it up based on host name

Check the state validity of a Spring proxied bean without try-catch

I have a bean being created by a service with the following class:
#Configuration
public class AccessManager {
#Bean(name="access", destroyMethod="destroy")
#Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
#Autowired
public Access create(HttpServletRequest request) {
System.out.println(request.getRemoteAddr());
return new Access();
}
}
Everything works as expected, except that when the application is starting, this method is being called, probably because I have some other singleton beans that use the Access bean. At the start up there is no request bound to the Thread, and it's expected to get a java.lang.IllegalStateException when trying to access any property of the request parameter.
No problem. The question is, is it possible to check if the underlying HttpServletRequest of the proxy request is null before calling a property that raises the exception?
You probably want to take a look at RequestContextHolder#getRequestAttributes(). That will return null if you're not currently in a context where request scope could be used.
#Configuration
public class AccessManager {
#Bean(name="access", destroyMethod="destroy")
#Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
#Autowired
public Access create(HttpServletRequest request) {
if (RequestContextHolder.getRequestAttributes() != null) {
System.out.println(request.getRemoteAddr());
}
return new Access();
}
}
I think the issue here is with separation of concerns. Usually your service layer should not have any dependency on the servlet classes. This is very much a controller/UI concern.
Your service class should be provided with the properties which it needs to do its job. In this case a String. This service method should be called from a controller method which is injected with the servlet request.
Something like the following:
#Controller
public class MyController {
#Autowired
private AccessManager accessManager;
#RequestMapping
public void handleRequest(HttpServletRequest request) {
accessManager.create(request.getRemoteAddr());
}
}
and your service would then look like this:
#Service
public class AccessManager {
public Access create(String remoteAddress) {
return new Access();
}
}
To sum up, anything annotated as #Service shouldn't have access to the Request.

Resources