I already integrated GWT with Spring MVC by implementing the Controller which is called
by DispatcherServlet using SimpleUrlHandlerMapping.
public class GwtRpcController extends RemoteServiceServlet implements Controller,
ServletContextAware {
#Override
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
super.doPost(request, response);
return null;
}
}
I want to use the new approach with the annotation #Controller, like below:
#Controller
public class GwtRpcController extends RemoteServiceServlet implements
ServletContextAware {
}
In this case there will be no handleRequest method, where should I do super.doPost(request, response); ?
See annotated web mvc controllers in spring 2.5
You should not to do doPost/doGet manually, just define controller method with corresponding parameters, return value and annonations for url mapping.
Related
I have the following controller class in my Spring boot project, split into interface and implementation:
public interface UserAccountController {
#RequestMapping(value = "/login", method = RequestMethod.POST)
public String login(#RequestBody UserAccountEntity account,
HttpServletResponse response) throws IOException;
#RequestMapping(value = "/signup", method = RequestMethod.POST)
public String create(#Valid #RequestBody UserAccountEntity userAccount,
HttpServletResponse response, BindingResult result);
}
#RestController
#RequestMapping("/api/authentication")
public class UserAccountControllerImpl implements UserAccountController {
#Autowired
private UserAccountService userAccountService;
#Override
public String login(#Valid #RequestBody UserAccountEntity account,
HttpServletResponse response) throws IOException {
//...
}
#Override
public String create(#Valid #RequestBody UserAccountEntity userAccount,
HttpServletResponse response, BindingResult result) {
//....
}
}
When I move RestController and RequestMapping annotations to the interface, it doesn't work. But annotating the methods on the interface work. How are these two annotations different?
#RestController inherits from #Controller, which inherits from #Component thus leading to the creation of a Spring Bean in your application context.
#RequestMapping inherits from #Mapper and is used to mark Rest- or Web-Controller methods as handler methods.
As to why Spring is implemented to disallow inheritance on the first and allow it on the second, I can only speculate:
I think your example constitutes a useful usecase for #Mapping inheritance, since you could have several RestControllers with different url prefixes, but other than that the same endpoints.
Making #Component annotations inheritable could lead to involuntarily created Spring Beans, since clients might fail to notice the annotation when implementing the interface.
I have a Spring Boot v1.4.0 application configured with Jersey for delivery of RESTful services.
I have a working app, but I'm now trying to enhance it with a multi-tenancy SCHEMA awareness strategy. I was hoping to set a TenantContext based on client auth headers using a Spring's HandlerInterceptor framework...
BUT, there seems to be an issue with the Interceptors being fired with Jersey. I can hit the APIs fine, ( i.e. curl -i -H "Accept: application/json" -X GET http://localhost:8080/api/products ), but the interceptors just won't fire. If I wire up a more basic app without Jersey for resource management, they fire fine?
Here is the current application set-up:
#SpringBootApplication
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).run(args);
}
}
Registering the Interceptor
#Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Autowired
HandlerInterceptor tenantInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(tenantInterceptor);
}
}
The Interceptor itself
#Component
public class TenantInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) throws Exception {
// FIXME: Put in a Logger impl
System.out.println("++++++++++++=======+++++++++ TenantInterceptor.preHandle() Checking for Tenant Routing");
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
TenantContext.clear();
// FIXME: Put in a Logger impl
System.out.println("++++++++++++=======+++++++++ TenantInterceptor.postHandle() Reset Tenant to " + TenantContext.getCurrentTenant());
}
}
The JerseyConfig
#Component
#ApplicationPath("api")
public class JerseyConfig extends ResourceConfig {
#PostConstruct
private void init() {
registerClasses(TenantsResource.class);
registerClasses(UsersResource.class);
registerClasses(ProductsResource.class);
}
}
I played around with the JerseyConfig #ApplicationPath("api") and the WebMvcConfig registry.addInterceptor(tenantInterceptor).addPathPatterns("patterns");. Tried the following one after the other, but no joy.
registry.addInterceptor(tenantInterceptor).addPathPatterns("/*");
registry.addInterceptor(tenantInterceptor).addPathPatterns("/**");
registry.addInterceptor(tenantInterceptor).addPathPatterns("/api/**");
registry.addInterceptor(tenantInterceptor).addPathPatterns("/api/*");
registry.addInterceptor(tenantInterceptor).addPathPatterns("/api/products");
registry.addInterceptor(tenantInterceptor).addPathPatterns("/api/products/");
Any help - much appreciated, or else I'll be resorting to hacking the Resource Controllers with smelly code :(.
Thanks - Derm
As mentioned by M.Deinum, HandlerInterceptor is not for Jersey, and it not some "underversal" interceptor. It is only for Spring MVC. For Jersey, you can use a ContainerRequestFilter. You would register it with you ResourceConfig.
See also:
Jersey docs for Filters and Interceptors
I would like to use the RepositoryEntityLinks class to get the link to a resource at various places in my code as per section 12.1 of the current Spring Data Rest manual
12.1. Programmatic Links Sometimes you need to add links to exported resources in your own custom built Spring MVC controllers. There are
three basic levels of linking available:
...
3 Using Spring Data REST’s implementation of RepositoryEntityLinks.
http://docs.spring.io/spring-data/rest/docs/current/reference/html/#_programmatic_links
I note the docs refer explicitly to "...your own custom built Spring MVC controllers" and it would seem that is the only place it is available. I would like to use the configured instance in a Spring Security AuthenticationSuccessHandler however the application fails to start with the error:
No qualifying bean of type[org.springframework.data.rest.webmvc.support.RepositoryEntityLinks] found
I have been able to successfully inject it to a controller as expected.
Can I use the RepositoryEntityLinks class outside of a Spring MVC Controller?
public class RestAuthenticationSuccessHandler implements AuthenticationSuccessHandler
{
#Autowired
private RepositoryEntityLinks entityLinks;
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException
{
//do something with entityLinks
}
}
Yes, You can. I have successfully used it in Assembler which generates links from HATEOAS model. Altough there may be some restrictions on where RepositoryEntityLinks class can be injected, for sure it can be used outside of Controllers.
Below you can see my working example. If anyone wnders this class extends ResourceAssemblerSupport which is part of spring-hateoas module. Maybe that's the thing that enables injection here.
#Component
public class UserAssembler extends ResourceAssemblerSupport<UserEntity, UserResource> {
#Autowired
private RepositoryEntityLinks repositoryEntityLinks;
public UserAssembler() {
super(UserController.class, UserResource.class);
}
#Override
public UserResource toResource(UserEntity userEntity) {
Link userLink = repositoryEntityLinks.linkToSingleResource(UserEntity.class, userEntity.getId());
Link self = new Link(entryLink.getHref(), Link.REL_SELF);
return new UserResource(userEntity, self);
}
}
The following works for me:
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class RestApiIntegrationTests {
#Autowired
private RepositoryEntityLinks repositoryEntityLinks;
#BeforeEach
public void initServletRequestAttributes() {
MockHttpServletRequest request = new MockHttpServletRequest();
ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(requestAttributes);
}
#Test
void test() {
System.out.println(repositoryEntityLinks.linkToCollectionResource(SomeClass.class));
}
}
The code is based on spring-data-rest-tests-core: AbstractControllerIntegrationTests, TestMvcClient.
I need to create a new EmployeeInfoCache instance (not a singleton) from info in the HttpServletRequest that is used to get info from an external app. I then want to give this object as a dependency to non-web-layer objects (where it will be set for all #Autowired references). EmployeeInfoCache itself has no web-layer dependencies (e.g. HttpServletRequest).
Can this be done? I thought about writing a spring interceptor which does the following but I don't know what to do to put an object in the spring context such that it will be used to resolve all #Autowired dependencies.
e.g.
public class MyInterceptor extends HandlerInterceptorAdapter
{
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception
{
- use info from HttpServletRequest to make calls to external app
- create EmployeeInfoCache object w/ this info
- add EmployeeInfoCache to spring application context where it will be used for resolution of #Autowired
}
}
And the remaining code:
// Assume don't have 'Component' or a similar annotation?
public class EmployeeInfoCache
{
...
}
// REST controller that calls the business logic method
#Controller
MyController
{
#Autowired
private MyBusinessObjectInterface myBusinessObject;
#RequestMapping(...)
public #ResponseBody MyResult myMethod(#RequestBody MyObject myObject, HttpServletRequest request, HttpServletResponse response)
{
myBusinessObject.doIt();
}
}
// Non-web-layer code that uses EmployeeInfoCache
#Service(...)
MyBusinessObject implements MyBusinessObjectInterface
{
// I want the EmployeeInfoCache instance created in MyInterceptor to be autowired here
#Autowired
private EmployeeInfoCache employeeInfoCache;
#Override
public void doIt()
{
employeeInfoCache.getName();
}
}
It sounds like you want to use a factory pattern. Have a Spring bean which is the factory method that returns the Cache.
http://kh-yiu.blogspot.in/2013/04/spring-implementing-factory-pattern.html
I would like to add web interface to my Java application, so that I can manipulate it's state using HTTP.
I have added to application context a Spring bean for some class that starts embedded Tomcat. This class of course has access to context that creates it. But I would like to store this context somehow in Tomcat class (org.apache.catalina.startup.Tomcat) so that later in can be retrieved in Servlets, so that I can do something like this:
public SomeClass extends extends HttpServlet {
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ApplicationContext appContext = getContextStoredEarlierInTomcatClass();
SomeBeanFromContext sbfc = appContext.getBean("sbfc", ApplicationContext.class);
sbfc.setSomeProperty(newValue);
}
}
Any idea how I could achieve it?
Thanks!
Classes including Servlets do not require an ApplicationContext to obtain references to String beans. This is done using dependency injection
#Controller
#RequestMapping("/mypage")
public class SomeClass {
#Autowired
private SomeBeanFromContext sbfc;
#RequestMapping(value = "/individualRequest", method = RequestMethod.GET)
public String doIndividualRequest(HttpServletRequest request) {
sbfc.setSomeProperty(newValue);
...
}
}
Spring MVC offers a complete method of injecting beans into target web controller classes using #Controller annotated classes.