AOP AfterReturning function returns twice - java

I have a little problem. I'm calling AfterReturning function after some function returns and my AfterReturning function's working twice and I don't want that. Here is the code:
#Aspect
#Component
#Configuration
public class AspectOP {
LogController logcontroller = new LogController();
#AfterReturning("execution(* com..*save*(..))")
public void doSomething() {
logcontroller.guestbook("XD");
}
}
I have 2 save function and we changed names but it's same again. I've tried remove #Component or #Aspect then it's not working.
EDIT
My save function
#Controller
#RequestMapping("/api")
public class EntryController {
#Autowired
EntryRepository repo;
Account account;
#RequestMapping(path = "/getir", method = RequestMethod.GET)
public #ResponseBody List<Entries> getir(){
return repo.findAll();
}
#RequestMapping(path = "/saveentry", method = RequestMethod.POST, consumes = "application/json")
public #ResponseBody Entries save(#RequestBody Entries entry) {
return repo.save(entry);
}
}
LogController.class
#Controller
public class LogController {
#MessageMapping("/guestbook")
#SendTo("/topic/entries")
public Log guestbook(String message) {
System.out.println("Received message: " + message);
return new Log(message);
}
}
My main objective is when something is saved, I send something to my socket. It's working but doSomething functions is working twice.

seems advice applied to your EntryRepository class as well. change your pointcut expression to something like, to be only applied to EntryController's save method
#AfterReturning("execution(* com.xyz.EntryController.save*(..))")
Examples here

Related

Can you set a dynamic value to #PreAuthorize in Spring?

Right now I use
#PreAuthorize("hasAuthority('CREATE_USER_PRIVILEGE')")
But I want the CREATE_USER_PRIVILEGE to come from a function(). Is this possible?
You could do something like this:
#RestController
class FooController {
#PreAuthorize("hasAuthority(#securityService.privilege)")
#GetMapping("/")
public ResponseEntity<String> helloSecurity(#RequestParam("id") Integer id){
return ResponseEntity.ok("Hello World");
}
}
#Service("securityService")
class SecurityService {
public String getPrivilege(){
return "CREATE_USER_PRIVILEGE";
}
}
Based on this great article
you have first to autowire your service using constructor or annotation then you can use the Spel language to use it as stated in the following example
#RequestMapping(value="/id/{domainObjectId}/dostuff", method=RequestMethod.POST, produces="application/json")
#PreAuthorize(value="hasRole('ROLE_DomainObjectAdmin') or #domainObjectServiceImpl.findDomainObject(#domainObjectId).getOwners().contains(#userAccount.getEmployee())")
public String setObjectiveComplete(#PathVariable String domainObjectId, UserAccount userAccount) {
// Do stuff
}
Based on the above solution, I've implemented something like this:
#Controller
class TestController {
//calling a PreAuthorize on method level/ can be used on class level as well
#PreAuthorize("hasAnyAuthority(#authorityService.authorities)")
#RequestMapping("/application")
public ModelAndView newPage() throws{
return new ModelAndView(view);
}
}
#Service("authorityService")
class AuthorityService{
#Value("${app.authorities}") // read roles from properties file
private String authorities;
public List<String> getAuthorities(){
// convert the comma separated Strings to list.
List<String> items = Arrays.asList(authorities.split("\\s*,\\s*"));
return items;
}
}

How can i use a RestControler function in another RestControler

I get org.springframework.beans.factory.UnsatisfiedDependencyException when i call SaleRestService in ProductRestService like in the code below.
How can i do it properly?
#RestController
#CrossOrigin("*")
public class ProductRestService {
#Autowired
private ProductRepository productRepository;
#Autowired
public SaleRestService saleRestService ;
#RequestMapping(value="/productQuatityMinusOne", method=RequestMethod.GET)
#ResponseBody
public void ProductQuatityMinusOne(#RequestParam(name="id") Long id){
Product p = productRepository.findProductById(id);
double salePrice = p.getPrice();
Date now = new java.util.Date();
Sale s = new Sale(id,salePrice,now);
saleRestService .saveOneSale(s);
p.setId(id);
int q = p.getQuantity()-1;
p.setQuantity(q);
productRepository.save(p);
}
}
#RestController
#CrossOrigin("*")
public class SaleRestService {
#Autowired
private SaleRepository saleRepository;
//Save one sale
#RequestMapping(value="/saveOneSale", method=RequestMethod.POST)
#ResponseBody
public Sale saveOneSale(#RequestBody Sale s){
return saleRepository.save(s);
}
}
You should not be calling your controllers from one another.
Two solutions:
Put the code of the saveOneSale in another #Service, and call that from your #RestControllers
You could redirect the http call of ProductQuatityMinusOne in ProductRestService to saveOneSale in SaleRestService by using a spring boot redirect, like this return "redirect:/saveOneSale";, but I dont know if that will work because you'd be redirecting to a POST handler.
Personlly I'd do the first solution:
remove #RestController from SaleRestService
create a SaleRestController class, anotate it with #RestController, and put a method with the following annotation: #RequestMapping(value="/saveOneSale", method=RequestMethod.POST)
in that method, call SaleRestService.saveOneSale
Everything should just work (TM)

Spring Data Rest / Spring Hateoas Custom Controller - PersistentEntityResourceAssembler

I'm attempting to add some additional business logic to the auto-generated endpoints from the RepositoryRestResource. Please see the code below:
Resource:
#RepositoryRestResource(collectionResourceRel="event", path="event")
public interface EventRepository extends PagingAndSortingRepository<Event, Long> {
}
Controller:
#RepositoryRestController
#RequestMapping(value = "/event")
public class EventController {
#Autowired
private EventRepository eventRepository;
#Autowired
private PagedResourcesAssembler<Event> pagedResourcesAssembler;
#RequestMapping(method = RequestMethod.GET, value = "")
#ResponseBody
public PagedResources<PersistentEntityResource> getEvents(Pageable pageable,
PersistentEntityResourceAssembler persistentEntityResourceAssembler) {
Page<Event> events = eventRepository.findAll(pageable);
return pagedResourcesAssembler.toResource(events, persistentEntityResourceAssembler);
}
}
I've looked at the following two stackoverflow articles:
Can I make a custom controller mirror the formatting of Spring-Data-Rest / Spring-Hateoas generated classes?
Enable HAL serialization in Spring Boot for custom controller method
I feel like I am close, but the problem that I am facing is that:
return pagedResourcesAssembler.toResource(events, persistentEntityResourceAssembler);
returns an error saying:
"The method toResource(Page<Event>, Link) in the type PagedResourcesAssembler<Event> is not applicable
for the arguments (Page<Event>, PersistentEntityResourceAssembler)".
The toResource method has a method signature that accepts a ResourceAssembler, but I'm not sure how to properly implement this and I can't find any documentation on the matter.
Thanks in advance,
- Brian
Edit
My issue was that I thought I could override the controller methods that are auto-created from #RepositoryRestResource annotation without having to create my own resource and resource assembler. After creating the resource and resource assembler I was able to add my business logic to the endpoint.
Resource:
public class EventResource extends ResourceSupport {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Resource Assembler:
#Component
public class EventResourceAssembler extends ResourceAssemblerSupport<Event, EventResource> {
public EventResourceAssembler() {
super(EventController.class, EventResource.class);
}
#Override
public EventResource toResource(Event entity) {
EventResource eventResource = createResourceWithId(entity.getId(), entity);
eventResource.setName(entity.getName());
return eventResource;
}
}
Updated Controller:
#RepositoryRestController
#RequestMapping(value = "/event")
public class EventController {
#Autowired
private EventRepository eventRepository;
#Autowired
private EventResourceAssembler eventResourceAssembler;
#Autowired
private PagedResourcesAssembler<Event> pageAssembler;
#RequestMapping(method = RequestMethod.GET, value = "")
#ResponseBody
public PagedResources<EventResource> getEvents(Pageable pageable) {
Page<Event> events = eventRepository.findAll(pageable);
// business logic
return pageAssembler.toResource(events, eventResourceAssembler);
}
}
The thing I don't like about this is that it seems to defeat the purpose of having a RepositoryRestResource. The other approach would be to use event handlers that would get called before and/or after the create, save, delete operations.
#RepositoryEventHandler(Event.class)
public class EventRepositoryEventHandler {
#HandleBeforeCreate
private void handleEventCreate(Event event) {
System.out.println("1");
}
}
There doesn't seem to be any events for the findAll or findOne operations. Anyways, both these approaches seem to solve my problem of extending the auto generated controller methods from RepositoryRestResource.
It requires a PagedResourcesAssembler, Spring will inject one for you if you ask.
public PagedResources<Foo> get(Pageable page, PagedResourcesAssembler<Foo> assembler) {
// ...
}
In this case the resource is Foo. It seems in your case the resource you're trying to return is an Event. If that's so, I would expect your code to look something like:
private ResourceAssembler<Event> eventAssembler = ...;
public PagedResources<Event> get(Pageable page, PagedResourcesAssembler<Event> pageAssembler) {
Event event = ...;
return eventAssembler.toResource(event, pageAssembler);
}
You provide the ResourceAssembler<Event> that tells Spring how to turn Event into a Resource. Spring injects the PagedResourcesAssembler<Event> into your controller method to handle the pagination links. Combine them by calling toResource and passing in the injected pageAssembler.
The final result can be returned simply as a body as above. You could also use things like HttpEntity to gain more control over status codes and headers.
Note: The ResourceAssembler you provide can literally be something as simple as wrapping the resource, such as Event, with a Resource object. Generally you'll want to add any relevant links though.
To hack it you can use just PagedResourcesAssembler<Object> like:
#RequestMapping(method = RequestMethod.GET, value = "")
#ResponseBody
public PagedModel<PersistentEntityResource> getEvents(
Pageable pageable,
PersistentEntityResourceAssembler persistentAssembler,
PagedResourcesAssembler<Object> pageableAssembler
) {
return pageableAssembler.toModel(
(Page<Object>) repository.findAll(pageable),
persistentAssembler
);
}

Difference between two #RequestMapping annotations

I am pretty new in Spring MVC and I have the following doubt.
In a controller, I have a method annotated in this way:
#Controller
#RequestMapping(value = "/users")
public class UserController {
#RequestMapping(params = "register")
public String createForm(Model model) {
model.addAttribute("user", new Customer());
return "user/register";
}
}
So this method handle HTTP Request toward the URL /users?register where register is a parameter (because the entire class handle request toward /users resource).
Is it the same thing if, instead using the params = "register" I use the following syntaxt:
#Controller
public class UserController {
#RequestMapping("/users/{register}")
public String createForm(Model model) {
model.addAttribute("user", new Customer());
return "user/register";
}
}
I have deleted the mapping at class level and I use #RequestMapping("/users/{register}").
Is it the same meaning of the first example?
NO, they are completely different constructs:
Code 1
#Controller
#RequestMapping(value = "/users")
public class UserController {
#RequestMapping(params = "register")
public String createForm(Model model) {
model.addAttribute("user", new Customer());
return "user/register";
}
}
In this case, createForm method will be called when a HTTP request is made at URL /users?register. Quoting from Spring Javadoc, it means this method will be called whatever the value of the register HTTP parameter; it just has to be present.
"myParam" style expressions are also supported, with such parameters having to be present in the request (allowed to have any value).
Code 2
#Controller
public class UserController {
#RequestMapping("/users/{register}")
public String createForm(Model model) {
model.addAttribute("user", new Customer());
return "user/register";
}
}
In this case, #RequestMapping is declaring register as a PathVariable. The method createForm will be called if a HTTP request is made at URL /users/something, whatever the something. And you can actually retrieve this something like this:
#RequestMapping("/users/{register}")
public String createForm(#PathVariable("register") String register, Model model) {
// here "register" will have value "something".
model.addAttribute("user", new Customer());
return "user/register";
}

#Path equivalent in spring-boot

Say I have controller with a method defined like this:
#RestController
#RequestMapping("/{user-id}/foo")
public class FooController {
#RequestMapping("bar")
public BarController someBarLogic() {
//
}
}
Is it possible to go further than {user-id}/foo/bar without specifying the whole root-path ? (Is there a way to relativize the path like #Path annotation in Jersey or an equivalent annotation in Spring-Boot ?)
It looks like you're looking for ant-style wildcards in path patterns. Have a look here:
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-requestmapping-patterns
Accordingly you can define a RequestMapping like this
#RestController
#RequestMapping("/foo")
public class DemoController {
#RequestMapping(value = "/bar/**", method = RequestMethod.GET)
public String getDemo() {
return "Hello world!";
}
}
which will match /foo/bar/baz.
Ok, another example based on your comment below:
#RestController
public class DemoController {
#RequestMapping(value = "/**/baz/**", method = RequestMethod.GET)
public String getDemo() {
return "Hello world!";
}
}
This will match the same url as above, and also /foo/bar/baz/bar/foo

Categories