I am working on a web application with spring web flow. While I am working on a flow.xml file I have get a decision state on it like this -
<decision-state id="checkPermissin">
<if test="requestParameters.canApprove" then="approve" else="warning" />
</decision-state>
When a request comes to the flow.xml then it get the request parameter canApprove from it and test whether it is true or false. Then it goes to the either of approve or warning state.
My question is - can I log/print the status of canApprove from the flow.xml file?
You can add an on-exit tag to the end of the 'decision-state' and call any service methods, spring beans, or static methods on the classpath.
Try this (untested):
<decision-state id="checkPermissin">
<if test="requestParameters.canApprove" then="approve" else="warning" />
<on-exit>
<evaluate expression="T(org.apache.log4j.Logger).getLogger('someLogger').info(requestParameters.canApprove)"/>
</on-exit>
</decision-state>
The above solution is more of a hack to fulfill what you are asking for. The "proper" way to log this is to extend a FlowExecutionListenerAdapter and to listen for your current flow + decision-state id "checkPermissin" then log whatever you desire about that flow but it would involve more setup/coding outside the flow.xml file. (see: Catch "dead" session in Spring webflow: the example is for catching exceptions but can easily be adapted for logging anything in a flow)
Related
I have the following configuration:
<int:channel id="responseXmlChannel">
<int:interceptors>
<int:wire-tap channel="responseXmlLogger" />
</int:interceptors>
</int:channel>
<int:logging-channel-adapter id="responseXmlLogger"
logger-name="test.SyncResponseLogger" level="DEBUG"
expression="'message received, headers:' + headers + ' payload:' + payload"/>
I am trying to add some session attributes to be logged. Current user for example.
I was taking a look at the header-enricher approach, but I could not find a good example of adding session attributes to the header using it.
Is there a way to do what I am trying to do? Is there other approach besides using the headers to add custom attributes to be logged?
My goal is to have in the logs something like
current user: $UserName. Payload: $Payload
OK. So, what is your question then? How to use header-enricher? I'm not sure what is your "session" object to be precise with the answer, but header-enricher can be configured to call any arbitrary method on beans. See its docs: https://docs.spring.io/spring-integration/docs/current/reference/html/message-transformation.html#header-enricher.
The other approach is too deal with ThreadLocal. That's exactly what the RequestContextHolder in Spring Web does for RequestAttributes with its HTTP session access. Although you need to keep in mind that moving to a different thread with queue or executor channels in between will lose the current thread context and you won't have access to that ThreadLocal. So, headers in the particular message is not so bad choice to do.
I have an HTTP Inbound Gateway in my Integration Application, which I will call during some save operation. It's like this. If I have one product, I will call the API once, and if I have more than once, then I will call multiple times. The problem is, for single invoke, SI works just fine. But for multiple calls, request and response get messed up. I thought Spring Integration Channels are just like MQ's, but it is not?
Let me explain this. Let's say I have 2 products. First, I invoke SI for Product A and then for B. Response of A got mapped to request B! It happens all the time. I don't want to use some dirty hacks like wait for the first response to come and invoke again. This means the system has to wait for a long time. I guess we can do it in Spring Integration using task executor, but with all the basic samples out there, I can't find the right one. So please help me find out how can I fix this issue!
My Configuration is :
<int:channel id="n2iMotorCNInvokeRequest" />
<int:channel id="n2iMotorCNInvokeResponse" />
<int:channel id="n2iInvoketransformerOut" />
<int:channel id="n2iInvokeobjTransformerOut" />
<int:channel id="n2iInvokegatewayOut" />
<int-http:inbound-gateway id="i2nInvokeFromPOS"
supported-methods="GET"
request-channel="i2nInvokeRequest"
reply-channel="i2nInvokeResponse"
path="/postProduct/{Id}"
mapped-response-headers="Return-Status, Return-Status-Msg, HTTP_RESPONSE_HEADERS"
reply-timeout="50000">
<int-http:header name="Id" expression="#pathVariables.Id"/>
</int-http:inbound-gateway>
<int:service-activator id="InvokeActivator"
input-channel="i2nInvokeRequest"
output-channel="i2nInvokeResponse"
ref="apiService"
method="getProductId"
requires-reply="true"
send-timeout="60000"/>
<int:transformer input-channel="i2nInvokeResponse"
ref="apiTransformer"
method="retrieveProductJson"
output-channel="n2iInvokeRequest"/>
<int-http:outbound-gateway request-channel="n2iInvokeRequest" reply-channel="n2iInvoketransformerOut"
url="http://10.xx.xx.xx/api/index.php" http-method="POST"
expected-response-type="java.lang.String">
</int-http:outbound-gateway>
<int:service-activator
input-channel="n2iInvoketransformerOut"
output-channel="n2iInvokeobjTransformerOut"
ref="apiService"
method="productResponse"
requires-reply="true"
send-timeout="60000"/>
The i2nInvokeFromPOS gateway is what we call from Web Application which is where all the products will be created. This Integration API will fetch that data, and post it to the backend system so that it will get updated to the other POS locations too!
Steps :
I will send the productId to i2nInvokeFromPOS.
apiTransformer -> retrieveProductJson() method will fetch the product details from DB based on the ID
Send the Request JSON to Backend system using http:outbound-gateway
Get the response from Backend and update the product status as uploaded in DB. Happens in apiService -> productResponse()
Once the response for A is received, all I'm getting is HTTP 500 Error for the Request B! But the Backend API is just fine.
The framework is completely thread-safe - if you are seeing cross-talk between different requests/responses then one (or more) of your components that the framework is invoking is not thread-safe.
You can't keep state in fields in, for example, code invoked from a service activator.
I am working currently on an API that is exposed by Mule ESB 3.5.0 (non-EE). This API accepts a XML file with accounts to be imported via HTTP and puts this task definition into a RabbitMQ queue. Another Mule flow is responsible for taking items from the queue one-at-a-time (thanks to processingStrategy="synchronous") and feeding them to the platform core. The queue is required as the core is able to process one-file-at-a-time.
The setup above is up & running smoothly. What I would like to achieve now, is to enable our customers to troubleshoot the integration by exposing an HTTPS endpoint, where import statuses will be available (identified by some GUID and SHA1 of the request).
I created a simple POJO component that handles the logic of adding the status updates, the method signature being:
void addStatus(final String guid, final String status)
I managed to invoke the method above by defining the bean as
<bean id="importStatusComponent" class="com.example.ImportStatusComponent" />
and invoking the java-component in the Mule flow with:
<invoke object-ref="importStatusComponent" method="addStatus"
methodArguments="#[flowVars.guid], Import started"
methodArgumentTypes="java.lang.String, java.lang.String" />
As we would like to expose this to customers and allow them to implement some programmatic checking of the status, I decided to change the status type to an enum-based dictionary ImportStatusEnum.
Unfortunately, I am unable to fed enum into MEL that goes into <invoke methodArgument=""> tag attribute.
Examples of what I have tried:
1) Arguments as two separate MEL expressions.
<configuration>
<expression-language>
<import class="com.example.ImportStatusEnum" />
</expression-language>
</configuration>
<invoke object-ref="importStatusComponent" method="addStatus"
methodArguments="#[flowVars.guid], #[ImportStatusEnum.STARTED]"
methodArgumentTypes="java.lang.String, com.example.ImportStatusEnum" />
2) Arguments as a single MEL expression.
<configuration>
<expression-language>
<import class="com.example.ImportStatusEnum" />
</expression-language>
</configuration>
<invoke object-ref="importStatusComponent" method="addStatus"
methodArguments="#[flowVars.guid, ImportStatusEnum.STARTED]"
methodArgumentTypes="java.lang.String, com.example.ImportStatusEnum" />
3) Fully qualified class names instead of imports (not shown here).
How to pass an enum value as method argument to invoke component in Mule? Any help will be highly appreciated :)
This one will work
<invoke object-ref="importStatusComponent" method="addStatus" methodArguments="#[flowVars.guid], #[com.example.ImportStatusEnum.STARTED]" methodArgumentTypes="java.lang.String, com.example.ImportStatusEnum" />
Expectation:
When i attempt booking, booking has been failed due to price change and throwing below price change exception.
Then i need to capture that errors in event and redirect the request to previous page and display the error message on top of the page.
As per my below logic i can able to redirect the request to previous page, while throwing price change exception but the issue is i can't able to
display my error message (Your booking price has been changed, please choose another) on previousPage.
Anybody can help me out. Thanks in advance.
BookAction.java
public class BookAction {
private static final String PRICE_CHANGED_ERROR = "Your booking price has been changed, please choose another";
public Event book(RequestContext context) {
try {
// Booking attempt
// Consider booking attempt failed and throwing price change exception
}catch (PriceChangeException priceChangeException) {
return handlePriceChange(context);
}
}
public Event handlePriceChange(RequestContext context) {
Errors pageErrors = getPageErrors(context);
if (true) { // Consider if its true and returning new Event "searchPage"
pageErrors.reject(PRICE_CHANGED_ERROR);
return new Event(this, "searchPage");
}
return error();
}
}
booking.xml
<webflow:flow>
<webflow:action-state id="book">
<webflow:evaluate expression="bookAction.book" />
<webflow:transition on="success" to="confirm" />
<webflow:transition on="error" to="handleFrd" />
<webflow:transition on="searchPage" to="searchPage" />
</webflow:action-state>
<webflow:end-state id="searchPage" view="externalRedirect:#{flowScope.bookState.searchResultURI}" />
</webflow:flow>
Unfortunately, this is a poorly documented part of SWF. What you are looking for is to use the flashScope. flashScope was specifically designed for this purpose after a variable in the flashScope is read once it is automatically deleted.
But... the design problem in SWF is that the flashScope only works between a Parent flow and an embedded child flow inside of it. It will not hold save your variables if you use an "externalRedirect" and try to access the error using "flashScope.error"... because technically the first flow ended and a new flow has begun. So how to solve this problem?
I was only able to solve it with 2 ways and both are hacks.
Option 1.
if the message is simple then in the 'externalRedirect:#{flowScope.bookState.searchResultURI}' append a HTTP GET param such as
?msg=An Error Occurred.
Although this will look ugly in the url bar, it is simpliest and the least painful solution.
Option 2.
is a bit more painful but provides the most flexibility if you want complex error msgs. Basically you have to merge the flashScope of Spring Web Flow and the flashMap of Spring MVC and send your externalRedirect to a spring mvc controller first.
You will need to create and register a FlowExecutionListenerAdapter
to merge (hack) the flashScope and the flashMap see the link directly below for step by step instructions (ignore the stuff about sessionMap)
Spring web flow how to add flash attribute when exiting from flow to external redirect
Then once you achieve that you can easily access your flash variable error msg using Spring MVC like so:
How to read flash attributes after redirection in Spring MVC 3.1?
Note: the variable will already be in the flashMap of spring mvc. So all you have to do is render a view in spring mvc and access the variable from the view.
My 2 cents: because of limitations like this I've personally stopped using webflow and only use Spring MVC exclusively now. I think Spring Web flow is still useful for very simple use cases (flow A -> flow B -> flow C) but if you're trying to to do something more complex or are trying to learn SWF from scratch right now... my advice to you is to just use Spring MVC... it will save you a lot of time in the long run. I think they are working on a major revision for SWF (3.0) maybe then all these limitations will get worked out
Indeed Spring Web Flow has a number of scopes where variables can be stored but when a flow ends all scopes end as well, unless the flow is a child flow delegating control back to the parent flow in which case you can use flash scope in addition to any parent flow scoped variables.
Spring Web Flow also knows how to interact with the Spring MVC flash scope. So when a top-level flow ends and the redirect is to a Spring MVC endpoint, there is a way to indicate that output variables should be put in Spring MVC flash scope, which would make them available after the redirect.
This is mentioned in the reference documentation http://docs.spring.io/autorepo/docs/webflow/current/reference/html/spring-mvc.html#spring-mvc-flash-output.
in booking.xml, add below changes
<webflow:transition on="searchPage" to="searchPage">
<set name="flowScope.error" value="'text u wanna show in next page'" type="string" />
</webflow:transition
And now in the redirected page try reading that text. hope it would work!
Use flash scope:
In xml
<set name="flashScope.flashScopeAttribute" value="'f'" />
In JSP
flash scope: #{flashScopeAttribute}
I'm using Spring Security to Secure my Web App.
I have a page where I show foo objects for administrators.
<intercept-url pattern="/show_foo/**" access="hasRole('ROLE_ADMIN')" />
But now I have a requirement that a foo cannot be seen by all the Administrators, for example only administrators with city="New York" can access to the element.
I've did something in my controller to solve this :
#RequestMapping(method=RequestMethod.GET,value="/show_foo"
public ModelAndView showfunction(Principal user)
{
User user2 = userService.getUserByName(user.getName());
if(/* some checks on user2 */)
/* show page */
else
/* show error page*/
}
So my question is : can I avoid the database call, because I need this almost in all of my pages and I find it ugly to check each time at the top of any controller the same thing over and over. Is there a Spring Security feature for this kind of use cases?.
With Expression based rules you can accesss principal even on rule. See: http://docs.spring.io/spring-security/site/docs/3.0.x/reference/el-access.html
For example if you can include needed information on principal object.
<intercept-url pattern="/show_foo/**" access="hasRole('ROLE_ADMIN') and principal.name=='xyzzy' " />
you have to put in some logic.
1.) Either load the user and country mapping and store somewhere in Static HashMap, remember to update the map if any changes done in mapping, can store same at session level.
2.) Load entries in 2nd level cache, or make queries cacheable, enable query caching as well.
You need to integrate Spring Security with Domain-ACLs. See a full explanation here.
Yo can consider mapping the relationship between Administrators and Cities using
ACL_OBJECT_IDENTITY instances.