What is needed:
There is simple web application running on Tomcat at address http://localhost:8080/.
Handler for following URL should be added:
GET http://localhost:8080/request/report/custom_report?from=2013-10-12&to=2014-10-12&download=true
which will simply write to the HttpServletResponse some data i.e. no views are involved.
What was done:
As per official Spring MVC documentation following mapping of DispatcherServlet was added to web.xml
<servlet>
<servlet-name>springDispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springDispatcher</servlet-name>
<url-pattern>/request/*</url-pattern>
<!-- PLEASE NOTE that mapping to /* is not an option -->
</servlet-mapping>
Now, because latest spring-webmvc-4.0.5.RELEASE is used I would like to add above mentioned handler with the minimum XML or Java configuration possible, so I create controller class:
package org.yura.servlet.spring;
#Controller
public class SpringRequestController {
#RequestMapping(value = "/report/custom_report",
method = GET,
produces = "application/pdf")
public void getCustomReport(
#RequestParam("from") #DateTimeFormat(pattern = "yyyy-MM-dd") final Date from,
#RequestParam("to") #DateTimeFormat(pattern = "yyyy-MM-dd") final Date to,
#RequestParam("download") final boolean download,
final HttpServletResponse response) throws IOException {
takeParamsAndWriteReportAsPdfToServletResponse(from, to, download, response.getOutputStream());
}
Then, in order for this Controller to be "picked up" by Spring I put springDispatcher-servlet.xml right next to web.xml in WEB-INF folder with following configuration (please advise if it can be simplified even more):
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="org.yura.servlet.spring" />
</beans>
The Problem
With this configuration, after starting Tomcat and navigating to above mentioned URL I get Error 404.
Question 1: Please advise what is wrong with handler URLs - should I specify them as relative or what? (because as per logs, DispatcherServlet is created normally)
Question 2: Is it possible to move configuration from springDispatcher-servlet.xml to my Controller class in order not to scatter request-handling logic across multiple files.
Thanks in advance...
You haven't enabled your MVC stack. Add
<mvc:annotation-driven />
to your springDispatcher-servlet.xml (along with the appropriate namespaces).
The configuration in springDispatcher-servlet.xml is not simply request handling configuration. It can contain any bean declaration. If anything, you can move it to a Java configuration, but it should not be part of your #Controller source code.
Related
I'm having trouble configuring the Dispatcher for Spring. What I am trying to achieve is:
Build REST WebService to receive requests
Have HTML + Ajax pages consuming the data (Therefore, I don't have Views in my Spring project)
So far I have only 2 HTML pages: Login (using j_security_check) and Main page. Both very simple. I also have a simple controller:
MainController.java
#RestController //Or #Controller and #ResponseBody, no difference, right?
public class MainController {
#RequestMapping("rest/main/data")
public String getData () {
return "{data: \"DATA HUEHUE\"}"; // Yes, I'm brazilian
}
}
And I have tried the following configuration for web.xml and dispatcher-servlet.xml:
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>/rest/*</url-pattern>
</servlet-mapping>
dispatcher-servlet.xml
<context:component-scan base-package="com.example.controller"/>
This doesn't work. I get the message INFO: Mapped URL path [/rest/main/data] onto handler 'mainController' but when I try to access I get No mapping found for HTTP request with URI [/myapp/rest/main/data] in DispatcherServlet with name 'dispatcher'
I also have tried:
On web.xml: <url-pattern>/</url-pattern>
On dispatcher-servlet: The same
What happened: The controller DID work but the application also tried to map my login.html and couldnt find a match so I got 404 ;-;
I'm aware of that "standard" configuration using a prefix and a sufix, but since I dont have views here I dont think that's the right approach.
I'm kinda new at Spring (as you may have noticed), so please be gentle on the answers.
Any ideas?
Thanks in advance :)
My project tree:
-project
--src
---main
----webapp
-----WEB-INF
------web.xml
------weblogic.xml
------dispatcher-servlet.xml
-----www
------main.html
-----login.html
(Login is outside www)
With the first approach if you modify the controller code to have /rest/main/data It will work.
#RestController //Or #Controller and #ResponseBody, no difference, right?
public class MainController {
#RequestMapping("/rest/main/data")
public String getData () {
return "{data: \"DATA HUEHUE\"}"; // Yes, I'm brazilian
}
}
What is happening in happening in the second approach is that since you have Spring Security configured you need to be authenticated first but for that it finds the Login.html and can not find it. This may be because of incorrect configuration.
This question already has answers here:
Why does Spring MVC respond with a 404 and report "No mapping found for HTTP request with URI [...] in DispatcherServlet"?
(13 answers)
Closed 5 years ago.
Ok, I know there are like 20 posts here with the same problem, but none of them seem to help me, so this will probably be a duplicate, but I've gone over all the other posts and none of them solved my problem, so there must be something that I'm doing wrong or I'm not doing the right modifications from the answers of the previous mentioned questions.
I'm trying to make a small application using Spring, and I'm still experimenting with it, but I've spent like 4 days trying to figure what's wrong and I just can't. I still get a HTTP Status 404 whenever I try to get a jsp back from the controller. Nothing but 404 status through Tomcat, nothing else...
WebAppController:
#Controller
public class WebAppController {
#RequestMapping(value="/login", method = RequestMethod.GET)
public String login() {
System.out.println("You have entered the login maprequest");
return "test1";
}
}
web.xml:
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<display-name>Hotel Application</display-name>
<servlet>
<servlet-name>WebApp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>WebApp</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>login.jsp</welcome-file>
</welcome-file-list>
</web-app>
webApp.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:component-scan base-package="com.iquestgroup" />
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/jsp/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
</beans>
This configuration works in a simple maven project with only the above mentioned in it. The problem is, the exact same thing isn't working in a maven project with 3 modules(persistence, service and webapp). In the webapp i've copied the exact same thing and when I run it on server I get 404 http status... even though the modules are building with success.
L.E. The first part of the accepted answer refers to a common servlet mapping mistake made by those who are starting with Spring. My problem was not related to it and I ended up removing it after initial answer. In order to not become confusing for readers, the first part of the accepted answer refers to the following code:
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
Change
<url-pattern>/*</url-pattern>
to
<url-pattern>/</url-pattern>
Currently, your mapped DispatcherServlet is marked as handling all requests because of /*. This means that it will also attempt to handle a request dispatched to /WEB-INF/jsp/test1.jsp. It obviously doesn't have a handler for that and will fail.
The pattern / is special in that it is the default fallback if no other pattern matches. If there's a Servlet that maps to the request path, that Servlet will be chosen before your mapped DispatcherServlet.
Most (probably all) Servlet containers come mapped with a Servlet to handle rendering JSPs with the url-pattern of *.jsp. That will take precedence over your DispatcherServlet mapped to /.
If you're still getting 404 after these changes, then there are a few possibilities.
Spring logs at the INFO level any handler methods (#RequestMapping annotated methods within #Controller beans) it registers. If you don't see any of those in your logs, then your web application hasn't been deployed correctly/successfully.
If your Servlet container is embedded in your IDE, check the appropriate tab/view on the apps that are deployed. If it's standalone, check that your generated .war file is in the appropriate directory and expanded correctly.
First of all, you can listen / request with any root controller like below.
#Controller
#RequestMapping ("/")
public class RootCtrl {
Logger logger = Logger.getLogger(RootCtrl.class);
#RequestMapping (value = "/", method = {RequestMethod.GET, RequestMethod.POST})
public String index(ModelMap model) {
// redirect to login page
return "redirect:admin/index";
}}
With that controller, you will map all requests to root. Than you may redirect request to your login controller.
It's all about request mapping. Your loginController or webController shold listen a request which should be specified before. In my application, login controller listens /admin request paths. Than, my login controller is like this:
#Controller
#RequestMapping ("/admin")
public class LoginCtrl {
#RequestMapping (value = "/index", method = {RequestMethod.GET, RequestMethod.POST})
public String index(#RequestParam (value = "captchaId", defaultValue = "") String captchaId, ModelMap model, HttpServletRequest request, HttpServletResponse response) {
if(StringUtils.isNullOrEmpty(captchaId)){
captchaId = RandomGUID.getRandomGUID();
}
model.addAttribute("captchaId", captchaId);
// redirect to login page
return "login";
}
When you will get request from this path : localhost/SampleProject/admin/index. Mappings will work.
I need to monitor java application and I am using javamelody.
But the problem is, I have to get the data that javamelody has so I can show it in another screen. I know that javamelody store its rdd files in temp/javamelody directory, now I need to change the storage-directory to another path so I can get the data from that path.
How can I set the storage-directory of javamelody?
Oh I think I've found the answer
I just have to set command line or xml file in my tomcat like this
<?xml version="1.0" encoding="UTF-8" ?>
<Context docBase="pathto\appname.war" path="javamelody" reloadable="false" >
<Parameter name='javamelody.storage-directory' value='pathname' override='false'/>
</Context>
Thank you for the help :D
In web.xml, define filter javamelody with parameter storage-directory as below:
<filter>
<filter-name>javamelody</filter-name>
<filter-class>net.bull.javamelody.MonitoringFilter</filter-class>
<init-param>
<param-name>storage-directory</param-name>
<param-value>/path/to/the/storage/directory</param-value>
</init-param>
</filter>
I tested using JavaMelody version 1.60.0. For more information refer to the JavaMelody user guide.
for javamelody-spring-boot-starter
javamelody.init-parameters.storage-directory=/tmp/javamelody-${spring.application.name}
For Spring Boot
public class JavaMelodyConfiguration implements ServletContextInitializer {
#Value(value="${javamelody.storage-directory}")
String jmStorageDir;
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.addListener(new SessionListener());
servletContext.setInitParameter("javamelody.storage-directory", jmStorageDir);
}
then you can set the javamelody.storage-directory in application.properties
I have developed a webservice in Java that runs as a servlet on a tomcat, parameters for the application are given to the servlet via the get request (e.g. servlet?method=search&query=searchterm123), the servlet recognizes if the method and the query are defined and in the case of an error gives back a string that is manually wrapped in xml code that I hardcoded via this.writer.println(answer);. If the method is right a new class is instantiated which does the search and then gives back an object which XStream converts into XML for me which I then again send back to the client with println wrapped into my xml overhead that is again hard coded String answer ="<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n".
Obviously this works and also very efficiently but this is far from elegant. I have looked into Apache CXF very briefly and also into things like RESTlet or JBOSS RestEasy, the latter two I found to much for my demands. Every tutorial I find on CXF always includes Spring and Maven which then overwhelms me a little. Do you have suggestions how I should turn my dirty hack into a pretty application?
I think using a JAX-RS framework (as CXF and Resteasy, but also Jersey) is the way to go in your case. Regarding serializing to XML, maybe have a look at JAXB (as included in Jersey too for instance). It should help automatically serialize any entity structure.
Regarding the complexity of such application: it should always depend on what infrastructure you use. If its just a simple Java EE server, its possibly best to use the implementation of that vendor (Jersey for Glassfish, Resteasy for JBoss). Otherwise, just use the build system you are familiar and comfortable with. You can easily replace the Maven dependencies with Ant and Ivy for example.
I can recommend CXF; I found it extremely easy to get going with its tutorial, especially once I bit the bullet and used Maven to manage dependencies (though really it's orthogonal to everything that CXF and Spring do).
But in order to make use of CXF I really recommend using Spring. You don't have to use all of Spring; the simple tutorial on the CXF site gives you enough to get going. This is especially true if you've already got the code that actually implements things already done, and separated out from the code to parse incoming URLS, render responses as XML, etc.; that's the part that CXF (and typically JAXB) will handle for you.
To help, here's a mega-simple example (with imports omitted for brevity).
I know it looks complicated, but almost all the second half consists of stuff you write once and then don't really touch again; as you build out to tackle your real code, you can do a lot without paying must attention to the framework code at all.
First, the interface definitions (including the XML type model):
public interface Foo {
#Path("/") #GET #Produces("application/xml");
FooDesc getDescription(#Context UriInfo ui);
#Path("{id}")
FooItem getFoo(#PathParam("id") String id);
}
#Path("/")
public interface FooItem {
#GET #Produces("application/xml")
FooItemContents getContents();
#PUT #Consumes("application/xml")
void setContents(FooItemContents desc);
#DELETE
Response delete();
}
// These classes are purely structural holders; no behavior.
#XmlRootElement #XmlType
public class FooDesc {
#XmlElement
public List<URI> foo;
}
#XmlRootElement #XmlType
public class FooItemContents {
#XmlElement
String bar;
}
Next, the implementation class:
public class FooImpl implements Foo {
public FooDesc getDescription(UriInfo ui) {
FooDesc desc = new FooDesc();
desc.foo = new ArrayList<URI>();
UriBuilder ub = ui.getAbsolutePathBuilder().path("{id}");
for (String id : realGetIdList()) // Hook to your code here!
desc.foo.add(ub.build(id));
return desc;
}
public FooItem getFoo(String id) {
final RealFoo r = realGetById(id); // Hook to your code here!
return new FooItem() {
public FooItemContents getContents() {
FooItemContents contents = new FooItemContents();
contents.bar = r.getBar(); // Hook to your code here!
return contents;
}
public void setContents(FooItemContents desc) {
r.setBar(desc.bar); // Hook to your code here!
}
public Response delete() {
r.close(); // Hook to your code here!
return Response.noContent().build(); // Return a simple HTTP 204
}
};
}
}
Now, plumbing it together at the Spring level with a WEB-INF/beans.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxrs="http://cxf.apache.org/jaxrs"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd">
<!-- Instantiate and connect the service framework -->
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-jaxrs-binding.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<jaxrs:server id="customerService" address="/">
<jaxrs:serviceBeans>
<ref bean="fooBean" />
</jaxrs:serviceBeans>
</jaxrs:server>
<!-- Instantiate your implementation class -->
<bean id="fooBean" class="your.package.FooImpl" />
</beans>
Now, to hook it all up as a webapp, a web.xml:
<web-app>
<!-- Magic to make Spring work and build the rest for us -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/beans.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Make CXF implement the servlet for us -->
<servlet>
<servlet-name>CXFServlet</servlet-name>
<display-name>CXF Servlet</display-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- You *must* use this servlet mapping or Bad Things Happen. -->
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
Now build a WAR including all the libs listed on the CXF site, putting the bits in the right places (you don't need Maven for this, but in the end it makes it easier) and deploy. That ought to work (i.e., you've now got enough to be dangerous!)
I want a Servlet to handle requests to files depending on prefix and extension, e.g.
prefix_*.xml
Since mapping on beginning AND end of request path is not possible, I have mapped all *.xml requests to my Servlet.
The question now is: how can I drop out of my servlet for XML files not starting with "prefix_", so that the request is handled like a "normal" request to an xml file?
This is probably quite simple but I do not seem to be able to find this out... :-/
Thanks a lot in advance
another solution (maybe fits for you) is if you are using/plan to use an Apache in front of that web container instance you could use the rewrite module of apache. Rewriting the url to something more easy to handle for the Webapp container.
Hope this helps.
David.
I would strongly suggest using a proper MVC framework for this. As you've discovered, the flexibility of the standard servlet API is very limited when it comes to request dispatching.
Ideally, you would be able to use your existing servlet code in combination with an MVC framework, with the framework doing the diapcthing based on path pattern, and your servlets doing the business logic. Luckily, Spring MVC allows you to do just that, using the ServletForwardingController. It'd be a very lightweight spring config.
So you'd have something like this in your web.xml:
<servlet>
<servlet-name>myServlet</servlet-name>
<servlet-class>foo.MyServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<url-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>*</url-pattern>
</url-mapping>
You would then have a WEB-INF/spring-servlet.xml file like this:
<beans>
<bean name="/prefix*.xml" class="org.springframework.web.servlet.mvc.ServletForwardingController">
<property name="servletName" value="myServlet"/>
</bean>
</beans>
And that would be pretty much it. All requests for /prefix*.xml would go to myServlet, and all others would fall through to the container.
Not shure, but once you catch all *.xml requests you can inspect the request again in your code via HttpServletRequest.getRequestURI()
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String uri =req.getRequestURI();
int i = uri.lastIndexOf('/');
int j = uri.lastIndexOf('.', i);
if (uri.substring(i+1, j).startsWith("prefix_")) {
// your code
}
}
(code not tested, only an idea ...)