JSF 2: Accessing the object a component's value was bound to - java

I have simple scenario, this example comes from PrimeFaces, but I guess it applies for every tag I would use in a similar way:
<p:autoComplete value="#{address.country}" id="#{layoutId}_country"
completeMethod="#{addressBean.completeCountry}" var="country"
itemLabel="#{country.name}" itemValue="#{country}"
converter="#{countryConverter}">
</p:autoComplete>
In a method of a bean (eg. addressBean.completeCounty) I have access to an AutoComplete object. What I'd like to get is the reference of its value (#{address.country}), not the value itself.
Where is that bound to?

What I'd like to get is the reference of its value (#{address.country}), not the value itself.
This question is a bit vague (it's likely the language barrier), but if I understand you correctly, you'd like to get #{address.country} as expression string for some reason. You can get it by UIComponent#getValueExpression() and then ValueExpression#getExpressionString().
public List<Country> completeCountry(String query) {
UIComponent component = UIComponent.getCurrentComponent(FacesContext.getCurrentInstance());
String valueEL = component.getValueExpression("value").getExpressionString();
// ...
}

Related

Xpages: Error calling java method in a CacheBean in EL

My Xpages app has a cacheBean for application wide settings. I have a managed Bean for a PC document, which has field status of type integer.
In the cacheBean I have a method getPCStatus(Integer status) that when given the number will return the string text of the status.
On my Xpage I have a text field which I want to bind to the result of
cacheBean.getPCStatus(PCBean.status)
so it will return "In Inventory" for a 1 and something else for a 2 etc.
However, the code is throwing an error.
Here is the code:
readonly="true">
<xp:this.value><![CDATA[#{CacheBean.getPCStatus(PCModelBean.status)}]]></xp:this.value>
</xp:inputText>
The error is
Error in EL syntax, property 'value': CacheBean.getPCStatus(PCModelBean.status)
I know I read something about this long ago but cannot remember how to handle this, but cannot find it.
I was wondering if the method getPCStatus should be in the PCBean or in the cacheBean?
The version of EL used n XPages doesn't have support for calling methods with parameters. If getPCStatus() were a zero-argument method, you could call it with #{CacheBean.pCStatus}, presumably, but as it is it's the parameter that's in your way.
There are a few common workarounds: if CacheBean itself implements Map or DataObject, then EL will call the get or getValue method, respectively, with whatever you put after the "." - you could use that to sort of fake method calls.
Alternatively, you could keep CacheBean a POJO (not implementing one of those interfaces) but have the return value from getPCStatus itself be a Map or DataObject, which would take whatever value you pass in (in this case, PCModelBean.status) and do the lookup, with a binding like #{CacheBean.pCStatus[PCModelBean.status]}. DataObjects aren't too bad to write: https://frostillic.us/blog/posts/FE0AE00B7CEC4F8885257D46006CAB68
Or, as an complete alternative to all of this, if you don't need your binding to be read+write, you could use SSJS to call the method.

Freemarker and Struts 2, sometimes it evaluates as a sequence+extended_hash

First let me say that using Struts2 + Freemarker is a real blast.
Yet there's something is driving me crazy, because I cannot understand why it happens. I ask here as maybe someone else has an idea to share about it.
I've got an action, with a property.
Say
private String myText;
Then I've got a setter and a getter:
public void setMyText(String myText)
{
this.myText = myText;
}
public String getMyText()
{
if (myText == null)
myText = "(empty)";
return this.myText;
}
The result (in struts.xml) is a freemarker result.
So in my Freemarker template there's a line like the following:
<p>The text is: ${myText}</p>
Now consider I'm calling the action without any text parameter: say the url is
http:localhost:8080/myapp/myaction
As the getter provides a default value, when the action is processed and the result passed to my template, the property is set to the default; so I get (html on the browser side)
<p>The text is: (empty)</p>
If I call my action with the parameter set, instead (I mean with something like:
http:localhost:8080/myapp/myaction?myText=hallo
) things go wrong. Freemarker fires the following exception:
Exception occurred during processing request: For "${...}" content:
Expected a string or something automatically convertible to string
(number, date or boolean), but this has evaluated to a
sequence+extended_hash (String[] wrapped into f.e.b.ArrayModel)
It seems that "myText" is found twice...
What am I doing wrong? Or, at least, is there anyone that can explain to me why it happens?
P.S.: it's really found twice; the following is a way to workaround the problem:
<#if myText?is_sequence>${myText[0]}<#else>${myText}</#if>
Yet it seems to me not viable to wrap every variable in that way.
P.P.S.: a further hint: in the freemarker template there's a call to another action some lines before. Something like:
<#s.action var="innerAction" name="getTable" namespace="/foo" />
If I comment the line above, everything works fine.
The myText could be a variable from the freemarker context, but if you want to use action property
<p>The text is: ${action.myText}</p>
Note, that action prefix is not required to access action properties. A property resolution method is applied when resolving freemarker variables:
Property Resoloution:
Your action properties are automatically resolved - just like in a
velocity view.
for example ${name} will result in stack.findValue("name"), which
generally results in action.getName() being executed.
A search process is used to resolve the variable, searching the
following scopes in order, until a value is found :
freemarker variables
value stack
request attributes
session attributes
servlet context attributes
And later you can read what objects are accessible from the context.
Objects in the Context:
The following variables exist in the FreeMarker views
req - the current HttpServletRequest
res - the current HttpServletResponse
stack - the current OgnlValueStack
ognl - the OgnlTool instance
This class contains useful methods to execute OGNL expressions against arbitary objects, and a method to generate a select list using
the <s:select> pattern. (i.e. taking the name of the list property, a
listKey and listValue)
struts - an instance of StrutsBeanWrapper
action - the current Struts action
exception - optional the Exception instance, if the view is a JSP exception or Servlet exception view
The error might be caused by searches from the value stack and returning something that you didn't expect depending on the structure of the stack at the moment of execution.
Adding a prefix to the variable to point out the exact location of the property should fix the redundancy in the code when searching in the value stack.

Dynamically creating a JSF form with java reflection

I'm trying to create a simple crud form to insert data into a database with hibernate, without knowing what the object type is. The ultimate goal is to only have one insert form for every table in the database. So far i get the methods that the current object has, check to see if it has any set methods and create a text input for every field that has a set.
UIViewRoot viewRoot = FacesContext.getCurrentInstance().getViewRoot();
HtmlPanelGrid hpg = (HtmlPanelGrid) viewRoot.findComponent("panel");
for (Method method : declaredFields) {
String name = method.getName();
if (name.contains("set")) {
HtmlOutputText hot = new HtmlOutputText();
HtmlInputText hit = new HtmlInputText();
hot.setValue(name.substring(3));
try {
hit.setValue(newObject.getClass().getMethod(name, String.class));
} catch (Exception ex) {
Logger.getLogger(ReflectController.class.getName()).log(Level.SEVERE, null, ex);
}
hpg.getChildren().add(hot);
hpg.getChildren().add(hit);
}
}
Here newObject is the object that is going to be inserted into the database later with hibernate. My problem is this:
How do assign a certain field from that object to the text input that is being created at the moment. So far if I put the method in the value like I'm doing above, it will just print out the method in the value attribute for that input. what i want is that when this form is submited, for to assign the value in that text box to the property with that name.
I can give you a partial answer - You need to create a ValueExpression dynamically
Application app = FacesContext.getCurrentInstance().getApplication();
hit.setValueExpression("value", app.getExpressionFactory().createValueExpression(FacesContext.getCurrentInstance().getELContext(), "#{bean.item}", Item.class));
The hard part will be creating the valueExpression that will actually map to a field within your object's value. That requires a great deal more thought but you will for sure need the dynamic valueExpression. As written, this will result in the execution of your bean's setItem();method with a parameter of type Item. You will require something a little more complex.
In JSF, binding input components to properties is accomplished with EL-expressions. You can create one programmatically as Steve shows, but that syntax is really ugly. On a related note, programmatic manipulation of the component tree is a rather unorthodox way of using JSF. The orthodox way to tackle your requirement would be something like:
<ui:repeat var="prop" value="#{genericEditorBean.propertyNames}">
<h:outputLabel value="#{prop}" for="input"/>
<h:inputText id="input" value="#{genericEditorBean.object[prop]}"/>
</ui:repeat>
where
public List<String> getPropertyNames() {
List<String> propertyNames = new ArrayList<>();
BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass());
for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
propertyNames.add(pd.getName());
}
return propertyNames;
}
(There really is no reason to reimplement scanning for Java Bean properties when the Java API offers a class for that very purpose. Unlike your home-grown version, this will also handle properties inherited from a super class ...)
I once used an open-source library named MetaWidget to do this.
It was a few years ago, but it worked well and was easy to set up.
It looks like the project is still active:
http://metawidget.sourceforge.net/index.php

How to get ID of calling component in the getter method?

Given the following example:
<h:inputText id="foo" size="#{configBean.size}" />
I would like to get the id of the calling component foo in the getter method so that I can return the size from a properties file by a key of foo.length.
public int getSize() {
String componentId = "foo"; // Hardcoded, but I should be able to get the id somehow
int size = variableConfig.getSizeFor(componentId);
return size;
}
How can I achieve this?
Since JSF 2.0, there's a new implicit EL variable in the component scope: #{component} which refers to the current UIComponent instance. Among its getter methods there's a getId() which you need.
So you can just do:
<h:inputText id="foo" size="#{configBean.getSize(component.id)}" />
with
public int getSize(String componentId) {
return variableConfig.getSizeFor(componentId);
}
Alternatively you can also make the variableConfig an #ApplicationScoped #ManagedBean so that you can just do:
<h:inputText id="foo" size="#{variableConfig.getSizeFor(component.id)}" />
(using the full method name in EL instead of the property name is necessary whenever you want to pass arguments to the method, so just variableConfig.sizeFor(component.id) won't work, or you must rename the actual getSizeFor() method to sizeFor() in the class)
I think the answer BalusC gave is the best possible answer. It shows one of the many small reasons why JSF 2.0 is such a big improvement over 1.x.
If you're on 1.x, you could try an EL function that puts the ID of the component into the request scope under some name your backing bean method can pick up.
E.g.
<h:inputText id="foo" size="#{my:getWithID(configBean.size, 'foo')}" />
The EL method's implementation could look something like this:
public static Object getWithID(String valueTarget, id) {
FacesContext context = FacesContext.getCurrentInstance();
ELContext elContext = context.getELContext();
context.getExternalContext().getRequestMap().put("callerID", id);
ValueExpression valueExpression = context.getApplication()
.getExpressionFactory()
.createValueExpression(elContext, "#{"+valueTarget+"}", Object.class);
return valueExpression.getValue(elContext);
}
In this case, whenever the getSize() method of the config bean is called, the ID of the calling component would be available via "callerID" in the request scope. To make it a little neater you should maybe add a finally block to remove the variable from the scope after the call has been made. (note that I didn't try the above code, but it hopefully demonstrates the idea)
Again, this would be a last resort when you're on JSF 1.x. The cleanest solution is using JSF 2.0 and the method BalusC describes.

JSF: navigation

I have to warn you: the question may be rather silly, but I can't seem to wrap my head around it right now.
I have two managed beans, let's say A and B:
class A
{
private Date d8; // ...getters & setters
public String search()
{
// search by d8
}
}
class B
{
private Date d9; //...getters & setters
public String insert()
{
// insert a new item for date d9
}
}
and then I have two JSP pages, pageA.jsp (the search page) and pageB.jsp (the input page).
What I would like to do is placing a commandbutton in pageB so to open the search page pageA passing the parameter d9 somehow, or navigating to pageA directly after b.insert(). What I would like to do is showing the search result after the insertion.
Maybe it's just that I can't see the clear, simple solution, but I'd like to know what the best practice might be here, also...
I though of these possible solutions:
including **A** in **B** and linking the command button with **b.a.search**
passing **d9** as a **hiddenInput** and adding a new method **searchFromB** in **A** (ugly!)
collapsing the two beans into one
JSF 1.1/1.2 raw doesn't provide an easy way to do this. Seam/Spring both have ways around this and there are a couple of things you can do. JSF 2 should also have solutions to this once it is released.
Probably the easiest and most expedient would be to collapse the two beans into one and make it session scoped. The worry, of course, is that this bean will not get removed and stay in session until the session times out. Yay Memory leaks!
The other solution would be to pass the date on as a GET parameter. For instance, you action method could call the
FacesContext.getCurrentInstance().getExternalContext().redirect("pageB?d9=" + convertDateToLong(d9));
and then get the parameter on the other side.
You should configure the navigation flow in faces-config.xml. In ideal scenario you would return a "status" message which would decide the flow. Read more at following link:
http://www.horstmann.com/corejsf/faces-config.html
http://publib.boulder.ibm.com/infocenter/rtnlhelp/v6r0m0/index.jsp?topic=/com.businessobjects.integration.eclipse.doc.devtools/developer/JSF_Walkthrough8.html
As far as passing the values from one page to another is concerned you can use backing beans. More about backing beans here:
http://www.netbeans.org/kb/articles/jAstrologer-intro.html
http://www.coderanch.com/t/214065/JSF/java/backing-beans-vs-managed-beans
Hope i have understood and answered correctly to your question
Way to share values between beans
FacesContext facesContext = FacesContext.getCurrentInstance();
Application app = facesContext.getApplication();
ExpressionFactory elFactory = app.getExpressionFactory();
ELContext elContext = facesContext.getELContext();
ValueExpression valueExp = elFactory.createValueExpression(elContext, expression, Object.class);
return valueExp.getValue(elContext);
In above code "expression" would be something like #{xyzBean.beanProperty}
Since JSF uses singleton instances, you should be able to access the values from other beans. If you find more details on this technique, I am sure you'll get what you are looking for.
Add commandButton action attribute referencing to B'insert method
<h:commandLink action="#{b.insert}" value="insert"/>
In B'insert method,add d9 parameter as request parameter. Then return an arbitrary string from insert method.
FacesContext fc = FacesContext.getCurrentInstance();
fc.getExternalContext().getRequestMap().put("d9", d9);
Then go to faces context and add navigation from B to A with "from-outcome" as the arbitrary String you returned from insert method. But don't add redirect tag to navigation tags as it will destroy the request coming from B and the parameter you added (d9) will be cleared.
<from-outcome>return string of insert method</from-outcome>
<to-view-id>address of A</to-view-id>
Then you might get the "d9" in A class by fetching it from request map at its constructor or in a place where its more appropriate (getters). You might add it into a session scope or place it to a hidden variable if you want to keep track of it later.
in class A, when page is navigated, A should be initialized as it will be referenced.
FacesContext fc = FacesContext.getCurrentInstance();
fc.getExternalContext().getRequestMap().get("d9", d9);
Sorry i cant give full code, as i have no ide at here, its internet machine at work. I could not give details therefore.
In my opinion, the simplest way is 3-rd option - have both query and insert methods in same class. And you can do something like that:
public String query () {
//...
}
public String Insert() {
//insert
return Query(); }
If your classes are managed Beans you can load class A from class B and call A.query() in your insert method at the end. Also class A can have
<managed-bean-scope>session</managed-bean-scope>
parameter in faces-config.xml and it wouldn't be instantiated again when loaded.

Categories