spring mvc request mapping inheritance - rest

I have a Spring MVC 3.2 app and I am trying to find the best way to handle my URLs. I am looking to support multiple customers with something like this:
www.myproduct.com/customerNameX/products
www.myproduct.com/customerNameX/office/
www.myproduct.com/customerNamex/financial
www.myproduct.com/customerNamex/...
I would like to have a controller for each of the path after customerName. I was hoping there was a way to do some sort of inheritance request mapping in Spring MVC. Here is my current attempt that results in deploy errors("Ambiguous mapping found. Cannot map 'rootController' bean method ")
#Controller
#RequestMapping( "/{officeId}/" )
public class RootController {
public #ResponseBody
Office getOffice( #PathVariable String officeId ) {
return offficeService.findOfficeByName(officeId);
}
}
#Controller
#RequestMapping( "/{officeId}/products" )
public class ProductsController extends RootController {
...
}
#Controller
#RequestMapping( "/{officeId}/office" )
public class OfficeController extends RootController {
...
}
The thought being that the root controller would get the first level requests, and the officeId would tell all other requests what officeId is being requests. But I don't think this is the correct approach.
I would really appreciate any tips on the best way to build my URL's properly.

Related

Spring AOP Pointcut for Spring Data Rest Controller (EndPoint)

I would like to do something on every api call to my spring boot app. I use Spring AOP to achieve this. Using:
#Pointcut("within(#org.springframework.stereotype.Controller *)")
public void controller() {
}
#Pointcut("within(#org.springframework.web.bind.annotation.RestController *)")
public void restController() {
}
#After("(controller() || restController())")
public void loggingAdvice(JoinPoint joinPoint) {
// TODO: do something
}
Using that I can get all the event when API is being called. However, I am also using spring rest data for crud mechanism that automatically generate API end point, for example:
#RepositoryRestResource(collectionResourceRel = "users", path = "users")
public interface UserRepository extends PagingAndSortingRepository<User, Long> {
User findByEmail(String email);
}
The question is, can I create a point cut for every API end point that is generated by spring rest data?
Following pointcut will target all the RESTful endpoint calls made at "/users"
Considering the package of UserRepository is rg.so.example.datarest
#Pointcut("execution(* rg.so.example.datarest.UserRepository.*(..))")
public void dataRest() {
}
A more generic pointcut to target all the Repository implementations in a package rg.so.example.datarest would be
#Pointcut("execution(* rg.so.example.datarest..*(..))")

Application layers. Mapping between api models and internal models

I have situation like this
I have controller code
#RestController
public class MyController implements SomeApi {
#Autowired
private final MyService myService ;
public ResponseEntity<AnswerObject> getSomething (RestModelObject obj) {
myService.getSomething(obj);
}
Below Service code:
#Service
public class MyServiceImpl implements MyService {
#Autowired
private final EntityRepository entityRepository;
public AnswerObject getSomething (RestModelObject obj) {
Entity entity = entityRepository.getSomething(obj);
AnswerObject answerObject = map(entity, new AnswerObject());
return answerObject;
}
}
I have here few layers as I can see - rest layer, business layer, persistence layer (let's suppose I have few data sources - DB and elastic, each have some repository bean).
As we can see Business layer (service) aware about entities, which is not really good I think.
So question is what is the best practices for this situation?
Mapping should happen on persistence layer?
Or Is it good idea to create some additional layer adapter which will be responsible for mappings between rest models to internal data models, and inject it to the service bean ?
Appreciate any good mature examples.
I think, it can be done on controller's level, like in example here. Correct me If I am wrong.

Spring Data Rest CrudRepository vs ReadOnlyRepository

I noticed an anomaly in the way Spring Data Rest repositories are behaving. I have two types of entities. in my application - readonly entities (for reference data like statecodes, country codes, zip codes etc.). I don't want to let the end user change these. So I implemented the following ReadOnly repository.
#NoRepositoryBean
public interface ReadOnlyRepository<T, ID extends Serializable> extends Repository<T, ID> {
T findOne(ID id);
Iterable<T> findAll();
}
#Repository
public interface StateRepository extends ReadOnlyRepository<State, Long> {
}
Now, all other entities have CrudRepositories associated with them because they are editable entities like addresses which reference the states and zip codes.
Here's an example.
#Repository
public interface CustomerRepository extends CrudRepository<Address, Long> {
}
I have a controller for both readonly and editable entities, with a pass-through call to the repositories.
#RestController
#RequestMapping(value = "/addresses", produces = MediaType.APPLICATION_JSON_VALUE)
public class AddressController {
#Autowired
private AddressRepository addressRepository;
#RequestMapping(method = RequestMethod.GET)
#ResponseStatus(HttpStatus.OK)
public Iterable<Address> getAllAddresses() {
return addressRepository.findAll();
}
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
#ResponseStatus(HttpStatus.OK)
public Address getAddress(#PathVariable("id") Long id) {
return addressRepository.findOne(id);
}
}
I have an identical Controller corresponding to the State entity.
Funnily enough, the request to StateController gives me a HATEOAS json response, while the request to Address gives me a non HATEOAS json response. What gives?
My bad. My application server did not redeploy certain repositories. This is a non-issue.
So for those running into these issues, you are likely using hot-code replace feature of your IDE. Consider restarting your app and it should be a non-issue.

How to safely have a #RestController class implementing an interface annotated with #FeignClient

Given the following code sample
-- client library code
#FeignClient("string-service")
public interface StringClient {
#RequestMapping(method = RequestMethod.GET, value = "/microservicestring")
public String home();
}
#Service
public class StringHystrixClient {
private final SpringClient springClient;
//....
}
-- service library code
#RestController
public class StringController implements StringClient {
public String home(){
return "World";
}
}
#SpringBootApplication
#EnableHystrix
#EnableEurekaClient
#EnableFeignClients
public class StringApplication { ....}
If the service library references the client library, when the application gets started, through component scanning we will get to a state where in filling the dependencies from StringHystrixClient, the spring container will not know what to do because there are two beans implementing StringClient.
One solution to avoid this would be to not implement the StringClient in the StringController, but the code duplication from the interface and the rest controller would be error prone. Can somebody point out a more elegant solution to this problem?

Wrapping Spring Data JPA with ApsectJ

Is it possible?
Currently I am using some aspects for my MVC controllers, what works really fine. I'm wrapping their responses and I have desired effect.
I also want to do this with Spring Data JPA repositories. But since they're generated based on the interface e.g:
public interface SomeRepository<T extends Some, ID extends Serializable> extends
BaseRepository<T, ID>, JpaSpecificationExecutor<T> {
public List<T> findById(Long id)
}
It generates me controller which is ready to use:
http://localhost:8080/findById?id=1234
I also want to wrap this controller. Is it possible?
This should work:
#Component
#Aspect
public class MyAdvice {
#Before("execution(* com.company.jpa.SomeRepository+.findById(..))")
public void intercept() { ... }
}
Basically, we are telling the framework to intercept the call to the findById method on any sub-class of SomeRepository.
Here is a sample application demonstrating this in action.