How to obtain httpServletRequest in validate method of SpringWebFlow - spring-webflow

I need to access httpServletRequest in the validate method of spring webflow. Please help me how to do this.
My webflow for view state is:
<var name="search" class="com.test.form.Search"/>
...................
<view-state id="search" model="search" view="searchPage">
<transition on="submit" to="searchAction">
</transition>
</view-state>
...............
validate method in search model class is:
public void validateLoanSearch(ValidationContext context) {
//I need to get a httpServletRequest here...
}
In action/controller class I can get it thru RequestContext but ValidationContext gives only the messageContext. Any idea? Please help.

I got the solution.
In the bean class or validator class inside the validate method use this:
RequestContext rc = RequestContextHolder.getRequestContext();
RequestContextHolder is becoming available inside the validate method.

Related

Spring Webflow: Can Validators Be Manually Set Per Flow?

I have multiple flow configured in my application:
<flow:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices" >
<flow:flow-location id="reservation1" path="/WEB-INF/flows/flow1.xml" />
<flow:flow-location id="reservation2" path="/WEB-INF/flows/flow2.xml" />
</flow:flow-registry>
These two flows use separate classes for their model attribute, call them Flow1DTO.java and Flow2DTO.java. However, they use a set of common JSPs/Tiles for their actual interface.
<form:form modelAttribute="reservationForm">
<!-- etc -->
</form:form>
Is it possible to define a separate Validator class per flow?
You can define a multiple validation methods for the same Model class for each specific view-state id. Where each custom validation method maps to a specific webflow view-state id.
Your validator class name for your model needs to be defined with the name "${model}Validator" (and have #Component annotation) and each validation method name needs to be named like this "validate${state} ([ModelClassType] model, ValidationContext context)"
So lets say you have a model class called "Reservation.java" and 2 different flows definition that use this model and each flow definition having a view-state definitions of
<!-- defined in your first flow file -->
<view-state id="ReservationSameDayViewState" view="sumting" model="reservationForm">
</view-state>
....
<!-- defined in your 2nd flow file -->
<view-state id="ReservationFutureViewState" view="sumting" model="reservationForm">
</view-state>
The validator class for Reservation model would like this:
#Component
public class ReservationValidator {
public void validateReservationSameDayViewState(Reservation reservation, ValidationContext context) {
// perform custom validation for first flow
}
public void validateReservationFutureViewState(Reservation reservation, ValidationContext context) {
// perform custom validation for 2nd flow
}
}
http://docs.spring.io/autorepo/docs/webflow/2.4.x/reference/html/views.html#view-validation-programmatic-validator
Also, it is bad practice to define your flows with integer increments. Change your flow registry definition to look like this. This way you don't have to keep manually adding flows to it every time you create new flows.
<flow:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices" base-path="/WEB-INF/flows">
<flow-location-pattern value="/**/*-flow.xml" />
</flow:flow-registry>
note: the flow names end with "-flow.xml". This is the unofficial standard way to define a flow-registry and flow names.
I figured out a solution that allowed me to validate multiple forms with a single implementation of a form validator.
The code looked something like this:
public void validateMethodName(Flow1DTO dto, ValidationContext context) {
valMethodName(dto, context);
}
public void validateMethodName(Flow2DTO dto, ValidationContext context) {
valMethodName(dto, context);
}
private void valMethodName(CommonFlowDTO dto, ValidationContext context) {
// do stuff
}
Putting validation methods in the DTO classes themselves was not an option. Validation required calls to the database, which would have coupled the DTO objects to the business logic and made the creation of the DTOs somewhat more complex.
I discovered that the validation methods could not specify an interface, leading to the duplicate methods for each concrete DTO class.

Submit form to Servlet CQ5

I created a Servlet call ShippingDetailsServlet.java and deployed it. I need to submit a HTML form to it. I am not sure what path I should put in the form action. Below is the form.
<form action="/services/mycompany/ShippingDetailsServlet" method="post">
Country: <input type="text" name="country" value="au"><br>
Quantity: <input type="text" name="quantity" value="1">
<cq:include path="./submit" resourceType="foundation/components/form/submit" />
Please let me know what path should I give for the form action so that it can be submitted to the Servlet.
Below is the Servlet.
package mycompany.servlets;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.sling.SlingServlet;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import javax.servlet.ServletException;
import java.io.IOException;
import java.io.PrintWriter;
#SlingServlet(
paths={"/services/mycompany/"}
)
#Properties({
#Property(name="service.pid", value="mycompany.ShippingDetailsServlet",propertyPrivate=false),
#Property(name="service.description",value="Shipping details servlet", propertyPrivate=false),
#Property(name="service.vendor",value="mycompany", propertyPrivate=false)
})
public class ShippingDetailsServlet extends SlingAllMethodsServlet
{
#Override
protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException
{
//Do something fun here
}
#Override
protected void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException
{
//Do something fun here
PrintWriter out = response.getWriter();
out.println("Hello");
}
}
That is not necessarily true. You can deploy servlets on custom paths, but in order to do so you need to modify the "Apache Sling Servlet/Script Resolver and Error Handler" and add the custom path to the "Execution Paths" section.
Also, if this form is going to be deployed to your publish instance, you may want to use a custom path other than /bin/ because CQ has a lot of admin servlets registered under /bin and exposing them to the public may present a security concern.
By default, servlets can be deployed only below the /bin path. Don't put trailing / to the paths and don't add any additional properties. Eg. use following annotation:
#SlingServlet(paths={"/bin/services/mycompany"})
public class ShippingDetailsServlet extends SlingAllMethodsServlet
Path in the form should be the same as in #SlingServlet:
<form action="/bin/services/mycompany" method="post">
And if you really want to create servlet outside the /bin, you need to add appropriate path to the Execution Paths property in Apache Sling Servlet/Script Resolver and Error Handler configuration page in the /system/console/configMgr console.
Got the answer to my question from the forums.adobe.com
Answer is as below.
if you have annotated your servlet like this:
#SlingServlet(methods = { "POST" }, paths = "/apps/mycompany/servlets/GenericServlet")
the form shoud post to the same same url as in paths, that is "/apps/mycompany/servlets/GenericServlet"
so if you would change you "paths" line in the servlet to "/services/mycompany/ShippingDetailsServlet"
the form would post to that one.

Spring Webflow: No actions were executed

I'm trying to implement an Action in SWF but I get the same error even in the simplest example.
Error: "java.lang.IllegalStateException: No actions were executed, thus I cannot execute any state transition"
import org.springframework.webflow.execution.Action;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;
public class HelloAction implements Action {
#Override
public Event execute(RequestContext rc) throws Exception {
return new Event(this, "success");
}
I've declared the bean.
<bean id="helloAction" class="app.action.HelloAction"/>
And in flow.xml..
<action-state id="intermedio">
<action bean="helloAction"/>
<transition on="success" to="fin"/>
</action-state>
<end-state id="fin" view="final" />
It works fine if I don't use "HelloAction". But if I want to use Action in SWF, I always get the previous error.
Is something else needed?
Thanks in advance.
<action-state id="intermedio">
<evaluate expression="helloAction.execute()">
<transition on="success" to="fin"/>
</action-state>

Collection/List property won't bind or update on form submit

So I have set up a wizard interface with spring web flow that gradually populates a single form object/model. It works fine for the first few steps that have to populate single String or primitive properties and a String array (from a checkbox interface).
Then I have a List<String> property. It renders properly as multiple textboxes with correct initialized values. But when I edit the textboxes on the browser and submit, the values do not take effect on the form bean. It still has the initial values.
Below is the detailed set-up:
Web flow on-start which creates the bean:
<on-start>
<evaluate expression="new mypackage.MyFormBean()" result="flowScope.myFormBean" />
</on-start>
Here are the relevant parts of my form bean:
public class MyFormBean implements Serializable {
...
private List<SlotBean> slots;
...
public List<SlotBean> getSlots() {
return slots;
}
public void setSlots(List<SlotBean> slots) {
this.slots= slots;
}
...
}
public class SlotBean {
...
private int quantity;
...
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity= quantity;
}
...
}
I have a series of view-states in my web flow with simple string or number field bindings set-up that are initialized, displayed and saved without issues to the form.
This view-state generates any number of SlotBean objects then initializes quantity with 2. These are set to the slots property.
<view-state id="generate-criteria" model="disciplineCatalogue">
...
<transition on="next" to="slots-grid">
<evaluate expression="myService.generateSlots(myFormBean)"/>
</transition>
...
</view-state>
Here is the jsp fragment. All it does is render a bunch of textboxes. There's also a next button:
<form:form id="slotsGrid" modelAttribute="myFormBean" action="${flowExecutionUrl}">
...
<c:forEach var="slot" items="${myFormBean.slots}" varStatus="idx">
<form:input path="slots[${idx.index}].quantity" />
</c:forEach>
...
<button type="submit" id="next" name="_eventId_next">Next</button>
...
</form:form>
The above code displays correctly with the initial values (2). It generates multiple textboxes like below:
<input id="slots0.quantity" name="slots[0].quantity" type="text" value="2"/>
So when this page is on the browser, I change the values of the quantity textboxes to different values and click the "next" button. On my browser's network debugger, I see that the form values are sent to the server:
slots[0].quantity:3
slots[1].quantity:1
slots[2].quantity:2
Here is the relevant web flow entry for the next button.
<view-state id="slots-grid" model="myFormBean">
<binder>
...
<binding property="slots" />
</binder>
...
<transition on="next" to="finished">
<evaluate expression="myService.create(myFormBean)"/>
</transition>
...
</view-state>
So I put a break point on the myService.create(myFormBean) method and it shows that all the quantity fields are still set to the original "2". The new values didn't bind to myFormBean.slots.
Is there anything you can see in my set-up that looks wrong?
Thanks for any time you can put into this
Spring Framework 3.1.1
Spring-Webflow 2.3.1
Tomcat 6.0.18
Eclipse Indigo
Cross-posted in:
http://forum.springsource.org/showthread.php?127809-Collection-List-property-won-t-bind-or-update-on-form-submit

Spring MVC 3, Interceptor on all excluding some defined paths

Is it possible to apply an interceptor to all controllers and actions, except some that are defined?
Just to be clear, I am not interested in applying an interceptor on a list of defined ones. I want to define those to exclude.
Thanks!
Since Spring 3.2 they added that feature with the tag
mvc:exclude-mapping
See this example from the Spring documentation:
<mvc:interceptors>
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" />
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/admin/**"/>
<bean class="org.springframework.web.servlet.theme.ThemeChangeInterceptor" />
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/secure/*"/>
<bean class="org.example.SecurityInterceptor" />
</mvc:interceptor>
Here's the link to the doc
For java based configuration, from the docs
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LocaleInterceptor());
registry.addInterceptor(new ThemeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**");
registry.addInterceptor(new SecurityInterceptor()).addPathPatterns("/secure/*");
}
}
When configuring an interceptor, you can specify a path pattern. The interceptor will be invoked only for controllers which the path matches the interceptor path pattern.
ref: http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/mvc.html#mvc-config-interceptor
But as you probably noticed it, the path pattern doesn't support exclusion.
So I think the only way is to code a blacklist of paths inside the interceptor. When the interceptor is invoked, retrieve the HttpServletRequest.getRequestURI() and check if the path is blacklisted or not.
You can build the blacklist inside a #PostConstruct annotated method of the interceptor, and so get the blacklisted path from a property file for instance.

Resources