Spring Framework Page Direction - java

Background: I'm relatively new to Java/Spring and inherited a project built on them. We're moving to AWS Elastic Beanstalk which changed the location of the main page for JSON requests from:
www.mywebsite.com/myApp/myAppJsonService
to:
www.mywebsite.com/myAppJsonService
That worked fine- all the functions that come out the JSON requests (most of them) are working perfectly. I have another page that takes a teacher's uploaded quiz via HTML form submit and parses the data. The form used to point to:
www.mywebsite.com/myApp/controllers/importQuiz
so I changed it to:
www.mywebsite.com/controllers/importQuiz
The web.xml file has:
<servlet>
<servlet-name>SpringDispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:context/Controllers.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringDispatcher</servlet-name>
<url-pattern>/controllers/*</url-pattern>
</servlet-mapping>
And the corresponding Controllers.xml code:
<bean id="importExamController" class="com.myapp.controllers.ImportExamController">
<property name="commandClass" value="com.myapp.objects.spring.FileUploadBean"/>
<property name="myappManager" ref="myappManager"/>
</bean>
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/importExam">importExamController</prop>
<prop key="/heartbeat">heartBeatController</prop>
</props>
</property>
</bean>
The way I read it, regardless of the preceding "myapp" in the URL, it should find "/controllers/" in the URL, look to the Controllers.xml file and find the "/importExam" and direct it to the "importExamController". That's not happening. Clearly, there is a fault in my logic, and I can't seem to find it. Any help would be very much appreciated.
EDIT:
Doing some digging through the logs, I found:
INFO: WSSERVLET14: JAX-WS servlet initializing
Aug 27, 2011 7:21:06 PM com.sun.xml.ws.transport.http.servlet.WSServletDelegate doGet
SEVERE: caught throwable
ClientAbortException: java.net.SocketException: Broken pipe
at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:373)
at org.apache.tomcat.util.buf.ByteChunk.append(ByteChunk.java:327)
at org.apache.catalina.connector.OutputBuffer.writeBytes(OutputBuffer.java:396)
at org.apache.catalina.connector.OutputBuffer.write(OutputBuffer.java:385)
and it goes on for a while. To me it looks like that is confirming the idea that the data is trying to be sent to something that is incorrectly mapped. Let me know if this might mean something else or if it's just irrelevant.

First off, why in the world would you want controllers in your URL? Secondly, you have the right idea of how it should work, and you're probably correct about something not being configured correctly.
You should really read up on the enhanced MVC functionality in Spring 3.0+. With annotations and the mvc namespace, your code would look like (e.g.):
#Controller
#RequestMapping("importExam")
public class ExamController {
private final examService;
#Autowired
public ExamController(ExamService examService) {
this.examService = examService;
}
#RequestMapping(method=RequestMethod.GET)
public String getExams(ModelMap model) {
model.addAttribute("exams", examService.getExams());
return "exams";
}
}
And your controller is just:
<mvc:annotation-config/>
to load the controller and bind the request mappings to urls. This allows for wildcards too. The above controller would wire all URL request for /importExam to this controller, and process the default GET method at the base context. You could add nested methods with additional request mappings which would reside beneath the /importExam context.

Related

Spring MVC Can't configure Dispatcher Servlet right

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.

spring RequestMapping 404 status [duplicate]

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.

Spring-MVC 406 Not Acceptable instead of JSON Response

I'm trying to return a JSON response with Spring 3.0.6, but I get a 406 response "Not Acceptable", with the description:
"The resource identified by this request is only capable of generating responses with characteristics
not acceptable according to the request "accept" headers ()."
I know that a very similar question has been asked before, but I can't make it work for my project, despite many
tests and I don't understand what I'm doing wrong.
In my Maven pom.xml I've the following:
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.8.5</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.8.5</version>
<scope>compile</scope>
</dependency>
In web.xml I reference webmvc-config.xml, and the log confirms that is loaded.
<servlet>
<servlet-name>mainServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/webmvc-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
In webmvc-config.xml I've the following:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<mvc:annotation-driven />
My Controller is:
#Controller
public class ClassifiedController {
#RequestMapping(value = "/classified/{idClassified}", headers = "Accept=*/*",
method = RequestMethod.GET)
#ResponseBody
public final Classified getClassified(#PathVariable final int idClassified) {
...
I tried with or without the headers parameter with the same results. If I call the URL
directly with Firefox the Request Headers contain the following (checked with firebug):
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
If I use the following JQuery:
$.ajax({
url: '/classified/38001',
type: 'GET',
dataType: 'json'
});
The following headers are sent:
Accept: application/json, text/javascript, */*; q=0.01
In both cases the result is a 406 error. I don't know what else should I check to make
it work.
UPDATE: I decided to debug through Spring and I found out that Jackson was called correctly and in org.codehaus.jackson.map.ser.StdSerializerProvider the method _findExplicitUntypedSerializer contains the following code:
try {
return _createAndCacheUntypedSerializer(runtimeType, property);
} catch (Exception e) {
return null;
}
This is unfortunate because hides the source of the problem. With the debugger I found out that that exception contained a very descriptive error message:
Conflicting getter definitions for property "reminded":
ClassifiedImpl#isReminded(0 params) vs
ClassifiedImpl#getReminded(0 params)
Now that I see the error message is a silly mistake and easy to fix, but without that it wasn't that obvious. In fact, fixing the problem, leaded to a working serialization.
Add the following in DispatcherServlet-servlet.xml.
<bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jacksonMessageConverter"/>
</list>
</property>
</bean>
I've stumbled upon the same error (406: content not acceptable) with Spring MVC and #RestController annotation.
The Spring handler:
#RequestMapping(value = "/stuff-acknowledgment/{id}", produces ="application/json;charset=UTF-8", headers="Accept=*")
public Message acknowledgeStuff(#PathVariable("id") String id, #ModelAttribute("ack") AckBean acquittement) {
Observation:
the URI has the form : http://www.host.com/stuff-acknowledgment/{id}
BUT $id has a very particular format: xxxcomplicatedhashxxx.png (or whatever extension you can think of).
Therefore:
Spring MVC interpret the extension and want to produce a result of that same mime type (even if I define it as a path variable), here an "image/png" MIME type even if I tell him to produce JSON. So a 406 exception is thrown.
Fix:
Remove the ".png" extension in the URI, or remove the PathVariable and put it in the body, or add a suffix behind the pathVariable (not tested but should work as well), the point is to avoid a file extension at the end of the URI.
P.S.: I know it doesn't answer the specific problem (with the solution in the update) in the question but I found that SO thread when searching for that problem and post my fix here for the record, hoping it can help someone in the future.
In terms of the MappingJacksonJson processing, you'll need to make sure that the Jackson ObjectMapper supports your object type for serialisation.
I ran into this problem because the objects that I wanted to return as JSON didn't have any getter methods for their properties. Jackson probably needs these. After adding them it worked.
although this thread is a little old...
u need to add the following (maven dependency):
org.codehaus.jacksonjackson-mapper-asl1.9.13

Java Webservice REST proper design best pracice

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

Java Servlet: pass a request back to default processing

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

Categories

Resources