I try to use the OmniFaces #Param annotation to inject a request parameter.
I also make use of its validatorClasses attribute to validate the parameter. Eventually this used validator needs a special attribute to function and I want to pass the value by setting the validatorAttributes attribute. Unfortunately I don't know how. The documentation provides a description but I just don't get it right.
Can someone help please?
Here's some code:
#Inject
#Param(
name = "the_param_name",
validatorClasses = MyFreshValidator.class,
validatorAttributes = ?
)
private MyFreshClass instance;
It would be ideal to give another object of the same class to the validator.
It's indeed slightly hidden in the showcase. If you open the CdiParamBean tab of the "Demo source code" section, then you'll find the managed bean's source code with below examples:
// Like <f:viewParam name="text2" value="#{bean.text2}" validatorMessage="..."><f:validateLength minimum="3">
#Inject #Param(
validatorClasses = LengthValidator.class,
validatorAttributes = #Attribute(name="minimum", value="3"),
validatorMessage = "{1}: Value is too too small! Please enter a minimum of 3 characters.")
private String text2;
// Like <f:viewParam name="date" value="#{bean.date}" converterMessage="..."><f:convertDateTime pattern="yyyyMMdd">
#Inject #Param(
converterClass = DateTimeConverter.class,
converterAttributes = { #Attribute(name="pattern", value="yyyyMMdd") },
converterMessage="{1}: \"{0}\" is not the date format we had in mind! Please use the format yyyyMMdd.")
private Date date;
Here, the #Attribute is the org.omnifaces.cdi.param.Attribute.
I'll look at improving the documentation in a future version.
Related
I'm using an External lib which has many classes with variables declared like that:
private String testBigDecimal;
public String getTestBigDecimal() {
return this.testBigDecimal;
}
public void setTestBigDecimal(BigDecimal testBigDecimal) {
this.testBigDecimal = BigDecimalValidador.validationXpto( testBigDecimal); //return String
}
Note that setter() method receives a BigDecimal value due to some validation process and return a String value. When those are called from server side everything is OK! ...but when called from view side (JSF page) with proper BigDecimalConverter I'm getting this Error:
javax.faces.component.UpdateModelException: javax.el.PropertyNotWritableException: ... Property [testBigDecimal] not writable on type ....
After Converter is correctly invoked, the code fails because it is still expecting for a setTestBigDecimal(Srting testBigDecimal) metohd.
My JSF Page: <h:inputText value="#{fooBean.testBigDecimal}" converter="bigDecimalConverter" />
Is there a way to ensure JSF can call the declared setter (which was in a "non standard way")?
In a local tests I changed 'String testBigDecimal' to 'BigDecimal testBigDecimal' type and everything works fine, but I can't change the Lib Source and Extend all this classes will be painful.
Anyone can help?
i have two jsf pages (home.jsf and employees.jsf) ,
home page has a button that navigates to employees page,
while navigating i store value in session scope
at (Managed bean)
public void putSessionAL(ActionEvent actionEvent) {
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("key","value");
}
public String navigate() {
return "employees";
}
i want to change Label at employees viewObject from UIHints tab depending on value stored at session using the following groovy expression
adf.context.sessionScope.key
and changed trustMode to trusted but it fires the following exception
oracle.jbo.script.ExprScriptException: JBO-29114 ADFContext is not setup to process messages for this exception. Use the exception stack trace and error code to investigate the root cause of this exception. Root cause error code is JBO-25188. Error message parameters are {0=Employees.FirstName, 1=, 2=oracle.jbo.script.ExprSecurityException}
at oracle.jbo.script.ExprScriptException.throwException(ExprScriptException.java:316)
at oracle.jbo.script.ExprScriptException.throwExceptionWithExprDef(ExprScriptException.java:387)
at oracle.jbo.ExprEval.processScriptException(ExprEval.java:599)
at oracle.jbo.ExprEval.doEvaluate(ExprEval.java:697)
at oracle.jbo.ExprEval.evaluate(ExprEval.java:508)
at oracle.jbo.ExprEval.evaluate(ExprEval.java:487)
at oracle.jbo.common.NamedObjectImpl.resolvePropertyRaw(NamedObjectImpl.java:680)
at oracle.jbo.server.DefObject.resolvePropertyRaw(DefObject.java:366)
One way to do it at the VO UIHint attribute label level will be programmaticaly by doing as follow :
In your VO go to the java tab and add the RowImpl java class
In the VORowImpl Add the following function
public String getMySessionLabel() {
return (String)FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("key");
}
In the Label add the following EL expression :
adf.object.getMySessionLabel()
This technique allow you more control than pure EL, if you want to do more than getting from session for example. In your case pure EL, as you did, should work as well. (Would need to check what is wrong with yours, maybe just missing the
#{adf.context.sessionScope.key}
If you attempt to get your label from a method in viewRowImpl. So this will be executed at least once for each row. I think this solution isn't fit for your case.
anyway ADF as a framework added strong policy and validations in EL in general and especially in version 12.2.x.
The solution for you case as following:
Create new class in model layer which extends oracle.jbo.script.ExprSecurityPolicy class
Override checkProperty method.
#Override
public boolean checkProperty(Object object, String string, Boolean b) {
if (object.getClass().getName().equals("oracle.adf.share.http.ServletADFContext") && string.equals("sessionScope")) {
return true;
}
return super.checkProperty(object, string, b);
}
Open adf-config.xml source and in startup tag set your class ExprSecurityPolicy property.
like:
<startup ExprSecurityPolicy="model.CustomExprSecurityPolicy">
I have to develop a processing page. However, I want to filter the grid based on some attributes. Not sure how to include that. Can anyone suggest.
Here is the code. ?? is the place where I want to include the attribute such as CaseNo.
[PXFilterable]
public PXProcessingJoin<CRCase,
LeftJoin<BAccount, On<CRCase.customerID, Equal<BAccount.bAccountID>>,
LeftJoin<CREmployee, On<CRCase.ownerID, Equal<CREmployee.userID>>,
LeftJoin<CSAnswers, On<CRCase.noteID, Equal<CSAnswers.refNoteID>>>>>,
Where<CRCase.majorStatus, NotEqual<CRCaseMajorStatusesAttribute.closed>,
And<CRCase.majorStatus, NotEqual<CRCaseMajorStatusesAttribute.released>,
And<CSAnswers.value, IsNotNull,
And<CSAnswers.attributeID, Equal<??>>>>>,
OrderBy<Desc<CRCase.caseCD>>> ProcessCase;
Try to use BQL constant. Like that:
public class decimal_100 : Constant<Decimal>
{
public decimal_100(): base(100m)
{ }
}
After that you can use this constant in the BQL, like that:
PXSelect<ARInvoice, Where<ARInvoice.CuryOrigDocAmount, Equal<decimal_100>>>
Please check T200 training example 3.1 for more information.
You can also take a look at ARDocType.invoice class.
Despite of FetchType.EAGER and JOIN FETCH, I get a LazyInitalizationException while adding some objects to a #ManyToMany collection via a JSF UISelectMany component such as in my case the <p:selectManyMenu>.
The #Entity IdentUser, with FetchType.EAGER:
#Column(name = "EMPLOYERS")
#ManyToMany(fetch = FetchType.EAGER, cascade= CascadeType.ALL)
#JoinTable(name = "USER_COMPANY", joinColumns = { #JoinColumn(name = "USER_ID") }, inverseJoinColumns = { #JoinColumn(name = "COMPANY_ID") })
private Set<Company> employers = new HashSet<Company>();
The #Entity Company, with FetchType.EAGER:
#ManyToMany(mappedBy="employers", fetch=FetchType.EAGER)
private List<IdentUser> employee;
The JPQL, with JOIN FETCH:
public List<IdentUser> getAllUsers() {
return this.em.createQuery("from IdentUser u LEFT JOIN FETCH u.employers WHERE u.enabled = 1 AND u.accountNonLocked=0 ").getResultList();
}
The JSF UISelectMany component causing the exception while submitting:
<p:selectManyMenu value="#{bean.user.employers}" converter="#{entityConverter}">
<f:selectItems value="#{bean.companies}" var="company" itemValue="#{company}" itemLabel="#{company.name}"/>
</p:selectManyMenu>
The relevant part of the stack trace:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection, could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:566)
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:186)
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:545)
at org.hibernate.collection.internal.PersistentSet.add(PersistentSet.java:206)
at com.sun.faces.renderkit.html_basic.MenuRenderer.convertSelectManyValuesForModel(MenuRenderer.java:382)
at com.sun.faces.renderkit.html_basic.MenuRenderer.convertSelectManyValue(MenuRenderer.java:129)
at com.sun.faces.renderkit.html_basic.MenuRenderer.getConvertedValue(MenuRenderer.java:315)
at org.primefaces.component.selectmanymenu.SelectManyMenuRenderer.getConvertedValue(SelectManyMenuRenderer.java:37)
...
How is this caused and how can I solve it?
While submitting, the JSF UISelectMany components need to create a brand new instance of the collection with the submitted and converted values prefilled. It won't clear out and reuse the existing collection in the model as that may either get reflected in other references to the same collection, or may fail with an UnsupportedOperationException because the collection is unmodifiable, such as the ones obtained by Arrays#asList() or Collections#unmodifiableList().
The MenuRenderer, the renderer behind UISelectMany (and UISelectOne) components who's responsible for this all, will by default create a brand new instance of the collection based on collection's getClass().newInstance(). This would in turn fail with LazyInitializationException if the getClass() returns an implementation of Hibernate's PersistentCollection which is internally used by Hibernate to fill the collection property of an entity. The add() method namely needs to initialize the underlying proxy via the current session, but there's none because the job isn't performed within a transactional service method.
To override this default behavior of MenuRenderer, you need to explicitly specify the FQN of the desired collection type via the collectionType attribute of the UISelectMany component. For a List property, you'd like to specify java.util.ArrayList and for a Set property, you'd like to specify java.util.LinkedHashSet (or java.util.HashSet if ordering isn't important):
<p:selectManyMenu ... collectionType="java.util.LinkedHashSet">
The same applies to all other UISelectMany components as well which are directly tied to a Hibernate-managed JPA entity. E.g:
<p:selectManyCheckbox ... collectionType="java.util.LinkedHashSet">
<h:selectManyCheckbox ... collectionType="java.util.LinkedHashSet">
<h:selectManyListbox ... collectionType="java.util.LinkedHashSet">
<h:selectManyMenu ... collectionType="java.util.LinkedHashSet">
See also the VDL documentation of among others <h:selectManyMenu>. This is unfortunately not specified in VDL documentation of <p:selectManyMenu>, but as they use the same renderer for converting, it must work. If the IDE is jerking about an unknown collectionType attribute and annoyingly underlines it even though it works when you ignore'n'run it, then use <f:attribute> instead.
<p:selectManyMenu ... >
<f:attribute name="collectionType" value="java.util.LinkedHashSet" />
...
</p:selectManyMenu>
Solution: Replace the editUserBehavior.currentUser.employers with collection that is not managed by Hibernate.
Why? When the Entity becomes managed, the Hibernate replaces your HashSet with its own implementation of Set (be it PersistentSet). By analysing the implementation of JSF MenuRenderer, it turns out that at one point it creates new Set reflectively. See the comment in MenuRenderer.convertSelectManyValuesForModel()
// try to reflect a no-argument constructor and invoke if available
During construction of PersistentSet initialize() is invoked and - as this class is only meant to be invoked from Hibernate - LazyInitializationException is thrown.
Note: This is my suspicion only. I don't know your versions of JSF and Hibernate but this is more likely the case.
I have a problem with evaluating EL expressions containing vars created by other tags.
I have a project where I am using a custom validator.
public class MyValidator implements Validator, StateHolder
I have a tag class associated with it:
public class MyValidatorTag extends ValidatorTag
this class allows for attribute fieldName, with the appropriate tld file for the tag:
<tag>
<name>my-validator</name>
<tag-class>my.packaga.MyValidatorTag</tag-class>
<body-content>JSP</body-content>
<description>This is my validator</description>
<attribute>
<name>fieldName</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<type>java.lang.String</type>
<description>This is some field I need</description>
</attribute>
</tag>
So far so good. But this setup doesn't allow one to use JSF EL expressions for attribute value.
So the validator class (not tag class) I use this code to evaluate attribute value:
public static String evaluateEl(String expression) {
String value = null;
if (expression == null) {
return "";
}
if ((expression.indexOf("#{") != -1)
&& (expression.indexOf("#{") < expression.indexOf('}'))) {
Object evaledValue =
FacesContext.getCurrentInstance().getApplication().createValueBinding(
expression).getValue(FacesContext.getCurrentInstance());
if (evaledValue != null) {
value = evaledValue.toString();
} else {
value = null;
}
} else {
value = expression;
}
return value;
}
It is limited to strings, and it works for most EL expressions. Expression Hello #{1 eq 2} will cause the attribute to have value Hello false.
But there is a case for which this will not work. Any expression which contains a var created by another tag doesn't work. Datatables, dataiterators and most notably <f:loadBundle>, e.g.
<f:loadBundle basename="mypackage.message.ui-strings" var="msgs" />
Followed by an input component containing validator:
<cust:my-validator fieldName="#{msgs['myfield1.name']}"></cust:my-validator>
Fieldname evaluates to empty string using the expression evaluation code above. Changing it to #{requestScope.msgs.... doesn't fix the problem. Looking at loadBundle implementation, it adds the var msgs to request scope, so I don't see why vars don't work for me. Please, somebody help me understand.
Any expression which contains a var created by another tag doesn't work. Datatables, dataiterators and most notably , e.g.
Tag handlers like <f:xxx>, <c:xxx> and some <ui:xxx> (those not having rendered attribute) are executed during JSF view build time, when a JSF UI component tree is to be produced. They are not part of the UI component tree. They have already done their job of producing the necessary JSF UI components.
UI components like <h:xxx> and some <ui:xxx> (those having rendered attribute) are executed during JSF view render time, when a large HTML string is to be produced which is to be sent to the HTTP response of the current HTTP request.
So they don't run in sync.
There are for the enduser of the validator several ways to go around this, all are listed in this answer: How to set converter properties for each row of a datatable?
For the developer, there's another solution, let the validator extend UIComponentBase instead and perform the job in processValidators(). The parent component is just available by UIComponent#getParent() and its submitted value is in turn available by UIInput#getSubmittedValue().