f:param is null in bean jsf - java

My problem is that the value of the parameter is null in the bean
In the xhtml I have this code:
<h:commandLink action="#{navigation.editNews}" value="#{new.title}">
<f:param name="newsId" value="#{new.id}" />
</h:commandLink>
In Navigation I redirect to news.xhtml
public String editNews(){
return "editNews";
}
This is the code in faces-config.xml
<navigation-case>
<from-action>#{navigation.editNews}</from-action>
<from-outcome>editNews</from-outcome>
<to-view-id>/news.xhtml</to-view-id>
<redirect />
</navigation-case>
I have a bean where I call method when I push a button in news.xhtml and I try to get param but it is null
FacesContext fc = FacesContext.getCurrentInstance();
Map<String,String> params = fc.getExternalContext().getRequestParameterMap();
String = params.get("newsId");

The <f:param> adds a request parameter. So this parameter has a lifetime of exactly one HTTP request. You've a <redirect/> in your navigation case which basically instructs the webbrowser to send a new request on the given location. This new request does not contain the parameter anymore.
You've basically 2 options:
Get rid of <redirect /> in the navigation case.
Make it a normal GET request instead. If you're on JSF2, use <h:link> instead.
<h:link action="news" value="#{news.title}">
<f:param name="newsId" value="#{news.id}" />
</h:link>
Or if you're still on JSF 1.x (the usage of navigation cases less or more hints this as they are superfluous in JSF 2 thanks to the new implicit navigation feature; or you must be reading outdated tutorials/books/answers targeted on JSF 1.x; also the absence of JSF 2.0 tag on your question is suspicious), then use a normal <a> link instead.
#{news.title}
See also:
Communication in JSF 2.0 - Processing GET request parameters

Related

How to pass parameters via URL to a bean class using JSF?

I have created a login page for my JSF application. I want to pass the username and password as parameters via the URL to later receive them as fields in a bean class. How can I do this?
You should pass it as POST parameter which is what JSF does by default ,
You can google for a quick example of a Login page with JSF , however if you want to read the request parameters from URL then you can do like
<a href="name.jsf?id=#{testBean.id}" />
You need something like this in your bean
#ManagedBean
#RequestScoped
public class TestBean {
#ManagedProperty(value = "#{param.id}")
private String id;
.....
}
You can also do this in your xhtml to get the same outcome, this will work with JSF 2.x as viewParam is not available in JSF 1.2
<f:metadata>
<f:viewParam name="id" value="#{testBean.id}" />
</f:metadata>
Above line will set the parameter id in bean from the request parameter id when your bean is created.
Well first of all, if you are thinking of appending the username and password as part of query string. THEN DO NOT DO IT, you are making your system vulnerable.
Regarding the answer to your question:
<h:commandLink action="#{ttt.goToViewPage()}" value="View">
<!-- To access via f:param tag, this does not maps directly to bean. Hence using context fetch the request parameter from request map. -->
<!-- <f:param name="selectedProfileToView" value="#{profile.id}" /> -->
<!-- Using this to replace the f:param tag to avoid getting the request object -->
<f:setPropertyActionListener target="#{ttt.selectedStudentProfile}" value="#{profile.id}" />
</h:commandLink>
f:param (as mentioned in comment), this will not map directly to the bean attribute, but you will have to use context to get the request object from which you can reference the value from the requestparametermap.
FacesContext context = FacesContext.getCurrentInstance();
Map<String, String> requestMap = context.getExternalContext().getRequestParameterMap();
f:setPropertyActionListener, this is the other attribute, which will map directly to the attribute of the managed bean.
<h:commandLink action="#{ttt.goToEditPage(profile.id)}" value="Edit">
If you look here I have mentioned the argument in the function. A method with similar signature should be present in the managed bean class, the value will be mapped directly to the function argument.

JSF 2 - which ways exist to find back to the calling page?

My web application has two search pages - search1.jsf and search2.jsf - which share the same bean which in turn calls a result page - result.jsf.
So both search pages have a commandButton
<h:commandButton value="Commit" action="#{search.commit}" />
The "search" bean then processes the search and forwards to the result page using
target = "result?faces-redirect=true";
Now the result.jsf doesn't know which search page has been used. How can I pass the calling page so that the called page is able to render a link back to the calling page?
Using the browser back button (or Javascript code) to go back in the history has the disadvantage that the user, after browsing through the results, only would go back one page in the results instead of going 'up' to the search parameter form.
Options I see so far are:
pass the caller page name through the chain to the result page, using <f:param> or action="#{search.commit(name_of_calling_page)}
put all 'search parameter' forms in one JSF page and conditionally switch the content
Or is there a JSF functions which returns the name of the calling JSF page?
You're redirecting to the result page which suggests that you're using a session scoped bean to hold the results (a request/view scoped simply doesn't survive redirects). Ignoring the fact that this is a poor/odd approach, you could just store the search type in that bean as well?
Anyway, easiest and cleanest is to use a single master view with a single master view scoped bean and conditionally display search1.xhtml, search2.xhtml and results.xhtml using <ui:include> in the master view.
search.xhtml
<ui:fragment rendered="#{empty search.results}">
<ui:include src="/WEB-INF/search#{search.type}.xhtml" />
</ui:fragment>
<ui:fragment rendered="#{not empty search.results}">
<ui:include src="/WEB-INF/results.xhtml" />
</ui:fragment>
As long as you return void or null in action methods, the same view scoped bean will be retained on every subsequent (and non-redirected!) request. If you intend to make it a GET instead of POST (so that it's bookmarkable and back-button-navigable such as Google), then you should actually change the forms and view parameters to make it GET instead of POST.

JSF 2.0 return null from action methods does not retain the original request parameters

I have a JSF application with a particular page to show/edit the details of a particular entity in my system (an attraction). I have a jsf page which takes the attractionId as a page parameter as follows:
<f:metadata>
<f:viewParam name="attractionId" value="#{attractionsBean.attractionId}" required="true" />
</f:metadata>
To submit the changes to the attraction I have this command button:
<p:commandButton styleClass="button"
ajax="false"
value="Update Attraction"
action="#{attractionsBean.updateAttraction()}" />
In the updateAttraction method, normally in JSF when you return null, the user is redirected to the same page showing any messages that might have been created. However, since the page requires an attractionId, it gives a validation error that the attractionId is null since it does not automatically add the original request parameter. Normally I do it as follows:
if (FacesContext.getCurrentInstance().getMessageList().size() > 0) {
return null;
}
But this does not send back the original parameters to display the specified attraction.
Can anyone suggest a clean solution for this please?
Thanks!!
You can add the includeViewParams=true parameter to an outcome to let jsf impl append all declared view parameters, but I think this can only work if you also include the view id. You could try something like this:
return FacesContext.getCurrentInstance().getViewRoot().getViewId() + "?includeViewParams=true";
Or you could define a constant including the view id in your managed bean:
private static final String SUCCESS = "attraction?includeViewParams=true";
...
if (FacesContext.getCurrentInstance().getMessageList().size() > 0) {
return SUCCESS;
}
Some more examples for this param can be found at this blog post about POST-REDIRECT-GET and JSF 2.0.

Serving files with JSF 2 / CDI, using bookmarkable URLs

My main question is : Is there a "good practice" to serve binary files (PDF, docs, etc) using JSF 2 with CDI, and using bookmarkable URLs ?
I've read the JSF 2 spec (JSR 314) and I see it exists a "Resource Handling" paragraph. But it seems to be used only to serve static files put in the war or jar files. I didn't really understood if it exists a way to interact here by registering some specific ResourceHandler ...
Actually, I was used to Seam's 2 way to do that : extending the AbstractResource class with getResource(HttpServletRequest, HttpServletResponse) method and getResourcePath() to declare which path to serve after <webapp>/seam/resource/ URL prefix and declaring the SeamResourceServlet in the web.xml file.
Here is what I did.
I've first saw How to download a file stored in a database with JSF 2.0 and tried to implement it.
<f:view ...
<f:metadata>
<f:viewParam name="key" value="#{containerAction.key}"/>
<f:event listener="#{containerAction.preRenderView}" type="preRenderComponent" />
</f:metadata>
...
<rich:dataGrid columns="1" value="#{containerAction.container.files}" var="file">
<rich:panel>
<h:panelGrid columns="2">
<h:outputText value="File Name:" />
<h:outputText value="#{file.name}" />
</h:panelGrid>
<h:form>
<h:commandButton value="Download" action="#{containerAction.download(file.key)}" />
</h:form>
</rich:panel>
</rich:dataGrid>
And here is the beans :
#Named
#SessionScoped
public class ContainerAction {
private Container container;
/// Injections
#Inject #DefaultServiceInstance
private Instance<ContainerService> containerService;
/// Control methods
public void preRenderView(final ComponentSystemEvent event) {
container = containerService.get().loadFromKey(key);
}
/// Action methods
public void download(final String key) throws IOException {
final FacesContext facesContext = FacesContext.getCurrentInstance();
HttpServletResponse response = (HttpServletResponse) facesContext.getExternalContext().getResponse();
final ContainerFile containerFile = containerService.get().loadFromKey(key);
final InputStream containerFileStream = containerService.get().read(containerFile);
response.setHeader("Content-Disposition", "attachment;filename=\""+containerFile.getName()+"\"");
response.setContentType(containerFile.getContentType());
response.setContentLength((int) containerFile.getSize());
IOUtils.copy(containerFileStream, response.getOutputStream());
response.flushBuffer();
facesContext.responseComplete();
}
/// Getters / setters
public Container getContainer() {
return container;
}
}
Here I had to switch to Tomcat 7 (I was using 6) in order to interpret correctly that EL expression. With #SessionScoped it worked, but not with #RequestScoped (when I clicked the button, nothing happend).
But then I wanted to use a link instead of a button.
I tried <h:commandLink value="Download" action="#{containerAction.download(file.key)}" /> but it generates some ugly javascript link (not bookmarkable).
Reading the JSF 2 spec, it seems that there is a "Bookmarkability" feature, but it is not realy clear how to use it.
Actually, it seems to work only with views, so I tried to create an empty view and created a h:link :
<h:link outcome="download.xhtml" value="Download">
<f:param name="key" value="#{file.key}"/>
</h:link>
<f:view ...>
<f:metadata>
<f:viewParam name="key" value="#{containerFileDownloadAction.key}"/>
<f:event listener="#{containerFileDownloadAction.download}" type="preRenderComponent" />
</f:metadata>
</f:view>
#Named
#RequestScoped
public class ContainerFileDownloadAction {
private String key;
#Inject #DefaultServiceInstance
private Instance<ContainerService> containerService;
public void download() throws IOException {
final FacesContext facesContext = FacesContext.getCurrentInstance();
// same code as previously
...
facesContext.responseComplete();
}
/// getter / setter for key
...
}
But then, I had a java.lang.IllegalStateException: "getWriter()" has already been called for this response.
Logic as when a view initiates, it uses getWritter to initialize the response.
So I created a Servlet which does the work and created the following h:outputLink :
<h:outputLink value="#{facesContext.externalContext.request.contextPath}/download/">
<h:outputText value="Download"/>
<f:param name="key" value="#{file.key}"/>
</h:outputLink>
But even if that last technique gives me a bookmarkable URL for my file, it is not really "JFS 2" ...
Do you have some advice ?
I agree with BalusC. Typically an application is not purely a JSF application but a Java EE application.
It's not for nothing that other things than JSF views exist for handling http requests in Java EE. In Java EE 6, your named CDI bean can btw also directly be mapped to a path using JAX-RS. This is an alternative to using Servlets. In that case you would use #Produces and #Path (see e.g. Input and Output binary streams using JERSEY?).
On the other hand, one advantage of <f:viewParam> in JSF is that you can easily attach validators to it. Neither Servlets nor JAX-RS resources have support for that at the moment.
<h:link> is also more comfortable to use than writing <h:outputLink value="#{facesContext.externalContext.request.contextPath}/..."> all the time. This can however be mitigated by wrapping this part in a Facelets tag or composite component.
(It would be great I think if a future version of the spec provided a link tag in JSF to link directly to JAX-RS resources (with optional container startup validation to ensure the link is legal)).
JSF is from the beginning on designed to be a MVC framework, not to be some REST file service.
A servlet is perfectly fine for the job. Annotate it with #WebServlet to get a better Java EE 6 feeling.
There is in fact a direct solution to this problem using PrettyFaces URLRewriteFilter -> http://ocpsoft.org/prettyfaces/serving-dynamic-file-content-with-prettyfaces/
This blog explains how to do exactly what you want to do, without having to use an entirely new MVC framework.

Disabling seam's redirect filter

I'm doing a project in seam that requires restful URLs. I have a view that is mapped to /group/{group}/{locale}. On that page I have a list of so called messages. Each message has a button to save changes to the message. This is linked to an action bean like this:
<h:commandButton type="submit" value="Save Changes" action="#{groupAction.update}" />
Each message has an anchor, so /group/{group}/{locale}#{id} can be used to make the browser go to that anchor. This is why I need a redirect after the POST:
<page view-id="/group.xhtml">
<rewrite pattern="/group/{group}/{locale}"/>
<param name="group" value="#{groupAction.group}"/>
<param name="locale" value="#{groupAction.locale}"/>
<navigation from-action="#{groupAction.update}">
<redirect view-id="/group.xhtml?group=#{group}&locale=#{locale}##{id}"/>
</navigation>
</page>
Also I have the following redirect rule (UrlRewriteFilter) to get to the proper RESTful URL:
<outbound-rule>
<from>^/group.xhtml\?group=([\.\w]+)&locale=([\.\w]+)\#([\.\w]+)\?cid=(\d*)$</from>
<to type="temporary-redirect" last="true">/group/$1/$2#$3</to>
</outbound-rule>
I strip the conversationId here. This has been tested an works. However seam still appends a '?conversationId={cid}'. So what's the problem? Well, imagine a URL like '/group/{group}/{locale}#{id}?conversationId={cid}'. Oviously the browser doesn't like this and will not automatically go to that anchor.
I did some research and discovered my problem in the seam documentation:
29.1.4.2. Conversation propagation with redirects
This filter allows Seam to propagate the conversation context across browser redirects. It intercepts any browser redirects and adds a request parameter that specifies the Seam conversation identifier. The redirect filter will process all requests by default, but this behavior can also be adjusted in components.xml:
<web:redirect-filter url-pattern="*.seam"/>
I don't need the redirect-filter and I tried putting something invalid in the url-pattern to "disable" the filter. However that didn't work. So my question is now:
How do I disable the redirect-filter in seam?
I can't find the answer. The seam documentation talks about disabling it in web.xml, but my attempts have not been succesful yet.
I've worked out where the unwanted conversationId query string parameter is coming from.
The <redirect/> results in a call to org.jboss.seam.faces.FacesManager.redirect(String viewId, Map<String, Object> parameters,
boolean includeConversationId)
This is called from the following code in org.jboss.seam.faces.Navigator which sets includeConversationId to true:
FacesManager.instance().redirect(viewId, parameters, true);
I cannot see any way to avoid this, so the fix/workaround is to do the redirect programmatically in the action method with:
FacesManager.instance().redirect(viewId, parameters, false);
OK, I managed to kill the redirect-filter by adding this to my compononents.xml:
<web:redirect-filter disabled="true" installed="false" />
However my main problem still exists, so that was apparently not the problem. It still adds the conversationId as an extra query string on the URL. Apperently I'm doing something wrong since it has been done before.
#Adeel Ansari:
components.xml:
<web:rewrite-filter view-mapping="/seam/*" />
pages.xml see my initial question.

Categories

Resources