So to make it relatively simple: I have some Primefaces-Page, which is supposed to represent database records in a table-structure.
I keep these records in a List<Customer> which resides in a #ConversationScoped backing bean. I have verified via debugging, that the List gets correctly filled with records from the Database (hibernate db FWIW). This is accomplished with the help of a "businessInterface" Distributor class, that is in fact nothing but a decoupling mask for (Database)Service-Classes.
As mentioned, I have verified that the Database, as well as the Distributor correctly return the expected values. Unfortunately in the View there are no records present and instead the emptyMessage is displayed.
The application is running on a jBoss 7.1.1-Final Application Server.
For better readability I have excluded the h:head, as well as h:body, ui:composition, ui:define and h:form around the provided code, as well as shortened the columns to the two different usages (property display and action exposition)
The View (shortened and truncated):
<ui:define name="inhalt">
<p:growl id="msgGrowl" autoUpdate="true" showDetail="true" />
<h:form onkeypress="if (event.keyCode == 13) {return false; }">
<p:dataTable var="customeritem" id="customerTable"
rowkey="#{customeritem.id}" value="#{customerListController.customerList}"
paginator="true" rows="13" autoUpdate="true"
filteredValue="#{customerListController.filteredCustomers}"
emptyMessage="no customers found!"
sortFunction="#{customerListController.filteredCustomers}">
<p:column sortBy="name" filterBy="name" headerText="Kunde"
filterMatchMode="contains">
<h:outputText value="#{customeritem.name}" />
</p:column>
<p:column>
<f:facet name="header">
<p:commandButton value="Neuer Kunde"
action="${customerListController.addCustomer()}"
icon="ui-icon-plus" />
</f:facet>
<p:commandButton id="doViewDetailsButton" icon="ui-icon-clipboard"
action="${customerListController.viewDetails(customeritem.getId())}" />
<p:tooltip for="doViewDetailsButton" value="Details ansehen" />
</p:column>
</p:dataTable>
</h:form>
</ui:define>
The Backing Bean:
#Named
#ConversationScoped
public class CustomerListController implements Serializable {
private static final long serialVersionUID = -5961625401284927892L;
private List<Customer> customerList = new ArrayList<Customer>();
private List<Customer> filteredCustomers = new ArrayList<Customer>();
#Inject
CustomerEditController customerEditController;
#Inject
CustomerDetailsController customerDetailsController;
#Inject
CustomerDistributor businessInterface;
public String addCustomer() {
return editCustomer(0l);
}
public String editCustomer(long customerId) {
setFilteredCustomers(null);
customerEditController.recieveCustomerById(customerId);
return Pages.CUSTOMER_EDIT;
}
public String viewDetails(long customerId) {
setFilteredCustomers(null);
customerDetailsController.recieveCustomerById(customerId);
return Pages.CUSTOMER_DETAILS;
}
public String deleteCustomer(long customerIdToDelete) {
businessInterface.delete(customerIdToDelete);
setFilteredCustomers(null);
fillCustomerList();
return Pages.CUSTOMER_LIST;
}
#PostConstruct
public void fillCustomerList() {
customerList.clear();
customerList.addAll(businessInterface.loadAll());
}
public List<Customer> getCustomerList() {
return customerList;
}
public List<Customer> getFilteredCustomers() {
return filteredCustomers;
}
public void setFilteredCustomers(List<Customer> filteredCustomers) {
this.filteredCustomers = filteredCustomers;
}
}
This used to work, when I had the Backing Bean in #SessionScoped, but as that required hackish workarounds to produce intuitive (and expected) behavior I decided to move the Backing Bean to a smaller scope. I therefore chose the #ConversationScoped, because the BackingBean needs to stay longer than the request lifecycle... (Also running a query against a db for every request is damn expensive...)
A short explanation on the CustomerEditController and CustomerDetailsController. They are the responsible ones for Editing and Showing further information on the single records if they are requested by clicking one of the Buttons.
The non-working stuff is the #PostConstruct public void fillCustomerList(). Everything else works as expected...
If you need any further information, please ask, I will provide context as needed ;)
I have found a successful workaround for this, but it's extremely hackish and I really dislike the approach, as it introduces additional behavior in a getter. I modified the Backing Bean as follows:
public List<Customer> getCustomerList() {
if (customerList.size() == 0) {
fillCustomerList();
}
return customerList;
}
But let me state this again,
this is definitely not the desired behavior and not a good approach at solving this problem.
EDIT:
I found a different fix after a little more digging and a lucky link. I modified the backing bean as follows:
#Inject
Conversation conversation;
#PostConstruct
public void init() {
if(conversation.isTransient()) {
conversation.end();
}
conversation.setTimeout(120000);
conversation.start();
}
and now It works even without the hackish behavior in the getter (as demonstrated above).
Related
I have started learning JSF, but sadly most tutorials out there present only a log in or a register section.
Can you point me to some more in depth examples? One thing I'm interested in is a page presenting a list of products. I'm on page home and I press on page products so that I can see the latest products added. And every time I visit the page, the product list will be created from the latest entries in the database. How can I handle this?
One way to solve this would be to create a session scoped managed bean in which I would place different entities updated through other managed beans. I found this kind of approach in some tutorials, but it seems quite difficult and clumsy.
Which would be the best approach to solve a thing like this? What is the correct usage of session scope in two-page master-detail user interface?
What is the correct usage of session scope
Use it for session scoped data only, nothing else. For example, the logged-in user, its settings, the chosen language, etcetera.
See also:
How to choose the right bean scope?
And every time I visit the page, the product list will be created from the latest entries in the database. How can I handle this?
Typically you use the request or view scope for it. Loading of the list should happen in a #PostConstruct method. If the page doesn't contain any <h:form>, then the request scope is fine. A view scoped bean would behave like a request scoped when there's no <h:form> anyway.
All "view product" and "edit product" links/buttons which just retrieve information (i.e. idempotent) whould be just plain GET <h:link> / <h:button> wherein you pass the entity identifier as a request parameter by <f:param>.
All "delete product" and "save product" links/buttons which will manipulate information (i.e. non-idempotent) should perform POST by <h:commandLink>/<h:commandButton> (you don't want them to be bookmarkable/searchbot-indexable!). This in turn requires a <h:form>. In order to preserve the data for validations and ajax requests (so that you don't need to reload/preinitialize the entity on every request), the bean should preferably be view scoped.
Note that you should basically have a separate bean for each view and also note that those beans doesn't necessarily need to reference each other.
So, given this "product" entity:
#Entity
public class Product {
#Id
private Long id;
private String name;
private String description;
// ...
}
And this "product service" EJB:
#Stateless
public class ProductService {
#PersistenceContext
private EntityManager em;
public Product find(Long id) {
return em.find(Product.class, id);
}
public List<Product> list() {
return em.createQuery("SELECT p FROM Product p", Product.class).getResultList();
}
public void create(Product product) {
em.persist(product);
}
public void update(Product product) {
em.merge(product);
}
public void delete(Product product) {
em.remove(em.contains(product) ? product : em.merge(product));
}
// ...
}
You can have this "view products" on /products.xhtml:
<h:dataTable value="#{viewProducts.products}" var="product">
<h:column>#{product.id}</h:column>
<h:column>#{product.name}</h:column>
<h:column>#{product.description}</h:column>
<h:column>
<h:link value="Edit" outcome="/products/edit">
<f:param name="id" value="#{product.id}" />
</h:link>
</h:column>
</h:dataTable>
#Named
#RequestScoped
public class ViewProducts {
private List<Product> products; // +getter
#EJB
private ProductService productService;
#PostConstruct
public void init() {
products = productService.list();
}
// ...
}
And you can have this "edit product" on /products/edit.xhtml:
<f:metadata>
<f:viewParam name="id" value="#{editProduct.product}"
converter="#{productConverter}" converterMessage="Unknown product, please use a link from within the system."
required="true" requiredMessage="Bad request, please use a link from within the system."
/>
</f:metadata>
<h:messages />
<h:form rendered="#{not empty editProduct.product}>
<h:inputText value="#{editProduct.product.name}" />
<h:inputTextarea value="#{editProduct.product.description}" />
...
<h:commandButton value="save" action="#{editProduct.save}" />
</h:form>
#Named
#ViewScoped
public class EditProduct {
private Product product; // +getter +setter
#EJB
private ProductService productService;
public String save() {
productService.update(product);
return "/products?faces-redirect=true";
}
// ...
}
And this converter for <f:viewParam> of "edit product":
#Named
#RequestScoped
public class ProductConverter implements Converter {
#EJB
private ProductService productService;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null || value.isEmpty()) {
return null;
}
try {
Long id = Long.valueOf(value);
return productService.find(id);
} catch (NumberFormatException e) {
throw new ConverterException("The value is not a valid Product ID: " + value, e);
}
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value == null) {
return "";
}
if (value instanceof Product) {
Long id = ((Product) value).getId();
return (id != null) ? String.valueOf(id) : null;
} else {
throw new ConverterException("The value is not a valid Product instance: " + value);
}
}
}
You can even use a generic converter, this is explained in Implement converters for entities with Java Generics.
See also:
How to navigate in JSF? How to make URL reflect current page (and not previous one)
JSF Controller, Service and DAO
JSF Service Layer
How to inject #EJB, #PersistenceContext, #Inject, #Autowired, etc in #FacesConverter?
Communication in JSF 2.0 - Contains several examples/hints
As a small improvement to what BalusC recommended, sometimes you can remove the required / requiredMessage part from the <f:viewParam> of your "details" screen and instead use the conditional rendering of the editing form (as BalusC did) with a reverse condition for recommending a specific link for the "list/master" screen or, even use a viewAction that would test the param and force a redirect to that list.
I have a problem with taking names of tUsers and print them on the screen. May be i dont call the method for getting names properly () because when i call the function listAgencies, it prints them correctly in the Eclipse console. Thanks for any advices!
In .xthml file, I have:
<h:panelGrid id="panel2" columns="2" cellpadding="5">
<c:forEach items="${agencyBean.listAgencies()}" var="inputBoxes">
<h:outputText value="${inputBoxes.gettUser().name}" />
<h:inputText />
</c:forEach>
</h:panelGrid>
My bean class:
#ManagedBean(name = "agencyBean")
#SessionScoped
public class AgencyBean {
private TAgency tEventType = new TAgency();
public void listAgencies() {
EntityManager em = HibernateUtil.getEntityManager();
// read the existing entries and write to console
Query q = em.createQuery("select u from TAgency u");
List<TAgency> agencyList = q.getResultList();
for (TAgency agency : agencyList) {
System.out.println("NAme: " + agency.gettUser().getName());
}
}
public TAgency gettEventType() {
return tEventType;
}
public void settEventType(TAgency tEventType) {
this.tEventType = tEventType;
}
}
TUser is another entity from where i want to get the name. I have getName() method which is public.
The problem is here:
<c:forEach items="${agencyBean.listAgencies()}" ... >
It should look for a getter method for listAgencies attribute, but instead it is a void method that will be executed and there's nothing to access to.
The best bet would be:
Creating an attribute in your class called List<TAgency> listAgencies.
Define proper getter and setter methods for listAgencies attribute. NEVER define business logic in managed bean getters. Related: Why JSF calls getters multiple times
Probably, change the scope of the bean to #RequestScope to load this list every time users access to this view. Related: How to choose the right bean scope?
Load the list using #PostConstruct method.
Based on these advices, the code would look like this:
#ManagedBean(name = "agencyBean")
#RequestScoped
public class AgencyBean {
private TAgency tEventType = new TAgency();
private List<TAgency> listAgencies;
#PostConstruct
public void init() {
EntityManager em = HibernateUtil.getEntityManager();
// read the existing entries and write to console
Query q = em.createQuery("select u from TAgency u");
List<TAgency> agencyList = q.getResultList();
for (TAgency agency : agencyList) {
System.out.println("NAme: " + agency.gettUser().getName());
}
}
public TAgency gettEventType() {
return tEventType;
}
public void settEventType(TAgency tEventType) {
this.tEventType = tEventType;
}
public List<TAgency> getListAgencies() {
return listAgencies;
}
public void setListAgencies(List<TAgency> listAgencies) {
this.listAgencies = listAgencies;
}
}
And your JSF code:
<!-- Note: no parenthesis usage -->
<c:forEach items="#{agencyBean.listAgencies}" var="inputBoxes">
<!-- no need to call the getter verbosely, Expression Language call it for you automatically -->
<h:outputText value="#{inputBoxes.user.name}" />
<!-- what you want to do here? -->
<h:inputText />
</c:forEach>
Also, probably you don't want to use <c:forEach> but <ui:repeat> instead. Related: JSTL in JSF2 Facelets... makes sense?
I'm fairly new to Java EE and I'm building a simple webshop using maven web application . I have a problem with my Stateful Session Bean. I've been searching the net and tried different sollutions(most of them for using servlets) but it doesn't seem to work.
Anyway, my problem is that I'm trying to use my session bean to keep track of what's in the shopping cart. I am using an arrayList to store the items. However, when I add a new item it just replaces the old item instead of adding it to the list. I'm guessing the session bean somehow updates or a new instance of it is created but I just can't seem to find any sollution or information about this.
The stateful session bean
#Stateful
#LocalBean
public class CartSessionBean{
private List contents;
public CartSessionBean(){
contents= new ArrayList();
}
public List getContents() {
return contents;
}
public void addProduct(String title) {
contents.add(title);
}
}
The Managed Bean
#ManagedBean
#RequestScoped
public class ProductController {
private List cartList = new ArrayList();
private int nrOfCartItems=0;
#EJB private CartSessionBean cart;
public String doAddCart(String title)
{
cart.addProduct(title);
setCartList(cart.getContents());
setNrOfCartItems(cart.getContents().size());
return "products.xhtml";
}
}
The Facelet
<h:form>
<p>
your cart contains <h:outputLabel class="" value="#{productController.nrOfCartItems}" /> items.
<ui:repeat value="#{productController.cartList}" var="cart">
<h:outputLabel value="#{cart}" />
</ui:repeat>
<h:commandButton value="go to checkout"/>
</p>
</h:form>
<h:form>
<h:dataTable value="#{productController.productList}" var="pr" border="0">
<h:column>
<h:graphicImage value="images/#{pr.picture}" />
</h:column>
<h:column>
<h2><h:outputText value="#{pr.product_name}"/></h2>
<p> in stock: <h:outputText value="#{pr.stock}"/><br/>
price: <h:outputText value="#{pr.price}"/> SEK<br/><br/>
<h:outputText value="#{pr.description}"/><br/></p>
<h:commandButton value="add to cart" action="#{productController.doAddCart(pr.product_name)}"/>
</h:column>
</h:dataTable>
</h:form>
Your managed bean should be SessionScope to live during the session.
In your case you always creating new ProductController bean for each request and because of that you always inject different CartSessionBean (there is no way how could container know that it should inject the same SessionBean into your RequestScope Managed Bean).
How can I access the view parameters from a viewscoped bean?
I have a page almost with the same content as this:
<f:metadata>
<f:viewParam name="name" value="#{goToUserpageRequest.name}" />
</f:metadata>
<ui:define name="content">
<h:outputText value="#{user.name}" styleClass="text"></h:outputText>
<h:outputText value="#{user.description}" styleClass="text"></h:outputText>
</ui:define>
GoToUserpageRequest is a bean which I use to redirect to this page, so I can send the value for name.
User is my viewscoped bean. I want to pass the value of viewParam name to user.name. How can I do that?
Thanks in advance!
There is an easier way for your case which I have just figured out while looking for a solution for the same situation.
just use this in your xhtml together :
<f:metadata>
<f:viewParam name="name" value="#{goToUserpageRequest.name}" />
</f:metadata>
<f:event type="preRenderView" listener="#{MY_BEAN.setName(goToUserpageRequest.name)}"/>
so you can send the goToUserpageRequest.name value back to your redirected view's bean (I called MY_BEAN)
You can get this information using the external context from your context. See the request parameters.
However, I would try to use a request scope bean and inject the view and parameter scope values into that. You can then manipulate your view scoped object from there. This approach is easier to unit test.
EDIT:
Here is a sample implementation:
#ManagedBean #RequestScoped
public class NameUpdater {
#ManagedProperty("#{param.name}") private String name;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
#ManagedProperty("#{user}") private User user;
public User getUser() { return user; }
public void setUser(User user) { this.user = user; }
#PostConstruct public void init() {
if(name != null) user.setName(name);
}
}
In order to create the request scoped bean, the binding expression would change to something like:
<h:outputText value="#{nameUpdater.user.name}" />
This question already has an answer here:
Using <h:dataTable><h:inputText> on a List<String> doesn't update model values
(1 answer)
Closed 6 years ago.
I have jsf page:
....
<form jsfc="h:form" action="">
<h:dataTable value="#{newMusician.strings}" var="preferredMusicGenre" id="musicGenresSelectTable">
<h:column>
<h:inputText value="#{preferredMusicGenre}" immediate="true"/>
</h:column>
</h:dataTable>
<p>
<input type="submit" jsfc="h:commandButton" value="Add" action="#{newMusician.saveNewMusician}"/>
</p>
</form>
....
And managed bean that has ArrayList of Strings:
#ManagedBean
#ViewScoped
public class NewMusician {
private ArrayList<String> strings = new ArrayList<String>();
public NewMusician() {
strings.add("olo");
}
public ArrayList<String> getStrings() {
return strings;
}
public void saveNewMusician() {
.....
}
....
}
Problem: When I change text in and press save button, in saveNewMusician() method I can see that ArrayList "strings" contain the same old value "olo", but not that one I inserted in input field.
The same problem if use h:selecOneMenu.
Situation is changed if use not string, but object that aggregate string and set value into string.
So if I'll use some POJO and change inputText to:
<h:inputText value="#{preferredMusicGenrePojo.string}" immediate="true"/>
Everything becomes Ok.
Question:
Why usage of 1 level getter <h:inputText value="#{preferredMusicGenre}"/> is incorrect, but usage of 2 level getter: <h:inputText value="#{preferredMusicGenrePojo.text}"/> is Ok?
A String is immutable. It doesn't have a setter for the value. You need to wrap this around in a bean (or POJO as you call it).
public class Musician {
private String preferredGenre;
// Add/generate constructor, getter, setter, etc.
}
Then change your managed bean as follows.
#ManagedBean
#ViewScoped
public class NewMusician {
private ArrayList<Musician> musicians = new ArrayList<Musician>();
public NewMusician() {
musicians.add(new Musician("olo"));
}
public ArrayList<Musician> getMusicians() {
return musicians;
}
public void saveNewMusician() {
// ...
}
// ...
}
And your datatable:
<h:dataTable value="#{newMusician.musicians}" var="musician">
<h:column>
<h:inputText value="#{musician.preferredGenre}" />
</h:column>
</h:dataTable>