Spring WebFlow - business logic in view-state or action-state - spring-webflow

What is the best place to call the business logic in? I have the following requirements.
1). Get the search criteria from user
2). If the current user is trying search for unauthorized results throw business exception
3). Retrieve results
4). If number of results==0, go to search page again and put a message.
5). If number of results>limit, go to search page again and put a message.
6). Go to showresults page.
This is what I have so far...
<flow....>
<on-start>
<evaluate expression="appConfig.setUpSupportData()" result="flowScope.supportData"/>
</on-start>
<view-state id="searchHome" view="searchHome" model="searchCriteria">
<transition on="search" to="doSearch"/>
</view-state>
<action-state id="doSearch">
<evaluate expression="searchUtil.getSearchResults(flowScope.searchCriteria)" result="flowScope.personList"/>
<evaluate expression="searchUtil.showSearchResults(flowScope.personList, flowRequestContext)" >
<attribute name="name" value="expression2"/>
</evaluate>
<transition on="expression2.yes" to="showSearchResults"/>
<transition on="expression2.no" to="searchHome"/>
</action-state>
<view-state id="showSearchResults"></view-state>
</flow>
My question is, should I be calling the getSearchResults() method in action-stae or in view-state="showSearchResults"? If I call the method in action-stae, I have to put the search results in flowscope so that second view can get the results - I am worried about memory in this scenario. If I call the method in second view- on-entry, I can put the personList in viewscope but how do I handle cases 2,4 and 5?
Thanks in advance!

You've answered part of your own question already:
Using an action-state to invoke business logic gives you a bit more control on how you react to different outcomes: go here on-exception, go somewhere else if everything is fine, and so on. The downside being that you need to put data in a longer lived scope (flow scope) to make it available to the view-state.
Using a view-state allows you to use a short lived scope (view scope or request scope) by retrieving the data using a render-action or entry-action, but doesn't give you much control when it comes to processing action outcomes.
Selecting between the two is a bit of a trade-of. If the search results can get very big, using a short lived scope to reduce memory load as much as possible is probably an important concern, so you would use a view-state. In other scenarios an action-state might be more appropriate.
One alternative approach is to introduce an application controller (invoked from the flow) that calls the business logic and returns an object that contains several things:
the search results
a potential info/warning message to display
You could then use a view-state and the view would be able to simply pick up the search results and info/warning message from the result object and display it.

Related

Access multiple request params in flow

I have an action state in a Spring Web Flow that take in parameters from a submitted form:
<action-state id="newToken">
<set name="requestScope.timestamp" value="requestParameters.timestamp" type="java.lang.String"/>
<set name="requestScope.origin" value="requestParameters.origin" type="java.lang.String"/>
<set name="requestScope.tokenHmacToValidate" value="requestParameters.tokenHmacToValidate" type="java.lang.String"/>
<transition to="validateToken"/>
</action-state>
However, only the first requestParameters value gets set (i.e. if timestamp is first, then only it gets set. If origin is first, then only it gets set). When I access the second and third values, they have a value of null instead of the value that is passed into it. Here is an example of form data that is passed on form submission:
_eventId=tokenValidationEvent
origin=https%3A%2F%2Flocalhost%3A8443
timestamp=20200218171041
tokenHmacToValidate=**REDACTED**
All the information is getting passed when the form is submitted, but only the first <set> tag is actually setting data. Am I receiveing the request wrong? Is there something I need to register somewhere that I'm not doing
This is the way <action-state> works. Only the first expression is evaluated.
If you want all three to be evaluated, you could use <on-entry> to evaluate the other 2:
<action-state id="newToken">
<on-entry>
<set name="requestScope.timestamp" value="requestParameters.timestamp" type="java.lang.String"/>
<set name="requestScope.origin" value="requestParameters.origin" type="java.lang.String"/>
</on-entry>
<set name="requestScope.tokenHmacToValidate" value="requestParameters.tokenHmacToValidate" type="java.lang.String"/>
<transition to="validateToken"/>
</action-state>
From https://docs.spring.io/spring-webflow/docs/current/reference/html/actions.html#action-state
After the execution of each action, the action-state checks the result to see if matches a declared transition to another state. That means if more than one action is configured they are executed in an ordered chain until one returns a result event that matches a state transition out of the action-state while the rest are ignored. This is a form of the Chain of Responsibility (CoR) pattern.
The result of an action's execution is typically the criteria for a transition out of this state. Additional information in the current RequestContext may also be tested as part of custom transitional criteria allowing for sophisticated transition expressions that reason on contextual state.
Note also that an action-state just like any other state can have one more on-entry actions that are executed as a list from start to end.

Avoid deletion of an object (using IObjectWillBeRemovedEvent) and do a redirect to a custom view/template?

I would like to abort the deletion of an object (A custom Content-Type), and redirect to a page (a view) that sets the workflow to a custom state named Unavailable, shows a message to the user "You succesfully deleted the object!". The object will still be on ZODB, but for some groups it'll simply not be seen, as if it was really deleted.
I can do a raise in a subscriber using IObjectWillBeRemovedEvent, but trying to use raise zExceptions.Redirect("url") doesn't work. The raise call avoids the deletion, but a message "The object could not be deleted" is shown instead of the redirection.
Anyone has a solution to this scenario?
As you can see Plone / Zope 2 object management is messy (yes, I am willing to burn karma just to say this). You need to override delete action in the user interface level, not on the object level.
Try to figure out how to customize delete actions in Plone user interface.
Make sure the default Delete actions is no longer visible and available (e.g. set higher needed permission for it e.g. cmf.ManagePortal)
Create another Delete action which goes according to your specialized workflow
I believe Delete can be configured from portal_actions, but there might be separate cases for deleting one object (Actions menu) and deleting multiple objects (folder_contents).
You need REQUEST.response.redirect("url"). I'm pretty sure that zExceptions.Redirect is the way that Zope internally handles response.redirect() calls. Be sure you still raise another exception after calling redirect() so that the transaction is aborte.
That said, this sure seems like the wrong way to accomplish this. For one thing, you'll do at least double indexing, which is done before the transaction aborts. Catalog indexing is the most expensive part of processing a request that modifies content so this creates wasteful load on your server.
Events are for doing additional stuff which is only tangentially related to the event. What you want is to fundamentally change what happens when someone deletes. Maybe you should patch/override the underlying deletion method on the container objects (folders?) to do your worklfow transition.
You could raise a OFS.ObjectManager.BeforeDeleteException in the event handler to stop the deletion. If you raise a LinkIntegrityNotificationException you get redirected to Plones nice Link intergrity page.
from OFS.interfaces import IObjectWillBeRemovedEvent
from plone.app.linkintegrity.exceptions import LinkIntegrityNotificationException
import grok
#grok.subscribe(ICSRDocument, IObjectWillBeRemovedEvent)
def document_willbemoved(doc, event):
raise LinkIntegrityNotificationException(doc)

custom validator against remote object

I have a need to validate a field against our database to verify unique-ness. The problem I seem to be having is that the validators doValidation() exits before we've heard back from database.
How can I have the validator wait to return its payload until after we've heard from the DB?
Or perhaps a better question might be (since I think the first question is impossible), how can I set this up differently, so that I don't need to wait, or so that the wait doesn't cause the validation to automaticallly return valid?
If you're using a remote object, you can specify the method call inside your remote declaration and assign a function to the result call. The result call only runs once the remote server returns something, so it won't be run before your validation.
Do your validation call in said result function call (which you will have to create) and you should be good. Your code should go something like this:
<s:RemoteObject id="employeeService"
destination="ColdFusion"
source="f4iaw100.remoteData.employeeData"
endpoint="http://adobetes.com/flex2gateway/"
result="employeeService_resultHandler(event)"/>
**<s:method name="dataCheckCall" result="dataCheckResult(event)"/>**
<s:RemoteObject />
And in your script:
function protected dataCheckResult(event:ResultEvent):void {
**doValidate();**
}
Edit: As soon as you call "dataCheckCall" the method will start running. If, for whatever reason, you want to call this WITHIN your validator, you can do so, and then dataCheckResult will run whenever it returns with it's payload (pretend doValidate is called elsewhere). I've left a message below as well.
You are trying to fit an asynchronous process (fetching data from a DB) into a synchronous process (checking all the validators in turn).
This won't work...
You'll need to either roll your own validator framework, or use a different method of determining the legality of your controls.
P.S. The MX validators are rubbish anyway!
What I've managed to do, seems to work, mostly. I don't like it, but it at least performs the validation against the remote source.
What I've done, then, is to use an 'keyUp' event handler to spin off the database lookup portion. In the meanwhile, I set up a string variable to act as some kind of a Flag, which'll be marked as 'processing'. When the response event fires, I'll examine its contents, and either clear the flag, or set it to some kind of other error.
Then, I have created a new 'EmptyStringValidator' will check the contents of this flag, and do its job accordingly.
Its indirect, but, so far, seems to work.

WPF binding isAsync Get State

I am using Binding IsAsync property to keep UI responsive and loading data from the get accessor of the property and proved to be a good option while using MVVM. This approach is great and doesn't need any manual code for async operations. There are few instances where my dataload is taking few seconds and during this time it is very difficult to differentiate between "no data" vs "data loading". Is there a property which I can detect the state of the binding "IsBusy" or "Loading", so that I can show some message that the loading operation is not complete?
Any help is appreciated.
I know, its an old thread. But if anybody is still interested...
You could use PriorityBinding, there is a superbly explained example in this article:
http://www.switchonthecode.com/tutorials/wpf-tutorial-priority-bindings
The idea is to stipulate a PriorityBinding which in turn defines several regular bindings like this:
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock.Text>
<PriorityBinding>
<Binding ElementName="MainWindow" Path="Slow" IsAsync="True" />
<Binding ElementName="MainWindow" Path="Fast" />
</PriorityBinding>
</TextBlock.Text>
</TextBlock>
The order of the bindings decides the priority, with the highest priority first. In this case the Fast binding (lowest priority) will populate the textblock immediately because you might have that bound to a string property "Loading..." or "Sorting..." depending on what is happening at the time, and there is no delay.
But later when the slow async binding's property returns a value, it's higher priority means it will then take over, since it is earlier in the list, and its results will be bound instead, showing actual results.
If you need to populate a progress popup you may be able to implement that in the getter of the bound property in your ViewModel, though I haven't tried anything like this.
According to the docs,
While waiting for the value to arrive, the binding reports the FallbackValue, if one is available, or the default value of the binding target property.
You can use this value to display a message to the user while the binding is loading.

Statefinalization/initialization activity only runs on leaf states

I am trying to get my Windows State Machine workflow to communicate with end users. The general pattern I am trying to implement within a StateActivity is:
StateInitializationActivity: Send a message to user requesting an answer to a question (e.g. "Do you approve this document?"), together with the context for...
...EventDrivenActivity: Deal with answer sent by user
StateFinalizationActivity: Cancel message (e.g. document is withdrawn and no longer needs approval)
This all works fine if the StateActivity is a "Leaf State" (i.e. has no child states). However, it does not work if I want to use recursive composition of states. For non-leaf states, StateInitialization and StateFinalization do not run (I confirmed this behaviour by using Reflector to inspect the StateActivity source code). The EventDrivenActivity is still listening, but the end user doesn't know what's going on.
For StateInitialization, I thought that one way to work around this would be to replace it with an EventDrivenActivity and a zero-delay timer. I'm stuck with what to do about StateFinalization.
So - does anyone have any ideas about how to get a State Finalization Activity to always run, even for non-leaf states?
Its unfortunate that the structure of "nested states" is one of a "parent" containing "children", the designer UI re-enforces this concept. Hence its quite natural and intuative to think the way you are thinking. Its unfortunate because its wrong.
The true relationship is one of "General" -> "Specific". Its in effect a hierachical class structure. Consider a much more familar such relationship:-
public class MySuperClass
{
public MySuperClass(object parameter) { }
protected void DoSomething() { }
}
public class MySubClass : MySuperClass
{
protected void DoSomethingElse() { }
}
Here MySubClass inherits DoSomething from SuperClass. The above though is broken because the SuperClass doesn't have a default constructor. Also parameterised constructor of SuperClass is not inherited by SubClass. In fact logically a sub-class never inherits the constructors (or destructors) of the super-class. (Yes there is some magic wiring up default constructors but thats more sugar than substance).
Similarly the relationship between StateAcivities contained with another StateActivity is actually that the contained activity is a specialisation of the container. Each contained activity inherits the set of event driven activities of the container. However, each contained StateActivity is a first class discrete state in the workflow same as any other state.
The containing activity actual becomes an abstract, it can not be transitioned to and importantly there is no real concept of transition to a state "inside" another state. By extension then there is no concept of leaving such an outer state either. As a result there is no initialization or finalization of the containing StateActivity.
A quirk of the designer allows you to add a StateInitialization and StateFinalization then add StateActivities to a state. If you try it the other way round the designer won't let you because it knows the Initialization and Finalization will never be run.
I realise this doesn't actually answer your question and I'm loath to say in this case "It can't be done" but if it can it will be a little hacky.
OK, so here’s what I decided to do in the end. I created a custom tracking service which looks for activity events corresponding to entering or leaving the states which are involved in communication with end users. This service enters decisions for the user into a database when the state is entered and removes them when the state is left. The user can query the database to see what decisions the workflow is waiting on. The workflow listens for user responses using a ReceiveActivity in an EventDrivenActivity. This also works for decisions in parent ‘superstates’. This might not be exactly what a "Tracking Service" is meant to be for, but it seems to work
I've thought of another way of solving the problem. Originally, I had in mind that for communications I would use the WCF-integrated SendActivity and ReceiveActivity provided in WF 3.5.
However, in the end I came to the conclusion that it's easier to ignore these activities and implement your own IEventActivity with a local service. IEventActivity.Subscribe can be used to indicate to users that there is a question for them to answer and IEventActivity.Unsubscribe can be used to cancel the question. This means that separate activities in the State's inialization and finalization blocks are not required. The message routing is done manually using workflow queues and the user's response is added to the queue with appropriate name. I used Guid's for the queue names, and these are passed to the user during the IEventActivity.Subscribe call.
I used the 'File System Watcher' example in MSDN to work out how to do this.
I also found this article very insructive: http://www.infoq.com/articles/lublinksy-workqueue-mgr

Resources