Calling setter from JSF page which was declared with different parameter from variable itself - jsf

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?

Related

.net core 3.1 - View Components - Not finding my Default.cshtml

I'm just getting to grips with ViewComponents in my Razor pages application.
I have a ViewComponents folder within my project that contains my ViewComponent .cs code:
public class RemoveFromCartViewComponent : ViewComponent
{
public IViewComponentResult Invoke()
{
var result = "123";
return View(result);
}
}
I then have another folder within Pages/Shared/Components called RemoveFromCart. Within this folder I have my default.cshtml
#model string
<h2>
#Model
</h2>
Simply putting the string within a h2 tag.
In my projects Layout.cshtml file I am invoking this ViewComponent:
<div>
#await Component.InvokeAsync("RemoveFromCart")
</div>
When I start my project, the error I get is:
*InvalidOperationException: The view 'Components/RemoveFromCart/123' was not found. The following locations were searched:
/Pages/Components/RemoveFromCart/123.cshtml
/Pages/Shared/Components/RemoveFromCart/123.cshtml
/Views/Shared/Components/RemoveFromCart/123.cshtml*
This is indication my view should be called 123.cshtml which doesnt seem right. What am I doing wrong here? I should simply expect to see the text 123 appear
Thanks
By returning View("123"), you are using this overload:
public ViewViewComponentResult View (string viewName)
Returns a result which will render the partial view with name viewName.
So you are passing the view name, instead of a string value as the view’s model.
You can change that by explicitly calling the View<TModel>(TModel) overload instead:
public IViewComponentResult Invoke()
{
var result = "123";
return View<string>(result);
}
In the long run, I would suggest you to create a model class instead so that you can pass an object instead of just a string. This will avoid having this particular problem and you are also able to easily expand the model contents later on.

WeldClientProxy cannot be cast

I use the Unbound ID library that has a class LDAPConnection which has no default constructor and which implements LDAPInterface. I produce the LDAPConnection as follows:
#Produces
#SimpleLdapConnection
#ApplicationScoped
public LDAPInterface createLdapConnection() throws GeneralSecurityException, LDAPException {
LDAPConnection conn = new LDAPConnection(host, port, username, password);
return conn;
}
I now want to inject this LDAPConnection class to a second producer, which should generate a Connection Pool:
#Inject
#SimpleLdapConnection
LDAPInterface simpleLdapConnection;
#Produces
#Default
#ApplicationScoped
public LDAPInterface produceLdapConnectionPool() throws GeneralSecurityException, LDAPException {
LDAPConnectionPool pool = new LDAPConnectionPool((LDAPConnection)simpleLdapConnection.g, connectionPoolInitialSize, connectionPoolMaxSize);
return pool;
}
To create the LDAPConnectionPool, I need to cast the simpleLdapConnection to an LDAPConnection (as it must be an LDAPConnection).
However, I get the error:
java.lang.ClassCastException:
org.jboss.weld.proxies.LDAPInterface$1687649628$Proxy$_$$_WeldClientProxy
cannot be cast to com.unboundid.ldap.sdk.LDAPConnection
at
at.rsg.lp.benutzerverwaltung.business.repository.LdapConnectionPoolProvider.produceLdapConnectionPool(LdapConnectionPoolProvider.java:59)
How can I get around this error?
P.S. changing the first producer to return an LDAPConnection does not work as I get the error "Injected normal scoped bean is not proxyable".
What you are running into, from CDI point of view, are the defined bean types of a producer method. This is backed by CDI specification.
In short, for producer methods, the bean types are derived from return types and the interfaces it implements. E.g. the actual implementation type is not included. The reason for that is exactly what you see when you saw when you tried to return the actual implementation type - impls often contain final methods or other bumps making them unproxyable.
There are two things I can think of to solve this:
[This one is likely to fail] Try putting #Typed annotation on your producer - I doubt it will work in this case, but it could be worth a shot. This annotation declares all the types the bean will have. You would use it like this - #Typed({LDAPInterface, LDAPConnection}).
[This should be a go-to option] If I were you, I would create a wrapper object just like you suggested. It won't really be all that ugly, just few bits and pieces of code should do the trick.

change label value using value stored at session

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">

JSF expression evaluation of attribute tags

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().

Liferay - Hook for GroupWrapper

I'm trying to override the getDescriptiveName() method in com.liferay.portal.model.Group
I found a wrapper (com.liferay.portal.model.GroupWrapper), so I tried to write a hook as written in the documentation :
liferay-hook.xml:
<service>
<service-type>com.liferay.portal.model.GroupWrapper</service-type>
<service-impl>fr.villedeniort.hook.expando.GroupWrapperImpl</service-impl>
</service>
fr.villedeniort.hook.expando.GroupWrapperImpl.java:
public class GroupWrapperImpl extends GroupWrapper {
public GroupWrapperImpl(Group group) {
super(group);
}
#Override
public java.lang.String getDescriptiveName()
throws com.liferay.portal.kernel.exception.PortalException,
com.liferay.portal.kernel.exception.SystemException {
return super.getDescriptiveName();
}
When the hook is deployed, it raises an exception :
java.lang.NoSuchMethodException: fr.villedeniort.hook.expando.GroupWrapperImpl.<init>(com.liferay.portal.model.GroupWrapper)
I browse the code I found out that it breaks at this part for a reason I ignore:
Constructor<?> serviceImplConstructor = serviceImplClass.getConstructor(new Class<?>[] {serviceTypeClass});
At this point, variables have theses values:
serviceType "com.liferay.portal.model.GroupWrapper" (id=14829)
serviceImpl "fr.villedeniort.hook.expando.GroupWrapperImpl" (id=14830)
serviceTypeClass Class<T> (com.liferay.portal.model.GroupWrapper) (id=14831)
serviceImplClass Class<T> (fr.villedeniort.hook.expando.GroupWrapperImpl) (id=14832)
Do you have any idea?
Thanks!
You should have also a constructor without any argument. Now you have one with constuctor arguments, but there is no pure class constructor that java searches when it makes class instance. After calling the pure constructor java then calls the argumented one.
I had similar case in some other context and this was the solution. <init> tag on the error message refers on this kind of issue.
Apparently, it's not possible to hook other classes than Services, so I had to find a different way. For my case, I hooked a JSP and wrote my own method to get the right descriptive name from the hook.

Resources