When I update the bean:
<bean id="viewResolver" class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
<property name="cache" value="true"/>
<property name="prefix" value=""/>
<property name="suffix" value=".vm"/>
<property name="toolboxConfigLocation" value="tools.xml" />
</bean>
With the tools.xml path for Velocity Tools, I get:
Caused by:
java.lang.ClassNotFoundException: org.apache.velocity.tools.view.ToolboxManager
I've tried plugging in tools version 2 and 1.4, neither have this package structure. Did I miss something obvious? What version of Velocity Tools is the Spring/Velocity component supporting?
I use a little bit simpler of a way. I also cannot force Velocity Tools to work due to lack of configuration documentation and examples. I just get the velocity-generic-tools-2.0.jar and make a little change in my view resolver:
<bean id="velocityViewResolver"
class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
<property name="order" value="1"/>
<property name="prefix" value="/WEB-INF/vm/"/>
<property name="suffix" value=".vm"/>
<property name="exposeSpringMacroHelpers" value="true"/>
<property name="contentType" value="text/html;charset=UTF-8"/>
<property name="attributesMap">
<map>
<!--Velocity Escape Tool-->
<entry key="esc"><bean class="org.apache.velocity.tools.generic.EscapeTool"/></entry>
</map>
</property>
</bean>
Then, in the velocity template you can use it as usual $esc.html($htmlCodeVar). This solution is very simple, without tons of configs and overriding spring classes.
Spring has very outdated Velocity support by default. I extend VelocityView class from Spring and override createVelocityContext method where I initialize Tools myself. Here is how it looks at the end.
With 3.0.5 I used a similar class to what serg posted, with the only modification being to use the updated classes which spring did not use (tail through VelocityToolboxView -> ServletToolboxManager (used in the createVelocityContext we have overridden) That is the class which is deprecated, so I modified the initVelocityToolContext in serg's answer to be:
private ToolContext getToolContext() throws IllegalStateException, IOException {
if (toolContext == null) {
XmlFactoryConfiguration factoryConfiguration = new XmlFactoryConfiguration("Default Tools");
factoryConfiguration.read(getServletContext().getResourceAsStream(getToolboxConfigLocation()));
ToolboxFactory factory = factoryConfiguration.createFactory();
factory.configure(factoryConfiguration);
toolContext = new ToolContext();
for (String scope : Scope.values()) {
toolContext.addToolbox(factory.createToolbox(scope));
}
}
return toolContext;
}
I also had to change the line which created the VelocityContext to call this method obviously.
My bean now looks like:
<bean id="viewResolver" class="org.springframework.web.servlet.view.velocity.VelocityLayoutViewResolver"
p:cache="false"
p:prefix=""
p:suffix=".vm"
p:layoutUrl="templates/main.vm"
p:toolboxConfigLocation="/WEB-INF/velocity/velocity-toolbox.xml"
p:viewClass="path.to.overriden.class.VelocityToolsLayoutView"
/>
Inspired by answers from Scott and serg, here's another way to do it that does not require XML: http://squirrel.pl/blog/2012/07/13/spring-velocity-tools-no-xml/
public class MyVelocityToolboxView extends VelocityView {
#Override
protected Context createVelocityContext(Map<String, Object> model,
HttpServletRequest request, HttpServletResponse response) {
ViewToolContext context = new ViewToolContext(getVelocityEngine(),
request, response, getServletContext());
ToolboxFactory factory = new ToolboxFactory();
factory.configure(ConfigurationUtils.getVelocityView());
for (String scope : Scope.values()) {
context.addToolbox(factory.createToolbox(scope));
}
if (model != null) {
for (Map.Entry<String, Object> entry : (Set<Map.Entry<String, Object>>) model
.entrySet()) {
context.put(entry.getKey(), entry.getValue());
}
}
return context;
}
}
Inspired by all the answers above, this is my implementation of VelocityLayoutView for spring and velocity-tools 2.0, added some improvement!
public class VelocityToolsView extends VelocityLayoutView {
private static final String TOOL_MANAGER_KEY = ViewToolManager.class.getName();
#Override
protected Context createVelocityContext(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) {
ServletContext application = getServletContext();
// use a shared instance of ViewToolManager
ViewToolManager toolManager = (ViewToolManager)application.getAttribute(TOOL_MANAGER_KEY);
if(toolManager == null) {
toolManager = createToolManager(getVelocityEngine(), getToolboxConfigLocation(), application);
application.setAttribute(TOOL_MANAGER_KEY, toolManager);
}
ViewToolContext toolContext = toolManager.createContext(request, response);
if(model != null) { toolContext.putAll(model); }
return toolContext;
}
private ViewToolManager createToolManager(VelocityEngine velocity, String toolFile, ServletContext application) {
ViewToolManager toolManager = new ViewToolManager(application, false, false);
toolManager.setVelocityEngine(velocity);
// generic & view tools config
FactoryConfiguration config = ConfigurationUtils.getVelocityView();
// user defined tools config
if(toolFile != null) {
FactoryConfiguration userConfig = ConfigurationUtils.load(application.getRealPath(toolFile));
config.addConfiguration(userConfig);
}
toolManager.configure(config);
return toolManager;
}
}
I found that this variation on #serg's technique worked for me.
Related
I'm trying to do a RequestMapping for URL from the Resources file to be variable according to the current Locale
I tried to use the PlaceHolders but i know it should load from Properties files . in addition to i have to load it as Bean during the run time thus it will load one time only with the default Locale so even if the changed the Locale , it will keep loading from the default Locale > en_US
Any Ideas ?
My Tries :
public class CustomPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
setProperties(convertResourceBundleToProperties(ResourceBundle.getBundle("urls", LocaleContextHolder.getLocale())));
super.postProcessBeanFactory(beanFactory);
}
}
and calling at in a Bean :
#Bean
public CustomPropertyPlaceholderConfigurer CustomPropertyPlaceholderConfigurer(){
return new CustomPropertyPlaceholderConfigurer();
}
Resources urls_ab.properties:
url.controller1=test
Controller :
#RequestMapping(value = "/${url.controller1}", method = RequestMethod.GET)
public String dd(ModelMap model){
return "__front_container";
}
When you make a change to your properties files that back your PropertyPlaceholderConfigurer, you will need to 'refresh' your application for changes to take effect. If you use a ConfigurableApplicationContext as your context, then you may call refresh on your context. The challenge is that in a web application, you will depend on your web.xml and not directly on the context object so refreshing to load the new/updated properties would require an application restart...or going through many unnecessary hoops. Consider the below which is an example within a Spring Webflow application. The locale is updated via the use of an interceptor. :
public class MyLocaleChangeInterceptor extends org.springframework.web.servlet.i18n.LocaleChangeInterceptor {
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
Locale locale = (Locale) WebUtils.getSessionAttribute(request, LOCALE_SESSION_ATTRIBUTE_NAME);
if (locale != null) {
try {
response.setLocale(locale);
} catch (Exception ex) {
response.setLocale(Locale.ENGLISH);
}
} else {
response.setLocale(Locale.ENGLISH);
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
}
super.postHandle(request, response, handler, modelAndView);
}
}
/** https://gist.github.com/jkuipers/3537965 Spring LocaleResolver that uses cookies but falls back to the HTTP Session when cookies are disabled*/
public class MyCookieLocaleResolver extends CookieLocaleResolver {
private SessionLocaleResolver sessionLocaleResolver = new SessionLocaleResolver();
#Override
protected Locale determineDefaultLocale(HttpServletRequest request) {
return sessionLocaleResolver.resolveLocale(request);
}
#Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
if (locale != null) {
try {
response.setLocale(locale);
} catch (Exception ex) {
response.setLocale(Locale.ENGLISH);
}
} else {
response.setLocale(Locale.ENGLISH);
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
}
super.setLocale(request, response, locale);
sessionLocaleResolver.setLocale(request, response, locale);
}
#Override
public void setDefaultLocale(Locale defaultLocale) {
sessionLocaleResolver.setDefaultLocale(defaultLocale);
}
}
<!--Then the XML:-->
<bean id="localeChangeInterceptor" class="MyLocaleChangeInterceptor">
<property name="paramName" value="lang"/>
</bean>
<!-- Saves a locale change using a cookie -->
<bean id="localeResolver" class="MyCookieLocaleResolver" >
<property name="defaultLocale" value="en" />
</bean>
<!--Then Spring-webflow specific XML settings:-->
<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping">
<property name="order" value="2"/>
<property name="flowRegistry" ref="flowRegistry" />
<property name="interceptors" >
<list>
<ref local="localeChangeInterceptor" />
</list>
</property>
<property name="defaultHandler">
<bean class="org.springframework.web.servlet.mvc.UrlFilenameViewController" />
</property>
</bean>
If using Spring MVC (without spring webflow), see here for a brilliant solution: Spring MVC LocaleChangeInterceptor annotation based doesn't work
MyKong also provides a good solution: http://www.mkyong.com/spring-mvc/spring-mvc-internationalization-example/
I am trying to find a best practice solution for my situation. I have two different sets of urls in my webapp that need to be secured/authenticated against two different sets of user bases.
For example, the /foo/* urls are to be accessible only to a certain set of users based on username/pwd tokens. And another set /bar/* urls are to be accessible only to a set of users (stored in a different database table from the first set) via username/pwd token mechanism.
I want to understand how to structure the shiro filter(s) in my web.xml and the corresponding shiro filter bean definition in my applicationContext.xml. Should I be using two different shiro filters (one for each url pattern), mapping to two different bean in the context file, where each bean has its own security manager that is configured with its own realm (each realm responsible for authenticating against the corresponding user base table).
Here I am following this two links:
multi-tenancy-in-shiro
Multiple-security-managers...
but I am getting errors in my application. Here I am pasting some sample code.
ApplicationContext-shiro.xml
<!-- Single realm app (realm configured next, below). If you have multiple
realms, use the 'realms' property instead. -->
<property name="realms">
<util:list>
<ref bean="adminAuthRealm" />
<ref bean="vendorAuthRealm" />
</util:list>
</property>
<!-- <property name="authenticator">
<bean class="com.yatra.mp.security.MultiLoginAuthenticator"/>
</property> -->
<property name="cacheManager" ref="cacheManager" />
<!-- Uncomment this next property if you want heterogenous session access
or clusterable/distributable sessions. The default value is 'http' which
uses the Servlet container's HttpSession as the underlying Session implementation. -->
</bean>`<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="unauthorizedUrl" value="/permissionDenied.do" />
<!-- The 'filters' property is usually not necessary unless performing
an override, which we want to do here (make authc point to a PassthruAuthenticationFilter
instead of the default FormAuthenticationFilter: -->
<property name="filters">
<util:map>
<entry key="adminAuthc" value-ref="adminAuthc" />
<entry key="vendorAuthc" value-ref="vendorAuthc" />
<entry key="adminUser" value-ref="adminUser" />
</util:map>
</property>
<property name="filterChainDefinitions">
<value>
/admin/welcome.do = anon
/vendor/welcome.do = anon
/vendor/signup.do = anon
/admin/signup.do = anon
/assets/** = anon
/admin/** = adminAuthc
/vendor/** = vendorAuthc
</value>
</property>
</bean>
<bean id="adminAuthc"
class="org.apache.shiro.web.filter.authc.PassThruAuthenticationFilter">
<property name="loginUrl" value="/admin/login.do" />
<property name="successUrl" value="/admin/home.do" />
</bean>
<bean id="vendorAuthc"
class="org.apache.shiro.web.filter.authc.PassThruAuthenticationFilter">
<property name="loginUrl" value="/vendor/login.do" />
<property name="successUrl" value="/vendor/home.do" />
</bean>
<bean id="adminUser" class="org.apache.shiro.web.filter.authc.UserFilter">
<property name="loginUrl" value="/admin/login.do" />
</bean>
<bean id="vendorUser" class="org.apache.shiro.web.filter.authc.UserFilter">
<property name="loginUrl" value="/vendor/login.do" />
</bean>`
MultiLoginAuthenticator.java
public class MultiLoginAuthenticator extends ModularRealmAuthenticator {
#Override
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
assertRealmsConfigured(); <------- Here I am getting null getRealms(). So I am getting exceptions
MultiLoginAuthenticationToken mlat = null;
Realm loginRealm = null;
if (!(authenticationToken instanceof MultiLoginAuthenticationToken)) {
throw new AuthenticationException("Unrecognized token , not a typeof MultiLoginAuthenticationToken ");
} else {
mlat = (MultiLoginAuthenticationToken) authenticationToken;
loginRealm = lookupRealm(mlat.getRealmName());
}
return doSingleRealmAuthentication(loginRealm, mlat);
}
protected Realm lookupRealm(String realmName) throws AuthenticationException {
Collection<Realm> realms = getRealms();
for (Realm realm : realms) {
if (realm.getName().equalsIgnoreCase(realmName)) {
return realm;
}
}
throw new AuthenticationException("No realm configured for Client " + realmName);
}
}
while i am configuring MultiLoginAuthenticator, i am getting null in getReamls() method. So it is throwing IllegalStateException.
If I remove this Authenticator configuration from applicationContext.xml file, then it is able to login correctly but it is calling different Realm for "doGetAuthorizationInfo" method. So It is not assigning any role and permission to that current Subject.
This is my Realm file (both Realms are same. Difference is both are calling different datasource.)
#Override
public boolean supports(AuthenticationToken token) {
if (token instanceof MultiLoginAuthenticationToken) {
return ((MultiLoginAuthenticationToken)token).getRealmName().equalsIgnoreCase("VendorAuthRealm");
}
return false;
}
#Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
logger.debug("authorization info.....");
logger.debug("realm size is : {}", principals.fromRealm(getName()).size());
logger.debug("realm name is : {}", principals.fromRealm(getName()));
int userId = (Integer) principals.fromRealm(getName()).iterator().next();
VendorUser vendorUser = vendorUserService.getVendorUser(userId);
if(vendorUser != null) {
logger.debug("vendor user first name is : {}", vendorUser.getFirstName());
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
if(vendorUser.getVendorProducts() != null){
logger.debug("vendor users products and rate types...");
info.addRoles(VendorYatraUtil.getProductSet(vendorUser.getVendorProducts()));
info.addStringPermissions(VendorYatraUtil.getCarRateTypeSet(vendorUser.getVendorCarRateTypes()));
}
return info;
}
return null;
}
#Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken authcToken) throws AuthenticationException {
MultiLoginAuthenticationToken token = (MultiLoginAuthenticationToken) authcToken;
logger.debug("username is : {}", token.getUsername());
if(vendorUserService.findVendorUser(token.getUsername()) != null){
VendorUser vendorUser = vendorUserService.findVendorUser(token.getUsername());
if (vendorUser != null){
return new SimpleAuthenticationInfo(vendorUser.getId(),
vendorUser.getPassword(), getName());
}
}
return null;
}
Thanks,
Ankit
I have solved this problem now.
Here in applicationContext.xml file is having order misplace for Custom authenticator class. I wrote authenticator property first and then define Realms.
It is authenticating successfully...
guys,
At first, I show you the part of applicationContext.xml on ingerating HttpServer.
And I read the construcotr arguments from the solr.properties.
<!-- solr configuration-->
<bean id="solrHttpServer" class="com.augmentum.ksp.solr.server.SolrHttpServer" scope="prototype">
<constructor-arg value="${solr.url}" />
<constructor-arg value="${solr.socketTimeOut}" />
<constructor-arg value="${solr.connTimeOut}" />
<constructor-arg value="${solr.maxConnDefault}" />
<constructor-arg value="${solr.maxConnTotal}" />
<constructor-arg value="${solr.maxRetries}" />
<constructor-arg value="${solr.allowCompression}" />
<constructor-arg value="${solr.followRedirects}" />
</bean>
And following is our query method in DAO layer, we have many methods in service layer use the "queryCount" method at the same time.
#Repository
public class SolrBaseDaoImpl implements SolrBaseDao {
#Autowired
private SolrHttpServer solrHttpServer;
#Override
public synchronized int queryCount(String coreName, String queryExpression, Set<String> filterQuerys)
throws SolrServerException, IOException {
if (null == queryExpression || "".equals(queryExpression)) {
return 0;
}
String baseUrl = SolrUtil.getSolrBaseURL(solrHttpServer.getBaseURL());
solrHttpServer.setBaseURL(baseUrl + "/" + coreName);
SolrQuery query = new SolrQuery();
query.setQuery(queryExpression);
// set filter query
if (null != filterQuerys) {
for (String fq : filterQuerys) {
query.addFilterQuery(fq);
}
}
query.setStart(0);
query.setRows(Integer.MAX_VALUE);
QueryResponse rsp = solrHttpServer.query(query);
return rsp.getResults().size();
}
}
Thus, a problem occurred. Reported that "SolrRemoteException:undefined field *".
We think that the source of the error is that there is a lot of request access the "queryCount" method **at the same time, thus we add a "synchronized" decorateor to the method, so that the problem nerver occurred any more.
But, as we all know, when there is a lot of request access the method one by one, which perfom very slowly. That is terrible!
Could you tell me your soultion to the problem ? or the error is not caused by the thread synchronization?
I am using spring 3.0 in my webapplication. I've got recently a problem in my application. I am using <mvc:annotation-drive/> tag in my spring-servlet.xml file but due to a requirement I've to remove this and place XML configuration instead.
But now my problem is it generates json output with quoted field names like if I return Boolean.TRUE I got "true" in the output. I want just true without quotes.
here is my XML configuration
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean" />
<bean id="pathMatcher" class="net.hrms.web.filters.CaseInsensitivePathMatcher" />
<bean name="handlerAdapter" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="webBindingInitializer">
<bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<property name="conversionService" ref="conversionService"></property>
</bean>
</property>
<property name="messageConverters">
<list>
<ref bean="byteArrayConverter"/>
<ref bean="jaxbConverter"/>
<ref bean="jsonHttpMessageConverter"/>
<bean class="org.springframework.http.converter.StringHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter"></bean>
</list>
</property>
</bean>
<bean name="byteArrayConverter" class="org.springframework.http.converter.ByteArrayHttpMessageConverter"></bean>
<bean name="jaxbConverter" class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter"></bean>
<bean name="handlerMapping" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="pathMatcher" ref="pathMatcher"></property>
</bean>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsps/"/>
<property name="suffix" value=".jsp"/>
</bean>
<bean id="jsonHttpMessageConverter"
class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="prefixJson" value="false"/>
<property name="supportedMediaTypes" value="application/json"/>
</bean>
any help would be much appreciable.
If you are using the FlexJSON plugin, you can create custom output for JSON. I am sure you can do that in Jackson too, but I have never done it. There is loads of examples on the FlexJSON site.
What happens if you return just the primitive value of true (or Boolean.TRUE.booleanValue() rather than the wrapped object version of Boolean.TRUE?
I believe that true and false values are hardcoded in Jackson library (writeBoolean is called even for the boxed booleans):
private final static byte[] TRUE_BYTES = { 't', 'r', 'u', 'e' };
#Override
public void writeBoolean(boolean state)
throws IOException, JsonGenerationException
{
_verifyValueWrite("write boolean value");
if ((_outputTail + 5) >= _outputEnd) {
_flushBuffer();
}
byte[] keyword = state ? TRUE_BYTES : FALSE_BYTES;
int len = keyword.length;
System.arraycopy(keyword, 0, _outputBuffer, _outputTail, len);
_outputTail += len;
}
so Jackson will never return double-quoted "true" in field values(if its behavior is not heavily overriden, for example with codecs).
So please check that MappingJacksonHttpMessageConverter methods are being invoked, and conversion is not performed somewhere else.
That is the default behavior, if you want your boolean values unquoted use a JAXB ContextResolver
Something like this
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import javax.xml.bind.JAXBContext;
import com.sun.jersey.api.json.JSONJAXBContext;
#Provider
public class JAXBContextResolver implements ContextResolver<JAXBContext> {
private JAXBContext context;
private Class[] types = {SomeClass.class}; //Add the classes processed by JAXB and exposing boolean properties
public JAXBContextResolver() throws Exception {
Map props = new HashMap<String, Object>();
props.put(JSONJAXBContext.JSON_NOTATION, JSONJAXBContext.JSONNotation.MAPPED);
props.put(JSONJAXBContext.JSON_ROOT_UNWRAPPING, Boolean.TRUE);
java.util.HashSet<String> eprops = new HashSet<String>();
eprops.add("someBooleanProperty"); //add properties you want unquoted
props.put(JSONJAXBContext.JSON_NON_STRINGS, eprops);
this.context = new JSONJAXBContext(types, props);
}
public JAXBContext getContext(Class<?> objectType) {
return (types[0].equals(objectType)) ? context : null;
}
}
I am trying to output some model data to a pdf using spring-mvc. It is not working and I was wondering if someone could offer some advice.
I have a spring-servlet.xml file that includes the following:
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="order" value="1"/>
<property name="prefix" value="/WEB-INF/view/"/>
<property name="suffix" value=".jsp"/>
</bean>
<bean id="xmlViewResolver" class="org.springframework.web.servlet.view.XmlViewResolver">
<property name="order" value="2"/>
<property name="location">
<value>/WEB-INF/spring-pdf-views.xml</value>
</property>
</bean>
In the spring-pdf-views.xml file I have this:
<bean id="MyPDF" class="com.example.MyPDFView"/>
This is my MyPDFView class:
public class MyPDFView extends AbstractPdfView {
#Override
protected void buildPdfDocument(Map<String, Object> model, Document document, PdfWriter writer,
HttpServletRequest request, HttpServletResponse response) throws Exception {
#SuppressWarnings("unchecked")
Map<String, String> data = (Map<String, String>) model.get("modelData");
Table table = new Table(2);
table.addCell("Date");
table.addCell("Name");
table.addCell(data.get("modelData.dateValue"));
table.addCell(data.get("modelData.nameValue"));
document.add(table);
}
}
Finally in my controller I have:
#RequestMapping(value="/pdfInformation", method=RequestMethod.POST)
public ModelAndView showPDF(ModelMap model, PDFInfo pdfInfo, BindingResult result) {
return new ModelAndView("MyPDF", model);
}
The problem I am seeing in the output is that it never gets to the xmlViewResolver. It is trying to render the MyPDF as a JSTL View. This is from my logs:
org.springframework.web.servlet.DispatcherServlet - Rendering view [org.springframework.web.servlet.view.JstlView: name 'MyPDF'; URL [/WEB-INF/view/MyPDF.jsp]] in DispatcherServlet with name 'spring'
What am I missing?
From the Javadoc for InternalResourceViewResolver:
Note: When chaining ViewResolvers, an InternalResourceViewResolver always needs to be last, as it will attempt to resolve any view name, no matter whether the underlying resource actually exists.
Swap the order of your resolvers.