I am a litter bit confuse about tomcat and his purples regarding servlets.
Tomcat is a servlet container, so if we implement HttpServlet this instance will be manage by apache tomcat (will make sure to call the method based on the URL request and HTTP verb)
public class ExampleHttpServlet extends HttpServlet
{
private String mymsg;
public void init() throws ServletException {
mymsg = "Http Servlet Demo";
}
public void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<h1>" + mymsg + "</h1>");
out.println("<p>" + "Hello Friends!" + "</p>");
}
public void destroy() {
// Leaving empty. Use this if you want to perform
//something at the end of Servlet life cycle.
}
}
So if we will have multiple servlets apache tomcat will find them, and based on the url will call the appropriate servlet.
But how is this working with spring boot and spring mvc if we don't implement any HttpServlet?
Let's say that we have a simple REST api. What will manage tomcat here if we don't implement any HttpServlet, or in this case will not be created any servlets, and tomcat will only forward the request to DispatcherServlet and from here DispatcherServlet identifies the controller and further the handler?
Or maybe is there created a servlet automatically for each Controller?
I found this picture but I don't see from where comes up the intermediat servlets in tomcat that forwards the request to Dispatcher.
I tried to find more details here but with not to much success. Any resource si welcome.
Thanks
Related
What i know about Filter and Interceptor is that Filters as J2EE Specifications are part of the webserver and not the Spring framework. So some older articles explain that it is impossible to register filters as Spring Bean while Interceptor is possible.
But the results I got when I tested today is that Filters can be Spring Bean and also inject Spring Bean on Filters are possible too like Interceptors.
(I tested on SpringBoot Framework)
#Component
public class CustomFilterTest implements Filter {
#Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws ServletException, IOException {
chain.doFilter(request, response);
}
#Override
public void init(final FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
#Override
public void destroy() {
Filter.super.destroy();
}
}
#RestController
#RequiredArgsConstructor
public class ProductController {
private final CustomFilterTest customFilterTest;
#GetMapping("/test")
public ResponseEntity<Void> temp() {
System.out.println(customFilterTest);
return ResponseEntity.noContent().build();
}
}
Can anyone please explain to me?
We have to make a distinction between a regular Spring application and a Spring Boot application here. As with both, you can register a servlet filter as a bean, but the mechanism is a bit different.
Spring Framework
In plain Spring use the DelegatingFilterProxy to achieve this. The task of the DelegatingFilterProxy is to look for a bean with the same name as the filter in the root application context (the ApplicationContext registered through the ContextLoaderListener). This bean has to be your managed servlet filter.
#Configuration
#EnableWebMvc
public class WebConfiguration {
#Bean
public void YourFilter myFilter() { ... }
}
Then for the web application you would register a DelegatingFilterProxy with the name myFilter to make this work.
public class MyWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
public void onStartup(ServletContext servletContext)
throws ServletException {
super.onStartup(servletContext);
servletContext.addFilter("myFilter", DelegatingFilterProxy.class);
}
Spring Boot
In Spring Boot it is a bit different as Spring Boot is also in control of your servlet container, like Tomcat. It basically means that Tomcat is also a managed bean in your ApplicationContext and Spring Boot can inject dependencies into it. So when Spring Boot detects a bean for the servlet filter it will automatically add it to the filter chain (without the need of a DelegatingFilterProxy).
Which means only an #Bean for your filter is needed.
#Configuration
public class WebConfiguration {
#Bean
public YourFilter myFilter() { ... }
}
Additionally you can configure things like URLs etc. by adding an additional FilterRegistrationBean for this filter.
Conclusion
For plain Spring the DelegatingFilterProxy has been around since Spring 1.2 which was released in 2005. This means if you are reading really, really, really old articles (before 2005) this was true, however with the addition of the DelegatingFilterProxy, this isn't anymore. With the release of Spring Boot, this became even a lesser issue, is it more or less is the only way to register a filter (as a managed bean).
I'm running a Spring Boot application with Jetty server. The APIs are served by the Jersey servlet. The static contents are placed in src/main/static and src/main/private-static.
Issue#1 Static content in src/main/static is not being served
The content in src/main/static is public and can be accessed freely. According to this: https://docs.spring.io/spring-boot/docs/1.3.8.RELEASE/reference/html/boot-features-developing-web-applications.html#boot-features-spring-mvc-static-content Spring boot should automatically serve the static content, but it does not.
Issues#2 How to set the static content directory to src/main/private-static for this custom servlet that checks for authentication before serving a file.
I've created another servlet to serve static content with authentication
#Bean
public ServletRegistrationBean<HttpServlet> privateStaticContent() {
return new ServletRegistrationBean<>(new HttpServlet() {
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
int status = isAuthenticated(req) ? 200 : 401;
resp.setStatus(status);
}
}, "/private-static/*");
}
Basically this servlet should serve files from the directory src/main/private-static, but I'm not sure how and where to configure this.
Issue#3 To make Jersey serve at /api endpoint, I've added this in application.properties
server.servlet.context-path=/api
But this makes all the servlets to use this /api endpoint. How can I configure this only for Jersey servlet and not for others. Is there something like server.servlet.jersey.context-path=/api ?
src/main/java/<dir>/<static-content> is not something that maven or gradle would ever see.
the src/main/java tree is only used for *.java, nothing else.
perhaps you should be using src/main/resources/<dir>/<static-content> instead?
How to get the DispatcherServlet instance from a spring WebApplicationContext object?
I want to perform a direct request to the the front controller servlet (avoiding the network interface) and check the response from a spring application, e.g. WebApplicationContext instance.
Notice, we are not able to use MockMvc to check the HTML rendered by JSP because: "if you use JSPs, you can verify the JSP page to which the request was forwarded, but no HTML is rendered"(according to MockMvc vs End-to-End Tests)
So maybe if we get the DispatcherServlet instance we could perform the request through its doDispatch method and check the content from response.
When you look at
DispatcherServlet.java, there is a constructor with WebApplicationContext as parameter, but the super constructor from FrameworkServlet.java does only hold the field itself, without any callbacks - so the given web application context will not know the dispatcher servlet.
So I don't think you can resolve the dispatcher servlet by the WebApplicationContext. Of course you could try to inject/autowire the DispatcherServlet - maybe this is also possible inside tests, but I am not sure.
I think the most easiest way to check for JSP outputs would be to test those parts with a real server as described at Testing with a running server . This should give you the complete output and web application behaviour inside your test without any workarounds.
Dispatcher Servlet : It acts as front controller for Spring based web applications. It provides a mechanism for request processing where actual work is performed by configurable, delegate components. It is inherited from HttpServlet, HttpServletBean and FrameworkServlet. It also implements ApplicationContextAware hence it is able to set ApplicationContext.
WebApplicationContext : It is an extension of a plain ApplicationContext. It is web aware ApplicationContext i.e it has Servlet Context information. When DispatcherServlet is loaded, it looks for the bean configuration file of WebApplicationContext and initializes it.
So you can set and get the instance of any ApplicationContext from DispatcherServlet but not vice-versa.
dispatcherServlet.getWebApplicationContext();
Although, you can get an instance of DispatcherServlet but I doubt it will serve your purpose as it's scope is protected.
#Autowired
DispatcherServlet dispatcherServlet;
Code inside DispatcherServlet
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
It is possible to create your own DispatcherServlet and perform actions. You can override the default one created by Spring boot app.
Sample:
public class SampleDispatcherServlet extends DispatcherServlet {
private final Log logger = LogFactory.getLog(getClass());
#Override
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// You can use other implementations of HttpServletRequest and
// HttpServletResponse as per your requirement
// e.g. FormContentRequestWrapper , ResourceUrlEncodingResponseWrapper many more
if (!(request instanceof ContentCachingRequestWrapper)) {
request = new ContentCachingRequestWrapper(request);
}
if (!(response instanceof ContentCachingResponseWrapper)) {
response = new ContentCachingResponseWrapper(response);
}
HandlerExecutionChain handler = getHandler(request);
try {
// Any task to be done
super.doDispatch(request, response);
} finally {
// Some task to be done
}
}
}
HandlerExecutionChain- Handler mapper is used to match current request to appropriated controller. Interceptors are the objects invoked before and after some of dispatching actions
Then we have to register the DispatcherServlet:
#Bean
public ServletRegistrationBean dispatcherRegistration() {
return new ServletRegistrationBean(dispatcherServlet());
}
//The bean name for a DispatcherServlet that will be mapped to the root URL "/"
#Bean(name = DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet() {
return new SampleDispatcherServlet();
}
I have a spring boot application that serves my jersey based api. I have a requirement to have the services layer serve blob data to a client as a stream. I wrote a servlet to do that and configured it as follows.
#Bean
public ServletRegistrationBean servletRegistrationBean(){
return new ServletRegistrationBean(new BlobReaderServlet(),"/blobReader/*");
}
However, in the servlet code I can't seem to inject any components (they are all null). I need to inject a component that actually loads the blob data from the database.
#WebServlet(name = "BlobReaderServlet",
urlPatterns = {"/blobreader"})
#Component
public class BlobReaderServlet extends HttpServlet {
Logger logger = Logger.getLogger(this.getClass().getName());
#Inject
DocumentLoaderComponent blobLoader;
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
I strongly suspect the servlet isn't a spring managed component after all and dependency injection isn't working. How can I get access to a component from the context?
UPDATE
It was much simpler than I thought.
#Override
public void init() throws ServletException {
ApplicationContext ac = (ApplicationContext) getServletConfig().getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
this.documentBlob = (DocumentBlob) ac.getBean("documentBlobBean");
}
You are correct that your servlet isn't a Spring managed bean. That is because you are instantiating the instance directly (i.e., calling new BlobReaderServlet() in your bean method). Another solution is to change your configuration class as follows:
#Bean
public ServletRegistrationBean servletRegistrationBean(){
return new ServletRegistrationBean(blobReaderServlet(),"/blobReader/*");
}
#Bean
public BlobReaderServlet blobReaderServlet(){
return new BlobReaderServlet();
}
This will allow Spring to manage the instance and perform autowiring on it.
Usually, i'm accessing business objects (services) with #ManagedProperty in a main class annotated with #ManagedBean.
Here, i have a Servlet and i want to inject my business objects.
The use of managedProperty doesn't work with the annotation #WebServlet.
When i use WebApplicationContext, it's working.
Does the use of WebApplicationContext is the clean way to do that or is there a cleaner method to do it ?
#WebServlet(urlPatterns ="/Graphic")
public class GraphicServlet extends HttpServlet {
// Method 1 - This is not working ( the usual way in a ManagedBean)
#ManagedProperty(value="#{participantBo}")
private ParticipantBo participantBo; // getters and setters added
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// Method 2 - This is working
WebApplicationContext springContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
ParticipantBo participantBo = (ParticipantBo) springContext.getBean("participantBo");
List<Participant> myParticipants = participantBo.findAllParticipant();
for (Participant participant : myParticipants) {
System.out.println(participant.getId_study());
}
}
}
Using WebApplicationContext is one of the ways to access the Spring web context from where you want. In fact, if you are able to reach Spring beans using #ManagedProperty, it means you have tied JSF and Spring frameworks in some way. #ManagedProperty should work only for JSF managed beans. However, being your servlet totally independent from the framework, accessing the Spring context this way is a choice.
See also:
https://stackoverflow.com/a/12244697/1199132
Spring Integration with other frameworks