I have been tasked with migrating JSF 1.x with RichFaces 3.x on JSP to JSF 2.x on Facelets. The previous developer used RichFaces <rich:dataTable>. As data a bean was sent to the table. This bean was a SerializableDataModel which has a walk() method allowing you to easily traverse the data table using the visitor pattern. This also allowed the table to only load data needed using lazy loading. We have over 216k rows of data and all of them are loaded when the page loads or when you try to change pages to view the next set of rows.
I thus need to migrate this to JSF 2.x. I have never used JSF 2.x before and I am simply asking if anyone has ever experience this, how they solved it and if they can give me some resources to get started.
I can not share more as it is against my works policies.
<h:dataTable value="#{records}" var="record" rows="20"
class="table table-striped"
styleClass="dataTableRecords dataListTable" rowClasses="odd, even"
id="dataTableRecords" rendered="#{records.rowCount > 0}">
</h:dataTable>
#ManagedBean(name="records")
#RequestScoped
public class Records extends BseDataModel{...}
BseDataModel is what contained our walk method, it now extends DataModel
If you think you can help please let me know, I can give some more limited information, if you need it please ask.
Related
In my application I want to display a <h:dataTable> with managed bean properties. Currently this table is created from a List<Folder>. Now I want to change the Folder to something more dynamic. That's because I don't want to change the Folder class if I decide to add another field later. I would just have to add another entry in the Map<String, Object> instead of introducing a new field in Folder.
So, is it possible to bind a List<Map<String, Object>> to the <h:dataTable>?
Is it possible to bind a List of HashMaps to the jsf component h:dataTable?
That's only possible if you generate the necessary <h:column> tags with a view build time tag such as JSTL <c:forEach>.
Here's a concrete kickoff example, assuming that your environment supports EL 2.2:
<h:dataTable value="#{bean.listOfMaps}" var="map">
<c:forEach items="#{bean.listOfMaps[0].keySet().toArray()}" var="key">
<h:column>
#{map[key]}
</h:column>
</c:forEach>
</h:dataTable>
(if your environment doesn't support EL 2.2, you'd need to provide another getter which returns the map key set as a String[] or List<String>; also keep in mind that a HashMap is by nature unordered, you might want to use LinkedHashMap instead to maintain insertion order)
When you're using Mojarra version older than 2.1.18, the disadvantage is that the #{bean} has to be request scoped (not view scoped). Or at least, the <c:forEach items> should refer a request scoped bean. A view scoped bean would otherwise be recreated on every single HTTP request as the <c:forEach> runs during view build time, when the view scope isn't availabe yet. If you absolutely need a view scoped bean for the <h:dataTable>, then you can always create a separate request scoped bean exclusively for <c:forEach items>. The solution would be to upgrade to Mojarra 2.1.18 or newer. For some background information, see also JSTL in JSF2 Facelets... makes sense?
JSF component libraries such as PrimeFaces may offer a <x:columns> tag which makes this more easy, such as <p:dataTable> with <p:columns>.
<p:dataTable value="#{bean.listOfMaps}" var="map">
<p:columns value="#{bean.listOfMaps[0].keySet().toArray()}" var="key">
#{map[key]}
</p:columns>
</p:dataTable>
I was wondering about this article explaining the direct access of EJB from JSF : http://www.mastertheboss.com/cdi/context-dependency-injection-with-jboss-weld
I do understand, that with CDI you can inject and access every bean type, including entity beans from the far end of the layer tier.
However.
[Question 1]: Should I discard the #Model controller and only apply #Named to all the classes? This kind of abandons the approach of having JSF --> ManagedBean--> EJB --> EntityBean to JSF --> EJB --> EntityBean. I just have the feeling this extra ManagedBean is causing me an extra layer without not much benefit.
[Question 2]: In what cases would you still want to maintain that extra Controller layer?
thanks
I don't think that there is a universal answer. It depends on your controller, both are valid arquitectures.
The key is whether your controller requires services offered by the container.
For example if your controller should execute outside a transaction then justifies the extra layer.
However, always tend towards simplicity if you can. If your application is simple enough to have just the three layers then don't over complicate it.
As far as replacing #Model for #Named goes, #Model is just a shortcut for a #RequestScoped #Named bean. Only replace #Model for #Named if you want the bean to have some other scope.
The best way to get the most from the overall platform is to use CDI as your controller layer (receiving requests from the JSF based frontend) and delegating internally to EJBs or other CDI beans for business logic. I would not use JSF ManagedBeans as the concept is deprecated in Java EE 7, in favor of the CDI programming model.
The controller can be used to always convert data types, separating out your front end and back end models.
I think the equation should be:
JSF = View (.xhtml) + Controller (ManagedBean)
EJB = Model
There's no skipping to go directly from View to Model unless you're putting the whole back-end logic inside a ManagedBean, which is obviously not a good practice.
I have a sample use case: I have an edit page that use GET parameter "id".
eg. edit?id=1
This edit page is backed by a ViewAccessScoped (CODI) Bean.
In this edit page, I have a datatable with links that link to the same "edit" page, but with another id. (eg. edit?id=2)
<h:link value="#{mecaPart.id}" outcome="edit" target="_blank">
<f:param name="id" value="#{mecaPart.id}" />
</h:link>
The problem, is that the window open correctly, but it is the same bean that is used! And so I am editing the same part...
I have placed a log in #PostConstruct, and it is the same bean reference that is called multiple times. (even with the new ID!)
My question, how can I tell JSF to create a new ViewAccessScoped backing bean when I click the link, and not re-use the actually used one?
Finally, I discovered that #ViewScoped CODI bean did not preserved the backing bean from page refresh. So, I have to use ViewAccessScoped.
According to Gerhard Petracek: http://os890.blogspot.fr/2011/08/scopes-view-scope-vs-view-access-scope.html
the view-scope of jsf2+ is bound to a concrete jsf page. that means: as soon as you navigate to a different page, the state gets lost. that's better than nothing, but not useful for a lot of use-cases. the main use-case which needs it are ajax-requests on a page and the data used by them aren't needed on other pages, but it's pretty easy to break it e.g. with a browser-refresh on a page which stores the data in a view-scoped bean and has no form with input components. (in a previous blog post i described how to use the infrastructure provided by codi to create a session based view-scope to overcome such disadvantages cause by storing view scoped beans as part of the tree-state.)
like with the view-scope view-access-scoped beans are available on a page, but they also exist for the next page. that means: they are forwarded to the next page and get destroyed autom. if they don't get used during the first request of the next page. that's e.g. useful for wizards. if you have a wizard page which doesn't use the bean or you have to support the possibility to interrupt a wizard, you can use the grouped-conversation scope (and even the window-scope) provided by codi. however, due to the powerful api of codi you can also destroy the scope manually at any time (if needed).
So, to solve the problem of opening a new tab with another "ID", I had to set "CODI Client Side WindowHandler", according to the CODI Wiki.
https://cwiki.apache.org/confluence/display/EXTCDI/JSF+WindowHandler
So I added:
<alternatives>
<class>org.apache.myfaces.extensions.cdi.jsf.impl.scope.conversation.ClientSideWindowHandler</class>
</alternatives>
To the file beans.xml, and I used #ViewAccessScoped. Everything is working smoothly now.
You can use #ViewScoped which also works in CODI
Please note: This question is about CDI scopes as we are using CDI scopes in the app and not JSF scopes.
1) Controller Bean (TestController.java) which is in RequestScoped (enterprise context) is called index_cut.xhtml, when we come for first time on this page.
2) On button “Load”, we load the following method to populate the sapFinancialPeriodList which works fine and displays the data
3) After changing the content on the page and submitting, the sapFinancialPeriodList appears as NULL in the following method –
Any suggestions?
Your bean is request scoped and you're loading the data model on action only instead of on (post)construction. When the HTTP response after the action which loaded the data is finished, then the bean is garbaged. The subsequent request would get a brand new instance of the bean with all properties set to default. However, as the same data model isn't been preserved during (post)construct, it remains empty.
In JSF2 you'd solve this with using #ViewScoped. This way the bean will live as long as you're interacting with the same view by postbacks (which return null or void).
In CDI you'd need to solve this using #ConversationScoped, which in turn requires some additional #Inject Conversation boilerplate, complete with begin() and end() calls at the right moments. For a concrete example, see also What scope to use in JSF 2.0 for Wizard pattern?.
An alternative is to pass the parameters responsible for creating the data model to the subsequent request via <f:param> in the command link/button as follows
<h:commandButton value="save" ...>
<f:param name="period" value="#{bean.period}" />
</h:commandButton>
and then recreate exactly the same data model in (post)constructor of the request scoped bean as follows
String period = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("period");
List<SapFinancialPeriod> sapFinancialPeriodList = someservice.list(period);
(the above is by the way nicer to solve with #ManagedProperty if you were using standard JSF; as far as I know CDI doesn't have an annotation which enables you to set a HTTP request parameter as a bean property)
See also:
How to choose the right bean scope?
Unrelated to the concrete problem, the upcoming JSF 2.2 solves this functional requirement in a nicer way using the new "Faces Flow" feature with the new #FlowScoped annotation and the new xmlns:j="http://java.sun.com/jsf/flow" tags.
Whats the most efficient way of doing pagination in JSF 2.0 app?
I use Primefaces datatable and it is intelligent enough to perform
pagination by itself with no coding at all.
<p:dataTable var="car" value="#{carBean.cars}" paginator="true" rows="10">
<!-- Multiple columns here-->
</p:dataTable>
The thing that I see, is that I need to place my bean to session scoped or greater.
#ManagedBean
#SessionScoped
public class CarBean{
public List<Car> getCars(){
//return data from DB
}
}
I wanted to know is there another efficient way on how to perform this?
I used EJB/JPA at the backend by the way. Would like to know any links or tutorials
to learn more about this.
Thanks.
You need to use LazyDataModel in order to have only the rows in memory which the client actually needs to see. See also the example in PrimeFaces showcase. This does pagination at DB level which is what you ultimately want.
RichFaces supports by the way the same in flavor of ArrangableDataModel, here's the RichFaces showcase example.
In a production app, we've used a lazy datamodel to deal with 700000 records in db. I'd suggest using M3 which has fixes on lazy datatable cases.
I have found that the built in pagination feature of the Primefaces data table is one of the best features and did a good amount of load testing on it, bringing in recordsets with over 30,000 Hibernate entities and found the performance to be lackluster. This of course means that you will have 30,000 entities in session so I have the following in my web.xml to help by storing session on the server side.
<context-param>
<description>State saving method: 'client' or 'server' (=default). See JSF Specification 2.5.2</description>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>server</param-value>
</context-param>
This will reduce the size of the ViewState allowing request/response size to be greatly reduced however server side memory can suffer enormously by doing this.
Another potential option in some JSF implementations to help mitigate the size of ViewStat or session memory usage is compression. The following link describes a number of SUN RI and MyFaces JSF configuration parameters that can be set, some of which give the option of compression of the session state. http://publib.boulder.ibm.com/infocenter/wasinfo/v7r0/index.jsp?topic=%2Fcom.ibm.websphere.express.doc%2Finfo%2Fexp%2Fae%2Frweb_jsfengine.html
As far as learning more about how the Primefaces DataTable pagination feature works, why not go straight to the source? Primefaces is after all an open source project, so just look at the code and see what you can learn: http://code.google.com/p/primefaces/source/browse/#svn%2Fprimefaces
Important note depending on which version of Primefaces you are using. Starting with 3.0.M2 (I think) if you want to use the row select feature you must implement a SelectableDataModel. This breaks a lot of legacy code and there were a number of bitches about that.
Easiest thing to do is to create an inner class like this:
private MyDataModel dataModel = null;
public MyDataModel getDataModel() {
if (dataModel != null) return dataModel;
dataModel = new MyDataModel(some list);
return dataModel;
}
public static class MyDataModel extends ListDataModel<SomeRecord>
implements SelectableDataModel<SomeRecord> {
MyDataModel(List<SomeRecord> source) {
super(source);
}
etc.
Then the value attribute to p:dataTable becomes #{bean.dataModel}.
Good luck.