Adding multiple servlets in single web.xml - java

I am trying to run two Servlet-class in a single web.xml but its not working, each servlet-class works fine independently.
web.xml:
<servlet>
<servlet-name>spring-ws</servlet-name>
<servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
<init-param>
<param-name>transformWsdlLocations</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-ws</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>240</session-timeout>
</session-config>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-ws-servlet.xml
/WEB-INF/health-page-servlet.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>health-page</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>health-page</servlet-name>
<url-pattern>/health.htm</url-pattern>
</servlet-mapping>
Do let me know if you can figure something wrong that i am doing.
I tried the below link but it doesnt work for me
Can I use Spring MVC and Spring WS in one single application?

This isn't going to work. The one which is mapped on /* overtakes all requests. You need to map it on / instead so that it will only intercept on requests which are not matched by all other existing servlets (including the JSP servlet which is implicitly mapped on *.jsp and all "normal" static resources like CSS/JS/image files!). See also Difference between / and /* in servlet mapping url pattern.
If being able to serve static resources is also required, then better map it on a more specific URL pattern like /ws/* and create a Filter which checks the request URI and then forwards accordingly. That filter can in turn safely be mapped on /*. See also this answer for a more concrete code example: How to access static resources when mapping a global front controller servlet on /*.

I am using Java configuration in my project and following code works fine for the same purpose:
public class Initializer implements WebApplicationInitializer {
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(ApplicationConfiguration.class);
ctx.setServletContext(servletContext);
MessageDispatcherServlet messageDispatcherServlet = new MessageDispatcherServlet();
messageDispatcherServlet.setApplicationContext(ctx);
messageDispatcherServlet.setTransformWsdlLocations(true);
Dynamic dynamic = servletContext.addServlet("messageDispatcherServlet", messageDispatcherServlet);
dynamic.addMapping("/ws/*");
dynamic.setLoadOnStartup(1);
dynamic = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
dynamic.addMapping("/");
dynamic.setLoadOnStartup(1);
}
}

you have a mapping for /* in the spring-ws section which is getting the request. you need to
come up with a different strategy... Try putting the /health.htm before the /* mapping.

Related

Spring MVC: WARNING: No mapping found for HTTP request with URI

I have this class in my Spring Web model-view-controller (MVC) framework. The version of the Spring Web model-view-controller (MVC) framework is 3.2.8.
I have this web.xml file.
...
<servlet-mapping>
<servlet-name>ecolabelWeb</servlet-name>
<url-pattern>*.do</url-pattern>
<url-pattern>/newdesign/manage/manageapplications</url-pattern>
<url-pattern>/newdesign/manage/manageapplications/</url-pattern>
<url-pattern>/newdesign/manage/manageapplications/*</url-pattern>
<url-pattern>/newdesign/manage/home</url-pattern>
<url-pattern>/newdesign/manage/home/</url-pattern>
<url-pattern>/newdesign/manage/home/*</url-pattern>
<!-- Explicitly mention /welcome.do for usage as welcome page -->
<url-pattern>/welcome/welcome.do</url-pattern>
</servlet-mapping>
...
and this controller:
/**
*
*/
#RequestMapping(value = { "/newdesign/manage/home",
"/newdesign/manage/home/",
"/newdesign/manage/manageapplications",
"/newdesign/manage/manageapplications/"}, method = {RequestMethod.GET})
public String manageApplications (#ModelAttribute("aplicationListForm") final AplicationListForm aplicationListForm,
HttpServletRequest request,
Model model ) throws ExecutionException {
User sessionUser = (User)request.getSession().getAttribute(Const.SESSION_USER);
..
}
this URL works properly
/newdesign/manage/manageapplications
but with this one newdesign/manage/home
I got this error
WARNING: No mapping found for HTTP request with URI [/devices/newdesign/manage/home]
I am really getting crazy !
I also tried to put it in another method with the same result
#RequestMapping(value = { "/newdesign/manage/home",
"/newdesign/manage/home/"}, method = {RequestMethod.GET})
public String cbHome (Model model ) throws ExecutionException {
..
}
This URL is working
http://127.0.0.1:7001/devices/newdesign/manage/manageapplications
not this one (?!)
http://127.0.0.1:7001/devices/newdesign/manage/home
Your configuration doesn't work because in your web.xml you have limited your application to work only with these url:
<url-pattern>/newdesign/manage/manageapplications</url-pattern>
<url-pattern>/newdesign/manage/manageapplications/</url-pattern>
<url-pattern>/newdesign/manage/manageapplications/*</url-pattern>
This is a tipical web.xml:
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
With this configuration you are telling Spring to use the dispatcher servlet mechanism to handle the incoming requests and the view rendering. This way he can handle all the url you are defining in controllers, like /newdesign/manage/home.
You need to create the dispatcher-servlet.xml file where you will configure the dispatcher.
See this link for more detailed informations: http://www.mkyong.com/spring-mvc/spring-mvc-hello-world-example/

set init params to jsp page without web.xml

I know that I can set init params for a jsp page using web.xml. For example
<servlet>
<servlet-name>init</servlet-name>
<jsp-file>/init.jsp</jsp-file>
<init-param>
<param-name>test</param-name>
<param-value>me</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>init</servlet-name>
<url-pattern>/init.jsp</url-pattern>
</servlet-mapping>
then in my init.jsp I can get the init params using getServletContext.getInitParameter..
However, I am using WebSevlet 3.0 annotations and I can't see equivalent jsp-file annotation. I am looking for something along these lines
#WebServlet(initParams=#WebInitParam(name="hello", value = "hello"),description = "A Simple Servlet", urlPatterns = { "/init.jsp" })
where I can use jsp-file annotation. So I need to set the jsp init params without using web.xml.
I know you said without using a web.xml - but you could still use a web.xml file with annotations - and just use it for the parameters - define the init params as a context-param. That would set the parameter for the whole application...
<context-param>
<param-name>someParameter</param-name>
<param-value>someValue</param-value>
</context-param>
Then in your code - reference it in the same way as above :
getServletContext.getInitParameter("someParameter");

#PathVariable is not working in spring

I have PersonController as below :
#Controller
#RequestMapping("person")
public class PersonController {
#RequestMapping(value= "/{personId}", method = RequestMethod.GET, produces={"application/json"})
public #ResponseBody Map<String, Object> getPerson(#PathVariable("personId") Integer personId) {
// code to get person
}
Tomcat starts up fine, I see this in the console :
Mapped "{[/person/{personId}],methods=[GET],params=[],headers=[] ,consumes=[],produces=[application/json],custom=[]}" onto public java.util.Map<java.lang.String, java.lang.Object> com.test.web.controller.PersonController.getPerson(java.lang.Integer)
But if I hit the url http://localhost:8080/sample/person/1 I get
HTTP Status 404 - /sample/person/1
In the web.xml
<servlet>
<servlet-name>app</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/servlet-context.xml</param-value>
</init-param-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>app</servlet-name>
<url-pattern>/sample/*</url-pattern>
</servlet-mapping>
I copy/pasted your PersonController class and it worked fine here.
So I did check your web.xml and your app servlet is mapping the pattern "/sample/*".
If I am corret, I suspect your project is called "sample" in Eclipse. In that case, you have to access your site as follows:
http://localhost:8080/sample/sample/person/1
The mapping in your web.xml will always start from your root context, and that is why you are getting 404 error.
If you want to access your controller from the root domain (in this case it is your actual Eclipse project name by default, but it can be configured too) you can use your servlet mapping as follows:
<servlet-mapping>
<servlet-name>app</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
I recommend that you use /rest/* or other mark since it will scale better for other types of content.
Let me know if it worked.

Multiple endpoints with Resteasy

I have two separate handfuls of REST services in one application. Let's say a main "people" service and a secondary "management" service. What I want is to expose them in separate paths on the server. I am using JAX-RS, RESTEasy and Spring.
Example:
#Path("/people")
public interface PeopleService {
// Stuff
}
#Path("/management")
public interface ManagementService {
// Stuff
}
In web.xml I currently have the following set-up:
<listener>
<listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
</listener>
<listener>
<listener-class>org.jboss.resteasy.plugins.spring.SpringContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>resteasy.servlet.mapping.prefix</param-name>
<param-value>/public</param-value>
</context-param>
<servlet>
<servlet-name>Resteasy</servlet-name>
<servlet-class>
org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Resteasy</servlet-name>
<url-pattern>/public/*</url-pattern>
</servlet-mapping>
The PeopleService and ManagementService implementations are just Spring beans.
Above web.xml configuration will expose them both on /public (so having /public/people and /public/management respectively).
What I want to accomplish is to expose the PeopleService on /public, so that the full path would become /public/people and expose the ManagementService on /internal, so that its full path would become /internal/management.
Unfortunately, I cannot change the value of the #Path annotation.
How should I do that?
actually you can. After few hours of debugging I came up with this:
1) Declare multiple resteasy servlets in your web.xml (two in my case)
<servlet>
<servlet-name>resteasy-servlet</servlet-name>
<servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
<init-param>
<param-name>resteasy.servlet.mapping.prefix</param-name>
<param-value>/openrest</param-value>
</init-param>
<init-param>
<param-name>resteasy.resources</param-name>
<param-value>com.mycompany.rest.PublicService</param-value>
</init-param>
</servlet>
<servlet>
<servlet-name>private-resteasy-servlet</servlet-name>
<servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
<init-param>
<param-name>resteasy.servlet.mapping.prefix</param-name>
<param-value>/protectedrest</param-value>
</init-param>
<init-param>
<param-name>resteasy.resources</param-name>
<param-value>com.mycompany.rest.PrivateService</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>private-resteasy-servlet</servlet-name>
<url-pattern>/protectedrest/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>resteasy-servlet</servlet-name>
<url-pattern>/openrest/*</url-pattern>
</servlet-mapping>
Please pay attention to the fact that we initialize personal resteasy.servlet.mapping.prefix and resteasy.resources for each our servlet.
Please don't forget to NOT include any botstrap classes as filters or servlets! And disable autoscan as well.
2) Create a filter that cleans up application from the RESTeasy's global information that it saves in context:
public class ResteasyCleanupFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException {
request.getServletContext().setAttribute(ResteasyProviderFactory.class.getName(), null);
request.getServletContext().setAttribute(Dispatcher.class.getName(), null);
chain.doFilter(request, response);
}
#Override
public void destroy() {
// TODO Auto-generated method stub
}
}
Register it for any request to your services (here I used it for all requests for simplisity):
<filter>
<filter-name>CleanupFilter</filter-name>
<filter-class>com.mycompany.ResteasyCleanupFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CleanupFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Thats it!Now you have two different REST services which lays under different prefixes : /openrest which meant to service all public requests and /protectedrest that takes care about all the private stuff in the app.
So why does it work (or why it does not work otherwise)?
When you call openrest instance for the first time it tries to initalize itself and when done saves the state in the global servletContext like this :
servletContext.setAttribute(ResteasyProviderFactory.class.getName(), deployment.getProviderFactory());
servletContext.setAttribute(Dispatcher.class.getName(), deployment.getDispatcher());
And if you will let it be your call to your second /protectedrest will get the SAME configuration! That is why you need to clean up this information some where. That is why we used our CleanupFilter which empty the context so brand new rest servlet could initialize itself with all the init parameters we declared.
This is a hack, but it does the trick.
This solution was tested for RESTEasy 2.3.6
EDITED
Works with 3.0.9.final as well!
AFAIK, you cannot have multiple servlet mappins for your JAX-RS implementation.
What you could do is: map RESTEasy to '/' (or '/api' for example if your application has other resources to serve and you don't want the JAX-RS part to interfere), then have the following #Path annotations:
#Path("/public/people")
public interface PeopleService {
// Stuff
}
#Path("/internal/management")
public interface ManagementService {
// Stuff
}

How to allow access to static content when having default servlet

I map all requests to /* to a specific servlet.
My static content is hidden by this configuration.
How can i allow access to specific files (such as crossdomain.xml)?
When you map /* to a specific servlet, all requests will be forwarded to that servlet, unless you provide a more explicit mapping to another servlet.
That is, if you have /* mapped to ServletA, and /static/* mapped to ServletB, then following Servlets will get called.
http://localhost:8080/abc.jpg -> ServletA
http://localhost:8080/static/abc.jpg -> ServletB
http://localhost:8080/xyz/abc.jpg -> ServletA
So one option you have is to write a Servlet to handle the static content, which will grab the file and return it as response. You can map that servlet to a prefixed by something like /static/*. This requires that all URL references to your static files to be updated to contain this '/static' part.
If that is not feasible for you, then probably you can use the same servlet, but mapped to multiple URL patterns (probably by extension) as follows.
<servlet>
<servlet-name>static-servlet</servlet-name>
<servlet-class>xxx.yyy.StaticServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>static-servlet</servlet-name>
<url-pattern>*.xml</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>static-servlet</servlet-name>
<url-pattern>*.jpg</url-pattern>
</servlet-mapping>
If you want this to be fine-grained to the level of each file, you can map the servlet to your file URL as well.
Cookbook:
Map your controller Servlet on a more specific url-pattern like /app/*.
Put all the static content in a specific folder like /static.
Create a Filter which is mapped on /* which transparently continues the chain for any /static requests and dispatches other requests to /app.
So, in a nutshell:
<filter>
<filter-name>filter</filter-name>
<filter-class>com.example.Filter</filter-class>
</filter>
<filter-mapping>
<filter-name>filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>controller</servlet-name>
<servlet-class>com.example.Controller</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>controller</servlet-name>
<url-pattern>/pages/*</url-pattern>
</servlet-mapping>
with the following in filter's doFilter():
String uri = ((HttpServletRequest) request).getRequestURI();
if (uri.startsWith("/static/")) {
chain.doFilter(request, response); // Goes to default servlet.
} else {
request.getRequestDispatcher("/app" + uri).forward(request, response);
}
No, you do not end up with extra /app path in the URL. It's fully transparent. Make if necessary "/static" and/or "/app" an <init-param> of the filter.
And one more(a direct) servlet mapping like this<servlet-mapping><servlet-name>StaticContentServlet</servlet-name><url-pattern>/crossdomain.xml</url-pattern></servlet-mapping>
probably you can put your static content under different URL like /static/* and then map this URL to a Servlet which responds with the static content.

Categories

Resources