Thymeleaf - th:checked works, th:field fails - thymeleaf

Thymeleaf
Im trying to bind a variable called 'permitirAcesso' to thymeleaf. When I use th:checked, it shows the value correctly. When I use th:field, it doesnt bind. What should I do?
I investigated and th:text shows the correct value. Ive also tried changing the variable name, but it still doesnt work. Ive also tried to use a common html checkbox, it still doesnt bind.
Ive simplified the page and removed everything except the form and the malfunction persists. It works with th:checked but fails to bind with th:field.
Here's my code:
<head>
<meta charset="UTF-8"/>
</head>
<body>
<form id="formulario" th:action="#{/painel-do-administrador/usuarios/salvar}" th:object="${usuario}" method="POST" class="action-buttons-fixed">
<input type="checkbox" name="permitirAcesso" id="permitirAcesso" th:field="*{permitirAcesso}" />
<input type="checkbox" name="permitirAcesso" id="permitirAcesso" th:checked="*{permitirAcesso}" />
</form>
</body>
My Java Code:
#Entity
#Table(name = "users")
public class User implements UserDetails, Comparable<User> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank(message = "O nome não pode ser nulo")
#NotNull(message = "O nome não pode ser nulo")
#Column(name = "username", nullable = false, length = 512, unique = true)
private String username;
#Column(name = "nome_completo", updatable = true, nullable = false)
private String nomeCompleto;
#Column(name = "password", updatable = true, nullable = false)
private String password;
#Column(name = "ultimo_acesso")
private ZonedDateTime ultimoAcesso;
#Email(message = "Insira uma e-mail válido")
#Column(name = "email", updatable = true, nullable = false)
private String email;
#Column(name = "permitir_acesso")
private boolean permitirAcesso;
#Lob
#Column(name = "avatar", columnDefinition = "BLOB")
private byte[] avatar;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(
name = "users_roles",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "role_id"))
private Set<Role> roles = new HashSet<>();
public User() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Override
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getNomeCompleto() {
return nomeCompleto;
}
public void setNomeCompleto(String nomeCompleto) {
this.nomeCompleto = nomeCompleto;
}
#Override
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public ZonedDateTime getUltimoAcesso() {
return ultimoAcesso;
}
public void setUltimoAcesso(ZonedDateTime ultimoAcesso) {
this.ultimoAcesso = ultimoAcesso;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public boolean isPermitirAcesso() {
return permitirAcesso;
}
public String getPermitirAcessoString() {
if (permitirAcesso) {
return "Sim";
} else {
return "Não";
}
}
public void setPermitirAcesso(boolean permitirAcesso) {
this.permitirAcesso = permitirAcesso;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
public void addRole(Role role) {
this.roles.add(role);
}
public boolean hasRole(Role role) {
Iterator<Role> iterator = this.roles.iterator();
while (iterator.hasNext()) {
if (role.equals(iterator.next())) {
return true;
}
}
return false;
}
public boolean hasRole(String roleName) {
for (Role role : this.roles) {
if (role.getName().equals(roleName)) {
return true;
}
}
return false;
}
#Override
#JsonIgnore
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.emptyList();
}
#Override
public int compareTo(User o) {
return getEmail().compareToIgnoreCase(o.getEmail());
}
#Override
#JsonIgnore
public boolean isAccountNonExpired() {
return true;
}
#Override
#JsonIgnore
public boolean isAccountNonLocked() {
return true;
}
#Override
#JsonIgnore
public boolean isCredentialsNonExpired() {
return true;
}
#Override
#JsonIgnore
public boolean isEnabled() {
return isPermitirAcesso();
}
}

Ok. This was a stupid mistake of mine. So what happened is that I had a Boolean Converter that converted Boolean to String. Removing the converter, fixed the issue.

Related

p:selectOneMenu not calling setter on form submit

I'm kinda new to JSF and primefaces and I'm running into a very annoying problem.
I'm making a very basic application to learn a bit more about primefaces. I have a simple form that has several textual input fields, two datepickers and one dropdown (selectOneMenu).
Everything works all values are put in the backing bean when I submit the form, except for the value from the dropdown menu. The setter for that item is never called. And the application does not call the public void saveNewActivity(ActionEvent evt) method on the controller as defined on the commandbutton. When I however remove or comment out the dropdown menu in html it does call that method (but the field for the dropdown menu is obviously null).
I've been trying things for nearly two days, and still can't get this to work properly.
I have the following code (snippets):
My html/jsf code
<div id="newActivitycontent">
<h:form id="newActivityForm">
<h:messages id="messages"/>
<table>
<tr>
<td>Gebruiker:</td>
<td><p:selectOneMenu value="#{plannedActivityController.newActivity.organiser}}"
converter="#{userConverter}">
<f:selectItem itemLabel="Kies een gebruiker" itemValue=""/>
<f:selectItems value="#{plannedActivityController.users}" var="user"
itemLabel="#{user.firstname} #{user.lastname}" itemValue="#{user}"/>
</p:selectOneMenu></td>
</tr>
<tr>
<td>Titel:</td>
<td><p:inputText value="#{plannedActivityController.newActivity.name}"/></td>
</tr>
<tr>
<td>Beschrijving:</td>
<td><p:inputText value="#{plannedActivityController.newActivity.desctription}"/></td>
</tr>
<tr>
<td>Startdatum:</td>
<td><p:calendar value="#{plannedActivityController.newActivity.startDateDate}"/></td>
</tr>
<tr>
<td>Einddatum:</td>
<td><p:calendar value="#{plannedActivityController.newActivity.endDateDate}"/></td>
</tr>
</table>
<p:commandButton id="btnSaveNewActivity" value="Opslaan"
actionListener="#{plannedActivityController.saveNewActivity}"
update=":overviewForm:activityTable messages"/>
<p:commandButton id="btnCancelNewActivity" value="Annuleren"
actionListener="#{plannedActivityController.cancelNewActivity}" onclick="hideAddNewUI()"
update=":overviewForm:activityTable" type="reset" immediate="true"/>
</h:form>
</div>
The controller that is used by that code:
#Named
#SessionScoped
public class PlannedActivityController implements Serializable {
#Inject
private ApplicationModel appModel;
#Inject
private SessionModel sessionModel;
#Inject
private ActivityMapper activityMapper;
#Inject
private UserMapper userMapper;
private ActivityBean newActivity;
private ActivityBean selectedActivity;
private List<ActivityBean> activities;
private List<UserBean> users;
public PlannedActivityController() {
}
#PostConstruct
public void onCreated() {
convertActivities();
onNewActivity();
users = userMapper.mapToValueObjects(appModel.getUsers());
}
public void convertActivities() {
List<PlannedActivity> originalActivities = appModel.getActivities();
this.activities = activityMapper.mapToValueObjects(originalActivities);
}
public void onRowEditComplete(RowEditEvent event) {
System.out.println("row edited : " + event.getObject());
//TODO: save changes back to db!
}
public void onRowSelectionMade(SelectEvent event) {
System.out.println("row selected : " + event.getObject());
selectedActivity = (ActivityBean)event.getObject();
}
//Activity crud methods
public void onNewActivity() {
newActivity = new ActivityBean();
newActivity.setId(new Date().getTime());
}
public void saveNewActivity(ActionEvent evt) {
PlannedActivity newAct = activityMapper.mapToEntity(newActivity);
if(newAct != null) {
appModel.getActivities().add(newAct);
}
convertActivities();
}
public void cancelNewActivity() {
//TODO: cleanup.
}
public void deleteSelectedActivity() {
if(selectedActivity != null) {
activities.remove(selectedActivity);
appModel.setActivities(activityMapper.mapToEntities(activities));
convertActivities();
} else {
//TODO: show error or information dialog, that delete cannot be done when nothing has been selected!
}
}
//Getters & Setters
public ApplicationModel getAppModel() {
return appModel;
}
public void setAppModel(ApplicationModel appModel) {
this.appModel = appModel;
}
public SessionModel getSessionModel() {
return sessionModel;
}
public void setSessionModel(SessionModel sessionModel) {
this.sessionModel = sessionModel;
}
public ActivityMapper getActivityMapper() {
return activityMapper;
}
public void setActivityMapper(ActivityMapper activityMapper) {
this.activityMapper = activityMapper;
}
public UserMapper getUserMapper() {
return userMapper;
}
public void setUserMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}
public ActivityBean getNewActivity() {
return newActivity;
}
public void setNewActivity(ActivityBean newActivity) {
this.newActivity = newActivity;
}
public ActivityBean getSelectedActivity() {
return selectedActivity;
}
public void setSelectedActivity(ActivityBean selectedActivity) {
this.selectedActivity = selectedActivity;
}
public List<ActivityBean> getActivities() {
return activities;
}
public void setActivities(List<ActivityBean> activities) {
this.activities = activities;
}
public List<UserBean> getUsers() {
return users;
}
public void setUsers(List<UserBean> users) {
this.users = users;
}
}
My activitybean:
public class ActivityBean implements Serializable {
private Long id = 0L;
private String name;
private String desctription;
private UserBean organiser;
private Calendar startDate;
private Calendar endDate;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesctription() {
return desctription;
}
public void setDesctription(String desctription) {
this.desctription = desctription;
}
public UserBean getOrganiser() {
return organiser;
}
public void setOrganiser(UserBean organiser) {
this.organiser = organiser;
}
public Calendar getStartDate() {
return startDate;
}
public void setStartDate(Calendar startDate) {
this.startDate = startDate;
}
public Date getStartDateDate() {
if(this.startDate == null) {
return null;
}
return this.endDate.getTime();
}
public void setStartDateDate(Date startDate) {
if(this.startDate == null) {
this.startDate = new GregorianCalendar();
}
this.startDate.setTime(startDate);
}
public String getStartDateString() {
if(this.startDate == null) {
return null;
}
return startDate.get(Calendar.DAY_OF_MONTH) + "/" + startDate.get(Calendar.MONTH) + "/" + startDate.get(Calendar.YEAR) + "";
}
public Calendar getEndDate() {
return endDate;
}
public void setEndDate(Calendar endDate) {
this.endDate = endDate;
}
public Date getEndDateDate() {
if(this.endDate == null) {
return null;
}
return endDate.getTime();
}
public void setEndDateDate(Date endDate) {
if(this.endDate == null) {
this.endDate = new GregorianCalendar();
}
this.endDate.setTime(endDate);
}
public String getEndDateString() {
if(this.endDate == null) {
return null;
}
return endDate.get(Calendar.DAY_OF_MONTH) + "/" + endDate.get(Calendar.MONTH) + "/" + endDate.get(Calendar.YEAR) + "";
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ActivityBean that = (ActivityBean) o;
if (id != null ? !id.equals(that.id) : that.id != null) return false;
return true;
}
#Override
public int hashCode() {
return id != null ? id.hashCode() : 0;
}
}
My userbean:
public class UserBean {
private Long id;
private String username;
private String firstname;
private String lastname;
private String email;
private String phone;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UserBean userBean = (UserBean) o;
if (id != null ? !id.equals(userBean.id) : userBean.id != null) return false;
return true;
}
#Override
public int hashCode() {
return id != null ? id.hashCode() : 0;
}
}
And the converter used by the selectOneMenu:
#Named
public class userConverter implements Converter{
#Inject
private PlannedActivityController activityController;
#Override
public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String s) {
for (UserBean user : activityController.getUsers()) {
if(user.getId().toString().equals(s)) {
return user;
}
}
return null;
}
#Override
public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object o) {
if(o instanceof UserBean) {
UserBean user = (UserBean)o;
return user.getId().toString();
}
return "";
}
}
The problem is here. Look closer. This is invalid EL syntax.
value="#{plannedActivityController.newActivity.organiser}}"
This should however have thrown a PropertyNotWritableException on submit something like this:
javax.el.PropertyNotWritableException: /test.xhtml #25,39 value="#{plannedActivityController.newActivity.organiser}}": Illegal Syntax for Set Operation
at com.sun.faces.facelets.el.TagValueExpression.setValue(TagValueExpression.java:136)
at javax.faces.component.UIInput.updateModel(UIInput.java:822)
at javax.faces.component.UIInput.processUpdates(UIInput.java:739)
at javax.faces.component.UIForm.processUpdates(UIForm.java:281)
at javax.faces.component.UIComponentBase.processUpdates(UIComponentBase.java:1244)
at javax.faces.component.UIComponentBase.processUpdates(UIComponentBase.java:1244)
at javax.faces.component.UIViewRoot.processUpdates(UIViewRoot.java:1223)
at com.sun.faces.lifecycle.UpdateModelValuesPhase.execute(UpdateModelValuesPhase.java:78)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:593)
This exception should have been logged to the server log. By default this will however not end up in an error page for the enduser because JSF/PrimeFaces have by default no form of feedback to enduser in case of exceptions which are thrown during ajax requests. You should however be able to see it in actual ajax response body in webbrowser's builtin HTTP traffic monitor.
The JSF utility library OmniFaces offers a FullAjaxExceptionHandler for the very problem of total absence of feedback on exceptions during ajax requests. You may find it useful. When I tried to recreate your problem, I was been presented a clear error page so that I don't need to dig in server log or HTTP traffic monitor for clues.

Can't get all values from web page

I am working with JSF 2 and Hibernate. I have XHTML page where bean is get from database and inserted into page. When I change the values of bean and submit for savign I am getting only the last value. What I making wrong?
Here is my XHTML page form code:
<h:form id="edit-slide-form">
<h:panelGrid columns="2">
<h:outputText value="#{msgs.slideTitle}" styleClass="label"/>
<h:inputText id="title" value="#{sliderAction.newSlide.title}" name="#{sliderAction.slide.title}" required="true"/>
<p></p><h:message for="title" errorClass="error"/>
<h:outputText value="#{msgs.description}" styleClass="label"/>
<h:inputTextarea id="description" value="#{sliderAction.newSlide.description}" name="#{sliderAction.slide.description}" required="true"/>
<p></p><h:message for="description" errorClass="error"/>
<p></p>
<h:commandButton value="#{msgs.save}" action="#{sliderAction.saveChanges}" styleClass="button"/>
<h:message for="edit-slide-form"/>
</h:panelGrid>
</h:form>
When I submit this form I can get only last value (e.g id="description").
SliderAction.class
#Named
#SessionScoped
public class SliderAction implements Serializable {
private static final long serialVersionUID = 1L;
private EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("iaau");
private EntityManager entityManager;
private int id;
private Integer coordinates[] = new Integer[4];
private Image image = new Image();
private Slide slide = new Slide();
private Slide newSlide = new Slide();
private List<Slide> slides = new ArrayList<Slide>();
private UploadedFile uploadedFile;
private boolean uploaded;
public String uploadSlideImage() {
ImageAction imageAction = new ImageAction();
if( uploaded ) {
imageAction.delete( image.getUniqueName(), image.getThumb() );
for( int i = 0; i < coordinates.length; i++ ) {
coordinates[i] = null;
}
}
imageAction.upload(uploadedFile, false);
setImage( imageAction.getImage() );
uploaded = true;
return null;
}
public String cropAndSave(){
ImageAction imageAction = new ImageAction();
imageAction.cropSlide( image, coordinates, 928, 318 );
newSlide = new Slide();
newSlide.setImage( image );
startTransaction();
entityManager.persist(image);
entityManager.persist(newSlide);
endTransaction();
closeTransaction();
uploaded = false;
setId(newSlide.getId());
return "edit";
}
public String saveChanges() {
startTransaction();
entityManager.merge(newSlide);
endTransaction();
closeTransaction();
return "list";
}
public void deleteSlide() {
startTransaction();
slide = (Slide)entityManager.find(Slide.class, id);
entityManager.remove(slide);
endTransaction();
closeTransaction();
ImageAction imageAtion = new ImageAction();
imageAtion.delete(slide.getImage().getUniqueName(), slide.getImage().getThumb());
}
public Slide getSlide() {
startTransaction();
slide = entityManager.find(Slide.class, id);
endTransaction();
closeTransaction();
return slide;
}
public void setSlide(Slide slide) {
this.slide = slide;
}
public Slide getNewSlide() {
startTransaction();
newSlide = entityManager.find(Slide.class, id);
endTransaction();
closeTransaction();
return newSlide;
}
public void setNewSlide(Slide newSlide) {
this.newSlide = newSlide;
}
#SuppressWarnings("unchecked")
public List<Slide> getSlides() {
startTransaction();
slides = entityManager.createQuery("from Slide").getResultList();
endTransaction();
closeTransaction();
return slides;
}
/*Declaration of EntityManager and other getter and setter methods*/
}
Slide.class
#Entity
public class Slide extends AbstractEntity<Integer>{
private static final long serialVersionUID = 1L;
private String title;
private String description;
private Image image;
private String link;
private Page page;
private boolean showOnFeed;
private boolean approved;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
#OneToOne
public Image getImage() {
return image;
}
public void setImage(Image image) {
this.image = image;
}
public String getLink() {
return link;
}
public void setLink(String link) {
this.link = link;
}
#OneToOne
public Page getPage() {
return page;
}
public void setPage(Page page) {
this.page = page;
}
#Column(name="show_on_feed")
public boolean isShowOnFeed() {
return showOnFeed;
}
public void setShowOnFeed(boolean showOnFeed) {
this.showOnFeed = showOnFeed;
}
public boolean isApproved() {
return approved;
}
public void setApproved(boolean approved) {
this.approved = approved;
}
}
Also I have notices this warning message:
[org.hibernate.ejb.internal.EntityManagerFactoryRegistry] (http-localhost-127.0.0.1-8080-1) HHH000436: Entity manager factory name (iaau) is already registered. If entity manager will be clustered or passivated, specify a unique value for property 'hibernate.ejb.entitymanager_factory_name'
can this cause such problems

Web API Filter stopped other filters from being hit

When I add the HttpHeader filter, the other filters stop working. Im not sure why.
BaseApiController.cs
[ExceptionHandling, ApiValidation, HttpHeader("X-Robots-Tag", "noindex, nofollow")]
public abstract class BaseApiController : System.Web.Http.ApiController
{
HttpHeaderFilter.cs
namespace Tournaments.Models.Mvc.Filters
{
public class HttpHeaderAttribute : System.Web.Http.Filters.FilterAttribute
{
public string Name { get; set; }
public string Value { get; set; }
public HttpHeaderAttribute(string name, string value)
{
Name = name;
Value = value;
}
public override bool AllowMultiple
{
get { return true; }
}
}
public class HttpHeaderFilter : System.Web.Http.Filters.IActionFilter
{
private readonly string _name;
private readonly string _value;
public HttpHeaderFilter(string name, string value)
{
_name = name;
_value = value;
}
public bool AllowMultiple
{
get { return true; }
}
public Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
return continuation().ContinueWith<HttpResponseMessage>((tsk) =>
{
HttpResponseMessage response = tsk.Result;
response.Headers.Add(_name, _value);
return response;
}, TaskContinuationOptions.OnlyOnRanToCompletion);;
}
}
}
Global.asax
protected override IKernel CreateKernel()
{
return Bootstrap.Configure((kernel) =>
{
kernel.Bind<IUnitOfWork>().To<UnitOfWork>();
kernel.Bind<IDatabaseFactory>().To<DatabaseFactory>().InRequestScope();
//kernel.Bind<IDatabaseFactory>().To<DatabaseFactory>().InScope(q => HttpContext.Current ?? StandardScopeCallbacks.Thread(q));
kernel.Bind<IEmailService>().To<EmailService>().WithConstructorArgument("templates", emailTemplates);
kernel.Bind<IPaymentMethod>().To<AuthorizeNetProvider>().Named("AuthorizeNet");
kernel.Bind<IPaymentMethod>().To<StripeProvider>().Named("Stripe");
kernel.Bind<List<IPaymentMethod>>().ToConstant(kernel.GetAll<IPaymentMethod>().ToList());
ServiceBindings.Register(kernel);
RepositoryBindings.Register(kernel);
ValidationBindings.Register(kernel);
kernel.BindHttpFilter<ApiValidationFilter>(System.Web.Http.Filters.FilterScope.Action)
.WhenControllerHas<ApiValidationAttribute>();
kernel.BindHttpFilter<HttpHeaderFilter>(System.Web.Http.Filters.FilterScope.Controller)
.WhenControllerHas<HttpHeaderAttribute>()
.WithConstructorArgumentFromControllerAttribute<HttpHeaderAttribute>("name", q => q.Name)
.WithConstructorArgumentFromControllerAttribute<HttpHeaderAttribute>("value", q => q.Value);
GlobalConfiguration.Configuration.Filters.Add(new ExceptionHandlingAttribute());
GlobalConfiguration.Configuration.DependencyResolver = new Common.Ioc.NinjectDependencyResolver(kernel);
GlobalConfiguration.Configuration.Formatters.Add(new CustomXmlFormatter());
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
});
}
I added this
TaskContinuationOptions.ExecuteSynchronously
Instead of
TaskContinuationOptions.OnlyOnRanToCompletion
and it works fine now.

java.lang.StackOverFlow in Primefaces's treeTable

I use this code:
JSF:
<p:treeTable id="treeSkill" value="#{skillManager.rootSkill}"
var="skill" selectionMode="single" widgetVar="skillsTreeTable"
style="border: 0;">
<p:ajax event="expand"
listener="#{skillManager.expandNodeListener}" />
<p:column> ..... </p:column>
<p/treeTable>
SkillManager:
#Named
#SessionScoped
public class SkillManager implements Serializable {
private static final long serialVersionUID = 1L;
private TreeNode rootSkill;
public SkillManager() {
initSkillTree();
}
public void expandNodeListener(NodeExpandEvent nee) {
TreeNode treeNode = nee.getTreeNode();
if (treeNode instanceof FetchChildren)
((FetchChildren) treeNode).fetchChildren();
if (treeNode instanceof LazySkillTreeNode)
((LazySkillTreeNode) treeNode).fetchSubchildren();
}
private void initSkillTree() {
rootSkill = new DefaultTreeNode("Root", null);
Skill realRootSkill = HrDaoFactory.getInstance().getSkillDAO().getRootSkill();
TreeNode realRootNode = new LazySkillTreeNode(realRootSkill, rootSkill);
for (Skill skill : realRootSkill.getChildrensSkills()) {
LazySkillTreeNode node = new LazySkillTreeNode(skill, realRootNode);
node.fetchChildren();
}
RequestContext.getCurrentInstance().update("woCatalogTabView:skillTreeForm");
}
}
LazySkillTreeNode:
public class LazySkillTreeNode extends LazyTreeNode implements FetchChildren {
private static final long serialVersionUID = 8856168173751148652L;
private boolean childrenFetched;
public LazySkillTreeNode(Object data, TreeNode parent) {
super(data, parent);
}
#Override
public void fetchChildren() {
if (childrenFetched)
return;
for (Skill skill : ((Skill) super.getData()).getChildrensSkills())
new LazySkillTreeNode(skill, this);
childrenFetched = true;
}
}
LazyTreeNode:
public abstract class LazyTreeNode extends DefaultTreeNode {
private static final long serialVersionUID = 8839307424434170537L;
private boolean subChildrenFetched;
public LazyTreeNode(Object data, TreeNode parent) {
super(data, parent);
}
public void fetchSubchildren() {
if (subChildrenFetched || isLeaf())
return;
List<TreeNode> treeNodeList = getChildren();
for (TreeNode node : treeNodeList) {
if (node instanceof FetchChildren)
((FetchChildren) node).fetchChildren();
}
subChildrenFetched = true;
}
}
Everything works fine, but if add/delete elements (after all this operations we call method initSkillTree() for rebuild tree) a lot of times, or if 2 or more users start to do it, we beginning to recieve in response from server this string:
<?xml version='1.0' encoding='UTF-8'?>
<partial-response><error><error-name>class java.lang.StackOverflowError</error-name><error-message><![CDATA[]]></error-message></error></partial-response>
Other problem that i don't have any information about error. No information in log files. In server.log nothing to.
We use: JSF (Mojarra 2.14), Primefaces 3.41, JBOSS 7.
And in the end error was in Controller class where method:
public void addOrUpdateSkill(Skill skill) {
Session session = null;
try {
session = HibernateUtil.getCurrentSession();
session.beginTransaction();
session.saveOrUpdate(skill);
session.getTransaction().commit();
evictAllSkillsFromSession();
} catch (Throwable e) {
logger.fatal(skill, e);
if (session.getTransaction() != null && session.getTransaction().isActive())
session.getTransaction().rollback();
throw new RuntimeException(e);
}
}
and stack trace was appeared in the row "logger.fatal(skill, e);"
you must pass the error message by first argument instead of Entity object.
Error appear because of it's toString() method implementation of Skill class:
#Entity
#Table(name = "SKILLS", schema = AppData.HR_SCHEMA)
public class Skill implements Serializable {
private static final long serialVersionUID = -2728239519286686549L;
#Id
#SequenceGenerator(name = "SKILLS_ID_GENERATOR", sequenceName = AppData.HR_SCHEMA + ".SKILLS_ID_SEQ")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SKILLS_ID_GENERATOR")
private BigDecimal id;
#Column(name = "NAME_ENG")
private String nameEng;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "UPDATED_AT")
private Date updatedAt;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "UPDATED_BY", referencedColumnName = "USER_ID")
private User updatedBy;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "PARENT_ID")
private Skill parentSkill;
#OneToMany(mappedBy = "parentSkill", fetch = FetchType.LAZY, orphanRemoval = true)
private List<Skill> childrensSkills;
#Column(name = "DESCRIPTION")
private String description;
#OneToMany(orphanRemoval = true, mappedBy = "skill")
private List<SkillJoinedAction> skillJoinedActions;
#OneToMany(orphanRemoval = true, mappedBy = "skill")
private List<SkillJoinedEmployee> skillJoinedEmployees;
public Skill() {
}
public Skill(String nameEng, User updateBy, String description) {
this.nameEng = nameEng;
this.updatedBy = updateBy;
this.updatedAt = new Date();
this.setDescription(description);
}
public BigDecimal getId() {
return id;
}
public void setId(BigDecimal id) {
this.id = id;
}
public String getNameEng() {
return this.nameEng;
}
public void setNameEng(String nameEng) {
this.nameEng = nameEng;
}
public Date getUpdatedAt() {
return this.updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
public User getUpdatedBy() {
return updatedBy;
}
public void setUpdatedBy(User updatedBy) {
this.updatedBy = updatedBy;
}
public List<Skill> getChildrensSkills() {
return childrensSkills;
}
public void setChildrensSkills(List<Skill> childrensSkills) {
this.childrensSkills = childrensSkills;
}
public Skill getParentSkill() {
return parentSkill;
}
public void setParentSkill(Skill parentSkill) {
this.parentSkill = parentSkill;
}
#Override
public String toString() {
return "Skill [id=" + id + ", nameEng=" + nameEng + ", updatedAt=" + updatedAt + ", updatedBy=" + updatedBy + ", parentSkill="
+ parentSkill + ", childrensSkills=" + childrensSkills + "]";
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public List<SkillJoinedAction> getSkillJoinedActions() {
return skillJoinedActions;
}
public void setSkillJoinedActions(List<SkillJoinedAction> skillJoinedActions) {
this.skillJoinedActions = skillJoinedActions;
}
public List<SkillJoinedEmployee> getSkillJoinedEmployees() {
return skillJoinedEmployees;
}
public void setSkillJoinedEmployees(List<SkillJoinedEmployee> skillJoinedEmployees) {
this.skillJoinedEmployees = skillJoinedEmployees;
}
}
as you can see in method:
#Override
public String toString() {
return "Skill [id=" + id + ", nameEng=" + nameEng + ", updatedAt=" + updatedAt + ", updatedBy=" + updatedBy + ", parentSkill="
+ parentSkill + ", childrensSkills=" + childrensSkills + "]";
}
was called method toString() on parentSkill who in his turn call toString() on childrensSkills... infinite recursion.

Struts 2 jquery grid component with jquery grid column type list which takes list of objects

The problem I have is that I am trying to have a multi select data grid column which will have a List objects (productsEntitled).
I've got the products to display properly by providing a buildSelect custom function to populate my Edit Dialog Box when a user click edit on a record.
When I have the column with the multi select <sjg:gridColumn name="productsEntitledListModel on my grid, the save functionality does not work and does not save. I don't see any errors on the browser console nor on the java console.
Any help will be appreciated as I am unable to find out what the problem is, I de-compiled the entire show case jar and nothing that helps with this issue.
My model look like this:
#Entity
#Table ( name = "USERS")
public class User {
private Long id;
private String name;
private String username;
private String password;
private String sourceIp;
private Device device;
private List<Product> productsEntitled;
This is my grid on a jsp page:
<s:url id="remoteurl" action="loadUsersJson"/>
<s:url id="editurl" action="editGridUserEntry"/>
<s:url id="selectproductsurl" action="loadProductsJson"/>
<sjg:grid gridModel="users"
id="gridUsers"
dataType="json"
width="1150"
href="%{remoteurl}"
draggable="true"
pager="true"
resizable="true"
navigatorAddOptions="{height:525, width:425, readAfterSubmit:true, draggable:true, resizable:true}"
navigatorEditOptions="{height:525, width:425, reloadAfterSubmit:true, draggable:true, resizable:true}"
navigatorDeleteOptions="{height:200, width:200, reloadAfterSubmit:true, draggable:true, resizable:true}"
editurl="%{editurl}"
navigator="true"
navigatorEdit="true"
navigatorAdd="true"
navigatorView="true"
navigatorDelete="true"
rowList="10,15,20"
rowNum="15"
multiselect="false"
onSelectRowTopics="rowselect">
<sjg:gridColumn name="id" editable="true" index="id" hidden="true" key="true" title="ID"/>
<sjg:gridColumn name="name" index="name" editable="true" edittype="text" title="NAME"/>
<sjg:gridColumn name="sourceIp" index="sourceIp" editable="true" edittype="text" title="SOURCE IP"/>
<sjg:gridColumn name="username" index="username" editable="true" edittype="text" title="USERNAME"/>
<sjg:gridColumn name="password" index="password" editable="true" edittype="password" title="PASSWORD"/>
<sjg:gridColumn name="role" index="role" editable="true" edittype="select" editoptions="{value:'Admin:Admin;User:User;'}" title="ROLE"/>
<sjg:gridColumn name="deviceId" jsonmap="device.id" key="true" hidden="true" editable="text" title="DEVICE ID"/>
<sjg:gridColumn name="deviceIp" jsonmap="device.ip" editable="true" edittype="text" title="DEVICE IP"/>
<sjg:gridColumn name="productsEntitledListModel"
width="300"
editable="true"
edittype="select"
editoptions="{dataUrl: '%{selectproductsurl}', multiple:true, buildSelect:customBuildSelect}"
title="PRODUCTS"/>
</sjg:grid>
These are my action classes:
Show the grid:
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.interceptor.ServletRequestAware;
import com.opensymphony.xwork2.ActionSupport;
public class AdminAction extends ActionSupport implements ServletRequestAware {
private static final long serialVersionUID = -1090720652366248768L;
private static final Log logger = LogFactory.getLog(AdminAction.class);
private HttpServletRequest request;
private AuthenticationTicket ticket;
private AdminService adminService;
private List<User> users;
private List<Product> products;
//private List<String>productsAllList;
private String userId;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public List<Product> getProducts() {
return products;
}
public void setProducts(List<Product> products) {
this.products = products;
}
public AuthenticationTicket getTicket() {
return ticket;
}
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
public void setTicket(AuthenticationTicket ticket) {
this.ticket = ticket;
}
#Override
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
public String redirectUsersTab() {
return "users";
}
public String redirectProductsTab() {
return "products";
}
private void initAdminService () {
logger.debug("initAdminService()...");
if (adminService == null) {
adminService = (AdminService)ServiceFinder.getContext(request).getBean("adminService");
}
}
public String loadUsersJson() {
initAdminService();
this.users = adminService.getUsersAll();
return "success";
}
public String loadProductsJson() {
initAdminService();
this.products = adminService.getProductsAll();
return "success";
}
//TODO: clean up
public String getAllProductsList() {
logger.debug("testParam, userId: " + this.userId);
initAdminService();
List<Product> temp = adminService.getProductsAll();
if (userId != null) {
User userTemp = new User();
userTemp.setId(new Long(userId));
List<Product> prodEntitled = adminService.getProductsByUser(userTemp);
logger.debug("Products entitled: " + prodEntitled);
}
//TODO: merge prod and prod entitled to Model to populate the grid
products = temp;
/*
if (temp != null && temp.size() > 0) {
this.productsAllList = new ArrayList<String>();
for (Product p : temp) {
this.productsAllList.add(p.getName());
}
}
*/
//logger.debug("this.productsAllList: " + this.productsAllList);
return "success";
}
}
Class to edit the grid:
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.interceptor.ServletRequestAware;
import com.opensymphony.xwork2.ActionSupport;
public class EditUsersGridAction extends ActionSupport implements ServletRequestAware {
private static final Log logger = LogFactory.getLog(EditUsersGridAction.class);
private static final long serialVersionUID = -5485382508029951644L;
private HttpServletRequest request;
private AdminService adminService;
private String oper = "";
private Long id;
private String name;
private String sourceIp;
private String password;
private String username;
private Long deviceId;
private String deviceIp;
private String devicePortDescription;
private String devicePortLayer;
private String deviceType;
private List<Product>productsEntitled;
private List<GridColumnListModel> productsEntitledListModel;
private List<Product>productsAvailable;
public List<GridColumnListModel> getProductsEntitledListModel() {
try {
if (productsEntitled != null && productsEntitled.size() > 0) {
productsEntitledListModel = new ArrayList<EditUsersGridAction.GridColumnListModel>();
for (Product p : productsEntitled) {
GridColumnListModel tmp = new GridColumnListModel();
tmp.setName(p.getName());
tmp.setValue(p);
productsEntitledListModel.add(tmp);
}
return this.productsEntitledListModel;
}else {
return null;
}
} catch (Exception ex) {
logger.error("Exception in getProductsEntitledString", ex);
return null;
}
}
public void setProductsEntitledListModel(List<GridColumnListModel> productsEntitledListModel) {
this.productsEntitledListModel = productsEntitledListModel;
}
public Long getDeviceId() {
return deviceId;
}
public void setDeviceId(Long deviceId) {
this.deviceId = deviceId;
}
public String getDeviceIp() {
return deviceIp;
}
public void setDeviceIp(String deviceIp) {
this.deviceIp = deviceIp;
}
public String getDevicePortDescription() {
return devicePortDescription;
}
public void setDevicePortDescription(String devicePortDescription) {
this.devicePortDescription = devicePortDescription;
}
public String getDevicePortLayer() {
return devicePortLayer;
}
public void setDevicePortLayer(String devicePortLayer) {
this.devicePortLayer = devicePortLayer;
}
public String getDeviceType() {
return deviceType;
}
public void setDeviceType(String deviceType) {
this.deviceType = deviceType;
}
public String getOper() {
return oper;
}
public void setOper(String oper) {
this.oper = oper;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSourceIp() {
return sourceIp;
}
public void setSourceIp(String sourceIp) {
this.sourceIp = sourceIp;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public List<Product> getProductsEntitled() {
return productsEntitled;
}
public void setProductsEntitled(List<Product> productsEntitled) {
this.productsEntitled = productsEntitled;
}
public List<Product> getProductsAvailable() {
return productsAvailable;
}
public void setProductsAvailable(List<Product> productsAvailable) {
this.productsAvailable = productsAvailable;
}
public HttpServletRequest getRequest() {
return request;
}
public void setRequest(HttpServletRequest request) {
this.request = request;
}
#Override
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
private void initAdminService () {
logger.debug("initAdminService()...");
if (adminService == null) {
adminService = (AdminService)ServiceFinder.getContext(request).getBean("adminService");
}
}
public String execute() throws Exception {
logger.debug("#### IN EditGridUsersAction ###");
initAdminService();
productsAvailable = adminService.getProductsAll();
User user = new User();
user.setName(name);
user.setPassword(password);
user.setUsername(username);
user.setSourceIp(sourceIp);
Device device = new Device();
device.setId(deviceId);
device.setIp(deviceIp);
device.setPortDescription(devicePortDescription);
device.setType(deviceType);
user.setDevice(device);
if (id != null) {
user.setId(id);
}
if (oper.equalsIgnoreCase("add")) {
logger.debug("products selected");
//user.setProductsEntitled(adminService.getProductsAll());
adminService.addUser(user);
} else if ( oper.equalsIgnoreCase("edit")) {
//TODO: convert model to products and add to user
logger.debug("now in edit");
List<Product> tempProd = new ArrayList<Product>();
for (GridColumnListModel gridColModel : productsEntitledListModel) {
tempProd.add(gridColModel.getValue());
}
user.setProductsEntitled(tempProd);
adminService.updateUser(user);
//return "input";
}else if (oper.equalsIgnoreCase("del")) {
logger.debug("in delete");
adminService.deleteUser(user);
}
return "success";
}
public class GridColumnListModel {
private String name;
private Product value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Product getValue() {
return value;
}
public void setValue(Product value) {
this.value = value;
}
}
}

Resources