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

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.

Related

Tomcat 9+: overriding Servlet mapping using #WebServlet annotation

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.

Spring 4 RestController Dispatcher url-pattern

I have an annotated rest controller, like the one below. I'm able to get the services to host fine, but only if I configure the full path for each individual service in web.xml:
#RestController
#RequestMapping("/service/")
public class StuffRestController
{
#RequestMapping("/getStuffList")
public List<Stuff> getStuffList() {
... make stuff ...
return stuffList;
}
... many other similar services ...
}
This is really the only spring resource in my application; although, we are using spring security.
The below are the only lines spring4-servlet.xml:
<mvc:annotation-driven />
<context:component-scan base-package="com.me.stuff.presentation.controller" />
<context:component-scan base-package="com.me.stuff.security" />
The StuffRestController class resides in the "...controller" package.
web.xml:
<servlet>
<servlet-name>spring4</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/spring4-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring4</servlet-name>
<url-pattern>/service/getStuffList</url-pattern>
</servlet-mapping>
All of the above actually all works fine and dandy, but there are 30 other services in this controller and I would rather not make a new servlet mapping for every method. The issue occurs when I attempt to "wildcard" the mapping. I've tried /service/, /service, and /service/*. And many other combinations.
Most all simply don't map properly, and I receive 404 errors. If I use /service/* it will engage the dispatcher servlet when /service/getStuffList is called, but it responds with:
WARNING: No mapping found for HTTP request with URI [/myapp/service/getStuffList] in DispatcherServlet with name 'spring4'
I'm sure this is something simple with how URL mappings are created, but it is eluding me.
The issue is you've included the path: /service/getStuffList in both your DispatcherServlet and the #RestController request mapping. So to access the rest controller method, you've to hit the following URL:
{contextPath}/service/getStuffList/service/getStuffList
So, either change the dispatcher servlet url-pattern to /, so it will handle every request coming to your application, and then based on path after myApp, will redirect to appropriate controller. Or, set the RestController mapping to /*. You should prefer the former approach.
If you want to have your servlet handle request coming at /service, then change the url-pattern to /service/*. But then you've to remove all the request mapping from class level. Else at current scenario, you've to hit the following url:
{contextPath}/service/service/getStuffList
However, if you want to include the dispatcher servlet url-pattern in path resolution (i.e., you want to map the class at /service and also map servlet to that path), you can set alwaysUseFullPath property to true of URL handler mapping. For that, add the following to your spring context xml file:
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name = "alwaysUseFullPath" value = "true" />
</bean>

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 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);

How to initialize Java EE 5 JAX-WS 2.0 Web Service with Parameters

Application configuration:
Web application using java first method of creating JAX-WS 2.0 Web Services with annotations.
WebLogic 10.3
My Requirements
The requirements I have are to deploy a single web service implementation class, but change logic based on the URL from which the service was accessed.
Question:
I'm assuming a good way to do this is to deploy different mappings in web.xml and initialize them with different parameters. Is there a better way?
What is the best way to switch logic off the URL from which the web service was accessed? Should I try to configure two servlet mappings in web.xml with initialization parameters (tried, but couldn't get it to work), or should I parse the URL in the service impl? Any other alternatives?
What I've Tried (but didn't work)
I have tried adding the <init-param> in the <servlet> element in web.xml. However, can't get to the ServletConfig object inside the web service to retrieve the param. The web service does not have all the functionality of a standard Servlet (even if I implement Servlet or ServletContextListener). I only have access to the WebServiceContext (it seems) and from there I can only get <context-param> elements--but I would need <init-param> elements instead.
In web.xml, I enter two <servlet> elements using the same Java class, but which map to two different URLs as follows. Notice how the "source" param is different in each Servlet mapping.
<servlet>
<servlet-name>Foo</servlet-name>
<servlet-class>com.Foo</servlet-class>
<init-param>
<param-name>source</param-name>
<param-value>1</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Foo</servlet-name>
<url-pattern>/Foo</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>Bar</servlet-name>
<servlet-class>com.Foo</servlet-class>
<init-param>
<param-name>source</param-name>
<param-value>2</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Bar</servlet-name>
<url-pattern>/Bar</url-pattern>
</servlet-mapping>
You very well may have, but did you try using MessageContext at runtime to determine what the source is?
#WebService
public class CalculatorService implements Calculator
{
#Resource
private WebServiceContext context;
#WebMethod
public void getCounter()
{
MessageContext mc = wsContext.getMessageContext();
// you can grab the HttpSession
HttpSession session = (HttpServletRequest)mc.get(MessageContext.SERVLET_REQUEST)).getSession();
// ...or maybe the path info is enough
String path = mc.get(MessageContext.PATH_INFO);
// the query itself should almost definitely be enough
String query = (String) mc.get(MessageContext.QUERY_STRING);
}
}
I got the idea from http://sirinsevinc.wordpress.com/category/jaxws/. Haven't tried it, though.

Categories