Evaluate EL expression from string as bean in JSF page - jsf

I am fairly new to JSF and am currently a frontend developer for a new project. I do not have access to the backend code and need to create a solution with the data I have access to on the page.
Currently I have a menu and there are security controllers on HTML elements to control visibility.
Example:
style="#{security.ConfigMenu?'visibility:visible':'visibility:hidden;width:0px;height:0px;padding-bottom: 0px;'}"
This page is hardcoded and need to make it more dynamic.
I need to evaluate this expression but use an array of beans stored as strings
var security = ["security.ConfigMenu", "security.AdminMenu", "security.Dashboardmenu"];
Need to handle #{mappSec.insightsConfigMenu'} with #{security[0]}
Is this possible?
I tried to replace the bean call with a string using the bean name
#{bean.method}
with
var bean = "bean.method";
#{bean}

Related

How can I use a session bean and deal with many tabs with different parameters?

in a jsf 2.2 application, there is page called test.xhtml that take a paramater called ‘id’ , for example test.xhtml?id=200 . The page is backed by a CDI Session bean named ‘TestBean’. The page has this code to load data:
<f:metadata>
<f:viewAction action="#{testBean.redirectNoParameters}"></f:viewAction>
</f:metadata>
Now based on the id, the application load a set of fields in the session bean with the right values.
public String redirectNoParameters() {
//Code…
//Load fields
test = testDao.find(id);
//Code…
}
Till now is all good.
Except that when the user opens a new tab in the browser and specify a DIFFERENT id, for example test.xhtml?id=300 . the Session bean override the current values of the previous parameter 200, with the new id 300 values.
So my question is how can I use a session bean and deal with many tabs with different parameters? How can I have a session bean for each tab? If this is not possible than what solution do people use for this kind of scenario ? Thanks.
A #SessionScope Bean is living as long as the users Session is active. (hence the name) - It is shared between Requests and Views, which is the "Problem" you are facing.
A #RequestScope Bean will be recreated upon every request (No Matter if first-Access or Ajax-Request), which is possible to use for your usecase (reloading data based on resubmitted IDs), but can be optimized. (This would be a traditional Request/Response-Model as known from PHP - JSF offers a better option)
Your case perfectly matches the #ViewScope. One Bean per View, living as long as the View is present. This would allow you to open an (almost) infinite amount of different Views (hence, the name), each having it's individual set of BackingBeans as long as they are #ViewScope. Multiple "Views" of the same page are possible, each View will maintain the reference to it's dedicated View-Scoped-Beans. (In your example: 2 opened text.xhtml pages and therefore 2 active instances of TestBean, each having its own ID)
The behaviour of your application is correct. Please read the section about Scopes of The Java EE 6 Tutorial.
Your application has created a session scoped bean for the current user and it will be used by all request for such user. Any variable created in such bean will be shared by any tab the user opens.
If you expect the user to interact on multiple tabs with different values for the same variable, consider using a request scoped bean.

Are there any ways to access back-end bean's method based on the specific URL in a JSF + PrimeFaces Project?

I want to access back-end bean's method without using any menu or link. For example, if I type http://localhost:8080/myproject/userObj/userNew, userNew method will be called. If I type http://localhost:8080/myproject/userObj/userList, userList method will be called. Are there any ways to setup like above?
At my current project, I use PrimeFaces Menubar. Attribute action is used to point to the bean's method. What I want is some mapping feature that will access bean's method based on the URL. Thanks!

Sharing components between views - how to improve my design?

I'm working on a JSF webapp which purpose is to wrap a command-line program.
One of its main functionality is the ability to share a session between users (eg. for courses purpose), so that when an input is sent to an instance of the application, the output sent to every subscriber for this session.
As a result of this design, the webapp is mainly composed of a view-scoped bean which will request a controller of the command-line application.
It has also been chosen to identify a session with the URL fragment (eg. mydomain/myapp/#SESSIONID), so that anyone using the URL with the same fragment will share inputs and outputs, using its own instance of the view-scoped bean but sharing the same controller
In order to push results to all subscribers, I'm using Primefaces Push. Results are primarily text that has to be appened to the webapp's terminal, but some commands lead to the programmatic creation of a JSF component.
In order to handle this, I just render these components to a string that I send to all subscribers.
Then, I realized that in order to handle ajax requests from components (and from every subscriber), the associated UIComponent needs to be added to the UIViewRoot in the context of (don't know how to express this) each view-scope bean.
As a matter of fact, I first tried to bind a "common container" (a UIForm) to a property of the view scoped bean, in which I would put the programmatically created components, but I obviously had to face the chicken/egg issue #BalusC talks about in his blog, because the component was added again on each ajax request. Setting javax.faces.PARTIAL_STATE_SAVING to false didn't help either (I'm using MyFaces 2.2.5)
So, as somewhat of a workaround, when the controller needs to create a new component, it basically adds the id of the component to the data pushed (in a HashMap converted to Json), and all subscribers will trigger (back) a remoteCommand to its own instance of the view-scoped bean, in order to update the "common container" from its own UIViewRoot.
This does work, but I don't like this way of doing it!
So:
would it be possible to handle this kind of sharing between view-scope beans (with the same name) which are stored in different HTTP sessions? I'm refering to this answer from #BalusC... maybe playing with javax.faces.ViewState - would it even be possible?
Is there a "magical" scope for my currently-view-scoped bean I could use?
Shall I rather use a completely different design?
Thanks!
If you want share data between all your application users you can use application scope.
If you still want to use view scope, you can connect your view scope with another application scope like this:
ApplicationView appView = BeanUtil.findBean("applicationView", FacesContext.getCurrentInstance());
import javax.faces.context.FacesContext;
public class BeanUtil {
#SuppressWarnings("unchecked")
public static <T> T findBean(String beanName, FacesContext context) {
return (T) context.getApplication().evaluateExpressionGet(context,
"#{" + beanName + "}", Object.class);
}
}

Audit trail for JSF application - Global valueChangeListener possible?

I am trying to implement an audit trail functionality for my web application that records:
lastModified (timestamp)
modifiedBy (user information)
userComment (reason for value change)
for each of my input fields (input fields are spread over several forms with different backing beans and different valueHolder classes).
The first two (lastModified and modifiedBy) are easily done with the help of an JPA AuditListener and #PrePersit and #PreUpdate methods.
The third one is a bit tricky since it requires user interaction. Best would be a dialog that asks for the user comment.
So there are (at least) two open issues: Can I establish a "global" valueChangeListener for all input fields in my application? Is this possible without attaching <f:valueChangeListener> to each single input component? Second: How can I grab the user comment. My idea is to put a p:dialog in my web page template but this dialog needs to know from which input component it is called.
Can I establish a "global" valueChangeListener for all input fields in my application? Is this possible without attaching to each single input component?
Yes, with a SystemEventListener which get executed during PreRenderViewEvent. You need to walk through the component tree as obtained by FacesContext#getViewRoot() to find all components which are an instanceofEditableValueHolder (or something more finer-grained) and then add the new YourValueChangeListener() by the addValueChangeListener() method. See also this answer how to register the system event listener: How to apply a JSF2 phaselistener after viewroot gets built?
Second: How can I grab the user comment. My idea is to put a p:dialog in my web page template but this dialog needs to know from which input component it is called.
You could in YourValueChangeListener#processValueChange() set the component in question as a property of some request or view scoped which you grab by evaluateExpressionGet().
Recorder recorder = (Recorder) context.getApplication().evaluateExpressionGet(context, "#{recorder}", Recorder.class);
recorder.setComponent(event.getComponent());
// ...
It will execute the EL and auto-create the bean in its scope if necessary. The bean in turn should also hold the property representing the user comment. Finally, you can use it in your <p:dialog>.
<p>You have edited #{recorder.component.label}, please mention the reason:</p>
...
<h:inputText value="#{recorder.comment}" />

JSF passing view parameters by reference - when object must be instantiated

Let's say I've got a register page & a register confirm page. I enter user
details into the register page, navigate to the register confirm page where
I can return back to the register page if there are any mistakes.
I'm going to use view parameters to make the registration data available
from the register page to the confirm page, and vice versa.
Supposing there are 20 items of data to be moving from page to page, that's
a lot of view parameters and a lot of setPropertyActionListeners, especially
as all the data is going to end up nicely packaged in a User object.
So what I want to do is input the data on the register page into the
properties of a User record and send a reference to it to the register
confirm page. What gave me an idea was seeing the BalusC WeakHashMap
converter. This is a JSF converter which has a static weak hash map and
generates a uuid as the value for a map entry and the object reference as
the key. So by specifying this as a converter for f:viewParam you send
the uuid in the query string.
This works fine. The issue I have is that on the register page I have to
get an instance of a User class with new. Then I can do:
<h:inputText value="#{bean.user.firstname}"/>
(etc...), and pass the user instance as a view parameter. It works fine from
the register to the confirm page. The issue is that when I perform the
reverse, sending the user reference back to the register page from the
confirm page I absolutely cannot prevent the register page backing bean
from re-instantiating the user object, after the setter has been called
as a result of the view parameter.
So the converter does it's job and retrieves the User object from the
hash map, calls setUser() in the backing bean, and then I see the
constructor for the User class firing.
I've tried calling new User() from the bean constructor, in #PostConstruct,
in a preRenderView (also checking if an ajax request), but nothing I try
prevents the work of the view parameter from getting wiped out if new is
involved. I'm sure there's a simple solution but I just can't see it right
now.
I'd be grateful for any suggestions for how to solve this problem.
The issue I have is that on the register page I have to get an instance of a User class with new.
So what code is initially creating this new User instance then? If you do this in the preRenderView handler, then you can simply check for null, can't you?
If the view parameter and converter haven't done their job, user would still be null and you create a new instance. The bean constructor and #PostConstruct won't do you any good here, since they both run before the view parameter does its thing, but the preRenderView event is guaranteed to run after it.
#ManagedBean
public class Bean {
private User user;
public void onPreRenderView() {
if (user == null) {
user = new User();
}
}
}
(Something to additionally consider is that the conversation scope already does exactly what you're trying to do here. This is part of CDI not JSF, but if you're running in a Java EE 6 Web Profile compliant AS (JBoss AS 6 or 7, Glassfish V3, Resin 4, ...) you already have it. Otherwise it's just an extra jar.)
After several attempts over more than a year to find a solid long term solution
to this problem, at last! I've found one. The solution comes in the form of the
Apache Myfaces CDI extensions project, aka Myfaces CODI.
This provides additional scopes such as the #ViewAccessScoped which ensures that
if a bean is referenced by a page then it is available for that page. Also
provided is support for conversation groups. In the scenario where I want to
pass an object reference from a register page to a register confirm page, the
confirm page can just access the registerView bean directly on the next request.
Alternatively you can #Inject one bean into another and access it on the next
request, or use f:setPropertyActionListener from the source page.
Myfaces CODI works fine with Mojarra and also with ajaxified component libraries
such as primefaces. The concept is similar to what is provided by Jboss Seam,
though I've found the additional scope support to be better thought out and I've
tested this on glassfish 3.1.1 with no problems.
If you're using #ManagedBean and scope annotations from the javax.faces.bean
package in your code, codi intercepts these annotations and uses it's own
CDI based versions, so you can convert to CDI simply by adding codi as a
dependency to your project and not changing any code.
For me this is like moving from black and white TV to colour TV, I wish I'd
found this stuff sooner.
CODI documentation

Resources