getViewRoot null from on-render execution action - spring

I need to achieve viewRoot because I add a new htmlPanelGroup dynamically using DB results but always return null.
How is possible throw a evaluate expression from SWF fragment which achieve viewRoot properly?
Thanks!
Flow
<on-entry>
<set name="viewScope.code" value="requestParameters.code" />
</on-entry>
<on-render>
<evaluate expression="eventProvider.createEvent(viewScope.code)" />
</on-render>
Method
public void createEvent(String idEvent){
logger.entry("EventProvider.createEvent()");
Page p=pageBo.getDao().get(Integer.valueOf(idEvent));
pageBo.getDao().initializeElements(p);
Set<Element> elements=p.getElements();
Application app=FacesContextWrapper.getCurrentInstance().getApplication();
UIComponent parent=FacesContextWrapper.getCurrentInstance().getViewRoot().findComponent("eventContainer");
....
....
....}

Finally, I've chosen to save requestParameter in a flowScope var and so I can use it when on-render execution is invoked
<on-entry>
<set name="flowScope.code" value="requestParameters.code" />
</on-entry>
<on-render>
<evaluate expression="eventProvider.loadPage(flowScope.code)" />
</on-render>

Related

Null context in a Spring flow

I am trying to customize the login process in Shibboleth IdP 3.0. It's basically a Spring web application, that amongst other things uses Spring flows.
I am getting the following exception (null context):
org.springframework.expression.spel.SpelEvaluationException: EL1011E:(pos 23): Method call: Attempted to call method verify(java.lang.String) on null context object
at org.springframework.expression.spel.ast.MethodReference.throwIfNotNullSafe(MethodReference.java:144)
It happens on the evaluate expression below, in this Spring web flow:
<action-state id="VerifyOtp">
<set name="flowScope.enteredOtp" value="requestParameters.j_otp"
type="java.lang.String" />
<set name="flowScope.otpSecretKey"
value="keyManager.findSecretKey(flowScope.otpUsername)"
type="ch.eifr.oshi.jaas.totp.SecretKey" />
<evaluate expression="flowScope.otpSecretKey.verify(flowScope.enteredOtp)"
result="flowScope.otpIsValid" />
<transition on="#{ flowScope.otpIsValid }" to="proceed" />
<transition on="#{ !flowScope.otpIsValid }" to="DisplayOtpPage" />
</action-state>
The flow imports the following beans:
(keyManager gets instantiated as expected)
<bean class="ch.eifr.oshi.jaas.totp.SecretKeysManagerImpl" id="keyManager" scope="singleton" />
<bean class="ch.eifr.oshi.jaas.totp.SecretKey" scope="prototype" />
I tried with and without the last line, it made no difference. I thought maybe I had to tell Spring about the classes it needs to load. That doesn't seem to be the case.
I think my problem is related to scope or dependencies, because I tried calling keyManager.toString() in a view and it returned something.
But if I call keyManager.findSecretKey('john.doe').getId() I get a null context exception. Note that the findSecretKey(String) method always returns a new key, it never returns null.
I was hoping to do it a better way, but only this worked:
<action-state id="ExtractOtpFromFormRequestAndValidate">
<set name="flashScope.enteredOtp" value="requestParameters.j_otp" type="java.lang.String"/>
<transition on="#{ keyManager.findSecretKey(flowScope.otpUsername).verify(flashScope.enteredOtp) }" to="proceed" />
<transition to="DisplayOtpPage" />
</action-state>

Adding spring webflow dynamic messages for general purposes (no validating forms)?

Every documentation and examples that I read to be about validation form fields but I wish to show messages for others cases, for example to warn the view or flow have changed. So, that messages must be added on action methods.
In my case the app send to users a confirmation email, on flow there is a decision-state which redirect to views depending on link got declared a request parameter or not. Then I define a action-state which evaluate a method to confirm the email or warning to user about account's state (deleted or already enabled)
<on-start>
<set name="requestScope.code" value="requestParameters.code" />
<set name="requestScope.ln" value="requestParameters.ln" />
</on-start>
<decision-state id="checkConfirmation">
<if test="requestScope.code==null" then="login" else="confirmation" />
</decision-state>
<action-state id="confirmation">
<evaluate expression="login.confirmation(requestScope.ln,requestScope.code)" />
<transition on="yes" to="confirmationOk" />
<transition on="noUserFound" to="noUserFound" />
<transition on="userEnabled" to="userEnabled" />
<transition on="error" to="error" />
</action-state>
<view-state id="userEnabled" view="confirmation.xhtml">
<on-entry>
<set name="viewScope.operation" value="'enabled'" />
</on-entry>
<transition on="login" to="login" />
</view-state>
confirmation method
public String confirmation(String language,String emailCode){
logger.entry("Login.confirmation()");
String emailDecode=new String(Base64.decode(emailCode.getBytes()));
User user=userBo.getDao().findNamedQueryUnique("getUserByEmail",emailDecode);
if(user!=null){
if(!user.isEnabled()){
user.setEnabled(true);
try{
userBo.getDao().merge(user);
if(!EmailProvider.sendEmailEnabled(language,emailDecode)){
return "error";
}
}catch(DataAccessException e){
e.printStackTrace();
FlashMessages.addMessage(language,"es.project.properties.message.msg","unexpected.exception");
return "error";
}
}else{
// JSF way - FlashMessages.addMessage(language,"es.project.properties.message.msg","user.enable.confirmation");
//Spring webflow way - MessageContext context=new DefaultMessageContext();
//MessageBuilder builder=new MessageBuilder();
//context.addMessage(builder.code("user.enable.confirmation").build());
**********************************************************
*** I wish to add messages here and show in next view ****
**********************************************************
return "userEnabled";
}
}else{
//FlashMessages.addMessage(language,"es.project.properties.message.msg","user.validation.norfound.exception");
return "noUserFound";
}
return "yes";
}
I tried to follow spring webflow examples (booking-faces and booking-mvc) adding messages.properties on flow folder or creating a MessageSource bean, but I'm really lost on this matter.
I believe you are not having the messages.properties in classpath properly.
Suppose your file is in folder structure as:
src
|
|-resources
|
|-messages.properties
i.e make sure its in classpath.
You need to declare the ResourceBundleMessageSource with properties file in your servlet context xml file as:
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="messages" />
</bean>
Then you can access the properties as:
<action-state id="confirmation">
...
<transition on="userEnabled" to="userEnabled">
<!--This is the property you want to set in else part of confirmation method - instead include here-->
<set name="flashScope.flashMessageKey" value="'es.project.properties.message.msg'" />
</transition>
</action-state>
Include this in view:
<%# taglib prefix="fmt" uri="http://java.sun.com/jstl/fmt" %>
Then you can view this message on view render by:
<fmt:message key="${flashMessageKey}"/>
I found a solution for Spring,JSF 2.2 and i18 in this post. Basically, you should define a spring message source and create a spring component which will be called for jsf outputtext tag. Then how #Prasad wrote it's necessary to set a key in a spring flow or programatically.
Properties resources are src/main/resources/error_en.properties and src/main/resources/error_es.properties
MessageSource Bean
#Bean
public ReloadableResourceBundleMessageSource messageSource(){
ReloadableResourceBundleMessageSource msg=new ReloadableResourceBundleMessageSource();
msg.setBasename("classpath*:error");
return msg;
}
Spring component
#Component(value="message")
public class MessageSourceProvider extends HashMap {
private static final long serialVersionUID = 1L;
#Autowired
private MessageSource messageSource;
#Override
public String get(Object key) {
ServletRequest request = (ServletRequest) FacesContextWrapper.getCurrentInstance().getExternalContext().getRequest();
String message;
try {
message = messageSource.getMessage((String) key, null, request.getLocale());
}
catch (NoSuchMessageException e) {
message = "???" + key + "???";
}
return message;
}
}
JSF tag
<h:outputText value="#{message.unexpected}" />

Spring webflow global transitions, determine the "validate" attribute dynamically

I have a global-transition declared and I would like the value of the "validate" attribute to be conditionned or set in an action-state.
Here is how I would like to do it :
<decision-state id="decision_view">
<if test="condition == true" then="actionState1" />
</decision-state>
<action-state id="actionState1">
<evaluate result="flowScope.validateGT1" expression="true"/>
</action-state>
<global-transitions>
<transition on="gtransition1" to="gtransition1"
validate="flowScope.validateGT1" /> // Does not work, syntax error
</global-transitions>
This syntax does not work at all. Is there a way to determine the validate boolean dynamically ?
The project I am working on is using a 2.3.1 version of Spring webflow.
Thanks.
I think you cannot use the expression for boolean as it expects only literals true or false:
http://www.w3.org/TR/xmlschema-2/#boolean
Check the attirbute type for "validate" in spring webflow xsd:
xsd:attribute name="validate" type="xsd:boolean"
Instead what you can do is retrieve the boolean value in validator itself and decide if validation should be done or not as:
public class YourValidator {
public void validateStateId(YourModel model, ValidationContext context) {
RequestContext requestContext = RequestContextHolder.getRequestContext();
boolean shouldValidate = (Boolean)requestContext.getFlowScope.get("validateGT1");
if(shouldValidate){
MessageContext messages = context.getMessageContext();
...
}
}
}
Try to use evaluate
<evaluate expression="flowScope.validateGT1" result="flag" />
<global-transitions>
<transition on="gtransition1" to="gtransition1"
validate="flag" />
</global-transitions>

SWF ignore FacesMessage on evaluate transition redirect to next view without updating form

On transition the flow redirect without rendering the FacesMessage in the current instance of the FacesContext (same issue with the MessageContext)
I checked the presence of FacesMessage in the FacesContext wich is correct but for a reason I dont touch the redirection is done even if the context contains messages inside
spring application context :
<faces:resources/>
<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping"
p:order="1"
p:flowRegistry-ref="flowRegistry">
<property name="defaultHandler">
<!--If no flow match, map path to a view to render; e.g. the "/home" path
would map to the view named "home" -->
<bean class="org.springframework.web.servlet.mvc.UrlFilenameViewController"/>
</property>
</bean>
<bean id="faceletsViewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver"
p:viewClass="org.springframework.faces.mvc.JsfView"
p:prefix="/WEB-INF/"
p:suffix=".xhtml"/>
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<bean class="org.springframework.faces.webflow.JsfFlowHandlerAdapter"
p:flowExecutor-ref="flowExecutor"/>
<webflow-config:flow-executor id="flowExecutor">
<webflow-config:flow-execution-listeners>
<webflow-config:listener ref="facesContextListener"/>
<webflow-config:listener ref="jpaFlowExecutionListener"/>
<webflow-config:listener ref="securityFlowExecutionListener"/>
</webflow-config:flow-execution-listeners>
</webflow-config:flow-executor>
<webflow-config:flow-registry id="flowRegistry"
flow-builder-services="facesFlowBuilderServices" base-path="/WEB-INF/flows">
<webflow-config:flow-location-pattern
value="/**/*-flow.xml"/>
</webflow-config:flow-registry>
<faces:flow-builder-services id="facesFlowBuilderServices"
development="true"/>
<bean id="facesContextListener"
class="org.springframework.faces.webflow.FlowFacesContextLifecycleListener"/>
<bean id="jpaFlowExecutionListener"
class="org.springframework.webflow.persistence.JpaFlowExecutionListener">
<constructor-arg ref="entityManagerFactory"/>
<constructor-arg ref="transactionManager"/>
</bean>
<bean id="securityFlowExecutionListener"
class="org.springframework.webflow.security.SecurityFlowExecutionListener"/>
controller :
#Named("registrationController")
class RegistrationController implements Serializable {
#Inject
#LangServiceQualifier
ILangService langService
#Inject
#RegistrationServiceQualifier
IRegistrationService registrationService
void validate(RegistrationModel model) {
model.user.profile.lang ?: model.user.profile.setLang(registrationLang())
try {
registrationService.validate(model)
model.header = new RequestHelper(request: FacesUtils.request).header
} catch (RegistrationException re) {
FacesContext context = FacesUtils.context
re.messages.each {
switch (it) {
case new UsernameAlreadyExistsException().message:
context.addMessage("register_form_username_inputText_id",
new FacesMessage(
severity: FacesMessage.SEVERITY_ERROR,
summary: FacesUtils.bundle.getString(it)))
break
case new PrimaryEmailAlreadyExistsException().message:
context.addMessage("register_form_currentEmailValue_inputText_id",
new FacesMessage(
severity: FacesMessage.SEVERITY_ERROR,
summary: FacesUtils.bundle.getString(it)))
break
case new EmailRetypeException().message:
context.addMessage("register_form_currentEmailValueRetype_inputText_id",
new FacesMessage(
severity: FacesMessage.SEVERITY_ERROR,
summary: FacesUtils.bundle.getString(it)))
break
case new PasswordRetypeException().message:
context.addMessage("register_form_password_inputText_id",
new FacesMessage(
severity: FacesMessage.SEVERITY_ERROR,
summary: FacesUtils.bundle.getString(it)))
break
}
}
}
}
spring web flow definition :
<flow xmlns="http://www.springframework.org/schema/webflow"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/webflow
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
<on-start>
<evaluate expression="requestParameters.contains('from')?requestParameters.get('from'):'home'"
result="flowScope.from"/>
<evaluate expression="registrationService.initModel()"
result="flowScope.model"/>
</on-start>
<view-state id="enterRegistration" model="model">
<transition on="cancelRegistrationAction" to="exitRegistrationAction"/>
<transition on="validateRegistrationAction" to="reviewRegistration">
<evaluate expression="registrationController.validate(model)"/>
</transition>
</view-state>
<view-state id="reviewRegistration" model="model">
<transition on="cancelRegistrationAction" to="exitRegistrationAction"/>
<transition on="reviseRegistrationAction" to="enterRegistration"/>
<transition on="confirmRegistrationAction" to="exitRegistrationAction">
<evaluate expression="registrationService.register(model)"/>
</transition>
</view-state>
<end-state id="exitRegistrationAction" view="externalRedirect:#{from}"/>
<on-end>
<evaluate expression="registrationController.clearRegistrationModel(flowScope.model)"/>
</on-end>
JSF page :
<h:form id="register_form">
<p:messages id="register_form_messages_id"/>
<p:outputLabel for="register_form_username_inputText_id"
value="#{msgs['user.username.label']}"/>
<p:inputText id="register_form_username_inputText_id"
label="#{msgs['user.username.label']}"
value="#{model.username}"/>
<p:outputLabel for="register_form_currentEmailValue_inputText_id"
value="#{msgs['user.currentEmailValue.label']}"/>
<p:inputText id="register_form_currentEmailValue_inputText_id"
label="#{msgs['user.currentEmailValue.label']}"
value="#{model.email}"/>
<p:outputLabel
for="register_form_currentEmailValueRetype_inputText_id"
value="#{msgs['user.currentEmailValueRetype.label']}"/>
<p:inputText
id="register_form_currentEmailValueRetype_inputText_id"
label="#{msgs['user.currentEmailValueRetype.label']}"
value="#{model.emailRetype}"/>
<p:outputLabel for="register_form_password_inputText_id"
value="#{msgs['user.password.label']}"/>
<p:password id="register_form_password_inputText_id"
label="#{msgs['user.password.label']}"
value="#{model.password}"/>
<p:outputLabel for="register_form_passwordRetype_inputText_id"
value="#{msgs['user.passwordRetype.label']}"/>
<p:password id="register_form_passwordRetype_inputText_id"
label="#{msgs['user.passwordRetype.label']}"
value="#{model.passwordRetype}"/>
<p:commandButton
id="exit_registration_cmd_btn_id"
action="cancelRegistrationAction"
value="#{msgs['cancel.button']}"
immediate="true"/>
<p:commandButton
id="validate_registration_cmd_btn_id"
action="validateRegistrationAction"
value="#{msgs['register.label']}"
update="#form"
ajax="false"/>
</h:form>
I'm running under openjdk7 tomcat7 mojarra primefaces and the issue is the same with glassfish 3.1.2, groovy 2.0.5 with groovy eclipse compiler, spring 3.2, spring webflow 2.3.1
Ok I found a workaround, but still dont know what "court-circuite" the JSF lifecycle to not let the message be displayed inside an evaluate of a transition, so I had to use a decision-state to block the transition and change the controller method signature, and made the validation process return a boolean to indicate the environment the weather of the validation and that way force to stay on this view with the same populated request map.
import javax.faces.application.FacesMessage
import javax.faces.context.FacesContext
import javax.inject.Inject
import javax.inject.Named
import javax.servlet.http.HttpServletRequest
#Named("registrationController")
class RegistrationController {
static final Logger log = LoggerFactory.getLogger(RegistrationController)
Logger getLog() { return log }
#Inject
#LangServiceQualifier
ILangService langService
#Inject
#RegistrationServiceQualifier
IRegistrationService registrationService
Lang registrationLang() {
return langService.supportedLangs.find {
it.code == FacesContext
.currentInstance
.externalContext
.requestLocale
.language
} as Lang ?: langService.defaultLang
}
Boolean validateModel(RegistrationModel registrationModel) {
FacesContext context = FacesContext.currentInstance
ResourceBundle bundle = ResourceBundle.getBundle(
II18nConfigService.BUNDLE_BASE_NAME_VALUE,
context.viewRoot.locale,
Thread.currentThread().contextClassLoader)
registrationModel.user.profile.lang ?: registrationModel.user.profile.setLang(registrationLang())
try {
registrationService.validate(registrationModel)
registrationModel.header = new RequestHelper(
request: context
.externalContext
.request as HttpServletRequest).header
} catch (RegistrationException re) {
assert !re.messages.empty
re.messages.each {
switch (it) {
case new UsernameAlreadyExistsException().message:
context.addMessage "register_form_username_inputText_id",
new FacesMessage(
severity: FacesMessage.SEVERITY_ERROR,
summary: bundle.getString(it))
break
case new PrimaryEmailAlreadyExistsException().message:
context.addMessage "register_form_currentEmailValue_inputText_id",
new FacesMessage(
severity: FacesMessage.SEVERITY_ERROR,
summary: bundle.getString(it))
break
case new EmailRetypeException().message:
context.addMessage "register_form_currentEmailValueRetype_inputText_id",
new FacesMessage(
severity: FacesMessage.SEVERITY_ERROR,
summary: bundle.getString(it))
break
case new PasswordRetypeException().message:
context.addMessage "register_form_password_inputText_id",
new FacesMessage(
severity: FacesMessage.SEVERITY_ERROR,
summary: bundle.getString(it))
break
}
}
return Boolean.FALSE
}
return Boolean.TRUE
}
}
flow definition
<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/webflow
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
<on-start>
<evaluate expression="requestParameters.contains('from')?requestParameters.get('from'):'home'"
result="flowScope.from"/>
<evaluate expression="registrationService.initModel()"
result="flowScope.registrationModel"/>
</on-start>
<view-state id="enterRegistration" model="registrationModel">
<transition on="validateRegistrationAction" to="isValidModelDecision">
<evaluate expression="registrationController.validateModel(registrationModel)"
result="flowScope.isValidModel"/>
</transition>
<transition on="cancelRegistrationAction" to="exitRegistrationAction" history="discard"/>
</view-state>
<decision-state id="isValidModelDecision">
<if test="isValidModel" then="reviewRegistration" else="enterRegistration"/>
</decision-state>
<view-state id="reviewRegistration" model="registrationModel">
<transition on="reviseRegistrationAction" to="enterRegistration"/>
<transition on="confirmRegistrationAction" to="exitRegistrationAction">
<evaluate expression="registrationService.register(registrationModel)" />
</transition>
<transition on="cancelRegistrationAction" to="exitRegistrationAction" history="discard"/>
</view-state>
<end-state id="exitRegistrationAction" view="externalRedirect:#{from}"/>
</flow>
It's only a workaround not an explainantion of what is wrong with my code, just my hack to continue.

Spring WebFLow Handling Request in JSP

I am newbie to Spring Webflow, I have am using Custom FlowHandler
I can reach the respective Controller, but if set an attribute in a request Object I don't get it in JSP that is View state.
How can I proceed in this?
you shouldn't set anything to your request attributes in spring webflow - there is no need, besides webflow does some internal request redirecting and probably your attributes are los.
all the variables you create in the flow are available in view as if they were set by adding attribute to request. wether you create them via tag or create by executing some service calls via tag
<on-start>
<evaluate expression="XXXControllerwsf.firstHit(flowRequestContext)" result="flowScope.res"/>
</on-start>
<decision-state id="urlcheck">
<if test="flowScope.res.reurl== 'splash' " then="splash" else="change"/>
</decision-state>
<view-state id="change" view="${flowScope.res.reurl}">
</view-state>
<view-state id="splash" view="forward:/XXX/jsp1/XXXchange1.jsp">
<transition on="buy" to="SignInSignUp"/>
</view-state>
</flow>
Hey Miceuz thanks for Replying .....
<on-start>
<evaluate expression="XXXXControllerwsf.firstHit(flowRequestContext)" result="flowScope.res"/>
</on-start>
<decision-state id="urlcheck">
<if test="flowScope.res.reurl== 'splash' " then="splash" else="change"/>
</decision-state>
<view-state id="change" view="${flowScope.res.reurl}">
</view-state>
<view-state id="splash" view="forward:/Jahia/jsp1/XXXXchange1.jsp">
<transition on="buy" to="SignInSignUp"/>
</view-state>
</flow>
On start of the flow I am invoking a Contorller which extends FormAction
In firsthit Method I am setting a request Attribute as below
request.setAttribute("rajan", "rajanweww");
request.setAttribute("rajan", Object);
If i try to access in JSP , getting it as null... as you said Webflow has internal redirect.. Then how can I access the Value that has been set in Controller?

Resources