I am new to Spring framework learning Spring mvc and spring web flow . I came across an evaluate expression in action state
<evaluate expression = " requestParameters.ishotelbooking" result="flowScope.hotelbooking" />
Couldn't able to figure it out what is actually happening ?? And one more thing i understand that we are assigning flow scope for hotelbooking object but how the framework understands the hotelbooking is the object of hotel class ie how we can understand the type of object here .. can someone guide me .. Thanks in advance 😊
This evaluate expression is taking the "isHotelBooking" request parameter and copying it into a variable "hotelBooking" that will be available in the entire flow (flowScope)
So when the action state is called, probably after a transition, in a request like "flowTransition?isHotelBooking=true" the request parameter "isHotelBooking" is only available in the request. So the evaluate element is copying that into another variable available in the entire flow.
Basically extending the scope of the variable from request to flow scope
FYI this could be replaced by
<set name="flowScope.hotelBooking" value="requestParameter.isHotelBooking"/>
[UPDATE]
For the type, the evaluate element has a result-type attribute that can be used to further define the type of the result. if not specified Webflow assumes it is of type Object.
The class/type is not always important since evaluation is done at runtime and using EL. Although it is useful if you are using an IDE (like IntelliJ or STS) so you can take advantage of auto-completion.
Related
I'm migrating to SpringMVC and Apache Tiles 3 from a Strut1 + Tiles project. I know only a little about Struts1+Tiles, it is too old and I'm stuck in Controller and ComponentContext in Struts-tiles. According to document from apache website, it was replaced by ViewPreparer and AttributeContext but I dont know the following line means:
ComponentContext compContext=(ComponentContext)pageContext.getAttribute(ComponentConstants.COMPONENT_CONTEXT,PageContext.REQUEST_SCOPE);
What is ComponentConstants.COMPONENT_CONTEXT? and how to change ComponentContext to AttributeContext
Please Help, Thanks.
Bidi, there are 2 ways of getting an AttributeContext:
The first one, like mck stated: through "org.apache.tiles.AttributeContext.STACK" key of request scope. However, the value is a STACK that contains 2 elements of AttributeContext type. The one we need is the first element. IMHO, this way is limited because since the data structure is a stack, getting also mean removing from the stack according to FIFO rule, so you can use the object for only once.
I am using the second way in my project. Because the execute() method of ViewPreparer already have a parameter of AttributeContext type, and this method is always called each time a page is rendered, so you can use this object to do the thing you want (or put it in request) when overriding the method.
AttributeContext is just a collection of key/value pairs. Normally, people use it to get access to some values which are attributes in the template, so fetching the values and putting them to the request can save the overhead. You can also create some static properties of the inheriting class and setting the values to them.
With the Spring-4 and Tiles-3 integration set up (there's spring docs on this as well as a number of good tutorials around) then the properties you put into spring's model map will be available in your jsps, this is not related to the AttributeContext.
AttributeContext only the other hand is (basically) only for holding the map of attributes. Attributes here are defined within a definition, used to identify template or string attributes (as is typically declared in you xml definitions), and come with properties of role, renderer, expression, and/or value.
If AttributeContext is what you are after: you can get hold of it through the current tilesContainer, and to get hold of the current container use the static TilesAccess, eg
TilesContainer tileContainer = TilesAccess.getCurrentContainer(request);
AttributeContext attributeContext = tilesContainer.getAttributeContext(request);
Bidi,
take a read of http://tiles.apache.org/framework/tutorial/advanced/runtime.html
particular the "Runtime Composition using APIs" section.
TilesContainer container = TilesAccess
.getContainer(request.getSession().getServletContext());
Request tilesRequest = new ServletRequest(
container.getApplicationContext(),
request,
response);
otherwise i suggest you take a dive into the Tiles codebase, it's not complicated code, especially the TilesAccess, Request, ApplicationContext stuff.
Building a web based application using Guice and have this peculiar situation -
I am using a lot of method interceptors that are lightweight.
My question is - I have a named binding say "Operation.Current" the value of which needs to be changed when one of these interceptors executes within a single request thread. So the bound value changes multiple times within a single request thread. I need this value to be injected as I need.
Currently I am using
request.setAttribute(Key.get(Operation.class, Names.named("Operation.Current")).toString(), op);
in my GuiceFilter to initialize the value. And I want to replace this value when the interceptors execute.
So I need to re seed my value present in the request as an attribute multiple times through the request.
What is a better way to solve this issue ? Since I see that the value is not in a real sense RequestScoped. So ideally this value should be non scoped and bound the name.
But how do I change the value as I need outside a Guice Module ?
You cannot modify the module binding once you created the injector. If you know all instances in advance, try the MapBinder, otherwise consider using a provider-binding, then you can evaluate the required instance dynamically for each call.
Our Topic object has BOTH isChannel and getChannel public methods. The object graph is too complex to change this. Channel has an Integer type.
We are migrating from one application server to Tomcat. When using this expression ${topic.channel.type}, in JSPs our current app server finds the getChannel method. However, Tomcat finds the isChannel method and we get errors since the return type is a Boolean, not a Channel. Is there a way to tell Tomcat to prefer getters over boolean public methods?
For now I'm just going to write a helper function or expose a new method, but I have a feeling I'm going to come across this quite a bit during the migration.
Unfortunately, you can't force a method call like that.
I have checked the Javabeans and EL specifications, but nowhere is specified what the preferred method is when both isXXX() and getXXX() methods are present. However, I do agree that it makes more sense to prefer the getXXX() one in this particular case. This should also be programmatically possible. I think it's worth the effort to report this as an issue against the Tomcat EL implementation.
In theory, this should be more of a JavaBeans issue than an EL implementation issue. One thing you might try is to find out how the java.beans.Introspector views your Topic class. One way to do that would be to run this code I wrote a while back for the Struts wiki. Depending on the complexity of your class, it might make sense to create an explicit java.beans.BeanInfo class to force the channel property to always be exposed as an Integer.
I got pretty big webflow definition, which I do not want to copy/paste for reusing. There are references to action bean in XML, which is kind natural.
I want to use same flow definiton twice: second time with actions configured differently (inject different implementation of service to it).
Is there easy way to do this?
Problem is I want to use same flow with different beans at once, in the same app. Copy/Paste is bad, but I dont see other solution for now.
You could try creating a new flow that extends the "pretty big one" and adding flowExecutionListeners to it.
The interface "FlowExecutionListener"defines methods for the following events in flow execution:
requestSubmitted
requestProceessed
sessionCreating
sessionStarting
sessionStarted
eventSignaled
transitionExecuting
stateEntering
viewRendered
viewRendering
stateEntered
paused
resuming
sessionEnding
sessionEnded
exceptionThrown
You can write a handler that injects the required resources to your flow (and use different handles with different flows) by storing it in the RequestContext, where you can access it in your flow definition.
Note that in that case you would still have to modify the "pretty big flow" to use those resources instead of referencing the beans directly.
I'm in the same fix that you're in...i have different subclasses which have corresponding action beans, but a lot of the flow is the same. In the past we have just copied and pasted...not happy with that!
I have some ideas I am going to try out with using the expression language. First, I came up with an action bean factory that will return the right action bean to use for a given class, then i can call that factory to set a variable that i can use instead of the hard-coded bean name.
Here's part of the flow:
<action-state id="checkForParams">
<on-entry>
<set name="flowScope.clientKey" value="requestParameters.clientKey"/>
<set name="flowScope.viewReportBean"
value="reportActionFactory.getViewBean(reportUnit)"/>
</on-entry>
<evaluate expression="viewReportBean"/>
The evaluate in the last line would normally refer directly to a bean, but now it refers to the result of the "set" I just did.
Good news--the right bean gets called.
Bad news--anything in the flow scope needs to be Serializable, so I get a NotSerializableException--arggh!
I can try setting something on a very short-lived scope, in which case it will need to get called all the time...or I can figure out some kind of proxy which holds the real bean as a proxy declared "transient".
BTW, I am using Spring 2.5.6 and webflow 2.0.7. Later versions may have better ways of handling this; in particular, EL's have gotten some attention, it seems. I'm still stuck with OGNL, which is the Spring 1.x EL.
I'm sure some webflow guru knows other ways of doing things in a less clunky fashion...
I don't think you can use the same webflow definition with the actions configured in two different ways.
If you want to use different actions you'll either have to reconfigure your action beans then redeploy your app or create a separate webflow definition with the differently configured beans.
This is a great Spring WebFlow resource.
Try to refactor the common configurable part in a subflow, and call the subflow from the different main flows where you want to reuse it.
Pass parameters to the subflow to configure it in any way needed, using the spring expression language to pass different spring beans, etc.
In a flow definition, I am trying to access a bean that has a dot in its ID
(example: <evaluate expression="bus.MyServiceFacade.someAction()" />
However, it does not work. SWF tries to find a bean "bus" instead.
Initially, I got over it by using a helper bean to load the required bean, but the solution is inelegant and uncomfortable. The use of alias'es is also out of the question since the beans are part of a large system and I cannot tamper with them.
In a nutshell, none of the solution allowed me to refernce the bean directly by using its original name. Is that even possible in the current SWF release?
I was able to do this by using both the bean accessor (#) symbol and single-quotes around the name of the bean.
Using your example: #{#'bus.MyServiceFacade'.someAction()}
This is a restriction of the EL parser (generally either OGNL or jboss-el for Spring Web Flow). EL uses dot notation for parsing the navigation chain,causing the initial behavior you describe (attempting to find the "bus" bean).
Try:
['bus.MyServiceFacade'].someAction()
or
'bus.MyServiceFacade'.someAction()
This may work, or it may not...but similar things are used in the Expression Language for JSPs.
In my experience, anything with a getter method can be accessed via dot notation. In your example, whatever object is being represented by the bus bean needs to have a getServiceFacade method and that the object returned by getServiceFacade would need to have a getSomeAction method.