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.
Related
I have a flow that makes two calls to a database to insert data, and one call to a stored procedure on another database. I would like the two insert calls to be processed as a transaction, i.e. either they both insert or neither does. I would also like the call to the stored procedure to be asynchronous, as I don't need to wait for a response and any errors during the stored procedure call should not impact the main message processing. Here's how I configured my flow:
<flow name="saveBrowser" doc:name="saveBrowser" >
<http:inbound-endpoint exchange-pattern="request-response" host="${my.host}" port="${my.port}" path="Browser/Save" doc:name="/Browser/Save" />
...assorted transformations...
<json:json-to-object-transformer doc:name="JSON to BrowserData" returnClass="com.mycompany.BrowserData"/>
<transactional action="ALWAYS_BEGIN" doc:name="Transactional">
<jdbc:outbound-endpoint exchange-pattern="request-response" queryKey="saveBrowser" queryTimeout="-1" connector-ref="firstJdbcConnector" doc:name="INSERT Browser"/>
<jdbc:outbound-endpoint exchange-pattern="request-response" queryKey="saveBrowserExtended" queryTimeout="-1" connector-ref="firstJdbcConnector" doc:name="INSERT BrowserExtended"/>
</transactional>
<async doc:name="Async">
<jdbc:outbound-endpoint exchange-pattern="one-way" queryKey="storedProcedureBrowser" queryTimeout="-1" connector-ref="secondJdbcConnector" doc:name="Stored Proc">
<jdbc:transaction action="NONE"/>
</jdbc:outbound-endpoint>
</async>
<json:object-to-json-transformer doc:name="BrowserData to JSON" sourceClass="com.mycompany.BrowserData" />
<catch-exception-strategy doc:name="Catch Exception Strategy">
...
</catch-exception-strategy>
</flow>
The error I get when I send a request to this flow, however, is that async scope can't be used with transactions. The documentation for transactional scope says, "You cannot build an asynchronous flow inside a transaction." However, the async scope is not within the transactional scope. It's adjacent but not within its scope. Why is this a problem?
I worked around this by passing the message to a separate flow containing the async scope and stored procedure call, via VM endpoints. If there's a better way to do this, I'd love to know.
I'm in community edition, FYI.
I have a little problem I can't solve so far. In BPEL I want to create an onAlarm eventHandler which fires immediatly (i.e. the "for" element is set to 'PT0S') and repeats every 2 seconds. This eventHandler shall contain a counter which increments every time the alarm fires.
The question is: How to initialize the counter? If the variable will be initialized within the onAlarm scope the value would not increment anymore. In the "normal" control flow the value also cannot be initialized, because it is not defined if the process or the onAlarm scope runs first. So I would get every now and then an uninitializedVariable exception.
My solution would be to not initialize the variable neither in the process scope nor in the onAlarm scope, but create a faultHandler wherein the variable will be initialized and afterwards the onAlarm flow will be executed. Problem is every uninitializedVariable execution will be caught now by this faultHandler and there may be another too.
So is there another possibility to deal with this problem or can I somehow find out which variable wasn't initialized properly so the faultHandler can get two control flows?
The solution should work on every BPEL engine.
Thanks, Michael
You can initialize a variable with a default value on its definition using a from-spec just like in an assignment. See section 8.4.1 of the spec for the details.
A default initialization can look like this:
<variables>
<variable name="Default" type="xsd:int" >
<from>5</from>
</variable>
</variables>
This should work as eventHandlers are installed after the start activity of a process has completed. By then, the variables defined in the root scope have already been initialized. To quote the spec, section 12.1:
Scope initialization consists of instantiating and initializing the
scope's variables and partner links; ... If a scope contains an
initial start activity then the start activity MUST complete before
the event handlers are installed.
So much for spec. I think nobody can tell whether this "works on every BPEL engine". As far as I know, it works on bpel-g, Orchestra and EasyBPEL, but not on Apache ODE or OpenESB.
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.
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.
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.