View Full Version : SWF Basic question 1
yfmoan
May 6th, 2005, 06:11 AM
Hi ,
Sometime l found it is difficult to do programming . May be because of lack of docs and examples ? or lack some stupid people like me to ask stupid question , hihihi ....:). But now l wish to ask a series basic questions about SWF , hope that clear all my "basic HOW TO" . Hope the authors don't mind ....
Q1.
Is this the right implemetation for SWF ? doing messages and errors that different from spring MVC.
public class AbstractLibraryAction extends FormAction implements ApplicationContextAware {
private ApplicationContext applicationContext;
private MessageSourceAccessor messageSourceAccessor;
private LibraryFacade library;
private static final String DATE_PATTERN = "yyyy-MM-dd";
public final void setLibrary(LibraryFacade library){
this.library = library;
}
protected final LibraryFacade getLibrary(){
return this.library;
}
public AbstractLibraryAction() {
}
public final void setApplicationContext(ApplicationContext context) throws BeansException {
if (context == null && !isContextRequired()) {
// reset internal context state
this.applicationContext = null;
this.messageSourceAccessor = null;
}
if (this.applicationContext == null) {
// initialize with passed-in context
if (!requiredContextClass().isInstance(context)) {
throw new ApplicationContextException(
"Invalid application context: needs to be of type [" + requiredContextClass().getName() + "]");
}
this.applicationContext = context;
this.messageSourceAccessor = new MessageSourceAccessor(context);
initApplicationContext();
}
else {
// ignore reinitialization if same context passed in
if (this.applicationContext != context) {
throw new ApplicationContextException(
"Cannot reinitialize with different application context: current one is [" +
this.applicationContext + "], passed-in one is [" + context + "]");
}
}
}
protected boolean isContextRequired() {
return true;
}
protected Class requiredContextClass() {
return ApplicationContext.class;
}
protected void initApplicationContext() throws BeansException {
}
public final ApplicationContext getApplicationContext() throws IllegalStateException {
if (this.applicationContext == null && isContextRequired()) {
throw new IllegalStateException(
"ApplicationObjectSupport instance [" + this + "] does not run in an ApplicationContext");
}
return applicationContext;
}
protected final MessageSourceAccessor getMessageSourceAccessor() throws IllegalStateException {
if (this.messageSourceAccessor == null && isContextRequired()) {
throw new IllegalStateException(
"ApplicationObjectSupport instance [" + this + "] does not run in an ApplicationContext");
}
return this.messageSourceAccessor;
}
public final void afterPropertiesSet() {
if (this.library == null) throw new BeanCreationException("Library Facade is required");
initAction();
}
protected void initBinder(RequestContext context, DataBinder binder){
SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_PATTERN);
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, null, new CustomDateEditor(dateFormat, false));
binder.registerCustomEditor(String.class, null, new StringTrimmerEditor("\r\n\f", false));
}
}
public class PublisherCRUDAction extends AbstractLibraryAction {
private static final String PUBLISHER_FORM_OBJECT_NAME = "publisher";
public PublisherCRUDAction() {
setFormObjectName(PUBLISHER_FORM_OBJECT_NAME);
setFormObjectClass(Publisher.class);
setValidator(new PublisherValidator());
}
public Event Save(RequestContext context) throws Exception {
// store publisher
Publisher publisher = (Publisher)context.getRequestScope().get(PUBLISHER _FORM_OBJECT_NAME);
HttpServletRequest request = ((HttpServletRequestEvent)context.getOriginatingEv ent()).getRequest();
Locale locale = RequestContextUtils.getLocale(request);
try{
getLibrary().storePublisher(publisher);
request.getSession().setAttribute("message_publisher_added",
getMessageSourceAccessor().getMessage("publisher.added",
new Object[] {publisher.getPublisherName()}, locale));
}
catch(org.springframework.dao.DataIntegrityViolati onException eive){
BindException errors = createBinder(context, publisher).getErrors();
errors.rejectValue("publisherName","publisher.name.already.exists",
"Publisher name already exists ...");
exposeFormObjectAndErrors(context, publisher ,errors);
return error();
}
return success();
}
public Event Search(RequestContext context) throws Exception {
// search publisher
HttpServletRequest request = ((HttpServletRequestEvent)context.getOriginatingEv ent()).getRequest();
String publisherName = request.getParameter("publisherName");
Locale locale = RequestContextUtils.getLocale(request);
if(publisherName != null){
ArrayList publishers = (ArrayList)getLibrary().findPublishersByName(publi sherName);
if (publishers.size() < 1) {
// no subject found
context.getRequestScope().setAttribute("message_no_publisher_found",
getMessageSourceAccessor().getMessage("no.publisher.found", locale));
return success();
}
// multiple subjects found
context.getRequestScope().setAttribute("model_name_publishers", publishers);
return success();
}
return success();
}
}
Q2.
What is the different between the usage of RequestScope and FlowScope ?
sometimes l saw example using RequestScope and sometime FlowScope.
BirthDate birthDate = (BirthDate)context.getRequestScope().get(BIRTHDATE _FORM_OBJECT_NAME);
PhoneBookQuery query = (PhoneBookQuery)context.getFlowScope().getRequired Attribute("query",PhoneBookQuery.class);
Keith Donald
May 6th, 2005, 08:09 AM
Well, webflow has a ton of docs that explain this stuff...are you reading them? We also have like 7 sample applications. :-)
Here are some suggestions:
- Stop downcasting to HttpServletRequestEvent to get the Request. It is not needed, and couples your action code with the servlet API unnecessarily. If you want to access HTTP equest parameters, just use the Event.getParameter(parameterName) method.
- Don't capitalize method names. This IS Java remember.
- Consider doing message resolution from your views instead of the controllers.
- I guess we should consider having AbstractAction be ApplicationContextAware for easy access to a Spring MessageSource, ResourceLoader, and BeanFactory - among other services. You certainly shouldn't be duplicating all the context configuration code accross your actions. I'm not sure if this is general enough though to warrant that.
- I would not recommend putting those success messages into the session like that. Use request scope, or do the resolution from the views. In general, keep your actions independent of the servlet API where possible.
- For all that Errors stuff you're doing, use the FormObjectAccessor class or super class helper methods. It'll make your life much easier. You really shouldn't be creating a BindException directly.
- Don't get/mess with the Locale manually in action code. The message source should always use the correct Locale based on a LocaleContext set for the request.
requestScope = objects there exist for the duration of the request into the web flow system
flowScope = objects there exist for the duration of the entire flow session
yfmoan
May 6th, 2005, 11:32 PM
Hi kdonald,
Thanks for your lengthy reply , :)
l did read all the docs , the 7 sample applications did not handle messages +errors + locale like my example , :)
- Stop downcasting to HttpServletRequestEvent to get the Request. It is not needed, and couples your action code with the servlet API unnecessarily. If you want to access HTTP equest parameters, just use the Event.getParameter(parameterName) method.
l used HttpServletRequestEvent to get the Request because it can avoid the problem l stated else where :) , command not clear problem
http://forum.springframework.org/viewtopic.php?t=5341
l have further comment about Event.getParameter(parameterName).
l have been following SWF started from Erwin's implemetation b4 it merged into spring.Before 2 months ago , Action's methods were return Strings instead of Events, that was simple enough , then l stop sometime to learn Webwork2. When l back to SWF these day,l found it change a lot , no more request , response , all wrap it into RequestContext , if u wanna to get a request , this is not a trivial thing , l searched the SWF codes hard to find the answer , but have to ask Erwin finally .....hihi.
You can see from SWF doc FAQ , people alway search for request , response and ApplicationContextAware , why ? may be it is hard to turn our (beginner , spring is my first MVC) head to those abstract things ...hihi
OK , back to Event.getParameter(parameterName) , what my feeling is , l sometime don't know where to get the tools (request,response,parameters,...etc) , this is different from Webwork ( l did the comparison as a beginner , and l learn Webwork recently) , they put all the tools inside ActionContext , u know where to get it , almost just one place only , it preserved simplicity to users , l think this is important , simplicity is KING.
- Don't capitalize method names. This IS Java remember.
no problem.
- Consider doing message resolution from your views instead of the controllers.
In spring MVC , command will be clear after each successful action , so l have to do it inside the controller to display publisherName , theses codes are learn from mraible , Sping Live . Thanks to matt , :)
new Object[] {publisher.getPublisherName()}, locale));
I guess we should consider having AbstractAction be ApplicationContextAware for easy access to a Spring MessageSource, ResourceLoader, and BeanFactory - among other services. You certainly shouldn't be duplicating all the context configuration code accross your actions. I'm not sure if this is general enough though to warrant that.
l asked Erwin in private b4, Erwin replied that may be spring not intend to support these service , that's why l have to implement ApplicationContextAware that extends FormAction in my AbstractLibraryAction. If l do it in AbstractAction, it will lose a lot service that in FormAction. In simpleFormController , all these are support.
to be continue ....
Keith Donald
May 6th, 2005, 11:45 PM
I see no reason why you need to be accessing the HttpServletRequest for the issue you're having -- just clear the command object out of flow scope. I write a lot of actions, I find I rarely need to access the servlet API directly.
Web flow is not Spring MVC, nor is it web work. It's a much more powerful controller that can run in any web environment. Don't use it where its not appropriate. It is appropriate for controlled navigations that guide the user through a multi-step process.
In any case, Web flow's architecture is simple and highly consistent: at its core is a finite state machine. Events occur that drive state transitions. When a new state is entered, behavior happens. An action invoked when an action state is entered has full access to the event that triggered its execution, including any input event parameters.
If the event originated from a HTTP environment, the parameters are HTTP request parameters. If the event orginated from a Portlet environment, well, they're portlet parameters. In any case, you access the event class consistently. It's that simple.
Request scope gives you a store for attributes that span a request into the web flow system. Flow scope gives you a store for attributes that span the life of the flow, with automatic cleanup when the flow ends.
The choice about the use of Event instead of String was one of consitency. It also gives you added flexbility, with very little cost there in terms of complexity. Erwin and I stand by the decision there.
It's very important to understand the core model of how Spring web flow works. Again, this is not Spring MVC or web work. You need to understand the model in play here--its very simple, very elegant, very powerful, but different than what you're used to.
yfmoan
May 6th, 2005, 11:56 PM
Use request scope, or do the resolution from the views. In general, keep your actions independent of the servlet API where possible.
Oh , sorry , that was my testing code.
For all that Errors stuff you're doing, use the FormObjectAccessor class or super class helper methods. It'll make your life much easier. You really shouldn't be creating a BindException directly.
l did not realise that the existance of this class , at lease not in example and docs , may be l miss .l will look at at ...:)
Don't get/mess with the Locale manually in action code. The message source should always use the correct Locale based on a LocaleContext set for the request.
l did this because l have this thread in my mind
http://forum.springframework.org/viewtopic.php?t=1108&highlight=messagesourceaccessor ,
getMessageSourceAccessor() is not request locale-aware
Thanks kdonald.
moon
Keith Donald
May 6th, 2005, 11:59 PM
Well, that thread is old as dirt. :-)
You're using Spring 1.2, right? It has the new locale context feature.
yfmoan
May 7th, 2005, 12:19 AM
Haha ...thanks for all your reply , l am getting satified.
the codes wrote two months ago , l guess , at that time , it was just RC1 or sandbox code , haha.
Will spring term add support for ApplicationContextAware in AbstractAction ? it is ugly to implement in AbstractLibraryAction , hihi...
These indeed clear all my doubt now , l like to discuss problem vigorously(hope u don't mind) , this will eventually show all the unclear problems , make thing more clear than b4 , so in my questions , u will find my point of views , right or wrong . But one thing for sure , l did the homework b4 asking question . haha....
yfmoan
May 7th, 2005, 03:18 PM
After reading FormObjectAccessor , still don't understand what this mean ,
For all that Errors stuff you're doing, use the FormObjectAccessor class
Where can l get a errors (BindException) object , do the following
errors.rejectValue("publisherName","publisher.name.already.exists",
"Publisher name already exists ...");
l come out with
FormObjectAccessor formObjectAccessor = new FormObjectAccessor(context);
BindException errors = (BindException)formObjectAccessor.getFormErrors (PUBLISHER_FORM_OBJECT_NAME,ScopeType.REQUEST);
errors.rejectValue("publisherName","publisher.name.already.exists",
"Publisher name already exists ...");
exposeFormObjectAndErrors(context, publisher ,errors);seem not so right. hihi...
don't understand this 2,
or super class helper methods
moon
Keith Donald
May 7th, 2005, 05:12 PM
Just do this:
Errors errors = new FormObjectAccessor(context).getFormErrors();
errors.rejectValue(...);
Since bindAndValidate already put a Errors instance in request scope for you and you call save after bindAndValidate, this should work fine. I see no need to expose another Errors instance.
yfmoan
May 7th, 2005, 09:49 PM
Ok , done! this is the better version , hihihi....
public class PublisherCRUDAction extends AbstractLibraryAction {
private static final String PUBLISHER_FORM_OBJECT_NAME = "publisher";
public PublisherCRUDAction() {
setFormObjectName(PUBLISHER_FORM_OBJECT_NAME);
setFormObjectClass(Publisher.class);
setValidator(new PublisherValidator());
}
public Event Save(RequestContext context) throws Exception {
Publisher publisher = (Publisher)context.getRequestScope().get(PUBLISHER _FORM_OBJECT_NAME);
try{
getLibrary().storePublisher(publisher);
context.getRequestScope().setAttribute("message_publisher_added",
getMessageSourceAccessor().getMessage("publisher.added",
new Object[] {publisher.getPublisherName()}));
//context.getRequestScope().removeAttribute("publisher");
}catch(org.springframework.dao.DataIntegrityViolat ionException eive){
Errors errors = new FormObjectAccessor(context).getFormErrors(PUBLISHE R_FORM_OBJECT_NAME,ScopeType.REQUEST);
errors.rejectValue("publisherName","publisher.name.already.exists",
"Publisher name already exists ...");
return error();
}
return success();
}
public Event Search(RequestContext context) throws Exception {
String publisherName = (String)context.getLastEvent().getParameter("publisherName");
if(publisherName != null){
ArrayList publishers = (ArrayList)getLibrary().findPublishersByName(publi sherName);
if (publishers.size() < 1) {
// no subject found
context.getRequestScope().setAttribute("message_no_publisher_found",
getMessageSourceAccessor().getMessage("no.publisher.found"));
return success();
}
// multiple subjects found
context.getRequestScope().setAttribute("model_name_publishers", publishers);
return success();
}
return success();
}
}
Remark:
1. no need get/mess with the Locale manually in action code. This is done automatically after spring RC2 and above.
2. SWF don't have getFormErrors() method in FormObjectAccessor.java now , l found it in sandbox , it have to wait until Preview 3 , l guess.
3. no more downcasting to get request or response. Get parameters using Event.
Question:
1. NoSuchFlowExecutionException: No executing flow could be found with id '697B7869-3FC8-6F51-FD18-3CEC73D3649C'
-- perhaps the flow has ended or expired? --> if session expired , How SWF handle this ?
2. add support for ApplicationContextAware in AbstractAction ? Hihihi ...
the edit function is nice , :) --> u can undo regret thing ...hihihi
Keith Donald
May 8th, 2005, 01:39 PM
Moon,
Few remarks:
- Don't call context.getLastEvent() -- use context.getOriginatingEvent() instead. That will get you the event that orginated the client request into the web flow system -- in this case the http servlet event. lastEvent returns the last event that occured -- it could be an internal event signaled by an action that executed previously in the current request context, for example (not what you want here.)
- Session expiration: consider setting up a HandlerExceptionResolver that maps the NoSuchFlowExecutionException to an error page, perhaps with a flow restart capability.
- We'll see about adding support for ApplicationContextAware. I'm not so sure yet. To be honest, I really think you should be doing message resolution from the views.
garpinc2
May 9th, 2005, 09:44 AM
- Session expiration: consider setting up a HandlerExceptionResolver that maps the NoSuchFlowExecutionException to an error page, perhaps with a flow restart capability.
Is it possible or will it be possible to have this as part of the config xml rather than coding it explicitly. For instance something like:
<webflow id="contactUs" start-state="pre.select.topic.action">
<exception class="org.springframework.web.flow.NoSuchFlowExecutionEx ception
" to="contactUs"/>
...
Keith Donald
May 9th, 2005, 07:57 PM
Yes good idea.
We also want global states and global state transitions as well: e.g a global 'help' event always spawning a help flow regardless of where it is signaled.
Keith
Keith Donald
May 9th, 2005, 07:58 PM
Can you create a JIRA issue for this? Thanks!
yfmoan
May 10th, 2005, 05:22 AM
l finally finished implement CRUD Controler in SWF , it has a simple look. l want to ask , If it possible to post my codes here for discussion ( 8,9 classes , l guess ) ? because l would like to know it is OK or not , but afraid "fooding" the forum ...hihi
To be honest. I really think you should be doing message resolution from the views
Yes . Keith , l saw u emphasized twice , if l do it in view , how can l display message like
"The Publisher Keith Publisher have been added ...
in my successView ? why it is important to do message resolution from the views ? hope u don't feel annoying to explain ..:)
moon
garpinc2
May 10th, 2005, 08:48 AM
JIRA http://opensource.atlassian.com/projects/spring/browse/SPR-933
Keith Donald
May 10th, 2005, 08:53 AM
Can you just use a JSTL fmt tag to lookup the message with the code you want, with the publisher name as an arg?
If you post the code, post a zip :-)
Keith
yfmoan
May 10th, 2005, 09:49 AM
l used spring:message tag already , l thing it is similar to jstl fmt tag.
<c:if test="${!empty message_publisher_added}" >
<spring:message text="${message_publisher_added}"/>
<c:remove var="message_publisher_added"/>
</c:if>
messages.properties
publisher.added=The Publisher <strong>{0}</strong> have been added ...
{0} is the publisher name arg , have to be injected using the code that in controller,
context.getRequestScope().setAttribute("message_publisher_added",
getMessageSourceAccessor().getMessage("publisher.added",
new Object[] {publisher.getPublisherName()}));
only controller know when to inject the publisher name arg , if try success , then inject , otherwise catch and throw error message (code above , save method).
u mean there is other way to do this ?
How to post a zip to the forum ? l study phpbb forum , can't find a way ..:)
moon
Keith Donald
May 10th, 2005, 09:58 AM
Right, I see. Your controller knows what the result is, so it's logical he set a result message for display. I like that better.
You might want to create superclass helper methods that are more concise though -- MessageSourceAccessor is just so many letters to type...
Maybe something like:
message("publisher.added", new Object[] { publisher.getName() }));
That one call could not only resolve the message, but expose it as an attribute in request scope for you.
yfmoan
May 10th, 2005, 11:49 AM
You might want to create superclass helper methods that are more concise though
may be u can consider adding suppport for this method , then l just call it , because l am a sincere user of SWF ....hihihi
Regarding my CRUD codes , l do think that it will benefit for those beginners , and fill some docs and basic HOW TOs , specially for those peoples using displaytag. l spend many hours to make it work , it may be trivial for experts , but not for beginners.
The important thing is don't waste time here , l did it , just copy and paste my codes (complain if it is bad) to play , and then ask more advance question about SWF , make it move faster ..... or save the time for your family... programming is hard ~~~.....hihihi
How to post zip here ?
moon
mraible
May 10th, 2005, 08:01 PM
If you want to send me an e-mail with your code - I can post it for download. Please include a README.txt with an explanation of how to install your code and how to walk through it's UI.
Thanks,
Matt
yfmoan
May 10th, 2005, 09:51 PM
Hi , matt
l send it to matt@raibledesigns.com , one thing to remind , remember to modify your tomcat server.xml to make UTF-8 work .....
URIEncoding="UTF-8"
<Connector port="8080"
maxThreads="150"
minSpareThreads="25"
maxSpareThreads="75"
enableLookups="false"
redirectPort="8443"
acceptCount="100"
debug="0"
connectionTimeout="20000"
disableUploadTimeout="true"
URIEncoding="UTF-8" />
it cost me a lot of time to find out while changing from tomcat 4.0 to tomcat 5.0
+ an original spring MVC version for comparison. (remember to reload the library.sql , minor different for these two versions-->subject have an additional version field)
moon
yfmoan
May 11th, 2005, 10:07 PM
Hi , one last question in this thread.
Can we make the following code more compact ? because it will growth fast if we have a lot subflows.
<bean id="publisher.Search.id.set"
class="org.springframework.web.flow.action.EventParameter MapperAction">
<property name="mapping" value="id,java.lang.Long"/>
<property name="targetScopeAsString"><value>flow</value></property>
</bean>
<bean id="id.attributeMapper"
class="org.springframework.web.flow.support.Parameterizab leFlowAttributeMapper">
<property name="inputMappings">
<list>
<value>id</value>
</list>
</property>
</bean>
id,java.lang.Long will be the most frequest use conversion , make it special ?
moon
Keith Donald
May 11th, 2005, 10:26 PM
Use the new xml syntax...
<subflow-state id="subFlowState2" flow="subFlow2">
<attribute-mapper>
<input name="attribute1"/>
<input name="attribute3" as="attribute2"/>
<output name="attribute2"/>
<output name="attribute1" as="attribute3"/>
</attribute-mapper>
<transition on="event2" to="endState2"/>
</subflow-state>
Soon you'll be able to map directly out of event or request scope from here as well, without a need for any action. e.g.
<subflow-state id="subFlowState2" flow="subFlow2">
<attribute-mapper>
<input value="${lastEvent.parameter.personId} as="personId"/>
</attribute-mapper>
<transition on="event2" to="endState2"/>
</subflow-state>
yfmoan
May 11th, 2005, 10:30 PM
in the sandbox now ?
Keith Donald
May 11th, 2005, 10:32 PM
yes
yfmoan
May 12th, 2005, 03:08 AM
codes break so fast ....
context.getOriginating() have to change to
context.getSourceEvent()if u use the sandbox jar (Preview2~preview3) to test the code above.
failed to setup.
with exception below ( l tried all the combinations , hihi ..),
org.springframework.beans.factory.BeanCreationExce ption: Error creating bean with name 'publisher.Update.Delete.FlowController' defined in ServletContext resource [/WEB-INF/library-servlet.xml]: Initialization of bean failed; nested exception is java.lang.NoClassDefFoundError: org/springframework/web/flow/SubflowState
java.lang.NoClassDefFoundError: org/springframework/web/flow/SubflowState
........
Here is my working code ,
PublisherActions.java instead of PublisherCRUD.java , hihi ..
public class PublisherActions extends AbstractLibraryAction {
private static final String PUBLISHER_FORM_OBJECT_NAME = "publisher";
public PublisherActions() {
setFormObjectName(PUBLISHER_FORM_OBJECT_NAME);
setFormObjectClass(Publisher.class);
//setValidator(new PublisherValidator());
}
public Event Save(RequestContext context) throws Exception {
Publisher publisher = (Publisher)context.getRequestScope().get(PUBLISHER _FORM_OBJECT_NAME);
try{
getLibrary().storePublisher(publisher);
context.getRequestScope().setAttribute("message_publisher_added",
getMessageSourceAccessor().getMessage("publisher.added",
new Object[] {publisher.getPublisherName()}));
//context.getRequestScope().removeAttribute("publisher");
}
catch(org.springframework.dao.DataIntegrityViolati onException eive){
Errors errors = new FormObjectAccessor(context).getFormErrors();
errors.rejectValue("publisherName","publisher.name.already.exists",
"Publisher name already exists ...");
return error();
}
return success();
}
public Event Edit(RequestContext context) throws Exception {
Publisher publisher = (Publisher)context.getRequestScope().get(PUBLISHER _FORM_OBJECT_NAME);
try{
getLibrary().storePublisher(publisher);
context.getRequestScope().setAttribute("message_publisher_edited",
getMessageSourceAccessor().getMessage("publisher.edited",
new Object[] {publisher.getPublisherName()}));
context.getRequestScope().removeAttribute("publisher");
}catch(org.springframework.dao.DataIntegrityViolat ionException eive){
//Errors errors = new FormObjectAccessor(context).getFormErrors(PUBLISHE R_FORM_OBJECT_NAME,ScopeType.REQUEST);
Errors errors = new FormObjectAccessor(context).getFormErrors();
errors.rejectValue("publisherName","publisher.name.already.exists",
"Publisher name already exists ...");
return error();
}
return success();
}
public Event Delete(RequestContext context) throws Exception {
Long id = new Long((String)context.getSourceEvent().getParameter ("id"));
//Long id = (Long)context.getRequestScope().getRequiredAttribu te("id", Long.class);
Publisher publisher = getLibrary().loadPublisher(id);
if (publisher != null) {
try{
getLibrary().deletePublisher(publisher);
context.getRequestScope().setAttribute("message_publisher_deleted",
getMessageSourceAccessor().getMessage("publisher.deleted",
new Object[] {publisher.getPublisherName()}));
//context.getRequestScope().removeAttribute("publisher");
}catch (org.springframework.orm.ObjectOptimisticLockingFa ilureException eoolfe){
Errors errors = new FormObjectAccessor(context).getFormErrors();
errors.rejectValue("publisherName","publisher.name.already.updated.or.deleted",
"The Publisher Name have been updated or deleted by other user...");
return error();
}
return success();
}
return success();
}
public Event SearchByName(RequestContext context) throws Exception {
String publisherName = (String)context.getSourceEvent().getParameter("publisherName");
if(publisherName != null){
ArrayList publishers = (ArrayList)getLibrary().findPublishersByName(publi sherName);
if (publishers.size() < 1) {
// no publisher found
context.getRequestScope().setAttribute("message_no_publisher_found",
getMessageSourceAccessor().getMessage("no.publisher.found"));
return success();
}
// multiple publishers found
context.getRequestScope().setAttribute("model_name_publishers", publishers);
return success();
}
return success();
}
public Event SearchById(RequestContext context) throws Exception {
Long id = (Long)context.getFlowScope().getRequiredAttribu te("id", Long.class);
if(id != null){
Publisher publisher = getLibrary().loadPublisher(id);
context.getRequestScope().setAttribute("publisher", publisher);
return success();
}
return success();
}
}
library-servlet.xml
<beans>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResou rceViewResolver">
<property name="viewClass"><value>org.springframework.web.servlet.view.JstlView</value></property>
<property name="requestContextAttribute"><value>rc</value></property>
<property name="prefix"><value>/WEB-INF/jsp/</value></property>
<property name="suffix"><value>.jsp</value></property>
</bean>
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleR esolver"/>
<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeI nterceptor"/>
<bean id="beanNameUrlMapping" class="org.springframework.web.servlet.handler.BeanNameUr lHandlerMapping"/>
<!-- Commons Validator Configuration -->
<bean id="validatorFactory" class="org.springmodules.commons.validator.DefaultValidat orFactory" >
<property name="validationConfigLocations">
<list>
<value>/WEB-INF/validator-rules.xml</value>
<value>/WEB-INF/validation.xml</value>
</list>
</property>
</bean>
<bean id="beanValidator" class="org.springmodules.commons.validator.DefaultBeanVal idator">
<property name="validatorFactory"><ref local="validatorFactory"/></property>
</bean>
<bean id="publisherValidator" class="org.springmodules.commons.validator.ConfigurableBe anValidator">
<property name="validatorFactory"><ref local="validatorFactory"/></property>
<property name="formName"><value>publisher</value></property>
</bean>
<bean id="publisherWithoutIdValidator" class="org.springmodules.commons.validator.ConfigurableBe anValidator">
<property name="validatorFactory"><ref local="validatorFactory"/></property>
<property name="formName"><value>publisherWithoutId</value></property>
</bean>
<bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMapp ingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.Exception">Error</prop>
<prop key="org.springframework.dao.DataAccessException">dataAccessFailure</prop>
<prop key="org.springframework.transaction.TransactionExcepti on">dataAccessFailure</prop>
</props>
</property>
</bean>
<bean id="publisherActions" class="org.yourschool.library.web.PublisherActions">
<property name="library"><ref bean="library"/></property>
<property name="validator"><ref bean="beanValidator"/></property>
</bean>
<!-- ************** -->
<bean id="publisher.Create.Search.FlowController" class="org.springframework.web.flow.mvc.FlowController">
<property name="flow" ref="publisher.Create.Search.Flow"/>
</bean>
<bean id="publisher.Create.Search.Flow" class="org.springframework.web.flow.config.XmlFlowFactory Bean">
<property name="location" value="/WEB-INF/flows/publisher.Create.Search-flow.xml"/>
</bean>
<!-- ************** -->
<bean id="publisher.Update.Delete.FlowController" class="org.springframework.web.flow.mvc.FlowController">
<property name="flow" ref="publisher.Update.Delete.Flow"/>
</bean>
<bean id="publisher.Update.Delete.Flow" class="org.springframework.web.flow.config.XmlFlowFactory Bean">
<property name="location" value="/WEB-INF/flows/publisher.Search.for.Update.Delete-flow.xml"/>
</bean>
<!-- ************** -->
<bean id="publisher.Update.FlowController" class="org.springframework.web.flow.mvc.FlowController">
<property name="flow" ref="publisher.Update.Flow"/>
</bean>
<bean id="publisher.Update.Flow" class="org.springframework.web.flow.config.XmlFlowFactory Bean">
<property name="location" value="/WEB-INF/flows/publisher.Update-flow.xml"/>
</bean>
<bean id="publisher.Search.id.set" class="org.springframework.web.flow.action.EventParameter MapperAction">
<property name="mapping" value="id,java.lang.Long"/>
<property name="targetScopeAsString"><value>flow</value></property>
</bean>
<bean id="id.attributeMapper" class="org.springframework.web.flow.support.Parameterizab leFlowAttributeMapper">
<property name="inputMappings">
<list>
<value>id</value>
</list>
</property>
</bean>
<!-- A type conversion service, a more flexible/threadsafe/lightweight alternative to the PropertyEditor! -->
<bean id="conversion.service" class="org.springframework.binding.convert.support.Defaul tConversionService" autowire="byType"/>
<!-- A formatter registry, a thread-safe cache for Formatters: a thread-safe, lighter weight alt. to PropertyEditors! -->
<bean id="formatter.locator" class="org.springframework.binding.format.support.ThreadL ocalFormatterLocator" autowire="byType"/>
<!-- A broadcaster that will notify the formatter registry to cleanup all thread locals on app shutdown -->
<bean id="thread.cleanupBroadcaster" class="org.springframework.binding.thread.support.Default ThreadCleanupBroadcaster"/>
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlH andlerMapping">
<property name="interceptors">
<list>
<ref local="localeChangeInterceptor"/>
</list>
</property>
<property name="mappings">
<props>
<prop key="/admin/index.htm">indexView</prop>
<prop key="/admin/publisher.Create.Search.htm">publisher.Create.Search.FlowController</prop>
<prop key="/admin/publisher.Search.for.Update.Delete.htm">publisher.Update.Delete.FlowController</prop>
<prop key="/admin/publisher.Update.htm">publisher.Update.FlowController</prop>
</props>
</property>
</bean>
<bean name="indexView" class="org.springframework.web.servlet.mvc.Parameterizabl eViewController">
<property name="viewName"><value>index</value></property>
</bean>
</beans>
publisher.Search.for.Update.Delete-flow.xml
<webflow id="publisher.Update.Delete.Flow" start-state="publisher.Search">
<view-state id="publisher.Search" view="publisher.Search.for.Update.Delete">
<transition on="searchByName" to="SearchByName"/>
<transition on="delete" to="Delete"/>
<transition on="searchById" to="publisherId.set"/>
</view-state>
<action-state id="SearchByName">
<action bean="publisherActions"/>
<transition on="success" to="publisher.Search"/>
<transition on="error" to="publisher.Search"/>
</action-state>
<action-state id="Delete">
<action bean="publisherActions"/>
<transition on="success" to="publisher.Search"/>
<transition on="error" to="publisher.Search"/>
</action-state>
<action-state id="publisherId.set">
<action bean="publisher.Search.id.set"/>
<transition on="success" to="publisher.Update.Flow"/>
<transition on="error" to="publisher.Search"/>
</action-state>
<subflow-state id="publisher.Update.Flow" flow="publisher.Update.Flow">
<attribute-mapper bean="id.attributeMapper"/>
<transition on="finish" to="done"/>
<transition on="error" to="publisher.Search"/>
</subflow-state>
<end-state id="done" view="redirect:/admin/publisher.Search.for.Update.Delete.htm"/>
<end-state id="error" view="publisher.Search"/>
</webflow>
l did it this way ,
<subflow-state id="publisher.Update.Flow" flow="publisher.Update.Flow">
<attribute-mapper>
<input name="id" as="id"/>
<output name="id"/>
</attribute-mapper>
<transition on="finish" to="done"/>
<transition on="error" to="publisher.Search"/>
</subflow-state>
no luck ....l almost did all the combinations , remove the "not so compact codes" , change this and that ...hihihi
this is my final step to CRUD in SWF , otherwise l have to write a lot to pollute my library-servlet.xml ....
Keith Donald
May 12th, 2005, 08:42 AM
Yes 'sourceEvent' now used in preference to 'originatingEvent' -- name tweak there. Hopefully that's not used too much by folks...
So you're saying the xml attribute mapping is not working, right? The CVS changes including that bugfix probably just haven't synced to the anonymous servers yet.
Any other trouble with the PR3 code?
yfmoan
May 12th, 2005, 09:44 AM
Hi Keith ,
no luck ..still no working. May be l send u my working code , it is the most efficency way, u can try it yourself ( if u have gmail address ).
yfmoan@gmail.com
yes , there is other problem , not just PR3 , sometimes PR1 , PR2. Sometimes l got
<IOException while loading persisted sessions: java.io.EOFException> , it happened when l use "ant stop" , "ant start" to restart tomcat server while a exception occured ...
moon
Keith Donald
May 12th, 2005, 11:08 AM
Patience, Moon - it takes up to 24 hours and I just committed the fix last night.
That EOFException I don't think is our problem -- our serialization logic is correct. I suspect it to be a tomcat issue, or perhaps it occurs when you switch jars.
yfmoan
May 17th, 2005, 03:34 AM
l would like make a conclusion of my finding Nemo , oh .. no ..is finding CRUD..hihi..
l always start to implement CRUD first when l start an web application.Two months ago , l did it using spring MVC , and l found it is hard to implement CRUDs all inside a controller , even though l have read Spring Live , by Matt , and other thread about "MultiActionFormController" http://forum.springframework.org/viewtopic.php?t=1588 , l am still not satisfied.
Reasons not satisfied:
R1. For Matt case , he used parameters "delete" ,"save" , "edit" ...etc to let the CRUD controller to know which action to take . l don't like this way , becasue it will make your controller more complex , you will have a lot of if ..then ..else inside your CRUD controller....once your application logic inside the controller become complex , readability of the codes inside the controller will reduce.
R2. For MultiActionFormController case , it need more work in xxx-servlet.xml just to let the CRUD controller know which action to take , although it is more elegance than the R1 , but it is not a standard way to do it in spring (means spring are not supported) , and you have to write the MultiActionFormController in your package , this is not elegance for me :) . ( l guess spring is not support it becasue of some reasons )
R3. Sometime it is not reasonable to make all CRUDs inside a controller if you want to reuse each CRUD actions.
http://forum.springframework.org/viewtopic.php?t=4342
So after R1,R2,R3 , l think it is better to implement all the CRUD actions separately , using different controllers for each actions , but this will immediately cause another problem , the controller numbers and codes in xxx-servlet.xml growth very fast because each action use one controller (if you can stand it , it will be a small problem) , sometime you will get a lot parameterizeViewControllers ...hihihi .
After l did the CRUD implementation with separate controllers for each actions , l found SWF.
The story l search for Nemo , ah ..no ...CRUD , has been show in the previous posts in this thread , it seem more elegance to do it in SWF , because all CRUD actions are now inside a controller - publisherActions(<-- ah ... this cannot be called a controller , Keith said . Because he treated the whole flow a controller , and the "action" inside a flow is called an action or command ....hihi )
Now my question , is SWF really better or more elegance than the separate implementation ? hm ...
My answers :
yes for .... codes readability ...it is more easy to read the action code (publisherActions).
No for ...... write less code than b4 ... you have to write more for xxx-flow.xml and xxx-servlet.xml.
No for ...... for less memory used for SWF .... you have to initialize a whole flow just for a small actions (C,R,U,D)
yes for ..... Actions reusability ... you can reuse the Actions (publisherActions) in any other flows (but not outside the flow , becasue you have to paid the price to initialize another same action because of the disability of all MVC framework to reuse a stateless controller. http://forum.springframework.org/viewtopic.php?t=4342 , but of course Actions (like publisherActions) can only exist within flow by default ..hihi )
l just curious that why other peoples using spring don't have the CRUD problem as mine :?:
my conclusion:
SWF not for such small flow , it is for more complex flow as stated many time in the docs l saw .... :(
then l have an immediate question from my finding ....what is the multiAction for ? the api doc said that it is a typical use case to do CRUD .....hihi
huh ~~ done
moon
Keith Donald
May 17th, 2005, 07:42 AM
Hmmm...
I'm confused as to why the dispatcher servlet XML file would be more verbose with Spring Web Flow. Seriously, all it takes is a single FlowController for the whole app, and typically one MultiAction + XmlFlowFactoryBean per flow. What are you doing in your config file?
In the future we may add a "FlowExporter" that makes one to many flow definitions available in the registry for access by a execution manager. This would require only a single bean to load all flows, instead of one FlowFactoryBean per Flow.
MultiAction is for centralizing action logic within a single class at the method level. The actual action methods can do whatever you want.
If you're concerned about memory usage on the server (as yes, SWF is by default stateful), consider serializing the flow out to the client using the ClientContinuationFlowExecutionStorage strategy.
I do not understand your last point in regards to action reusability. Why is an Action not completely reusable in any flow? How does the fact that it is stateless make an impact on reusability?
Keith
yfmoan
May 17th, 2005, 08:52 AM
I'm confused as to why the dispatcher servlet XML file would be more verbose with Spring Web Flow. Seriously, all it takes is a single FlowController for the whole app, and typically one MultiAction + XmlFlowFactoryBean per flow. What are you doing in your config file?for xxx-servlet.xml , almost the same amount of code , more on xxx-flow.xml.
do not understand your last point in regards to action reusability. Why is an Action not completely reusable in any flow? How does the fact that it is stateless make an impact on reusability? Sorry , l miss a s in
can only exist within flows by default
MultiAction is for centralizing action logic within a single class at the method level. The actual action methods can do whatever you want. l did comparison for both CRUD implementations only. Both have cons and pros as stated above. l think CRUD are not typical use case as stated in MultiAction api docs , it is suitable for more complex use case.
If you're concerned about memory usage on the server (as yes, SWF is by default stateful), consider serializing the flow out to the client using the ClientContinuationFlowExecutionStorage strategy. l just compared both CRUD implementations memory usage --> SWF more.
when will SWF PR3 out ? :) ...the subflow problem still not solved ... l am waiting to test ....hihi
moon
Powered by vBulletin® Version 4.2.1 Copyright © 2013 vBulletin Solutions, Inc. All rights reserved.