First of all, I don't know how can config restful url request for spring webflow,
for example,how can I invoke my webflow when type address:
http://localhost/app/order/edit/1002
It's easy to write spring mvc controller to handle this,but in case of webflow, I don't know how to pass parameters.
Can anybody help me?
Thanks
Try reading request parameter like following.
It processes "http://example.com/message?messageId=3" but when rendering view, the URL changes to something like "http://example.com/message?execution=e1s1".
Flow code:
<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/webflow
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
<on-start>
<evaluate expression="FooWebFlowController.create(flowRequestContext)"
result="flowScope.fooModel"/>
</on-start>
<view-state id="view" model="fooModel" view="fooView">
</view-state>
</flow>
FooWebFlowController bean:
import org.springframework.webflow.execution.RequestContext;
#Component
public class FooWebFlowController {
#Autowired
private FooDAO fooDAO;
public Foo create(RequestContext requestContext) {
String messageId = requestContext.getRequestParameters().get("messageId")
Foo foo = fooDAO.findByMessagId(messageId);
return foo;
}
}
Is the RequestPathFlowExecutorArgumentHandler what you're looking for?
Flow executor argument handler that
extracts arguments from the request
path and exposes them in the URL path.
This allows for REST-style URLs to
launch flows in the general format:
http://${host}/${context
path}/${dispatcher path}/${flowId}
<bean id="flowController" class="org.springframework.webflow.executor.mvc.FlowController">
<property name="flowExecutor" ref="flowExecutor" />
<property name="argumentHandler">
<bean class="org.springframework.webflow.executor.support.RequestPathFlowExecutorArgumentHandler" />
</property>
</bean>
Related
I'm very new to Spring MVC and Java EE at all (I came from PHP+Zend2). My english is poor too. I use NetBeans.
My problem is that my custom converter does not work. Here's some code:
applicationContext.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"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<bean id="universalDAO" class="dao.UniversalDAO"/>
<bean id="sessionManager" class="utils.SessionManager"/>
<bean id="idToEntityConverterFactory" class="utils.IdToEntityConverterFactory">
<property name="dao" ref="universalDAO"/>
</bean>
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<ref bean="idToEntityConverterFactory" />
<bean id="temp" class="utils.TempConverter" />
</list>
</property>
</bean>
<bean name="universalService" class="service.UniversalService">
<property name="universalDAO" ref="universalDAO"/>
</bean>
<bean name="sessionApplicationService" class="service.SessionApplicationService">
<property name="universalDAO" ref="universalDAO"/>
<property name="sessionManager" ref="sessionManager"/>
</bean>
<bean name="systemUserApplicationService" class="service.SystemUserApplicationService">
<property name="universalDAO" ref="universalDAO"/>
</bean>
<aop:aspectj-autoproxy />
<bean id="loggerAspect" class="aspect.LoggerAspect"/>
</beans>
I also have tried version with:
class="org.springframework.format.support.FormattingConversionServiceFactoryBean"
IdToEntityConverterFactory is a ConverterFactory created with this tutorial but it is not important now. I wrote simpler one not to do mess.
TempConverter.java
package utils;
import entity.Role;
import org.springframework.core.convert.converter.Converter;
public class TempConverter implements Converter<String, Role> {
#Override
public Role convert(String id) {
return new Role();
}
}
Here is .jsp fragment:
<form:select path="${names[item.index]}" items="${valueOptions[names[item.index]]}" />
When I submit the form there appears an error:
Failed to convert property value of type java.lang.String[] to required type java.util.List for property roleList; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [entity.Role] for property roleList[0]: no matching editors or conversion strategy found
I found solution to similar problem here . It has something to do with
<mvc:annotation-driven>
but I don't use such tag anywhere in my application (should I?).
My question is how to make any converter work while binding form data to Java object.
EDIT:
I figured out some workaround. I have overriden initBinder method in my Controller:
#Override
protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
binder.setConversionService(conversionService);
}
conversionService had to be previously set in Controller of course:
private ConversionService conversionService;
//...
public void setConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
}
dispatcher-servlet.xml:
<bean class="controller.SystemUserFormController" p:applicationService-ref="systemUserApplicationService" p:sessionManager-ref="sessionManager" p:conversionService-ref="conversionService" />
It works now but it is kind of inconvenience because:
I have to add extra code p:conversionService-ref="conversionService" in every Controller I need converter to be used.
It works out-of-the-box in every toutorial I found on the internet but not for me. I am just curious what am I doing different.
Kindest regards!
Your questions:
I have to add extra code p:conversionService-ref="conversionService" in every Controller I need converter to be used.
You can use #Autowired to inject ConversionService.
You can implement common parent class for your controllers with #InitBinder
You can use abstract parent bean definition <bean abstract="true" ...>
It works out-of-the-box in every toutorial I found on the internet but not for me. I am just curious what am I doing different.
Just use <mvc:annotation-driven>. This easy-to-use configuration is there so that you don't need to configure stuff manually.
How to do it
You can implement WebBindingInitializer. This bean needs to be set up on handler adapter.
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="webBindingInitializer" ref="yourBindingInitializer" />
</bean>
However this approach is kind of painful if you are not already defining hanlder adapter yourself. When you define this bean it disables some DispatcherServlet's default behavior. So you might need to do a bit more than to define this bean.
Off-topic advice
Problem with Spring is that the internet is full of obsolete tutorials. Please use the official guide and reference app. Start using namespace (or even Java) config, autowiring, #Controller components and #RequestMapping.
I belive there is not such think as mvc:annotation-config. There are 2 other things:
context:annotation-config
mvc:annotation-driven
Please tell me if I am wrong
I have tried both and both doesn't work. Here's what what have I done:
Removed p:conversionService-ref="conversionService" from my Controller bean
Added #Autowired annotation to my setter
#Autowired
public void setConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
}
Added context:annotation-config/ (or mvc:annotation-driven/) to applicationContext.xml
Unfortunately setter has never been executed!
My source is here
Quote: "When Spring finds an #Autowired annotation used with setter methods, it tries to perform byType autowiring on the method."
I also have tried using setter with exactly the same type as bean class - still nothing.
I'm trying to return JSON when using the #RestController. I'm using Spring 4.1.
Here's the exception I'm getting when calling listrestsites.html using a GET request.
I have the fasterxml Jackson core and databind jars in my build path.
Output of the accept from #requestheader = accept: application/json, text/javascript, /; q=0.01
Any help is appreciated. Thank you,
[DEBUG,ExceptionHandlerExceptionResolver] Resolving exception from
handler [public java.util.List
com.amci.spring3.controller.SitesRestController.listRestSites(java.lang.String)]:
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not
find acceptable representation [DEBUG,DefaultListableBeanFactory]
Returning cached instance of singleton bean
'exceptionControllerAdvice' [DEBUG,ExceptionHandlerExceptionResolver]
Invoking #ExceptionHandler method: public
org.springframework.web.servlet.ModelAndView
Here's my Restcontroller class:
#RestController
public class SitesRestController {
#Autowired
private AssetService assetService;
#RequestMapping("/listrestsites.html")
public List<Asset> listRestSites(#RequestHeader(value="accept") String accept) {
System.out.println(getLogLevel());
System.out.println("accept: " + accept);
return assetService.findAssets();
}
}
Also, snippet from my spring.xml:
<property name="defaultViews">
<list>
<!-- JSON View -->
<bean
class="org.springframework.web.servlet.view.json.MappingJackson2JsonView">
</bean>
</list>
</property>
<property name="ignoreAcceptHeader" value="true" />
</bean>
Please make sure that you have the following in your Spring xml file:
<context:annotation-config/>
<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>
and all items of your POJO should have getters/setters. Hope it helps
Credit answer to this question
I have a bean as follows:
<bean id="myBean" class="MyBeanClass">
<constructor-arg value="\WEB-INF\myfile.dat"/>
</bean>
In the bean's contructor, I need to build the file's full path. To do that, I have to first find the app's root path first.
Thanks and regards.
Update
Per Michael-O's suggestion, here is my solution (so easy).
Spring bean:
<bean id="myBean" class="MyBeanClass">
<constructor-arg value="/myfile.dat"/> <!--under WEB-INF/classes-->
</bean>
Java:
public MyBeanClass(String path) throws Exception {
ClassPathResource file = new ClassPathResource(path);
lookup = new LookupService(file.getFile().getPath(), LookupService.GEOIP_MEMORY_CACHE);
}
Michael, thanks!!!
Use Spring's Resource class in your bean and spring will do the rest for you.
After seeing #curious1's edit, there is a better solution to his answer. Please do not use that. Go with this one:
beans.xml:
<!-- START: Improvement 2 -->
<context:annotation-config />
<bean id="service" class="LookupService">
<constructor-arg value="classpath:/myfile.dat"/> <!--under WEB-INF/classes-->
<constructor-arg>
<util:constant static-field="LookupService.GEOIP_MEMORY_CACHE"/>
</constructor-arg>
</bean>
<!-- END: Improvement 2 -->
<!-- Spring autowires here -->
<bean id="myBean" class="MyBeanClass" />
<!-- START: Improvement 1 -->
<bean id="myBean" class="MyBeanClass" />
<constructor-arg value="classpath:/myfile.dat"/> <!--under WEB-INF/classes-->
</bean>
<!-- END: Improvement 1 -->
Java:
public MyBeanClass(Resource path) throws Exception {
lookup = new LookupService(path.getInputStream(), LookupService.GEOIP_MEMORY_CACHE);
}
This is source-agnostic, does not rely on files and is the Spring way.
Edit 2: Rethinking my code, it can be even better:
public class MyBeanClass {
#Autowired
LookupService service;
}
and configure LookupService in your beans.xml.
Maybe you should consider using:
getClass().getClassLoader().getResourceAsStream()
inside constructor. This will use your classpath, so you "WEB-INF\myfile.dat", will be visible. Next think is use resource directory to put all resources in one directory (default: under root directory in WAR file)
I just created a small POC on Ibm sprin mvc portlet and it works fine. But I have a question that has been bothering me. In the tutorial it says we can have same DispatcherPortletor we can multiple DispatcherPortlet with its own applicationContext and handler mapping. I have something like this in my portlet.xml:
<description>HelloWorld Portlet using Spring MVC portlet 1</description>
<portlet-name>HelloSpringPortletMVC1</portlet-name>
<display-name>Hello World Spring Portlet MVC Framework Portlet1</display-name>
<portlet-class> org.springframework.web.portlet.DispatcherPortlet </portlet-class>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>VIEW</portlet-mode>
</supports>
<supported-locale>en</supported-locale>
<portlet-info>
<title> HelloSpringPortletMVC1 </title>
<short-title> HelloSpringPortletMVC1 </short-title>
<keywords> spring portlet </keywords>
</portlet-info>
<description>HelloWorld Portlet using Spring MVC portlet 2</description>
<portlet-name>HelloSpringPortletMVC2</portlet-name>
<display-name>Hello World Spring Portlet MVC Framework Portlet2</display-name>
<portlet-class> org.springframework.web.portlet.DispatcherPortlet </portlet-class>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>VIEW</portlet-mode>
</supports>
<supported-locale>en</supported-locale>
<portlet-info>
<title> HelloSpringPortletMVC2 </title>
<short-title> HelloSpringPortletMVC2 </short-title>
<keywords> spring portlet </keywords>
</portlet-info>
I am assuming since we have specified the portlet-class to be org.springframework.web.portlet.DispatcherPortlet, each time the portlet is rendered it will create a new instance of DispatcherPortlet, so how can we have just a single instance of DispatcherPortlet that will be used for all the portlets.
I am assuming since we have specified the portlet-class to be org.springframework.web.portlet.DispatcherPortlet, each time the portlet is rendered it will create a new instance of DispatcherPortlet, so how can we have just a single instance of DispatcherPortlet that will be used for all the portlets
There is no need create multiple instances of DispatcherPortlet, you already defined DispatcherPortlet that will be route all your portlet requests to Controllers, like in Spring MVC application DispatcherServlet forwarding your request to Controllers. All that you need is create Controller class(classes) and describe spring configuration
part of portlet.xml
<portlet>
<portlet-name>My Portlet</portlet-name>
<display-name>My Portlet</display-name>
<portlet-class>org.springframework.web.portlet.DispatcherPortlet</portlet-class>
<init-param>
<name>contextConfigLocation</name>
<value>classpath:/my_portlet.xml</value>
</init-param>
...
</portlet>
my_portlet.xml like this
<?xml version="1.0" encoding="UTF-8"?>
<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:annotation-config />
<context:component-scan base-package="my.package.portlet.controllers" />
<bean class="org.springframework.web.portlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
<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/view/"/>
<property name="suffix" value=".jsp"/>
</bean>
<bean name="myService" class="my.package.services.MyService"/>
</bean>
</beans>
And controller class something like this (it can contain mistakes because taken from memory )
#Controller
#RequestMapping("view")
public class MyController {
#Autowired
private MyService myService;
#RequestMapping
public ModelAndView index(RenderRequest request, RenderResponse response) {
ModelAndView modelAndView = new ModelAndView("index.jsp");
modelAndView.addObject("attribute1", "Attribute 1 value");
modelAndView.addObject("attribute1", new Object());
return modelAndView;
}
#RenderMapping(params = "form=result")
public ModelAndView doSomething(RenderRequest request, RenderResponse response) {
ModelAndView modelAndView = new ModelAndView("result.jsp");
...
return modelAndView;
}
#ActionMapping(params = "submitSomething=yes")
public void submitSomething(#ModelAttribute("something") Something something, BindingResult bindingResult, ActionRequest request, ActionResponse response) {
// do what you want with form data
request.setAttribute("result", someResult);
response.setRenderParameter("form", "result");
}
}
The main idea that you can have multiple Controller classes with different methods that obtains different render/action parameters. Also big plus that you can use spring beans inside controller classes.
Please note that I have Existing project in struts 1.x and with the following steps I am trying to integrate Spring MVC to it.
I have a maven project which on deployment is able to read servlet and load respective Controller class from jar file on server start-up and gives following info about controller class:
[org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping] Rejected bean name 'SSOController': no URL paths identified
Looks like it is not loading URLS hence I am not able to make any restful web service call on it.
my web.xml has following entry :
<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>/hello.do</url-pattern>
</servlet-mapping>
Dispatcher Servlet has following entries:
<context:annotation-config />
<context:component-scan base-package="com.ga.action.controller" />
<mvc:resources location = "/resources/" mapping = "/resources/**" />
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/" />
<property name="suffix" value=".jsp" />
</bean>
<!-- JAXB2 marshaller. Automagically turns beans into xml -->
<bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>com.ga.action.controller.PEMUtil</value>
</list>
</property>
</bean>
Controller class code follows:
#Controller
#RequestMapping("/hello.do")
public class TestController {
#RequestMapping(method = RequestMethod.GET, value = "/")
public #ResponseBody String printTokenMessage(
#RequestParam("nLiveToken") String message,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
System.out.println(message);
}
}
Use XML configuration instead of #Conteroller annotation for external jar file controllers. Because the #Controller annotation isn't available in the server class loader.
http://forum.spring.io/forum/spring-projects/web/108774-controllers-from-external-jar
Since the exception indicates the use of BeanNameUrlHandlerMapping there should be a bean named /hello.do in your context.
You do use the #RequestMapping annotation so I guess you assumed annotation driven handler mapping would be enabled by default. Which isn't the case. If the ServletDispatcher finds no handler mapping it will create a BeanNameUrlHandlerMapping for you.
If you want to use annotations in your application you should define
<mvc:annotation-driven />
in your context configuration.