Basically my question is the same as this one answered five years ago: I would like to output the URL of a server-provided image as from <p:graphicImage>, but just the URL and not an <img> tag. What I am looking for is a solution like this:
(This is not working code, just to illustrate:)
<p:graphicImage var="url" value="#{MyForm.image}"/>
<span id="imageData" data-image="#{url}/>
Which should output to the HTML like this:
<span id="imageData" data-image="http://localhost:8080/contextPath/javax.faces.resource/dy...
I am currently using a JavaScript work-around for this, but this requirement seems so common to me that I would expect newer development has already targeted it and there is a built-in solution. Is there news on that topic since the last five years?
Just took a look at the GraphicImageRenderer wanting to figure out how they produce that dynamic URL. They are using a static method on their DynamicContentSrcBuilder:
protected String getImageSrc(FacesContext context, GraphicImage image) throws Exception {
String name = image.getName();
if (name != null) {
// ...
} else {
return DynamicContentSrcBuilder.build(context, image.getValue(), image, image.isCache(), DynamicContentType.STREAMED_CONTENT, image.isStream());
}
}
So why not just reproduce this call on the p:graphicImage component?
I'll first bind it to a property of a RequestScoped bean and then get the URI from that bean which reproduces behavior of the renderer shown above:
The XHTML code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:pt="http://xmlns.jcp.org/jsf/passthrough">
<h:head/>
<body>
<p:graphicImage binding="#{myBean.imgComponent}"
value="#{myBean.img}" />
<h:outputText id="imageData" pt:data-image="#{myBean.getImgUri()}" />
</body>
</html>
And my bean aka myBean:
package test;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import javax.enterprise.context.RequestScoped;
import javax.faces.context.FacesContext;
import javax.inject.Named;
import org.primefaces.application.resource.DynamicContentType;
import org.primefaces.component.graphicimage.GraphicImage;
import org.primefaces.model.StreamedContent;
import org.primefaces.util.DynamicContentSrcBuilder;
#Named
#RequestScoped
public class MyBean {
private GraphicImage imgComponent;
private String imgUri;
public StreamedContent getImg() throws IOException {
// ...
}
public GraphicImage getImgComponent() {
return imgComponent;
}
public void setImgComponent(GraphicImage imgComponent) {
this.imgComponent = imgComponent;
}
public String getImgUri() throws UnsupportedEncodingException {
assert null != imgComponent;
if (null == imgUri) {
// here we reproduce the GraphicImageRenderer behavior:
imgUri = DynamicContentSrcBuilder.build(FacesContext.getCurrentInstance(), imgComponent.getValue(),
imgComponent, imgComponent.isCache(), DynamicContentType.STREAMED_CONTENT, imgComponent.isStream());
}
return imgUri;
}
}
Alternatively we can use the GraphicImageRenderer and publish the protected method getImageSrc:
public class MyBean {
// ...
public String getImgUri() throws Exception {
assert null != imgComponent;
if (null == imgUri) {
imgUri = new GraphicImageRendererXtension().getPublicImageSrc(FacesContext.getCurrentInstance(),
imgComponent);
}
return imgUri;
}
}
public class GraphicImageRendererXtension extends GraphicImageRenderer {
// publish the protected GraphicImageRenderer#getImageSrc method:
public String getPublicImageSrc(FacesContext context, GraphicImage image) throws Exception {
return getImageSrc(context, image);
}
}
But why did I write #{myBean.getImgUri()} instead of #{myBean.imgUri}? I'm still not sure, but using the latter produces a PropertyNotFoundException:
javax.el.PropertyNotFoundException: /myView.xhtml #20,46 value="#{myBean.imgUri}": ELResolver did not handle type: [null] with property of [imgUri]
at com.sun.faces.facelets.el.TagValueExpression.getValue(TagValueExpression.java:117)
at javax.faces.component.ComponentStateHelper.eval(ComponentStateHelper.java:200)
at javax.faces.component.ComponentStateHelper.eval(ComponentStateHelper.java:187)
at javax.faces.component.UIOutput.getValue(UIOutput.java:179)
at com.sun.faces.renderkit.html_basic.HtmlBasicInputRenderer.getValue(HtmlBasicInputRenderer.java:205)
at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.getCurrentValue(HtmlBasicRenderer.java:360)
at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeEnd(HtmlBasicRenderer.java:171)
at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:949)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1912)
at javax.faces.render.Renderer.encodeChildren(Renderer.java:176)
at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:918)
at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeRecursive(HtmlBasicRenderer.java:309)
at com.sun.faces.renderkit.html_basic.GroupRenderer.encodeChildren(GroupRenderer.java:114)
at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:918)
at org.primefaces.renderkit.CoreRenderer.renderChild(CoreRenderer.java:86)
at org.primefaces.renderkit.CoreRenderer.renderChildren(CoreRenderer.java:73)
at org.primefaces.component.layout.LayoutUnitRenderer.encodeEnd(LayoutUnitRenderer.java:49)
at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:949)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1912)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1908)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1908)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1908)
at com.sun.faces.application.view.FaceletViewHandlingStrategy.renderView(FaceletViewHandlingStrategy.java:491)
at javax.faces.view.ViewDeclarationLanguageWrapper.renderView(ViewDeclarationLanguageWrapper.java:126)
...
Caused by: javax.el.PropertyNotFoundException: ELResolver did not handle type: [null] with property of [imgUri]
at org.apache.el.parser.AstValue.getValue(AstValue.java:174)
at org.apache.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:190)
at org.apache.webbeans.el22.WrappedValueExpression.getValue(WrappedValueExpression.java:68)
at com.sun.faces.facelets.el.TagValueExpression.getValue(TagValueExpression.java:115)
... 122 more
Possibly invoking the DynamicContentSrcBuilder.build() method somehow messes up the ongoing resolving of the value expression #{myBean.imgUri}, while #{myBean.getImgUri()} is not affected.
Getting back to your question: no there is no news. The dynamic URI is built and directly sent to the browser while rendering. The GraphicImage component does not provide any getter for this. What I presented here is a server side variant of a work around without the need to create a new servlet.
Edit:
Of course you can also parameterize the getImageUri method and bind your Image component to some arbitrary variable which would bring you closer to your demonstration var="..." example:
public class MyBean {
// ...
public String getImgUri(GraphicImage imgComponent) throws Exception {
assert null != imgComponent;
String imgUri = new GraphicImageRendererXtension().getPublicImageSrc(FacesContext.getCurrentInstance(),
imgComponent);
return imgUri;
}
}
Then use this method by:
<p:graphicImage binding="#{myImage}" value="#{myBean.img}" />
<h:outputText id="imageData" pt:data-image="#{myBean.getImgUri(myImage)}" />
This way there's no need to create a bean or method for each image.
Related
I have this composite component:
inputMask.xhtml
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:composite="http://xmlns.jcp.org/jsf/composite"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">
<composite:interface>
<composite:attribute name="value" />
<composite:attribute name="mask" type="java.lang.String" required="true" />
<composite:attribute name="converterId" type="java.lang.String" default="br.edu.ufca.eventos.visao.inputmask.inputMask" />
</composite:interface>
<composite:implementation>
<h:outputScript library="script" name="inputmask.js" target="head" />
<h:inputText id="mascara">
<c:if test="#{cc.getValueExpression('value') != null}">
<f:attribute name="value" value="#{cc.attrs.value}" />
</c:if>
<f:converter converterId="#{cc.attrs.converterId}" />
<f:attribute name="mask" value="#{cc.attrs.mask}" />
</h:inputText>
<h:outputScript target="body">
defineMask("#{cc.clientId}", "#{cc.attrs.mask}");
</h:outputScript>
</composite:implementation>
</html>
In my last question:
Error trying to add composite component programmatically ("no tag was defined for name")
I was getting this error:
javax.faces.view.facelets.TagException: //C:/wildfly-10/standalone/tmp/eventos.ear.visao.war/mojarra7308315477323852505.tmp #2,127 <j:inputMask.xhtml> Tag Library supports namespace: http://xmlns.jcp.org/jsf/composite/componente, but no tag was defined for name: inputMask.xhtml
when trying to add the above composite component programmatically with this code:
Map<String, String> attributes = new HashMap<>();
attributes.put("mask", "999.999");
Components.includeCompositeComponent(Components.getCurrentForm(), "componente", "inputMask.xhtml", "a123", attributes);
but I managed to solve this problem this way:
The implementation of the method Components#includeCompositeComponent from OmniFaces 2.4 (the version I was using) is this:
public static UIComponent includeCompositeComponent(UIComponent parent, String libraryName, String tagName, String id, Map<String, String> attributes) {
String taglibURI = "http://xmlns.jcp.org/jsf/composite/" + libraryName;
Map<String, Object> attrs = (attributes == null) ? null : new HashMap<String, Object>(attributes);
FacesContext context = FacesContext.getCurrentInstance();
UIComponent composite = context.getApplication().getViewHandler()
.getViewDeclarationLanguage(context, context.getViewRoot().getViewId())
.createComponent(context, taglibURI, tagName, attrs);
composite.setId(id);
parent.getChildren().add(composite);
return composite;
}
So I decided to give a try to the code from an earlier version of OmniFaces (with some change adding the attributes parameter from me) of this method:
public static UIComponent includeCompositeComponent(UIComponent parent, String libraryName, String resourceName, String id, Map<String, String> attributes) {
// Prepare.
FacesContext context = FacesContext.getCurrentInstance();
Application application = context.getApplication();
FaceletContext faceletContext = (FaceletContext) context.getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY);
// This basically creates <ui:component> based on <composite:interface>.
Resource resource = application.getResourceHandler().createResource(resourceName, libraryName);
UIComponent composite = application.createComponent(context, resource);
composite.setId(id); // Mandatory for the case composite is part of UIForm! Otherwise JSF can't find inputs.
// This basically creates <composite:implementation>.
UIComponent implementation = application.createComponent(UIPanel.COMPONENT_TYPE);
implementation.setRendererType("javax.faces.Group");
composite.getFacets().put(UIComponent.COMPOSITE_FACET_NAME, implementation);
if (!attributes.isEmpty()) {
ExpressionFactory factory = application.getExpressionFactory();
ELContext ctx = context.getELContext();
for (Map.Entry<String, String> entry : attributes.entrySet()) {
ValueExpression expr = factory.createValueExpression(ctx, entry.getValue(), Object.class);
composite.setValueExpression(entry.getKey(), expr);
}
}
// Now include the composite component file in the given parent.
parent.getChildren().add(composite);
parent.pushComponentToEL(context, composite); // This makes #{cc} available.
try {
faceletContext.includeFacelet(implementation, resource.getURL());
} catch (IOException e) {
throw new FacesException(e);
} finally {
parent.popComponentFromEL(context);
}
return composite;
}
And finally the error was gone. The composite component was dynamically added to the page.
But another problem appeared.
The action in a button to add the component is more or less like this:
if (Components.findComponent("form:a123") == null)
{
Map<String, String> attributes = new HashMap<>();
attributes.put("value", "#{bean.cpf}");
attributes.put("mask", "999.999.999-99");
includeCompositeComponent(Components.getCurrentForm(), "componente", "inputMask.xhtml", "a123", attributes);
}
As you can see, the composite component is only added once.
When the component is first added, the script code that is in the component:
<h:outputScript target="body">
defineMask("#{cc.clientId}", "#{cc.attrs.mask}");
</h:outputScript>
is added to the page. I can see it when I visualize the html source code in the browser. But on postbacks, this script code is not rendered anymore. It's not in the genereted html page. The <h:outputScript> with target="head" is rendered everytime, as expected, but not this one.
From my point of view, maybe there's still someting missing in the assembling of the composite component code in the method above to fix the script code even on postbacks on the page. I really don't know. It's just a guess.
Do you know what's going on or what's missing?
---- UPDATE 1 ----
I think that I really found the source of the problem. It seems that it's a bug in JSF related with scripts in composite components included programatically.
Here's what I found:
I noticed that the correct code from OmniFaces to include my composite component is this:
Components.includeCompositeComponent(Components.getCurrentForm(), "componente", "inputMask", "a123", attributes);
The correct is "inputMask", not "inputMask.xhtml". But as I told you before, when I use this code I get this error instead:
Caused by: javax.faces.FacesException: Cannot remove the same component twice: form:a123:j_idt2
So I suspected that the component with the id form:a123:j_idt2 was one of the h:outputScript present in the composite component. So I changed the composite component code to this:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:composite="http://xmlns.jcp.org/jsf/composite"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">
<composite:interface componentType="inputMask">
<composite:attribute name="value" />
<composite:attribute name="mask" type="java.lang.String" required="true" />
<composite:attribute name="converterId" type="java.lang.String" default="br.edu.ufca.eventos.visao.inputmask.inputMask" />
</composite:interface>
<composite:implementation>
<h:inputText id="mascara">
<c:if test="#{cc.getValueExpression('value') != null}">
<f:attribute name="value" value="#{cc.attrs.value}" />
</c:if>
<f:converter converterId="#{cc.attrs.converterId}" />
<f:attribute name="mask" value="#{cc.attrs.mask}" />
</h:inputText>
<script type="text/javascript">
defineMask("#{cc.clientId}", "#{cc.attrs.mask}");
</script>
</composite:implementation>
</html>
Removing all references to the h:outputScript tag. (Of course, I placed the inputmask.js script outside the composite component for the component to continue to work).
And now when I run the code, the component is finally added to the page without errors. But, as I said before with the code from an earlier version of OmniFaces, the script is still not rendered in postbacks. JSF only renders it when the component is added, loosing it on postbacks. I know this is not an expected behaviour.
So, I ask you: do you know how I can solve this script problem? Or at least any workaround I can use in this case?
Thank you in advance.
---- UPDATE 2 ----
I found a workaround for it. I did this in a backing component for the composite component and it worked, the script is always rendered:
#Override
public void encodeEnd(FacesContext context) throws IOException
{
super.encodeEnd(context);
ResponseWriter writer = context.getResponseWriter();
writer.startElement("script", this);
writer.writeText(String.format("defineMask('%s', '%s');",
getClientId(), getAttributes().get("mask")), null);
writer.endElement("script");
}
but it's kind of ugly and seems unnecessary. Again, if the component is not included programmatically, I don't need the backing component. It seems like a bug in JSF. Could some of you test and confirm this? I mean, test if a composite component with script in it added programmatically loses its script on postback.
P.S.: I'm using OmniFaces 2.4 and Mojarra 2.2.13.
The solution (workaround) is to remove all script from the composite component and create a backing component for it to do precisely what JSF was supposed to do:
package br.edu.company.project.view.inputmask;
import java.io.IOException;
import java.util.Map;
import javax.faces.component.FacesComponent;
import javax.faces.component.NamingContainer;
import javax.faces.component.UIInput;
import javax.faces.component.UINamingContainer;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import org.omnifaces.util.FacesLocal;
#FacesComponent("inputMask")
public class InputMask extends UIInput implements NamingContainer
{
private static final String SCRIPT_FILE_WRITTEN =
"br.edu.company.project.SCRIPT_FILE_WRITTEN";
#Override
public String getFamily()
{
return UINamingContainer.COMPONENT_FAMILY;
}
#Override
public void encodeBegin(FacesContext context) throws IOException
{
writeScriptFileIfNotWrittenYet(context);
super.encodeBegin(context);
}
#Override
public void encodeEnd(FacesContext context) throws IOException
{
super.encodeEnd(context);
writeMaskDefinition(context);
}
private void writeScriptFileIfNotWrittenYet(FacesContext context) throws IOException
{
if (FacesLocal.getRequestMap(context).putIfAbsent(
SCRIPT_FILE_WRITTEN, true) == null)
{
writeScript(context, w -> w.writeAttribute(
"src", "resources/script/inputmask.js", null));
}
}
private void writeMaskDefinition(FacesContext context) throws IOException
{
writeScript(context, w -> w.writeText(String.format(
"defineMask('%s', '%s');", getClientId(),
getAttributes().get("mask")), null));
}
private void writeScript(FacesContext context, WriteAction writeAction)
throws IOException
{
ResponseWriter writer = context.getResponseWriter();
writer.startElement("script", this);
writer.writeAttribute("type", "text/javascript", null);
writeAction.execute(writer);
writer.endElement("script");
}
#FunctionalInterface
private static interface WriteAction
{
void execute(ResponseWriter writer) throws IOException;
}
}
Again, you don't need this if your composite component won't be included programmatically. In this case, JSF works as expected and you don't need the backing component.
If someone have the time, I think it would be nice to file a bug report to the Mojarra team.
I'm using JSF2 and Tomcat server. I programmed a simple example in which:
User Selects a faculty from "h:selectOneMenu"
Upon selection, the value of "h:inputText" is changed to "odd" or "even" based on facultyNo
Also, upon selection, the value of "h:selectBooleanCheckBox" is changed to "checked" if facultyNo is even and "not checked" if facultyNo is odd
Everything works fine for "h:inputText". On the other hand, the value of "h:selectBooleanCheckBox" does not change. Why is this happening?
By the way, the use of boolean value inside a HashMap is intentional because the project I'm working on has lots of boolean values in a HashMap. So, replacing the Hashmap with a simple boolean property and using a getter and a setter for it is definitely not a solution for my case.
The code for the xhtml page is below:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<head>
<title>Dummy Title</title>
</head>
<body>
<h:form>
<label for="faculty">Faculty</label>
<h:selectOneMenu id="faculty" value="#{test.selectedFaculty}" converter="faccon" valueChangeListener="#{test.facultyChange}" onchange="submit()">
<f:selectItems value="#{start.app.faculties}"/>
</h:selectOneMenu>
<h:selectBooleanCheckbox id="mycheck" value="#{test.x.get(0)}"></h:selectBooleanCheckbox>
<h:outputText value="#{test.res}"></h:outputText>
<h:commandButton value="Save" action="#{test.saveChoices}" />
</h:form>
</body>
</html>
The code for the backing bean is below
import java.io.Serializable;
import java.util.HashMap;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.event.ValueChangeEvent;
import com.myWork.Application;
import com.myWork.Faculty;
#ManagedBean(name="test")
#RequestScoped
public class TestBean implements Serializable
{
private HashMap<Integer,Boolean> x;
private String res;
private Faculty selectedFaculty;
#PostConstruct
public void init(){
Application app = Application.getInstance();
selectedFaculty = app.getFaculties()[0];
x = new HashMap<Integer, Boolean>();
if (selectedFaculty.getFacultyNo()%2==0)
{
x.put(0, true);
res = "even";
}
else
{
x.put(0, false);
res = "odd";
}
}
public HashMap<Integer,Boolean> getX() {
return x;
}
public void setX(HashMap<Integer,Boolean> x) {
this.x = x;
}
public Faculty getSelectedFaculty() {
return selectedFaculty;
}
public void setSelectedFaculty(Faculty selectedFaculty) {
this.selectedFaculty = selectedFaculty;
}
public String getRes() {
return res;
}
public void setRes(String res) {
this.res = res;
}
public void facultyChange(ValueChangeEvent e){
Faculty fac = (Faculty) e.getNewValue();
if (fac.getFacultyNo()%2==0)
{
x.put(0, true);
res = "even";
}
else
{
x.put(0, false);
res = "odd";
}
}
public String saveChoices(){
return "test";
}
}
Any help is greatly appreciated.
Actually your problem is not related to JSF, it is related to EL. in your case, EL treat you map keys as long not int, so you should change your map to be:
HashMap<Long, Boolean> map = new HashMap<>();
and set the key values as follows :
map.put(0L, false);
map.put(1L, true);
to force it to be auto-boxed to long.
Note: the problem described in detailed in the following question :
EL access a map value by Integer key
In order to send updates to a different component, you can make use of partial page rendering by making Ajax calls to send parameters to Managed bean.
Within <h:selectBooleanCheckBox>
<h:selectOneMenu id="faculty" value="#{test.selectedFaculty}" converter="faccon" valueChangeListener="#{test.facultyChange}">
<f:selectItems value="#{start.app.faculties}"/>
</h:selectOneMenu>
<h:selectBooleanCheckbox id="mycheck" valueChangeListener="#{test.facultyChange}" value="#{test.x.get(0)}">
<f:ajax event="change" execute="#form" render="faculty"/>
</h:selectBooleanCheckbox>
Edit: Removed call to onSubmit() in selectOneMenu. introduced an ajax call in selectBooleanCheckbox for partial page refresh.
Refer full solution at this post.
I have a page containing a p:commandLink that calls a very simple method on a session-scoped backing bean. The backing bean method simply logs a message to the console and redirects to a welcome page.
When run, the link does nothing. There are no errors reported in h:messages and no stack traces on the server console. However, if I use the exact same code on another page then the link works fine.
The main difference between the pages is that the page containing the 'dead' link is protected by a login filter (the page is in the 'restricted' folder) whereas the page that contains the 'working' link is not in the restricted folder.
I've followed the advice in several other threads about action links that don't work. I don't have nested forms or rendering problems, etc. I'm wondering if the login filter may be having some effect on the link.
Here's the page containing the link that doesn't work (it resides in location: restricted/secret.xhtml). I've deliberately minimised the contents to eliminate other potential causes.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<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:p="http://primefaces.org/ui">
<h:head>
<title>Restricted Page</title>
</h:head>
<h:body>
<h:form>
<h:messages />
<br/>
<p:commandLink action="#{loginBean.testMethod}" value="Test" />
</h:form>
</h:body>
</html>
Here's the method in the backing bean (which DOES work if called using the exact same commandLink code on an unprotected page:
public String testMethod() {
logger.info("xxxxxxxxxxxxxx Test method called xxxxxxxxxxxxxx");
return("WelcomeMember.xhtml");
}
The login filter is as follows:
package com.mymato.coop;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
#WebFilter("/restricted/*")
public class AuthenticationFilter implements Filter {
#SuppressWarnings("unused")
private FilterConfig config;
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
if (((HttpServletRequest) req).getSession().getAttribute(
LoginBean.AUTH_KEY) == null) {
((HttpServletResponse) resp).sendRedirect("../login.xhtml");
} else {
chain.doFilter(req, resp);
}
}
public void init(FilterConfig config) throws ServletException {
this.config = config;
}
public void destroy() {
config = null;
}
}
For future reference, is there a good way to debug this type of problem? I've run into the dead link problem a few times now. It's very difficult to solve because it fails silently. At least if there was an error message then that would be a clue.
I need your help is solving the error in the log. In my jsp, I am having selectmanycheckbox:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<%# page deferredSyntaxAllowedAsLiteral="true" %>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:form>
<h:selectManyCheckbox value="#{com.favoriteCar2}">
<f:selectItems value="#{com.favoriteCar2Value}" />
</h:selectManyCheckbox>
<br/>
<h:selectManyCheckbox value="#{com.favoriteCar3}">
<f:selectItems value="#{com.favoriteCar3Value}" />
</h:selectManyCheckbox>
<h:commandButton value="Submit" action="results" />
<h:commandButton value="Reset" type="reset" />
</h:form>
and mybean:
import java.io.Serializable;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
#ManagedBean
#SessionScoped
public class com implements Serializable{
private static final long serialVersionUID = 7134492943336358840L;
public String[] favoriteCar1;
public String[] favoriteCar2;
public String[] favoriteCar3;
public String[] favoriteCar4;
public String[] getFavoriteCar2Value()
{
favoriteCar2 = new String [5];
favoriteCar2[0] = "116";
favoriteCar2[1] = "118";
favoriteCar2[2] = "X1";
favoriteCar2[3] = "Series 1 Coupe";
favoriteCar2[4] = "120";
return favoriteCar2;
}
public String getFavoriteCar2InString()
{
return Arrays.toString(favoriteCar2);
}
private static Map<String, Object> car3Value;
static
{
car3Value = new LinkedHashMap<String, Object>();
car3Value.put("Car3 - 316", "BMW 316");
car3Value.put("Car3 - 318", "BMW 318");
car3Value.put("Car3 - 320", "BMW 320");
car3Value.put("Car3 - 325", "BMW 325");
car3Value.put("Car3 - 330", "BMW 330");
}
public Map<String, Object> getFavoriteCar3Value()
{
return car3Value;
}
public String getFavoriteCar3InString() {
return Arrays.toString(favoriteCar3);
}
}
The log is showing the error and no checkbox is shown in the jsp:
java.lang.IllegalArgumentException: Expected a child component type of UISelectItem/UISelectItems for component type javax.faces.SelectMany(j_id_id2). Found [Ljava.lang.String
Even I tried static children and it is not populating them.
So can you please help
It's because your SelectItems value is a String[], see the java docs regarding the value attribute of SelectItems in JSF 1.2 :
Value binding expression pointing at a List or array of SelectItem
instances containing the information for these options.
You are also returning the value of your selectManyCheckbox in the SelectItems which doesn't make sense, you should better learn more about how to use SelectItems. You can find many examples in the selectOneMenu wiki page which is very similar to the selectManyCheckbox, Or in The Java EE 6 Tutorial (Note that this links are JSF 2.0 but that may help you to understand the concept).
Regarding your example, that should be something like this:
private List<SelectItem> favoriteCar2Value;
// (we will add only a getter, setter is not necessary)
public List<SelectItem> getFavoriteCar2Value() {
favoriteCar2Value = new ArrayList<SelectItem>();
favoriteCar2Value.add(new SelectItem("116", "116 label"));
favoriteCar2Value.add(new SelectItem("118", "118 label"));
favoriteCar2Value.add(new SelectItem("X1", "X1 label"));
favoriteCar2Value.add(new SelectItem("Series 1 Coupe", "Series 1 Coupe label"));
favoriteCar2Value.add(new SelectItem("120", "120 label"));
return favoriteCar2Value;
}
Finnaly, maybe it's time for you to consider migrating to JSF 2.0 which may let you working with facelets instead of JSP, benefit from Ajax support... For a clear comparative see: What are the main disadvantages of Java Server Faces 2.0?
The view and bean were working until I tried to fix non-standard names, and I've now broken the connection between the two. Oddly, the "back" button has the correct link, but content just doesn't show, nor log. Why doesn't Detail.getComments() execute?
I've been going through the weld docs and trying to better understand #Inject. There seems to be a lifecycle problem which I don't understand, either. If it's not lifecycle, then I cannot even speculate as to why Detail.getComments() never shows in the glassfish logs:
INFO: MessageBean.getModel..
INFO: SingletonNNTP.returning messages..
INFO: MessageBean.getModel..
INFO: SingletonNNTP.returning messages..
INFO: MessageBean.getModel..
INFO: SingletonNNTP.returning messages..
INFO: Detail..
INFO: Detail.getId..null
INFO: Detail.getId..SETTING DEFAULT ID
INFO: Detail.onLoad..2000
INFO: Detail.getId..2000
INFO: Detail.getId..2000
INFO: Detail.setId..2000
INFO: Detail.getId..2019
INFO: ..Detail.setId 2019
INFO: Detail.back..
INFO: Detail.getId..2019
INFO: ..Detail.back 2,018
INFO: Detail.getId..2019
The value 2000 is a default, which only happens when id==null, which it never should. It should pull in that attribute right away. So, I'm not sure whether that's a problem with the scope (I only just now found out that CDI doesn't support #SessionScoped), the lifecycle, or something else. Perhaps I need to use #Inject on that variable?
The view, detail.xhtml:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">
<body>
<f:metadata>
<f:viewParam name="id" id="id" value="#{detail.id}" />
</f:metadata>
<ui:composition template="./complexTemplate.xhtml">
<ui:define name="top">
<h:link value="back" outcome="detail" includeViewParams="true">
<f:param name="id" value="#{detail.back}"/>
</h:link>
<ui:define name="content">
<h:outputText value="#{detail.content}" rendered="false"/>
</ui:define>
<ui:define name="bottom">
bottom
</ui:define>
</ui:composition>
</body>
</html>
and the backing bean:
package net.bounceme.dur.nntp;
import java.io.Serializable;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.enterprise.context.ConversationScoped;
import javax.inject.Named;
import javax.mail.Message;
#Named
#ConversationScoped
public class Detail implements Serializable {
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(Detail.class.getName());
private static final Level level = Level.INFO;
private String id = null; //should never get default value in getter
private Message message = null;
private SingletonNNTP nntp = SingletonNNTP.INSTANCE;
private String forward = null; //id + 1
private String back = null; //id - 1
private String content = null; //message.content
public Detail() {
logger.log(level, "Detail..");
}
#PostConstruct
private void onLoad() {
logger.log(level, "Detail.onLoad..{0}", getId());
}
public Message getMessage() {
logger.log(level, "Detail.getMessage..");
return message;
}
public void setMessage(Message message) {
logger.log(level, "Detail.setMessage..");
this.message = message;
}
public String getId() {
logger.log(level, "Detail.getId..{0}", id);
if (id == null) {
logger.log(level, "Detail.getId..SETTING DEFAULT ID");
id = String.valueOf(2000);
}
return id;
}
public void setId(String id) throws Exception {
logger.log(level, "Detail.setId..{0}", getId());
this.id = id;
logger.log(level, "..Detail.setId {0}", getId());
}
public String getForward() {
logger.log(level, "Detail.forward..");
int f = Integer.parseInt(getId());
f = f + 1;
logger.log(level, "..Detail.forward {0}", f);
forward = String.valueOf(f);
return forward;
}
public void setForward(String forward) {
this.forward = forward;
}
public String getBack() {
logger.log(level, "Detail.back..");
int b = Integer.parseInt(getId());
b = b - 1;
logger.log(level, "..Detail.back {0}", b);
back = String.valueOf(b);
return back;
}
public void setBack(String back) {
this.back = back;
}
public String getContent() throws Exception {
logger.log(level, "Detail.getContent..{0}", getId());
message = nntp.getMessage(Integer.parseInt(getId()));
content = message.getContent().toString();
return content;
}
public void setContent(String content) {
this.content = content;
}
}
which never seems to have, according to the above logs, Detail.getContent() invoked, despite that being part of the view: <h:outputText value="#{detail.content}" rendered="false"/>
It's odd in that Detail.content() was getting invoked prior to my changing this class to better follow naming conventions. I'm going through some Weld and Oracle Java EE 6 docs, but don't at all mind being directed to a fine manual. The docs I find describing this are invariably using #ManagedBeans, however, which I am not. There seem many gotchas, as described in this answer by #Kawu.
Adding #Inject to the id field causes a deploy error:
init:
deps-module-jar:
deps-ear-jar:
deps-jar:
library-inclusion-in-archive:
library-inclusion-in-manifest:
compile:
compile-jsps:
In-place deployment at /home/thufir/NetBeansProjects/NNTPjsf/build/web
Initializing...
deploy?DEFAULT=/home/thufir/NetBeansProjects/NNTPjsf/build/web&name=NNTPjsf&contextroot=/NNTPjsf&force=true failed on GlassFish Server 3.1.2
Error occurred during deployment: Exception while loading the app : WELD-001408 Unsatisfied dependencies for type [String] with qualifiers [#Default] at injection point [[field] #Inject private net.bounceme.dur.nntp.Detail.id]. Please see server.log for more details.
/home/thufir/NetBeansProjects/NNTPjsf/nbproject/build-impl.xml:749: The module has not been deployed.
See the server log for details.
BUILD FAILED (total time: 9 seconds)
Surely, injecting a String isn't the problem, perhaps it's a bug.
I understand your frustration, and I see that the problem is more your setup / understanding in general. But still, it's pretty hard to find any real questions to answer, maybe you can try to split your problems next time.
Here are some answers:
Why doesn't Detail.getComments() execute?
Hm, maybe because it's not in the bean? I guess that you are refrerring to detail.getContent instead?
which never seems to have, according to the above logs,
Detail.getContent() invoked, despite that being part of the view:
Try rendered = true :-)
#PostConstruct
private void onLoad() {
logger.log(level, "Detail.onLoad..{0}", getId());
}
You've put an awful lot of logic into the getter. Try debugging with the field, not with the getter...
The value 2000 is a default, which only happens when id==null, which it never should.
It looks like private String id = null; is a perfect explanation why id will be null.
Try to keep in mind that modern frameworks like JSF, CDI and Java EE do a lot of stuff behind the scenes, using reflection, proxies and interceptors. Don't rely on classical understanding of when (and how often) a constructor is called, for example.
Again, consider moving your initialisation logic away from the getter. #PostConstruct would be the place that the fathers of the Java EE-spec had chosen for it.
To be honest: Nothing looks extremely wrong, but your code is kind of messy, and extremely hard to understand and to follow.
Try removing all indirections like this one...
int b = Integer.parseInt(getId());
... and everything will look much better.
Oh, and is there a specific reason why you declare a fixed log-level for the whole class? Try something like this
private static final Logger LOG = Logger.getLogger(Some.class);
...
LOG.info("...");
Hope that gives you a start. Feel free to post further questions, preferably a bit shorter and with single, isolated aspects to answer.