I have 2 JSF managed beans A and B and I need to expire/destruct/destroy A after 2 minutes and B after 5 minutes. I checked this related question Timing out from a bean, but it is expiring whole session. I do not want to expire whole session.
How can I achieve this with a custom scope?
Given that you're using JSF bean management facility (and thus not CDI, which would require a completely different answer), you can achieve this with #CustomScoped. The #CustomScoped value must refer a Map implementation in a broader, usually existing, scope.
Something like:
#ManagedBean
#CustomScoped("#{timeoutScope}")
public class TimeoutBean {}
As the #CustomScoped annotation doesn't support passing additional arguments, setting the timeout can only be done via an additional custom annotation like below:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface Timeout {
/** Minutes. */
int value();
}
#ManagedBean
#CustomScoped("#{timeoutScope}")
#Timeout(5) // Expires after 5 minutes.
public class TimeoutBean {}
Now, here's a kickoff example of how the #{timeoutScope} looks like, including #PostConstruct support (automatically) and #PreDestroy support (manually):
#ManagedBean
#SessionScoped
public class TimeoutScope extends HashMap<String, Object> {
private static final long serialVersionUID = 1L;
#Override
public Object put(String name, Object bean) {
Timeout timeout = bean.getClass().getAnnotation(Timeout.class);
if (timeout == null) {
throw new IllegalArgumentException("#Timeout annotation is required on bean " + name);
}
Long endtime = System.nanoTime() + (timeout.value() * (long) 6e10);
Object[] beanAndEndtime = new Object[] { bean, endtime };
return super.put(name, beanAndEndtime);
}
#Override
public Object get(Object key) {
Object[] beanAndEndtime = (Object[]) super.get(key);
if (beanAndEndtime == null) {
return null;
}
Object bean = beanAndEndtime[0];
Long endtime = (Long) beanAndEndtime[1];
if (System.nanoTime() > endtime) {
String name = (String) key;
ScopeContext scope = new ScopeContext("timeoutScope", Collections.singletonMap(name, bean));
FacesContext context = FacesContext.getCurrentInstance();
context.getApplication().publishEvent(context, PreDestroyCustomScopeEvent.class, scope);
return null;
}
return bean;
}
}
You see, it's session scoped and implements Map. As to the scope, this way it's tied to a specific user session, not to the whole application. If you actually want to share the bean across all user sessions in the application, then make it application scoped instead. As to the Map, henever JSF needs to find a managed bean, it first tries get(). If it returns null (i.e. bean doesn't exist yet), then it will auto-create the managed bean instance and perform a put().
Inside the put(), it's a matter of extracting and calculating the timeout and store it in the map. Inside the get(), you just check the timeout and return null to indicate JSF that bean doesn't exist anymore. JSF will then simply auto-create it and come back at put(), etc.
Do note that I'm using System#nanoTime() instead of System#currentTimeMillis() as the latter is tied to OS (operating system) time, not to hardware time (and it's thus sensitive to a.o. DST and enduser-controlled changes in time).
Related
I have a class
#RequestScoped
public class AFactory {
private final HttpServletRequest request;
protected AFactory () {
this.request = null;
}
#Inject
public AFactory (HttpServletRequest request) {
this.request = request;
}
#Produces
public A getA() {
int random = ...;
A a = new A(request);
a.setRandom(random);
return a;
}
}
I understand since I am doing new A(), I return the real instance.
Is this the expected way to use producers?
Is there a way to return the proxied instance?
CDI installs proxies for beans of all scopes except for the #Dependent pseudo scope. A #Produces annotated method (short producer) is called by CDI whenever it needs to get a (new) instance of a bean. This instance is then put into some pool for the corresponding scope.
The proxy will always return the bean-instance from the pool. Try adding some debug message to the producer method to see how often it will be called. A producer for an #ApplicationScoped bean should be called only once, and a #RequestScoped producer should be called once per request.
In the examples above no scope is given to the producer method (the scope of the factory class is not used for the producer method), so the default scope (#Dependent) will be used. And this means (as no proxy is used for this scope), a new instance will be injected every time #Inject A is found by CDI. See also this Question and Answer and the referenced documentation for more details.
So to your concrete questions:
The observed behavior is expected
Proxies will only be used if scope is not #Dependet which is the default.
I'm beginning with JSF (Mojarra 2.2 and Glassfish 4) and currently practicing with a web application which job is to store Clients and their Orders in DB.
When creating a new Order, one feature is to allow choosing an existing client from a JSF <h:selectOneMenu>. An Order entity stores a Client entity among other attributes...
I've followed BalusC's great answer about prepopulating a <h:selectOneMenu> from a DB (here), and have successfully populated mine from data stored in an eager ApplicationScoped ManagedBean, but I can't manage to retrieve the selected item in the backing bean as complex object. It is always null.
This is driving me mad and your help will be truly appreciated!
Here are the relevant code snippets:
#ManagedBean(eager = true)
#ApplicationScoped
public class Data implements Serializable {
private static final long serialVersionUID = 1L;
#EJB
private ClientDao clientDao;
private List<Client> clients;
#PostConstruct
private void init() {
clients = clientDao.lister();
}
public List<Client> getClients() {
return clients;
}
}
Order creation bean (note: 'commande' means order ;)
#ManagedBean
#RequestScoped
public class CreerCommandeBean implements Serializable {
private static final long serialVersionUID = 1L;
private Commande commande;
private String choixNouveauClient = "nouveauClient";
#EJB
private CommandeDao commandeDao;
public CreerCommandeBean() {
commande = new Commande();
}
public void inscrire() {
System.out.println("client : " + commande.getClient()); // prints **NULL**
// ... orderService to store in DB
}
... getters and setters
Client converter:
#FacesConverter(value = "clientConverter", forClass = Client.class)
public class ClientConverter implements Converter {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null) {
return null;
}
Data data = context.getApplication().evaluateExpressionGet(context, "#{data}", Data.class);
for (Client c : data.getClients()) {
if (c.getId().toString().equals(value)) {
return c;
}
}
throw new ConverterException(new FacesMessage(String.format("Cannot convert %s to Client", value)));
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return (value instanceof Client) ? String.valueOf(((Client) value).getId()) : null;
}
}
Facelet excerpt:
<p:outputPanel id="gridContainerAncienClient">
<p:selectOneMenu value="#{creerCommandeBean.commande.client}"
rendered="#{creerCommandeBean.choixNouveauClient == 'ancienClient'}">
<f:converter converterId="clientConverter" />
<f:selectItems value="#{data.clients}" var="cli"
itemValue="#{cli}" itemLabel="#{cli.prenom} #{cli.nom}" />
</p:selectOneMenu>
</p:outputPanel>
CreerCommandeBean is #RequestScoped. That means it will live only for one request.
When you select a client to be assigned to #{creerCommandeBean.commande.client} you do this by a request. #{creerCommandeBean.commande.client} is now the selected client. Then the request is over, the bean gets destroyed and your "changes" are lost.
When you try to retrieve that data, you do that by a request again: A new instance of CreerCommandeBean is created and the constructor assigns the property commande with a new instance of Commande whose property client again is probably null.
Solution:
Use a broader scope. e.g. #ViewScoped which makes the bean "live" as long as you stay in the same view - no matter how many requests you make.
Tip:
Read BalusC's Post on Communication is JSF 2.0. Parts might be slightly different in JSF 2.2 but it's still a good and comprehensive introduction.
I got stuck with similar problem, only to realize that I forgot to implement equals() and hashCode() method in my Object. Client Class in this case.
I should blame myself for skipping the instructions in BalusC's blog.
"...Please note the Object#equals() implementation. This is very important for JSF. After conversion, it will compare the selected item against the items in the list. As the Object#equals() also require Object#hashCode(), this is implemented as well...."
I' have a question about initialization of List in the POJO as it follows the next code:
public class Person {
//other fields...
private List<String> friends=new ArrayList<>();
public List<String> getFriends() {
return friends;
}
public void setFriends(List<String> friends) {
this.friends = friends;
}
}
OR is it better like this and have initalization in other class(like for example Bean(JSF))
public class Person {
//other fields...
private List<String> friends;
public List<String> getFriends() {
return friends;
}
public void setFriends(List<String> friends) {
this.friends = friends;
}
}
So my question is what approach is better?
If it's a managed bean as you say, you should do this in a method annotated with #PostConstruct
public class Person {
private List<String> friends;
#PostConstruct
public void init(){
friends = new ArrayList<String>();
}
//getter and setter...
}
The practice of doing any initialization in the getter and setter is generally frowned upon within the context of JSF. See Why JSF calls getters multiple times
Also, per the API for #PostConstruct, the contract specifies safety features and guarantees that if an exception is thrown in a method annotated as such, the bean should not be put into service. There are no such guarantees on a plain constructor.
In a managed bean, injection happens immediately after construction. This means that any operations you're carrying out in the constructor cannot depend on any injected resources (via #ManagedProperty). Whereas in a #PostConstruct method, you'll have access to all the resources declared on the managed bean
EDIT: It's important to note that there can be only one #PostConstruct for any #ManagedBean, so all important initializations should happen in there.
It's also worthwhile to note that, while the #PostConstruct method is the ideal place to initialize a backing bean variable/List, there are implications regarding the scope of the managed bean
#RequestScoped: In a managed bean with this annotation, the method will be called per submit of the JSF view concerned. A #RequestScoped bean is destroyed and recreated with every request, The implication of this is that depending on your setup, the list initialized in the #PostConstruct may be reset to empty or default values during each request. Under certain circumstances, conversion errors may occur as a result of the re-initialization of the list mid-JSF request.
#ViewScoped: In a managed bean with this annotation, you're guaranteed to have the #PostConstruct method run once, if and only if you're dealing with the same instance of the #ViewScoped bean. If the viewscoped bean is destroyed and recreated, the #PostConstruct method will run again.
#SessionScoped: A bean with this annotation is created once and stays alive until the user's HTTP session ends. In this scenario, the #PostConstruct method is guaranteed to run once and only once until the bean is destroyed
See also
https://stackoverflow.com/a/3406631/1530938
I would suggest this:
public class Person {
//other fields...
private List<String> friends=new ArrayList<>();
// returns a copy to protect original list
public List<String> getFriends() {
Collections.unmodifiableList(new ArrayList<>(friends));
}
public void addFriend(String> friend) {
this.friends.add(friend);
}
public void addFriends(List<String> friends) {
this.friends.addAll(friends);
}
}
In my opinion it would be best to handle that in the constructors. If a default constructor is used, initialize the list in the constructor.
public Person() {
friends = new ArrayList<>();
}
If a constructor which accepts parameters is used, let the calling class pass in a list.
public Person(ArrayList<> friends) {
this.friends = friends;//friends
}
My suggestion, add a null check in the getter:
public class Person {
//other fields...
private List<String> friends;
public List<String> getFriends() {
if (this.friends == null) friends = new ArrayList<String>();
return friends;
}
}
But also notice I have omitted the setter. Instead, in any client code, call like this:
personInstance.getFriends().add("Some Item");
Or if you have a full list to add:
personInstance.getFriends().addAll(someStringCollection);
It depends. Usually first way preferable because you may want to add something to collection later. If you won't know was your collection initialized or not you must check it every time.
I have a navigation managed bean for each user.
and I need it to initialize first before any other bean because a value is required from the bean.
May I know how do I perform that?
I have tried eager="true" but it doesn't work.
any quick and easy solution via faceconfig would be greatly appreciated.
Just perform the desired initialization job in bean's #PostConstruct.
#PostConstruct
public void init() {
// Here.
}
It'll be invoked when the bean is injected/referenced from another bean for the first time.
The eager=true works only on application scoped beans.
From what I see you should reference the other bean. Let's assume a have a utility class that can pull a bean from the context.
Basically ->
//Bean A
public void doSomething()
{
String required = Utility.getBeanB().getRequiredValue();
use(required);
}
...
//Bean B
public String getRequiredValue()
{
return "Hi, I'm a required value";
}
I have several large web apps that have a "Session Bean" that stores stuff like user preferences, shared objects etc... and this method works perfectly. By using a reference to the bean you eliminate the need to chain the initialization. That method will always DEPEND on the method in the other bean, thus guaranteeing the order of initialization.
There's a variety of ways to access the bean but I usually go through the EL route ->
Get JSF managed bean by name in any Servlet related class
Best of luck, I try to stay "functionally pure" when I can--and I hope that get's a laugh considering the language!
Here's some cool hax for ya, in case other solutions aren't working for you due to various circumstances...
Let's say I have a class Alpha that I want initialized first:
public class Alpha {
#PostConstruct
public void init() {
}
}
I can put the following method in Alpha:
#ManagedBean(name = "Alpha", eager = true)
public class Alpha {
public static void requireAlpha() {
FacesContext context = FacesContext.getCurrentInstance();
Object alpha = context.getApplication().evaluateExpressionGet(context, "#{Alpha}", Object.class);
System.out.println("Alpha required: " + alpha.toString());
}
#PostConstruct
public void init() {
}
}
Then, in any classes that are initializing too early, simply call:
Alpha.requireAlpha();
// ...
// (Code that requires Alpha to be initialized first.)
And if there's a ChildAlpha class that extends Alpha that you really want to be initialized (rather than the parent), make sure to use "ChildAlpha" instead, in both the name="" and the EL Expression ("#{}").
See here for more infos: Get JSF managed bean by name in any Servlet related class
I have this code for a backing bean:
#PostConstruct
public void refreshData()
{
rows = (int) dd.getRows();
pages = dd.getPages();
getRender();
}
// action
public void getCount(String sql, Object... values)
throws Exception
{
dd.getCount(sql, values);
rows = (int) dd.getRows();
pages = dd.getPages();
}
// getter methods
public boolean getRender() {
System.out.println("pages: "+pages);
boolean rendered = pages > 0? true: false;
return rendered;
}
public int getRows() {
return rows;
}
public int getPages() {
return pages;
}
Does the refreshData() method with the #PostConstruct directive get executed after or before all the getter methods? I ask this because I notice the getRender() method always return zero even though the getPages() returns a number like 10 for example.
I have no idea what you mean with "before all the getter methods". At least the #PostConstruct is called immediately after the construction of the bean and the setting of all managed properties (the bean properties which are definied in faces-config.xml).
Thus roughly:
Bean is constructed.
Managed properties are set.
#PostConstruct is called.
Bean is brought in JSF lifecycle.
Your problem is likely that the value is been overriden by something else. Just run the debugger or have your code reviewed by an expert.
The JSF 1.2 spec says specifically (Page 11, Item 119 preface):
Methods on managed beans declared to be in request, session, or application
scope, annotated with #PostConstruct, must be called by the JSF implementation after
resource injection is performed (if any) but before the bean is placed into scope.
(More details in the spec.)