I'm using Spring 3, and trying to set up a simple web-app using annotations to define controller mappings. This seems to be incredibly difficult without peppering all the urls with *.form or *.do
Because part of the site needs to be password protected, these urls are all under /secure. There is a <security-constraint> in the web.xml protecting everything under that root. I want to map all the Spring controllers to /secure/app/.
Example URLs would be:
/secure/app/landingpage
/secure/app/edit/customer/{id}
each of which I would handle with an appropriate jsp/xml/whatever.
So, in web.xml I have this:
<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>/secure/app/*</url-pattern>
</servlet-mapping>
And in despatcher-servlet.xml I have this:
<context:component-scan base-package="controller" />
In the Controller package I have a controller class:
package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
#Controller
#RequestMapping("/secure/app/main")
public class HomePageController {
public HomePageController() { }
#RequestMapping(method = RequestMethod.GET)
public ModelAndView getPage(HttpServletRequest request)
{
ModelAndView mav = new ModelAndView();
mav.setViewName("main");
return mav;
}
}
Under /WEB-INF/jsp I have a "main.jsp", and a suitable view resolver set up to point to this. I had things working when mapping the despatcher using *.form, but can't get anything working using the above code.
When Spring starts up it appears to map everything correctly:
13:22:36,762 INFO main annotation.DefaultAnnotationHandlerMapping:399 - Mapped URL path [/secure/app/main] onto handler [controller.HomePageController#2a8ab08f]
I also noticed this line, which looked suspicious:
13:25:49,578 DEBUG main servlet.DispatcherServlet:443 - No HandlerMappings found in servlet 'dispatcher': using default
And at run time any attempt to view /secure/app/main just returns a 404 error in Tomcat, with this log output:
13:25:53,382 DEBUG http-8080-1 servlet.DispatcherServlet:842 - DispatcherServlet with name 'dispatcher' determining Last-Modified value for [/secure/app/main]
13:25:53,383 DEBUG http-8080-1 servlet.DispatcherServlet:850 - No handler found in getLastModified
13:25:53,390 DEBUG http-8080-1 servlet.DispatcherServlet:690 - DispatcherServlet with name 'dispatcher' processing GET request for [/secure/app/main]
13:25:53,393 WARN http-8080-1 servlet.PageNotFound:962 - No mapping found for HTTP request with URI [/secure/app/main] in DispatcherServlet with name 'dispatcher'
13:25:53,393 DEBUG http-8080-1 servlet.DispatcherServlet:677 - Successfully completed request
So... Spring maps a URL, and then "forgets" about that mapping a second later? What is going on?
Thanks.
I have exactly the same problem as you. The way to set 'alwaysUseFullPath' is pretty straightforward. My conf file is as following:
<bean
class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"
p:order="3" > <!-- a higher value meaning greater in terms of sorting. -->
<property name="alwaysUseFullPath" value="true" />
<property name="interceptors">
<list>
<ref local="myInterceptor" />
</list>
</property>
</bean>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="alwaysUseFullPath" value="true" />
</bean>
Ah. As usual, found the answer after posting the question :-)
Changing the RequestMapping annotation to just /main fixes the problem. The documentation is not very clear on how all this is specified.
Put something like this in your #Configuration class:
#Bean(autowire = Autowire.BY_TYPE)
public AnnotationMethodHandlerAdapter handlerAdapter(){
final AnnotationMethodHandlerAdapter annotationMethodHandlerAdapter = new AnnotationMethodHandlerAdapter();
annotationMethodHandlerAdapter.setAlwaysUseFullPath(true);
return annotationMethodHandlerAdapter;
}
Related
I'm trying to modify a wicket application to store the session in redis via spring-session. The session is showing up in redis, but I've run into a problem that whenever the application makes a standard wicket ajax call, the response from wicket includes an Ajax-Location header that is interpreted by wicket-ajax-jquery.js triggering a page redirect. But this only happens AFTER the first ajax call has been successful. For example, the first ajax call may look like this:
http://host:port/context/help/admin?0-1.IBehaviorListener.0-smartTable-tableArea-records-0-row-detailToggleCell-detailToggleLink&_=1636756805561
and the response headers do NOT include Ajax-Location. And then later, the next ajax call may look like this:
http://host:port/context/help/admin?1-1.IBehaviorListener.0-smartTable-tableArea-records-0-row-detailToggleCell-detailToggleLink&_=1636756906417
But the response header now includes this:
Ajax-Location: ./admin?2
and instead of just doing the ajax update to the page, the entire page redirects to the URL specified in that header because of code in src/main/java/org/apache/wicket/ajax/res/js/wicket-ajax-jquery.js
Digging down into the wicket-core code using the debugger, consider this where it doesn't produce the Ajax-Location header and works properly:
Step completed: "thread=ba6f07:3", org.apache.wicket.core.request.handler.ListenerInterfaceRequestHandler.respond(), line=197 bci=169
ba6f07:3[1] print canCallListenerInterfaceAfterExpiry
canCallListenerInterfaceAfterExpiry = false
ba6f07:3[1] print freshPage
freshPage = false
ba6f07:3[1] print isStateless
isStateless = false
ba6f07:3[1] print component
component = "[AjaxLink [Component id = detailToggleLink]]"
and then compare with this where it DOES produce an Ajax-Location header and doesn't work properly:
Breakpoint hit: "thread=ba6f07:7", org.apache.wicket.core.request.handler.ListenerInterfaceRequestHandler.respond(), line=197 bci=169
ba6f07:7[1] print canCallListenerInterfaceAfterExpiry
canCallListenerInterfaceAfterExpiry = false
ba6f07:7[1] print freshPage
freshPage = true
ba6f07:7[1] print isStateless
isStateless = false
ba6f07:7[1] print component
component = null
The difference being that when it doesn't work, freshPage is true and component is null.
Note: this pattern is fully functional in another similar application that I have and I’ve spent some time comparing the two. Clearly, something is missing from the original application in the app that I’m working on but I haven’t been able to identify it yet.
My redis http session config class looks like this:
import javax.annotation.PostConstruct;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration;
import org.springframework.session.web.http.CookieHttpSessionStrategy;
import org.springframework.session.web.http.DefaultCookieSerializer;
#Configuration
#EnableRedisHttpSession
public class MyRedisHttpSessionConfig extends RedisHttpSessionConfiguration
{
private JedisConnectionFactory connectionFactory;
#PostConstruct
public void init()
{
CookieHttpSessionStrategy strategy = new CookieHttpSessionStrategy();
DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
cookieSerializer.setCookieName( "SESSION" );
strategy.setCookieSerializer(cookieSerializer);
setHttpSessionStrategy( strategy );
}
#Bean
public JedisConnectionFactory connectionFactory() throws Exception
{
return connectionFactory;
}
public void setConnectionFactory( JedisConnectionFactory connectionFactory )
{
this.connectionFactory = connectionFactory;
}
}
my web.xml has this:
...
<filter>
<filter-name>requestLoggingFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter>
<filter-name>springSessionRepositoryFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter>
<filter-name>myApplicationWicketFilter</filter-name>
<filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
<init-param>
<param-name>applicationFactoryClassName</param-name>
<param-value>org.apache.wicket.spring.SpringWebApplicationFactory</param-value>
</init-param>
<init-param>
<param-name>filterMappingUrlPattern</param-name>
<param-value>/*</param-value>
</init-param>
</filter>
...
<filter-mapping>
<filter-name>springSessionRepositoryFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
...
<filter-mapping>
<filter-name>ariesApplicationWicketFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
...
and my spring beans config file has this:
...
<!-- The RedisHttpSessionConfiguration creates an http Filter bean with name "springSessionRepositoryFilter" which is referenced in web.xml -->
<context:annotation-config/>
<util:constant static-field="org.springframework.session.data.redis.config.ConfigureRedisAction.NO_OP"/>
<bean class="MyRedisHttpSessionConfig">
<property name="connectionFactory" ref="webTierRedisConnectionFactory"/>
</bean>
<bean id="webTierRedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${service-tier:redisSentinelMasterName}"/>
<property name="port" value="${service-tier:redisSentinelHostPortCsv}"/>
<property name="usePool" value="true"/>
<property name="poolConfig">
<bean class="redis.clients.jedis.JedisPoolConfig">
<property name="maxWaitMillis" value="5000"/>
<property name="maxTotal" value="50"/>
<property name="maxIdle" value="5"/>
<property name="minIdle" value="1"/>
<property name="testWhileIdle" value="true"/>
<property name="timeBetweenEvictionRunsMillis" value="60000"/>
<property name="numTestsPerEvictionRun" value="10"/>
</bean>
</property>
</bean>
...
Ivy Dependencies include:
<!-- these are for redis httpsession -->
<dependency org="redis.clients" name="jedis" rev="2.8.1"/>
<dependency org="org.springframework.data" name="spring-data-redis" rev="1.7.4.RELEASE"/>
<dependency org="org.springframework.data" name="spring-data-keyvalue" rev="1.1.4.RELEASE"/>
<dependency org="org.springframework.session" name="spring-session" rev="1.2.2.RELEASE"/>
and wicket 7.5.0 and spring 4.2.8 stuff.
Anybody have any insights on what might be going on? Why after putting the session into redis (which it is showing up there, I see it (via redis-cli and keys and dump commands), most ajax calls are triggering full page redirects due to response headers from the ajax call including Ajax-Location?
This turned out to be caused by a custom org.apache.wicket.IPageManagerProvider implementation which needed to be replaced with a version that was compatible with redis.
Here is the code I have for redirecting unmapped requests to 404 page
#RequestMapping("/**")
public ModelAndView redirect() {
ModelAndView mv = new ModelAndView();
mv.setViewName("errorPage");
return mv;
}
The above code works fine but the problem is with the web resources like css and js files
also go inside this redirect method and it is not loading any of the files. But i already have this code in my dispatcher servlet,but spring controller is not recognizing this resources mapping.
<mvc:resources mapping="/resources/**" location="/WEB-INF/web-resources/" />
so i tried some regex expression in the request mapping to negate the resources url something like this
#RequestMapping("/{^(?!.*resources/**)}**")
public ModelAndView redirect() {
ModelAndView mv = new ModelAndView();
mv.setViewName("errorPage");
return mv;
}
But this is not working as expected ..So if anyone could help it would be great :)
I found the solution to handle 404 (Unmapped links), I used a SimpleUrlHandlerMapping to do this.
I added the below code to my dispatcher servlet .xml
<!-- Here all your resources like css,js will be mapped first -->
<mvc:resources mapping="/resources/**" location="/WEB-INF/web-resources/" />
<context:annotation-config />
<!-- Next is your request mappings from controllers -->
<context:component-scan base-package="com.xyz" />
<mvc:annotation-driven />
<!-- Atlast your error mapping -->
<bean id="errorUrlBean" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/**">errorController</prop>
</props>
</property>
</bean>
<bean id="errorController" class="com.xyz.controller.ErrorController">
</bean>
com.xyz.controller.ErrorController class
public class ErrorController extends AbstractController {
#Override
protected ModelAndView handleRequestInternal(HttpServletRequest arg0,
HttpServletResponse arg1) throws Exception {
// TODO Auto-generated method stub
ModelAndView mv = new ModelAndView();
mv.setViewName("errorPage");
return mv;
}
}
I found the below reasons
#RequestMapping("/**") uses RequestHandlerMapping and
<mvc:resources mapping="/resources/**" location="/WEB-INF/web-resources/" />
uses SimpleUrlHandlerMapping
RequestHandlerMapping takes presedence over SimpleUrlHandlerMapping, so that was the reason all resources request went inside the redirect method in my case.
So i just changed the #RequestMapping("/**") request to SimpleUrlHandlerMapping by configuring that as a bean as given above in my dipatcher servlet and mapped it at the last and it solved the problem.
Also add the below code to your web.xml
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/jsp/error.jsp</location>
</error-page>
Now this simple solution can be used to redirect all unmapped requests i.e) 404 errors to an error page :)
I have a RESTful service that has a single path parameter and a parameter that is unmarshalled from the request body. The request body parameter is XML for which I have an XSD. I've been trying to validate the XML payload against the XSD but to no avail. I've tried the following, as described here:
<jaxrs:server address="/"
serviceClass="my.endpoint.class">
<jaxrs:schemaLocations>
<jaxrs:schemaLocation>classpath:schema/myschema.xsd</jaxrs:schemaLocation>
</jaxrs:schemaLocations>
</jaxrs:server>
The schemas are being found (there are no errors at least) but what I expect to be an invalid payload is not throwing an exception. Parameters that don't match the XSD contents are coming through as null. It may not be relevant but my auto-generated payload class has three attributes, some of which are required.
I've had a brief go at creating a MessageBodyReader, as described here but I think I'm having scope issues and my schema object is not available when readFrom is called.
Any help or suggestions would be gratefully appreciated!
Turns out the servlet I was using wasn't accepting the jaxrs configuration shown above. I changed from using this:
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
to this:
<servlet-class>
org.apache.cxf.transport.servlet.CXFServlet
</servlet-class>
<init-param>
<param-name>config-location</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</init-param>
Snippet of my applicationContext.xml
<jaxrs:server address="/">
<jaxrs:schemaLocations>
<jaxrs:schemaLocation>classpath:schema/myschema1.xsd</jaxrs:schemaLocation>
<jaxrs:schemaLocation>schema/myschema2.xsd</jaxrs:schemaLocation>
<jaxrs:schemaLocation>schema/myschema3.xsd</jaxrs:schemaLocation>
</jaxrs:schemaLocations>
<jaxrs:serviceBeans>
<bean class="my.package.endPoint1" />
<bean class="my.package.endPoint2" />
</jaxrs:serviceBeans>
<jaxrs:features>
<cxf:logging />
</jaxrs:features>
</jaxrs:server>
Schema references are from the resources directory, adjacent to WEB-INF.
I'm just starting with Spring MVC trying to create a new project, and came accross an issue for which no manual or tutorial seems to help...
I have set up a simple application with no logic, just trying to get Spring configured properly. The controller just returns the name of a view to be displayed, but the view resolver is not rendering the jsp, and returning a 404 error....
Any help is greatly appreciated.
My web.xml is:
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<servlet>
<servlet-name>openstats</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>openstats</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<display-name>OpenStats API Server</display-name>
</web-app>
An my openstats-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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-2.5.xsd">
<context:component-scan base-package="org.openstats.api.controller"/>
<!-- Enable to request mappings PER METHOD -->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
<!-- Enable annotated POJO #Controller -->
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
<!-- Define the view resolver to use jsp files within the jsp folder -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass"><value>org.springframework.web.servlet.view.JstlView</value></property>
<property name="prefix"><value>/jsp/</value></property>
<property name="suffix"><value>.jsp</value></property>
</bean>
</beans>
The controller itself has no logic whatsoever, it's simply:
#Controller
public class ProductController {
#RequestMapping(value = "/products.do", method = RequestMethod.GET)
public ModelAndView listProducts(HttpServletRequest request) {
ModelAndView model = new ModelAndView("index");
return model;
}
}
The controller is reached, the issue is when attempting to render...
I set up log4j in debug, and this is part of what I get:
02:08:19,702 DEBUG
DispatcherServlet:1094 - Testing handler adapter
[org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter#397b6074]
02:08:19,803 DEBUG
HandlerMethodInvoker:134 - Invoking
request handler method: public org.springframework.web.servlet.ModelAndView
org.openstats.api.controller.ProductController.listProducts(javax.servlet.http.HttpServletRequest)
02:08:19,833 DEBUG
DefaultListableBeanFactory:1367 -
Invoking afterPropertiesSet() on bean
with name 'index' 02:08:19,876 DEBUG
InternalResourceViewResolver:81 -
Cached view [index] 02:08:19,877 DEBUG
DispatcherServlet:1181 - Rendering
view
[org.springframework.web.servlet.view.JstlView:
name 'index'; URL [/jsp/index.jsp]] in
DispatcherServlet with name
'openstats' 02:08:19,877 DEBUG
JstlView:240 - Rendering view with
name 'index' with model {} and static
attributes {} 02:08:19,923 DEBUG
JstlView:234 - Forwarding to resource
[/jsp/index.jsp] in
InternalResourceView 'index'
02:08:19,926 DEBUG
DispatcherServlet:955 -
DispatcherServlet with name
'openstats' determining Last-Modified
value for [/api-server/jsp/index.jsp]
02:08:19,927 DEBUG
DispatcherServlet:1054 - Testing
handler map
[org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping#440c4cee]
in DispatcherServlet with name
'openstats' 02:08:19,928 DEBUG
DefaultAnnotationHandlerMapping:179 -
No handler mapping found for
[/jsp/index.jsp] 02:08:19,929 DEBUG
DispatcherServlet:962 - No handler
found in getLastModified 02:08:19,937
DEBUG DispatcherServlet:781 -
DispatcherServlet with name
'openstats' processing request for
[/api-server/jsp/index.jsp]
02:08:19,938 DEBUG
DispatcherServlet:843 - Bound request
context to thread: GET
/api-server/products.do HTTP/1.1
My jsp folder is right within "webapp" and the index.jsp file exists.
Thanks in advance.
I do have the same problem with Spring 3.x.
Any progress so far?
EDIT:
I figured it out myself :-) I used the following servletmapping:
<servlet-mapping>
<servlet-name>spring-frontcontroller</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
Editing the url-pattern to e.g. *.do fixes the problem of not rendering the JSP.
But this leaves the question how this is possible with your url-pattern.
Does your web.xml define index.jsp in the welcome-file-list, if so it may be getting overridden. Try changing the jsp name to products.jsp.
e.g.
#Controller
public class ProductController {
#RequestMapping(value = "/products.do", method = RequestMethod.GET)
public String handleRequest() {
return "products";
}
}
doesn't your .jsp and your .do conflict? since a file cannot end with both .jsp and .do .. therefore it will never resolve... so you should get rid of .jsp or change your url pattern to /*
Changing
import org.springframework.web.servlet.ModelAndView;
by
import org.springframework.web.portlet.ModelAndView;
Works in my case.
I'd like to introduce Spring MVC to an application that has up till now used simple direct access to JSP files i.e www.example.com/login.jsp which contains the business logic and presentation details.
I'd like to strip out the business logic and keep only the presentation in the JSP. To do this, I've moved the jsp file from webapp/login.jsp to webapp/WEB-INF/jsp/login.jsp and then mapped all urls with the pattern *.jsp to Spring's DispatchServlet which goes to a Controller and then a view which (should) forward to /WEB-INF/jsp/login.jsp.
The web.xml looks like this:
<servlet>
<servlet-name>springapp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springapp</servlet-name>
<url-pattern>*.jsp</url-pattern>
</servlet-mapping>
springapp-context.xml
<bean id="urlFilenameViewController" class="org.springframework.web.servlet.mvc.UrlFilenameViewController"/>
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<value>
/*.jsp=urlFilenameViewController
</value>
</property>
</bean>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"></property>
</bean>
However, the fundamental problem with this is that I'm mapping external URL requests ending in .jsp to internal web requests that also map to .jsp. The internal .jsp request then goes through the DispatchServlet for a second time which returns an error as it cannot find anything to handle the request:
WARN - DispatcherServlet.noHandlerFound(1077) | No mapping found for HTTP request with URI [/myapp/WEB-INF/jsp/login.jsp] in DispatcherServlet with name 'springapp'
Given that I cannot change the external URLs, is there a way to get round this issue when mapping external file types to the same internal file type?
We address this in our application by using a different pattern for request URLs (*.htm instead of *.jsp). This avoids the problem and it is good practice anyway because there may not be a 1-to-1 relationship between a URL and a JSP.
I suggest you:
map Spring MVC requests to a different pattern ( e.g *.do);
use a UrlRewriteFilter or your application server's url rewrite functionality to map *.jsp calls to *.do;
I'm not sure how to tell Spring to ignore its internal requests for JSP files, but intercept other JSP requests. Why don't you keep your old jsps, but just have them forward to a controller. E.g. "page.jsp":
<%# taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<c:redirect url="/page.htm" />
This way, you can keep your old URLs intact, but the only function is to redirect to the controllers.