custom converter on p:selectCheckboxMenu

UI Components for JSF
Post Reply
Jan Eckert
Posts: 84
Joined: 11 Sep 2014, 10:13
Location: Brussels, Belgium

06 Feb 2018, 14:37

Hey folks!

I have a problem with my custom converter on a p:selectCheckboxMenu and hope that someone might be able to help me out. In my (simplified) example I deal with books that only have an author and a title

Code: Select all

package checkboxselect;

import org.apache.commons.lang.builder.ToStringBuilder;

public class Book {
	private String author;
	private String title;
	
	public Book(String author, String title) {
		this.author = author;
		this.title =  title;
	}
	
	public String getAuthor() {
		return author;
	}
	public void setAuthor(String author) {
		this.author = author;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}
	
	@Override
	public String toString() {
		return ToStringBuilder.reflectionToString(this);
	}
}

Building on the top of this class I have a ManagedBean BookStash. It holds a List of available books the user is able to choose from and also another List of Books he eventually selected which is a subset of the former. I am aware that I could/should implement the second List as a List of indices, but please stay with me here ...

Code: Select all

package checkboxselect;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

@ManagedBean
@SessionScoped
public class BookStash implements Serializable{

	private final static transient Logger LOGGER = LogManager.getLogger(BookStash.class);
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 537394663400478599L;
	private List<Book> books;
	private List<Book> selection;
	
	private BookService bs = new BookService();
	
	public BookStash() {
		
		this.books = new ArrayList<Book>();
		this.books.add(new Book("Victor Kramnik", "The isolated pawn"));
		this.books.add(new Book("Judith Polgar", "Trading pieces"));
		
		this.selection = new ArrayList<>();
	}
	
	public List<Book> getBooks() {
		return books;
	}

	public void setBooks(List<Book> books) {
		this.books = books;
	}

	public List<Book> getSelection() {
		return selection;
	}

	public void setSelection(List<Book> selection) {
		LOGGER.debug("Selection value set to: " + selection);
		this.selection = selection;
	}

	public BookService getBs() {
		return bs;
	}

	public void setBs(BookService bs) {
		this.bs = bs;
	}

}

I now want to use a p:selectCheckboxMenu as the control the user has to pick from the list

Code: Select all

<?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://xmlns.jcp.org/jsf/facelets"
	xmlns:p="http://primefaces.org/ui"
	xmlns:h="http://xmlns.jcp.org/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core">
<h:head>
	<title>Book Store</title>

</h:head>

<h:body>
	<h:form id="bookstoreForm">
		<h:outputLabel for="bookSelect" value="Please choose" />
		
		<p:selectCheckboxMenu value="#{bookStash.selection}" multiple="true" panelStyle="width:250px" id="bookSelect">
			<f:selectItems value="#{bookStash.books}" var="b" itemValue="#{b}" itemLabel="#{bookStash.bs.createBookLabel(b)}"/>
			<f:converter converterId="BookConverter"/>
		</p:selectCheckboxMenu>			
		<h:commandButton action="result" value="Submit" />
	</h:form>
	
</h:body>
</html>
I map out the available values in the f:selectItems child and I even fix the labels with an uninteresting createBookLabel(..) method. The HTML value of a book is supposed the be "author~title", e.g.: "Jan Eckert~My JSF struggles". The converter 'BookConverter' is straightforward but I think irrelevant to my question and therefore omitted. When I now hit the JSF page specified above, I get promising results. The logs say
12:13:38,833 INFO [stdout] (default task-77) Object checkboxselect.Book@74c8c2c2[author=Victor Kramnik,title=The isolated pawn] converted to String Victor Kramnik~The isolated pawn
12:13:38,833 INFO [stdout] (default task-77) Object checkboxselect.Book@4e5433f5[author=Judith Polgar,title=Trading pieces] converted to String Judith Polgar~Trading pieces
14:31:42,704 INFO [stdout] (default task-72) Object checkboxselect.Book@30dc7884[author=Victor Kramnik,title=The isolated pawn] converted to String Victor Kramnik~The isolated pawn -->
checkboxselect.BookConverter.getAsString(BookConverter.java:43)
14:31:42,704 INFO [stdout] (default task-72) Object checkboxselect.Book@78edd582[author=Judith Polgar,title=Trading pieces] converted to String Judith Polgar~Trading pieces -->
checkboxselect.BookConverter.getAsString(BookConverter.java:43)
as expected since the book instances get converted into their String representations to be rendered in the HTML. Indeed, this looks correct as well, the values are correctly set:

Code: Select all

  <div id="bookstoreForm:bookSelect">
  	<input id="bookstoreForm:bookSelect:0" name="bookstoreForm:bookSelect" type="checkbox" value="Victor Kramnik~The isolated pawn" data-escaped="true" />
  	<label for="bookstoreForm:bookSelect:0">V.K - The isolated pawn</label>
  	
  	<input id="bookstoreForm:bookSelect:1" name="bookstoreForm:bookSelect" type="checkbox" value="Judith Polgar~Trading pieces" data-escaped="true" />
  	<label for="bookstoreForm:bookSelect:1">J.P - Trading pieces</label>
  </div>
Now the problem begins, if I now select one of the books and submit (the resulting page has nothing on it ... just a text reading "You made it to the results page"), I get the following logs:
:32:03,609 INFO [stdout] (default task-78) String Judith Polgar~Trading pieces converted to Object checkboxselect.Book@65969bee[author=Judith Polgar,title=Trading pieces] --> checkboxselect.BookConverter.getAsObject(BookConverter.java:22)

14:32:03,625 INFO [stdout] (default task-78) Object checkboxselect.Book@30dc7884[author=Victor Kramnik,title=The isolated pawn] converted to String Victor Kramnik~The isolated pawn --> checkboxselect.BookConverter.getAsString(BookConverter.java:43)

14:32:03,625 INFO [stdout] (default task-78) Object checkboxselect.Book@78edd582[author=Judith Polgar,title=Trading pieces] converted to String Judith Polgar~Trading pieces --> checkboxselect.BookConverter.getAsString(BookConverter.java:43)

14:32:03,625 SEVERE [javax.enterprise.resource.webcontainer.jsf.application] (default task-78) Error Rendering View[/faces/standalone/checkboxmenu/index.xhtml]: java.lang.RuntimeException: Inconvertable type: class java.lang.String of value 'Judith Polgar~Trading pieces'
at checkboxselect.BookConverter.getAsString(BookConverter.java:31)
at org.primefaces.renderkit.InputRenderer.getOptionAsString(InputRenderer.java:175)
at org.primefaces.component.selectcheckboxmenu.SelectCheckboxMenuRenderer.encodeMultipleLabel(SelectCheckboxMenuRenderer.java:225)
at org.primefaces.component.selectcheckboxmenu.SelectCheckboxMenuRenderer.encodeMarkup(SelectCheckboxMenuRenderer.java:79)
at org.primefaces.component.selectcheckboxmenu.SelectCheckboxMenuRenderer.encodeEnd(SelectCheckboxMenuRenderer.java:52)
at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:920)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1863)
at javax.faces.render.Renderer.encodeChildren(Renderer.java:176)
at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:890)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1856)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1859)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1859)
at com.sun.faces.application.view.FaceletViewHandlingStrategy.renderView(FaceletViewHandlingStrategy.java:458)
at com.sun.faces.application.view.MultiViewHandler.renderView(MultiViewHandler.java:134)
at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:337)
at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:337)
at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:120)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:219)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:659)
at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:85)
at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
at org.wildfly.extension.undertow.security.SecurityContextAssociationHandler.handleRequest(SecurityContextAssociationHandler.java:78)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:131)
at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60)
at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77)
at io.undertow.security.handlers.NotificationReceiverHandler.handleRequest(NotificationReceiverHandler.java:50)
at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at org.wildfly.extension.undertow.deployment.GlobalRequestControllerHandler.handleRequest(GlobalRequestControllerHandler.java:68)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:292)
at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:81)
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:138)
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135)
at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
at org.wildfly.extension.undertow.security.SecurityContextThreadSetupAction.lambda$create$0(SecurityContextThreadSetupAction.java:105)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1508)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1508)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1508)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1508)
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:272)
at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81)
at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:104)
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:326)
at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:812)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
I understand the first line. The converter is asked to convert the submitted text value to a book object. I do not understand the rest ... for some reason the two available options are again converted back to Strings although there is no rendering of them requested (result.xhtml doesn't map them out).
And for me even less understandable is the error trace. Why on earth is my BookConverter.getAsString() method (that is supposed to get Books to convert them into a representable String) called with the String representation? It happens in the encoding phase, this is correct but why is it called with the String and not the object instance?

Any help would be appreciated.


EDIT:

The Converter:

Code: Select all

package checkboxselect;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

@FacesConverter(value="BookConverter")
public class BookConverter implements Converter {
	private final static String DELIMITER = "~";
	private final static transient Logger LOGGER = LogManager.getLogger(BookConverter.class);

	@Override
	public Object getAsObject(FacesContext context, UIComponent component, String value) {
		
		String[] parts = value.split(DELIMITER);
		Book result = new Book(parts[0], parts[1]);
		
		LOGGER.debug(String.format("String %s converted to Object %s", value, result));
		
		return result;
	}

	@Override
	public String getAsString(FacesContext context, UIComponent component, Object value) {
		
		if (!(value instanceof Book)) {
			throw new RuntimeException(String.format("Inconvertable type: %s of value '%s'", value.getClass(), value));
		}
		
		Book book = (Book)value;
		StringBuilder sb = new StringBuilder();
		sb
			.append(book.getAuthor())
			.append(DELIMITER)
			.append(book.getTitle());
		
		String result = new String(sb);
		
		LOGGER.debug(String.format("Object %s converted to String %s", value, result));
		return result;
	}
}
Last edited by Jan Eckert on 06 Feb 2018, 15:39, edited 2 times in total.
Primefaces 6.1+
Wildfly 11

kukeltje
Expert Member
Posts: 9605
Joined: 17 Jun 2010, 13:34
Location: Netherlands

06 Feb 2018, 14:57

Start by not using a sysout for your logging but a real logging framework. You (and WE since you omit the converter code, yet mention it explicitly in the title and several times in the post) can then see where this is actually called. You can also set breakpoints there to see where it is called from. I have too little knowledge of the details of JSF to tell by heart what is going on. But as is always mentioned in Stackoverflow, an 'mcve' is always the best to create and post!.

Jan Eckert
Posts: 84
Joined: 11 Sep 2014, 10:13
Location: Brussels, Belgium

06 Feb 2018, 15:38

Thanks for your remark, I edited my initial post accordingly.

As an additional remark: If I throw out the p:selectCheckboxMenu, chose a h:inputtext instead and modify the Bookstash accordingly to hold only one result instance of book, it works flawlessly.

Downloadlink mcsv: http://www.filedropper.com/showcase (you need Maven installed)
navigate to the root folder of the project with the pom.xml, run

Code: Select all

mvn wildfly:run
after starting up (make sure not to have a appserver already running at port 8080), the website will be available under
Last edited by Jan Eckert on 06 Feb 2018, 16:18, edited 2 times in total.
Primefaces 6.1+
Wildfly 11

RueKow
Posts: 331
Joined: 21 Jun 2011, 23:34
Location: Germany - Wiesbaden

06 Feb 2018, 16:05

Another approach for selecting objects: "Selectable DataTable"
There is no converter necessary. ;)
Rüdiger

PrimeFaces 11.0 | Ultima 2.0
Mojarra 2.3 | Tomcat 8/9 | Win7/10 | OS X 10.14

Jan Eckert
Posts: 84
Joined: 11 Sep 2014, 10:13
Location: Brussels, Belgium

06 Feb 2018, 16:08

Thank you for this fallback option :D
Primefaces 6.1+
Wildfly 11

kukeltje
Expert Member
Posts: 9605
Joined: 17 Jun 2010, 13:34
Location: Netherlands

06 Feb 2018, 16:26

PF 6.1+ meaning you tried 6.2-RC1 as well? Just to make sure that if I try to reproduce/debug I can use the latest from trunk.

Jan Eckert
Posts: 84
Joined: 11 Sep 2014, 10:13
Location: Brussels, Belgium

06 Feb 2018, 22:21

kukeltje wrote:
06 Feb 2018, 16:26
PF 6.1+ meaning you tried 6.2-RC1 as well? Just to make sure that if I try to reproduce/debug I can use the latest from trunk.
Yes, Sir! PF 6.2-RC1 on JSF 2.2 on Wildfly 11
Primefaces 6.1+
Wildfly 11

Post Reply

Return to “PrimeFaces”

  • Information
  • Who is online

    Users browsing this forum: Google [Bot] and 41 guests