One of my projects uses Spring MVC to handle URL mappings and dispatch logic. I have now to use a third party library which uses its own HttpServlet as the main entry point for its functionalities, but as it's an optional drop-in replacement for another library I can't just put the <servlet> declaration in the web.xml: I'd rather use a Controller and Spring profiles to switch between such implementations without having to edit the web.xml.
Is there anything offered OOTB by Spring to handle such cases? I don't seem to find it right away.
Thanks in advance!
Since registering the third party servlet in your web.xml is a no-go, I think your best bet would be to create a singleton instance of the servlet in your ApplicationContext, and then create a custom view that delegates to said servlet's service method.
You can see an example of custom views in action in this tutorial.
Answering my own question here just in case my approach can be useful to others.
There are two key factors I needed to consider:
the proper initialization of the servlet
give the servlet full control over the HTTP layer (e.g. setting HTTP headers, etc)
In my specific case there's no need for properly handling the servlet destruction, as it's a no-op.
I ended up writing a dedicated Controller, to be instantiated only if a specific Spring profile is activated, which takes care of instantiating and initializing the Servlet. Then, all the requests will be directly handled in a void handler method, as follows:
public class ServletDelegatingController implements ServletConfig {
private final DelegateServlet delegate = new DelegateServlet();
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
delegate.service(request, response);
}
// properly initializes the servlet
public void setServletConfig(ServletConfig servletConfig) {
try {
delegate.init(servletConfig);
} catch (ServletException e) {
throw new IllegalStateException("Failure while initializing the servlet", e);
}
}
}
The delegating-servlet.xml for the DispatcherServlet looks like the following:
<beans profile="custom">
<bean id="cmisServiceFactory"
class="com.foo.ServletDelegatingController"/>
</beans>
Related
In my previous experience:
When using pure servlet, we define servlets so that it will serve requests that match specific urls.
When using struts2, we define a filter so that it will serve requests that match specific urls.
When using springMVC in a traditional xml configuration style, we define a dispatcher servlet so that it will serve requests that match specific urls.
But with spring-boot:
Seems no servlet or filter is defined explicitly. But it still could serve specific urls.
The questions is:
Is it still using servlet? If yes, how it get to serve urls without defining servlet or filter explicitly?
Additional related questions (base on tips from comments):
It seems the implementation of SpringBootServletInitializer will be invoked on deploy, but who is going to invoke it?
As you can see here in details, on startup, while initializing an embedded server (Tomcat by default), Spring Boot creates and registers DispatcherServlet as a servlet.
Spring then, as usual, scans your own classes (including the one you invoke SpringApplication.run() from) and sets corresponding mapping for your controllers, if you have any. For example mapping for /hello here:
#RestController
#EnableAutoConfiguration
public class TestSpring {
#RequestMapping("/hello")
String hello() {
return "Hello World!";
}
public static void main(String[] args) throws Exception {
SpringApplication.run(TestSpring.class, args);
}
}
I am new to Spring. I noticed that when handling static resources, there are two options available:
Option 1:
If Spring's DispatcherServlet is mapped to / with the below code, which makes it the "Default Servlet", it's possible to map certain static resources to Spring handlers with RequestMapping annotation (overriding the AbstractAnnotationConfigDispatcherServletInitializer class):
#Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
Then we can still enable the container's "Default Servlet" to handle those static resources whose URL pattern is not covered by Spring request mapping (overriding the WebMvcConfigurerAdapter class):
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
This basically uses the servlet container's "Default Servlet" as the catch-all to handle all the static resources missed by Spring's DispatcherServlet.
Option 2:
(overriding the WebMvcConfigurerAdapter class)
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
super.addResourceHandlers(registry);
registry.addResourceHandler("*.efi").addResourceLocations("/");
}
Why are there two options?
What are the main differences between these approaches?
Are there any other options?
I usually take option 2 because I want to stick to Spring, but I know that's not a strong reason.
Some reference related to static resources handling:
Serve Static Resources with Spring
Spring Framework 4.1 - handling static web resources
Spring MVC – How to include JS or CSS files in a JSP page
ADD 1
It seems option 2 provides much more flexibility regarding the resource mapping. And even resources within WEB-INF folder can be mapped.
Here is a concrete example of when Falling Back On the "Default" Servlet To Serve Resources is not applicable.
This is a typical implementation of the above approach:
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer)
{
configurer.enable();
return;
}
However, the current best practice for handling 404 errors in Spring 4 appears to be to use setThrowExceptionIfNoHandlerFound:
#Override
protected DispatcherServlet createDispatcherServlet(WebApplicationContext servletAppContext)
{
DispatcherServlet dispatcherServlet = (DispatcherServlet) super.createDispatcherServlet(servletAppContext);
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
return dispatcherServlet;
}
Unfortunately, according to the documentation for DispatcherServlet:
Note that if DefaultServletHttpRequestHandler is used, then requests
will always be forwarded to the default servlet and a
NoHandlerFoundException would never be thrown in that case.
Indeed, this is the case. Combining both of the above approaches does not result in a NoHandlerFoundException being triggered, and this in turn prevents my 404 custom error page from resolving. Now, if I were to comment out my configureDefaultServletHandling method, the NoHandlerFoundException is thrown and my error handling (via #ControllerAdvice as shown in the linked answer) resolves to my custom 'notFoundPage'.
Unfortunately, this now means that my static resources (i.e., 'default.css') are not resolved:
DEBUG org.springframework.web.servlet.DispatcherServlet - Handler execution resulted in exception - forwarding to resolved error view: ModelAndView: reference to view with name 'notFoundPage'; model is {}
org.springframework.web.servlet.NoHandlerFoundException: No handler found for GET /webapp-test/style/default.css
I do not see any way to reconcile these two approaches so that they will not interfere with each other. My conclusion is that the "Default Servlet" approach is not appropriate for serving static resources in this case, which leaves us with the addResourceHandlers method.
Among the benefits of using the addResourceHandlers method are:
...serve static resources from locations other than the web application root, including locations on the classpath.
The cache-period property may be used to set far future expiration headers so that they will be more efficiently utilized by the client.
The handler also properly evaluates the Last-Modified header (if present) so that a 304 status code will be returned as appropriate, avoiding unnecessary overhead for resources that are already cached by the client.
Also see this answer for a more complicated example of how handling static resources with the default servlet can cause unwanted side effects.
We are trying to have a spring MVC Controller that works as a portlet and a servlet, in order to be deployed in a Liferay context or as a standalone version. But it seems that the we have a conflict if we decide to have multiple RequestMappings on the method level (as opposed to having just 1 mapping on the level of the controller). we get the error shown below.
Note that if we decide to just have a requestMapping on the level of the controller that hosts a servlet mapping and a portlet mapping, it works.
#RequestMapping({"view", "/"})
The controller that does not work:
#Controller("controller")
#RequestMapping("VIEW")
public class MyController {
#RenderMapping
public ModelAndView doView(RenderRequest request, RenderResponse response) throws Exception {
HttpServletRequest portletHttpReq = PortalUtil.getHttpServletRequest(request);
HttpServletResponse portletHttpResp = PortalUtil.getHttpServletResponse(response);
return doView(portletHttpReq, portletHttpResp);
}
#RequestMapping(value="/home")
protected ModelAndView doView(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {
// do something
return new ModelAndView("view");
}
}
The resulting error:
[...]
Caused by: java.lang.IllegalStateException: Mode mappings conflict between method and type level: [/home] versus [view]
Do you have any suggestions on how we could implement such thing? What we would really like to avoid is having to maintain 2 controllers for every portlet/servlet.
Thank you.
I don't really think this is a good idea... the #RequestMapping annotation on the class level is going to cause problems for sure, simply because Spring portlet MVC expects the portlet mode, while Spring Web MVC expects a root URL.
Also, your code doesn't seem to be correct either, since ModelAndView exists in both the portlet MVC as the web MVC part of the Spring framework, and since you cannot import both, you'll have to specify the full package for one of them, and since you're not doing that either, your code is just wrong.
Except the technical issues, both portlets and servlet have different terminology and point of views. These are some key questions that pop up with me if I hear this:
What are you going to do about the different phases (ACTION and RENDER)
What about the the different portlet modes (VIEW, EDIT, HELP, ...)
What are you going to do about the specific portlet features (PortletSession, PortletPreferences, ...)
How are you going to handle the different kind of requests (ResourceRequest, ActionRequest, RenderRequest, PortletRequst, EventRequest vs HttpServletRequest)
How are you going to handle security? The portal container provides authentication for you, a standalone web application does not.
And these are just questions from a portlet mindset, I'm pretty sure there are also technical issues if you look at it from the web application point of view.
It makes much more sense to divide your code into a view layer and business logic. Put the business logic in a separate package or separate services, and build a separate portlet- and standalone application, using the same/shared business logic.
I recently started working on a spring MVC project which uses spring security.
I had to do some pre-checks before user's request gets to the controller.
This is what I want to achieve, like I have worked a lot in struts and in struts we can extend all the action classes to a superclass let's say BaseAction and then write some validation here so that they gets called before calling the sub class methods.
I would like to achieve same thing here but don't know how to start.
I cannot use filters as i need to make database calls and web-service calls in pre checks.
I just need the pointers .
You can implement an interceptor using HandlerInterceptorAdapter.
http://static.springsource.org/spring/docs/3.0.x/reference/mvc.html#mvc-handlermapping-interceptor
Configuring the applicationContext in XML.
<mvc:interceptors>
<bean class="my.package.MyInterceptor" />
</mvc:interceptors>
The interceptor.
public class MyInterceptor extends HandlerInterceptorAdapter {
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response, Object handler) {
// your logic
return true;
}
}
Returns true if the execution chain should proceed with the next interceptor or the handler itself. Else, DispatcherServlet assumes that this interceptor has already dealt with the response itself.
I have a function I want to execute straight after tomcat has started and loaded all its attributes successfully. I don't want to use ServletContextListener as this will require the function to start before tomcat starts. Can someone suggest how to go about this?
ServletContextListener.contextInitialized(..) is the method that is called after all servlets and filters have initialized for a given application.
if there are multiple ServletContextListeners, some of them are called before the others (logically)
if there are multiple applications (hence multiple contexts) some of them are started before others.
Update I will now assume your setup, although you didn't share it:
you start spring via a listener (and not with a servlet)
you configure hibernate within spring
In that case, you have two options:
define your ServletContextListener after the one for spring in web.xml, thus guaranteeing it will be invoked after it
use spring's lifecycle processor
You could create a startup servlet and then add that to the end of your web.xml:
<servlet>
<servlet-name>StartupServlet</servlet-name>
<servlet-class>com.your.package.MyStartupServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
public class MyStartupServlet extends HttpServlet {
public void init(ServletConfig config) throws ServletException {
try {
// Startup code here
} catch (Exception e){
// Log exception
}
}
public java.lang.String getServletInfo() {
return "StartupServlet";
}
}
I think JMX Tomcat supports can meet your requirement, even no ServletContextListener is deployed in container.