Getting a GET request param into an #ViewScoped bean - java

I have a (request-scoped) list from which the user may select a "PQ" (list of links). When clicked or otherwise entered into the browser the main page for each PQ shall be displayed. Each PQ's page is of the form
http://localhost:8080/projectname/main.jsf?id=2
Here's the PQ bean first:
#Named
#ViewScoped
public class PqHome implements Serializable
{
#PersistenceContext(unitName="...")
private EntityManager em;
private Integer id;
private PQ instance;
#PostConstruct
public void init()
{
System.out.println("ID is " + id); // ID from URL param
instance = em.find(PQ.class, id);
}
public Integer getId()
{
return id;
}
public void setId(Integer id)
{
this.id = id;
}
public PQ getInstance()
{
return instance;
}
}
Here's the main.xhtml:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
...>
<ui:define name="metadata">
<f:metadata>
<f:viewParam name="id" value="#{pqHome.id}">
<f:convertNumber integerOnly="#{true}" />
</f:viewParam>
<!--f:event type="preRenderView" listener="#{pqHome.init}" /-->
</f:metadata>
</ui:define>
<ui:define name="title">
<h:outputText value="Main" />
</ui:define>
...
</ui:composition>
Any time I select or otherwise refresh the page/URL I get a NullPointerException from the EntityManager:
org.jboss.weld.exceptions.WeldException: WELD-000049 Unable to invoke [method] #PostConstruct public de.mycomp.myproj.beans.PqHome.init() on de.mycomp.myproj.beans.PqHome#4f0ea68f
at org.jboss.weld.bean.AbstractClassBean.defaultPostConstruct(AbstractClassBean.java:595)
...
Caused by: java.lang.IllegalArgumentException: id to load is required for loading
at org.hibernate.event.spi.LoadEvent.<init>(LoadEvent.java:87)
at org.hibernate.event.spi.LoadEvent.<init>(LoadEvent.java:59)
at org.hibernate.internal.SessionImpl.get(SessionImpl.java:961)
at org.hibernate.internal.SessionImpl.get(SessionImpl.java:957)
at org.hibernate.ejb.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:787)
at org.hibernate.ejb.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:762)
at org.jboss.as.jpa.container.AbstractEntityManager.find(AbstractEntityManager.java:221)
at de.mycomp.myproj.beans.PqHome.init(PqHome.java:47)
... 56 more
[Line 47 is em.find(...)]
The line
<f:event type="preRenderView" listener="#{pqHome.init}" />
doesn't make things any better. I'm pretty desparate now.
How do you get URL GET request params into an #ViewScoped bean?
Note: I bet it's not a trivial thing to do. Chances are I'm doing something wrong here conceptually, so any tips on how to improve are welcome. I felt that I needed to choose #ViewScoped because there will be more complex AJAX-based GUI on that page which I'd really like to keep accessible via URL GET params.
Thanks

There is a better way to get id from url. Just use it in #PostConstruct init() method to get "id" from url:
FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("id");
You can still use ViewScoped and #PostConstruct.

The #PostConstruct is invoked directly after bean's construction and all dependency injection (such as #PersistenceContext, #EJB, #ManagedProperty, #Inject, etc..etc..).
The <f:viewParam> sets its value during the update model values phase, which is far after (post)construction of the bean. So inside the #PostConstruct the <f:viewParam> value is simply not yet been set. It'll be still null at that point.
You're close with <f:event type="preRenderView">, but you have to remove the #PostConstruct annotation.
So:
<f:viewParam name="pq" value="#{pqHome.id}">
<f:convertNumber integerOnly="#{true}" />
</f:viewParam>
<f:event type="preRenderView" listener="#{pqHome.init}" />
with
private Integer id;
public void init() {
instance = em.find(PQ.class, id);
}
Unrelated to the concrete problem, I'd suggest to use a Converter for this instead. See also Communication in JSF 2.0 - Converting and validating GET request parameters.
Also the combination #Named #ViewScoped won't work as intended. The JSF-specific #ViewScoped works in combination with JSF-specific #ManagedBean only. Your CDI-specific #Named will behave like #RequestScoped this way. Either use #ManagedBean instead of #Named or use CDI-specific #ConversationScoped instead of #ViewScoped.

Related

Missing Resource in EL implementation: ???propertyNotReadable?

I'm using JSF2.1. I don't have any jars in my WEF-INF folder. My classpath is only referencing JSF2.1 and Java EE. I'm using JDK1.7.
I'm getting "Missing Resource in EL implementation: ???propertyNotReadable???" when trying to test out processing GET parameters using f:event type="preRenderComponent". Right now I'm just trying to do a simple test. My page has a lot on it, so I'm only showing the important parts here. The part I added is the <f:metadata> block. When I remove that block, the error goes away.
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:c="http://java.sun.com/jsp/jstl/core">
<f:metadata>
<f:viewParam name="team" value="#{appBean.team}" />
<f:event type="preRenderComponent" listener="#{appBean.init}" />
</f:metadata>
In my backing bean, I have:
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#ManagedBean
#ViewScoped
public class AppBean implements Serializable{
private static final long serialVersionUID = 1778234L;
private Long team;
public AppBean(){
}
public void init(){
if (team != null) System.out.println(team);
else System.out.println("team undefined");
}
public void setTeam(Long team){
this.team = team;
}
}
And the way I'm testing this out is to go to my url and add ?team=123 to the end. I'm expecting System.out to print 123. Any ideas on how to fix this error?
I figuered it out... I forgot to include:
public Long getTeam(){
return team;
}

How do I let spring set certain variables for all controllers/actions

I use a configuration of google app engine, spring and tiles where each controller actions results in some set of nested (tiles) jsp's to be rendered. Some of the elements of the jsp are to be calculated/set for (almost) each controller, for example the login/logout link somewhere on a page:
<%
com.google.appengine.api.users.UserService userService =
com.google.appengine.api.users.UserServiceFactory.getUserService();
com.google.appengine.api.users.User user = userService.getCurrentUser();
if (user != null) {%>
<li class="active">Feeds</li>
<li class="active">Logout</li>
<%} else {%>
<li class="active">Login</li>
<%}%>
Of course, this is possible and I could also make static-classes which can simplify such code. Yet this is not something I like to have in my jsp, moreover it is (possibly) impossible in templating engines like thymeleaf do execute code like that. Therefore, how do I do something like this:
#RequestMapping("/foo")
public class FooController {
#RequestMapping(value="/{bar}", method = RequestMethod.GET)
public String getMovie(#PathVariable String bar, ModelMap model) {
model.addAttribute("bar", bar);
model.addAttribute("message", "message");
UserService userService = UserServiceFactory.getUserService();
User user = userService.getCurrentUser();
model.addAttribute("isLoggedIn", user==null);
return "somepage";
}
}
So summarized: How do I prevent that the isLoggedIn-code is duplicated everywhere, preferably a solution different than needing to call some "initUserModel(model)" method.
Tiles knows the concept of a ViewPreparer. ViewPreparer are executed before the definition is rendered and is a good place to set attributes that are common for your Tiles views. Those attributes can be request attributes (as shown below) or tiles attributes as shown in the example on the mentioned ViewPreparer tutorial page.
A ViewPreparer is implemented as a "normal" spring service:
#Component
public class YourViewPreparer implements ViewPreparer {
#Autowired
private UserService userService;
#Override
public void execute(TilesRequestContext tilesContext, AttributeContext attributeContext) {
// Some magic here to get the HttpRequest...
Object[] requestObjects = tilesContext.getRequestObjects();
if (requestObjects.length == 2) {
HttpServletRequest request = (HttpServletRequest) requestObjects[0];
User user = userService.getCurrentUser();
request.setAttribute("isLoggedIn", user != null);
}
}
}
Then configure your TilesConfigurer by setting the preparerFactoryClass property to pick up any ViewPreparer beans like this:
<bean class="org.springframework.web.servlet.view.tiles2.TilesConfigurer" id="tilesConfigurer">
<property name="definitions">
...
</property>
<property name="preparerFactoryClass" value="org.springframework.web.servlet.view.tiles2.SimpleSpringPreparerFactory" />
</bean>
And define the preparer in your view declaration. You can extend all your views from a base view so that the preparer declaration has to be done only once.
<definition name="main" preparer="com.example.YourViewPreparer">
...
</definition>
<definition name="myView" extends="main">
...
</definition>
Alternatively you could also implement your own HandlerInterceptor if you have other views than Tiles views.

ServletException - property not found? What's wrong here?

I'm just setting up a simple testclass. Code completions works in eclipse, but I cannot launch the page:
backing bean:
#Named
#RequestScoped
public class TestBean {
public String getString() {
return "click me";
}
}
jsf:
<h:commandButton value="#{testBean.getString()}" />
also tried testBean.string and testBean.getString.
exception:
javax.servlet.ServletException: input.xhtml #41,52 value="#{testBean.getString()}":
The class 'TestBean$Proxy$_$$_WeldClientProxy' does not have the property 'getString'.
What is wrong here??
In JSF you don't use parentheses or the "get" part of the name.
Use this instead.
<h:commandButton value="#{testBean.string}" />

JSF Bean property not updated [duplicate]

This question already has an answer here:
JSF does not populate #Named #RequestScoped bean with submitted input values
(1 answer)
Closed 5 years ago.
I have a bean with a field called 'name', and a JSF form that has an inputText mapped with this field. The initial value of the field is well displayed on the form.
The problem is when I submit the form, the value is not updated with the content of the inputText. In the savePlayer() method below, the value of name is always 'name', not what I typed inside the form input.
The bean :
#Named
#RequestScoped
public class PlayerForm {
#Inject
private PlayerRepository playerRepository;
private String name = "name";
public String savePlayer(){
Player player = new Player();
player.setName(name);
playerRepository.savePlayer(player);
return "saveUserOk";
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
The form :
<h:form>
<h:inputText value="#{playerForm.name}" />
<h:commandButton value="Submit" action="#{playerForm.savePlayer}" />
</h:form>
Thanks very much for any help!
This can happen if you imported #RequestScoped from the package javax.faces.bean (JSF) instead of from javax.enterprise.context (CDI). Every single EL expression #{} would then create a brand new and completely separate instance of the bean. The given form example would then end up in two instances of the bean, one where the name is set and another where the action is invoked.
The javax.faces.bean.RequestScoped annotation can only be used in conjunction with JSF's own #ManagedBean annotation not with CDI's #Named annotation.

<h:inputText> howto execute bean method on blur?

I have <h:inputText> on form and what I need is to execute some method from backing bean on BLUR event:
public void test()
{
System.out.print("HELLO!");
}
Can you help me?
You can use <f:ajax>
<h:form>
<h:inputText value="#{managedBean.val}" >
<f:ajax event="blur" render="result" listener="#{managedBean.test}"/>
</h:inputText>
</h:form>
#ManagedBean(name = "managedBean")
public class Bean {
private String val; // getter and setter
...
public void test() {
System.out.print("HELLO!");
}
}
Alternative :
If you are using richfaces then you can use a4j:jsFunction
See Also
JSF2: Ajax in JSF – using f:ajax tag
How-to-update-a-value-displayed-in-the-page-without-refreshing

Categories