How do I implicitly map folder views without breaking resource mappings? - java

I am attempting to setup a very simple base configuration for a spring web application that can handle the following cases:
map resource root requests to /index, eg / maps to /index, /resource/ maps to /resource/index
map /static/** to /static/ (this is a resources view - css,js,images)
handle some specific request paths with controller mappings
map all other requests to a url based view, eg /resource/page maps to /WEB-INF/views/resource/page.jsp
In my current configuration what I have is:
<mvc:annotation-driven/>
<mvc:default-servlet-handler/>
<mvc:resources mapping="/static/**" location="/static/"/>
<mvc:view-controller path="/" view-name="index"/>
which works for static resources and root requests (/) but not for resource based root requests (/resource/). When attempting to handle all other requests (**/) I break handling of the static resources.
<mvc:view-controller path="**/*" />
Is there a way to do all of these things at the same time? It doesn't need to be an xml only solution, I'm fine with a code configuration or hybrid solution.

I have managed to find a solution by implementing my own Controller and Config.
view-servlet.xml
<mvc:resources mapping="/static/**" location="/static/"/>
<context:component-scan base-package="com.example.web.view"/>
com.example.web.view.ViewConfig
#Configuration
public class ViewConfig {
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver vr = new InternalResourceViewResolver();
vr.setPrefix("/WEB-INF/views/");
vr.setSuffix(".jsp");
return vr;
}
#Bean RequestMappingHandlerMapping requestMappingHandlerMapping(){
return new RequestMappingHandlerMapping();
}
#Bean RequestMappingHandlerAdapter requestMappingHandlerAdapter(){
RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
adapter.setOrder(2); //process after mvc:resources
return adapter;
}
}
com.example.web.view.ViewController
#Controller
public class ViewController {
#RequestMapping(value = "**/")
public String all(HttpServletRequest request) {
String path = request.getRequestURI().replace(request.getContextPath(),"");
return path.endsWith("/") ? path +"index" : path;
}
}
The magic was with setting the order value on the adaptor in ViewConfig. Spring creates a default mapping handler that all RequestMappings found by component-scan are added to. The reason why my initial setup failed was that the ordering of this handler is to match before the one registered by mvc:resources. Creating my own adaptor and setting its order to be processed after mvc:resources allowed both to work.
Information regarding this can found in the spring documentation 19.9.1 Setting up the dispatcher for annotation support.
Of course the ViewConfig can also be implemented purely in xml:
<mvc:resources mapping="/static/**" location="/static/"/>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="order" value="2"/>
</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/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
<context:component-scan base-package="com.example.web.view"/>
Update: Changed to RequestMappingHandlerMapping and RequestMappingHandlerAdapter from DefaultAnnotationHandlerMapping and AnnotationMethodHandlerAdapter to reflect changes made in Spring 3.1.x and above.

Related

Converting Spring XML to JavaConfig with Variable and Resource Import

I have a spring boot application where I am trying to convert the following spring xml config to java config:
<bean id="pageDAO" factory-bean="springWSDaoFactory"
factory-method="createPageDAO" lazy-init="true">
<constructor-arg type="java.lang.String" value="${cds.host}" />
<constructor-arg type="java.lang.Integer" value="${cds.port}" />
</bean>
<!-- CoreApi + plugins configuration -->
<import resource="classpath:coreAPI_SpringWSContext.xml"/>
<bean name="springWSDaoFactory" class="com.blan.torque.dao.springws.SpringWSDAOFactory" lazy-init="true">
<property name="serviceVersion" value="${service.version}"/>
<property name="securityKey" value="${service.key}"/>
</bean>
Here's what I have for javaconfig so far:
#Bean
public PageDAO pageDAO() {
return springWSDAOFactory().createPageDAO(null, null);
}
#Bean
public SpringWSDAOFactory springWSDAOFactory() {
SpringWSDAOFactory springWSDAOFactory = new SpringWSDAOFactory();
springWSDAOFactory.setServiceVersion(null);
springWSDAOFactory.setSecurityKey(null);
return springWSDAOFactory;
}
I have no idea how to implement <import resource...../> in Java let alone import the variables like ${cds.host}. I've put null everywhere as place holders. But any ideas on how to do this with annotations?
In your configuration class you can use the annotation #ImportResource instead of <import resource... />.
To read Strings from a property file, try to declare an String using #Value and the use the previously declared string.
For example:
#Value("${service.version}")
private String serviceVersion;
Then using it as parameter
springWSDAOFactory.setServiceVersion(this.serviceVersion);
I hope have helped you.

Unable to instantiate Spring MVC Controller

I am new to Spring, and have a simple problem.
I have written a simple controller with a simple constructor, annotated as shown below.
#Controller
public class LoginController
{
private LoginService loginService;
#Inject
public LoginController(LoginService loginService)
{
System.out.println("LoginController constructor - initializing login service");
this.loginService = loginService;
}
When I try to access my web app in a browser, I get the following error:
No default constructor found;
When I replace the #Inject annotation with the #Autowired annotation, it works. Can anyone explain why? Everything I've read says that have identical behavior. I'm using Tomcat 6.0.43.
Also, below is my spring dispatcher servlet xml:
<mvc:resources mapping="/resources/**" location="/resources/" />
<mvc:annotation-driven/>
<context:component-scan base-package="com.rockwell_collins.webquery"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
You must be missing the JavaEE Inject API from your runtime classpath. Spring will only look for #Inject if the class is found in the classpath.
You can get it from Maven here and add it to your runtime classpath (manually or through dependencies).

Spring MVC. Define custom HttpMessageConverter over XML configuration file

After half a day googling I have managed to register a custom HttpMessageConverter over the configuration class. Like this:
#Configuration
public class WebConfig extends WebMvcConfigurationSupport {
#Bean
#Override
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
RequestMappingHandlerAdapter handlerAdapter = super.requestMappingHandlerAdapter();
handlerAdapter.getMessageConverters().add(0, new CustomConverter());
return handlerAdapter;
}
}
Is there a way to do it using XML configuration file?
Here is the answer for Spring 3.2:
<mvc:annotation-driven >
<mvc:message-converters register-defaults="false">
<bean class="me.MyCustomMessageConverter"/>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="writeAcceptCharset" value="false"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
Do not forget to define the mvc namespace: http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
I did not find, how to add the own converter on the top on the converters list. It is possible to redefine the converters stack using register-defaults attribute.
Standard stack is defined in the constructor of this Spring class RequestMappingHandlerAdapter. One can copy needed converters from there.

Spring MVC dynamic view + handling non existing views

I'm going to implement a Spring MVC controller which uses dynamic view passed as a parameter to view:
#Controller
#RequestMapping("/page")
public class PageController {
#RequestMapping(value = "/{page}", method = {RequestMethod.GET})
public ModelAndView page(#PathVariable("page")String page) {
System.out.println("page = " + page);
return new ModelAndView(page);
}
}
Views are resolved by UrlBasedViewResolver:
<bean id="viewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
<property name="cache" value="false"/>
</bean>
Is there any way to return a default view when non-existing view requested? Maybe to do check if there's no view exists, then return new ModelAndView('requested_page_not_found')?
Not with UrlBasedViewResolver and friends. Once the chain has reached it, you're committed -- if the view doesn't exist, you're going to be directed to whatever 404 page your container has configured.
Note: When chaining ViewResolvers, a UrlBasedViewResolver always needs to be last, as it will attempt to resolve any view name, no matter whether the underlying resource actually exists.

Using VelocityView or plain text in Controller response?

I'm trying to return content other than json in my Controller, but I can't seem to get it to work. Ideally, I'd like to return a rendered velocity template as plain text or html.
This is what I have in my controller:
#RequestMapping( value = "time", headers = "Accept=*/*", method = RequestMethod.GET )
public #ResponseBody
Date getCurrentTime( HttpServletRequest request, HttpServletResponse response ) {
response.setContentType( "text/plain" );
return new Date();
}
And this is in my springmvc-servlet.xml (I know this is not right...but I'm a bit lost here):
<context:component-scan base-package="com.paml.alerter.controller" />
<!-- Configures the #Controller programming model -->
<mvc:annotation-driven />
<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" />
</bean>
<!-- This bean sets up the Velocity environment for us based on a root path
for templates. Optionally, a properties file can be specified for more control
over the Velocity environment, but the defaults are pretty sane for file
based template loading. -->
<bean id="velocityConfig"
class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
<property name="resourceLoaderPath" value="/WEB-INF/velocity/" />
</bean>
Does anyone know how to set this up right?
TIA
If your method is annotated with #ResponseBody, then the Spring MVC view layer will be bypassed entirely.
If you're not interested in JSON output, then #ResponseBody is inappropriate - just remove it, and your Velocity views will be used.
If you need to switch between JSON and some other View layer, then you should consider removing #ResponseBody and using ContentNegotiatingViewResolver instead. See the Spring docs for how to set this up.

Categories

Resources