Tomcat 9+: overriding Servlet mapping using #WebServlet annotation - java

Which is the precedence order of #WebServlet annotation vs web.xml servlet mapping?
More specifically we've a use case where we would rather not modify the web.xml but would need to override one of the servlet mappings.
Our web.xml has something like:
<servlet>
<servlet-name>foo</servlet-name>
<servlet-class>com.whatever.simple.foo</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>foo</servlet-name>
<url-pattern>/foo/*</url-pattern>
</servlet-mapping>
And we are considering overriding this using:
#WebServlet("/foo/*")
public class OurCustomImplementation extends HttpServlet {
}
So the question is:
is the specification allowing us to add some parameter to do so (e.g. override or prioriry parameters)?
is the specification guaranting that our class will always override web.xml?
Will Tomcat 9+ allow to do it, even if maybe using some extensions?

Both Servlet 3.1 Final and 4.0.Final specs say in 12.2 Specification of Mappings:
If the effective web.xml (after merging information from fragments and
annotations) contains any url-patterns that are mapped to multiple servlets then the deployment must fail.
So the spec clearly doesn't allow this.
Alternative solution: create a WebFilter that does a forward dispatch to another url, that your overriding servlet handles. Or just do your thing in the filter itself.

Related

#WebFilter annotation with web module 3 - confused about multiple url patterns

I'm really confused about the WebFilter annotation with Dynamic Web Modules >= 3.0. I read this article ( https://www.concretepage.com/java-ee/jsp-servlet/how-to-use-filter-in-servlet-3-with-webfilter-annotation ) telling that #WebFilter cannot be used without web.xml because #WebFilter does not define order, it only reduces the configuration stored in web.xml.
I was not able to create a web.xml without the url patterns. E.g. this is invalid:
<filter-mapping>
<filter-name>OAuthSecurityFilter</filter-name>
</filter-mapping>
<filter-mapping>
<filter-name>GuiceFilter</filter-name>
</filter-mapping>
It is invalid because in addition to filter-name, one has to provide at least a server name or an url mapping. But in that case, I wonder what it means if I also specify the value to the annotation:
#WebFilter(value="/*", filterName="OAuthSecurityFilter")
public class OAuthSecurityFilter implements Filter {
You see, if you cannot specify the filter without the url pattern in web.xml, and if you must provide the filter in web.xml, then what is the point in having the "value" in the WebFilter annotation? Will it be ignored? Will it override the url-pattern given in web.xml?

Servlet Mapping Error appears when "#WebServlet" annotation is present

My web.xml looks like this:
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>com.ajayramesh.jrecycled.servlets.Login</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
I have a class called Login.java with an auto-generated annotation that says:
#WebServlet("/login")
right above my HttpServlet class extension declaration. When this annotation is present, I get the following error when I try to start my server:
The servlets named [LoginServlet] and [com.ajayramesh.jrecycled.servlets.Login] are both mapped to the url-pattern [/login] which is not permitted
I only have one definition in my web.xml and only a single web.xml. When I remove this annotation, the server works fine. To my understanding, annotations are not supposed to have an effect on the runtime of the program, and are only meant to optimize compilation? On a side note, what exactly does that annotation do?
You can't use the same mapping for both annotation and web.xml, you can use either one of it. App server treat it as duplicate url mapping.
Basically, declaring servlet and servlet-mapping elements in web.xml is equal to annotating a servlet class with #WebServlet.

Why have 1 extra level for servlet config in web.xml?

The servlet config looks like this -
<servlet>
<servlet-name>smart</servlet-name>
<servlet-class>SuperSmart</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>smart</servlet-name>
<url-pattern>/dumb</url-pattern>
</servlet-mapping>
Its said that all these aliases are for security. I get that. But why do we have to map it to a servlet-name first and then to the servlet-class ? Why can't the server find the url pattern and directly map it to the servlet-class ?
The aim of this is that the servlet could be referenced by more than one mapping, so you can map one servlet to more than one url (or pattern) and not just one.
The name is sort of like and "ID" that tells the container which <servlet> part goes with which <servlet-mapping> part (as well as ties it to other parts of the XML config in container specific XML files)
You can have multiple url patterns tied to the same servlet name.
I agree it seems ugly, but it's an attempt to keep the servlet config DRY, as servlet-name can be used in filter-mappings as well as servlet-mappings.
In servlet spec 3.0 you can annotate the servlets themselves which is neater.
It was designed that way to allow other components, such as filters, can access it. Filters can either be associated with a Servlet by using the <servlet-name> style:
<filter-mapping>
<filter-name>Image Filter</filter-name>
<servlet-name>ImageServlet</servlet-name>
</filter-mapping>

Servlet 3.0 or Glassfish issue? Web-app ignores asyncSupported for filter when in annotation

I added a filter to my web-app (which already supports asynchronous operations), and forgot to insert the proper annotation on the filter to to preserve asynchronous support. Easy problem to solve. I put the following the annotation in the filter class, which ends up looking like this:
#WebFilter(asyncSupported=true)
public class MainFilter implements Filter
{
...
}
However, after redeploying, asynchronous operations remained unsupported, giving me this error:
Request is within the scope of a filter or servlet that does not
support asynchronous operations
I was able to rectify the problem by removing the annotation, and placing its equivalent in web.xml:
<filter>
<filter-name>MainFilter</filter-name>
<filter-class>com.MyWebApp.Filters.MainFilter</filter-class>
<async-supported>true</async-supported>
</filter>
What I'm confused about is the fact that the container seems to respect the asyncSupported annotation in servlet files, but does not for filters. Is there a specific reason why, or is this a GlassFish 3.0 bug?

Servlet Mapping: Is it possible to add servlet mappings at runtime?

Is it possible to add a servlet mapping at runtime? either through a ContextListener or within a Servlet's init() method?
Within the application I am using, all requests are handled through a single Servlet and their mappings are defined in the web.xml file. The number of mappings are increasing and adding a new mapping requires a new release.
Mapping the servlet to /* would not work since requests dispatched to JSPs and static content would also be routed to this Servlet.
Using a filter might be an option nevertheless, it will add a requirement for the filter to know what path to prefix to the RequestDispatcher.
For example:
Dispatcher Servlet Mapping
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/action/*</url-pattern>
</servlet-mapping>
Routing Filter Definition
<filter>
<filter-name>RoutingFilter</filter-name>
<filter-class>com.ssv.web.filter.RoutingFilter</filter-class>
<init-param>
<param-name>exclude</param-name>
<param-value>/static/**:/action/**:/**/*.jsp</param-value>
</init-param>
<init-param>
<param-name>dispatcher-prefix</param-name>
<param-value>/action/**</param-value>
</init-param>
</filter>
In the above example, I would like to skip creating the filter, the filter mapping, and the servlet mapping and instead dynamically define mappings in a ContextListener.
I'm using Tomcat.
In servlet 3.0 (tomcat 7) it is possible: ServletContext.addServlet(..)
But in your case it may be wiser not to do that. Spring's DispatcherServlet for example is mapped to /*. It forwards to JSPs internally, and handles requests in its own non-servlet components, so that it doesn't need to register them dynamically. Take a look at spring-mvc anyway, even if you don't end up using it.
To dynamically add your own servlet, you need a servletcontext Object. From the api docs, there exists a method to bind your Servlet to the servletContext.
Servletconfig.getServletContext().addServlet("YourServletname",yourServletname.class);

Categories