So.. I have a method that consumes multipart/form-data. I'm trying to pass an object named User (ignoring some fields), and user avatar file
#ExecuteOn(TaskExecutors.IO)
#Operation(summary = "Endpoint for user registration")
#Post(uri = "/register",consumes = {MediaType.MULTIPART_FORM_DATA},produces = MediaType.APPLICATION_JSON)
#Requires(bean = User.class)
public HttpResponse<DefaultAppResponse> register(
#Part("credential") User credential,
#Part("avatar") #Nullable CompletedFileUpload avatar
){
try {
if(avatar != null) {
Files saved = filesService.save(avatar, dirPattern + avatars);
saved.setOid(transactionalRepository.genOid().orElseThrow());
filesRepository.save(saved);
credential.setAvatarPath(saved);
}
credential.setUserRegDate(new Date(System.currentTimeMillis()));
userRepository.save(credential);
return HttpResponse.ok(
errorService.success()).status(201
);
} catch (Exception e) {
registerLog.error(e.getMessage(), e.getStackTrace());
throw new InternalExceptionResponse("Error: " +e.getMessage() , errorService.error("error: " +e.getMessage()));
}
}
User.class
#Entity
#Table(name = "users", schema = "public")
#Introspected
#JsonView(Default.class)
public class User extends BaseEntity{
#JsonInclude
#JsonProperty("user_name")
#Column(name = "user_name")
#JsonAlias("userName")
private String userName;
#JsonInclude
#JsonProperty("user_birthday")
#Column(name = "user_birthday")
#JsonAlias("userBirthday")
#JsonFormat(pattern = "yyyy-MM-dd")
private Date userBirthday;
#JsonInclude
#JsonFormat(pattern = "yyyy-MM-dd")
#JsonProperty("user_reg_date")
#Column(name = "user_reg_date")
#JsonAlias("userRegDate")
private Date userRegDate;
#JsonInclude
#JsonProperty("user_email")
#Column(name = "user_email")
#JsonAlias("userEmail")
private String userEmail;
#JsonProperty("user_password")
#Column(name = "user_password")
#JsonAlias("userPassword")
#JsonView(WithPassword.class)
private String userPassword;
#ManyToOne
#JoinColumn(name = "avatar_path")
#JsonProperty("avatar_path")
#JsonInclude
#JsonAlias("avatarPath")
private Files avatarPath;
#JsonInclude
#JsonProperty("user_phone_number")
#Column(name = "user_phone_number")
#JsonAlias("userPhoneNumber")
private String userPhoneNumber;
#JsonInclude
#JsonProperty("user_is_confirm")
#Column(name = "user_is_confirm")
#JsonAlias("userIsConfirm")
private Boolean userIsConfirm;
public User(
String oid, String userName,
Date userBirthday, Date userRegDate, String userEmail, String userPassword, Files avatarPath,
String userPhoneNumber, Boolean userIsConfirm
) {
super(oid);
this.userName = userName;
this.userBirthday = userBirthday;
this.userRegDate = userRegDate;
this.userEmail = userEmail;
this.userPassword = userPassword;
this.avatarPath = avatarPath;
this.userPhoneNumber = userPhoneNumber;
this.userIsConfirm = userIsConfirm;
}
public User() {
}
public Files getAvatarPath() {
return avatarPath;
}
public void setAvatarPath(Files avatarPath) {
this.avatarPath = avatarPath;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Date getUserBirthday() {
return userBirthday;
}
public void setUserBirthday(Date userBirthday) {
this.userBirthday = userBirthday;
}
public Date getUserRegDate() {
return userRegDate;
}
public void setUserRegDate(Date userRegDay) {
this.userRegDate = userRegDay;
}
public String getUserEmail() {
return userEmail;
}
public void setUserEmail(String userEmail) {
this.userEmail = userEmail;
}
public Boolean getUserIsConfirm() {
return userIsConfirm;
}
public void setUserIsConfirm(Boolean userIsConfirm) {
this.userIsConfirm = userIsConfirm;
}
public String getUserPassword() {
return userPassword;
}
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}
public String getUserPhoneNumber() {
return userPhoneNumber;
}
public void setUserPhoneNumber(String userPhoneNumber) {
this.userPhoneNumber = userPhoneNumber;
}
}
But, When I trying to transfer an object, it simply cannot accept / parse / map it (underline correctly) What do I get in response
Request(Swagger)
Response
{
"message": "Bad Request",
"_links": {
"self": {
"href": "/api/reg/register",
"templated": false
}
},
"_embedded": {
"errors": [
{
"message": "Required Part [credential] not specified",
"path": "/credential"
}
]
}
}
If I remove #Part("credentails"), then it will contain an entity with fields equivalent to null.
See screenshot
Debug Evaluation
In my research, I saw that type:application/json is not being passed.But avatar file has type:image/png. See CURL request(generated from swagger)
curl -X 'POST' \
'http://localhost:8080/api/reg/register' \
-H 'accept: application/json' \
-H 'Content-Type: multipart/form-data' \
-F 'credential={
"oid": "null",
"avatar_path": "null"
"user_reg_date": "null",
"user_name": "User FUllName"
"user_birthday": "2001-10-28",
"user_email": "email#gmail.com",
"user_password": "123123",
"user_phone_number": "some-valid-phone-number"
}' \
-F 'avatar=#sticker.png;type=image/png'
Question: what could be the problem?
Related
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.
I have the following nodes and relationships defined:
CarMaker and Models
A CarModel is made CarMaker in multiple years, and that is represented as a property of the MADE_IN relationship.
A CarModel is made by one CarMaker only.
A CarMaker can make multiple CarModels in multiple years.
I have defined the following Classes to represent the nodes: CarModel, CarMaker and the relationship MADE_IN
CarModel
#NodeEntity
public class CarModel {
private Long id;
private String name;
#Relationship (type="MADE_IN", direction = Relationship.UNDIRECTED)
private Set<MadeIn> madeIns = new HashSet<MadeIn>();
private Set<String> years = new HashSet<String>();
public CarModel() {
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void addMadeIn(MadeIn madeIn) {
System.out.println ("Found CarMaker: " + madeIn.getCarMaker());
this.madeIns.add(madeIn);
}
private Set<MadeIn> getMadeIn() {
return madeIns;
}
public Set<String> getYears() {
Iterator<MadeIn> itr = madeIns.iterator();
while (itr.hasNext()) {
years.add(((MadeIn) itr.next()).getYear());
}
Set<String> sortedYears = years.stream().collect(Collectors.toCollection(TreeSet::new));
return sortedYears;
}
}
CarMaker
public class CarMaker {
#GraphId private Long id;
private String name;
#Relationship (type="MADE_IN", direction = Relationship.UNDIRECTED)
private Set<CarModel> carModels = new HashSet<>();
public CarMaker() {
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<CarModel> getCarModels() {
return carModels;
}
public void setCarModels(CarModel carModel) {
carModels.add(carModel);
}
}
MADE_IN
#RelationshipEntity(type="MADE_IN")
public class MadeIn {
#GraphId private Long relationshipId;
#Property private String year;
#StartNode private CarMaker carMaker;
#EndNode private CarModel carModel;
public MadeIn() {
}
public MadeIn(CarMaker carMaker, CarModel carModel, String year) {
this.carMaker = carMaker;
this.carModel = carModel;
this.year = year;
}
public Long getRelationshipId() {
return relationshipId;
}
public void setCarMaker(CarMaker carMaker) {
this.carMaker = carMaker;
}
public CarMaker getCarMaker() {
return this.getCarMaker();
}
public void setCarModel(CarModel carModel) {
this.carModel = carModel;
}
public CarModel getCarModel() {
return this.getCarModel();
}
public String getYear() {
return year;
}
public void setYear(String year) {
this.year = year;
}
}
When I make a request to retrieve a CarModel, I receive a response with the details of that model and all years when it was manufactured:
{
"id": 260248,
"name": "Ulysse",
"years": [
"1994",
"1995",
"1996",
"1997",
"1998",
"1999",
"2000",
"2001",
"2004",
"2005",
"2006",
"2007",
"2008",
"2009",
"2010",
"2011",
"2012"
]
}
The problem is when I try to request the CarModels made by a CarMaker:
{
"id": 4152072,
"name": "BMW",
"carModels": []
}
I noticed that if I reverse the annotations #StartNode and #EndNode on the MadeIn class I get the information about the CarModels made by a CarMaker, however I will not longer get the information about the years when those models were made.
{
"id": 4152072,
"name": "BMW",
"carModels": [
{
"id": 260852,
"name": "120",
"years": []
},
{
"id": 261430,
"name": "Z18",
"years": []
},
{
"id": 262044,
"name": "L7",
"years": []
},
Any idea on what am I missing, or what I am doing wrong ?
Thanks in advance for any help.
--MD
When I Edit the user all are saving but when I change the password it is getting the error.Please help me.
NullPointerException occurred when processing request: [POST] /openbrm /user/save
Stacktrace follows:
java.lang.NullPointerException
at com.sapienter.jbilling.client.authentication.CompanyUserDetails.getPlainUsername(CompanyUserDetails.java:84)
at com.sapienter.jbilling.client.authentication.JBillingPasswordEncoder.isPasswordValid(JBillingPasswordEncoder.java:75)
at com.sapienter.jbilling.client.user.UserHelper.bindPassword(UserHelper.groovy:155)
at jbilling.UserController.save(UserController.groovy:304)
at grails.plugin.springsecurity.web.filter.GrailsAnonymousAuthenticationFilter.doFilter(GrailsAnonymousAuthenticationFilter.java:53)
at grails.plugin.springsecurity.web.authentication.logout.MutableLogoutFilter.doFilter(MutableLogoutFilter.java:82)
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)
UserController.groovy
def save () {
UserWS user = new UserWS()
user.mainRoleId= Constants.TYPE_ROOT
UserHelper.bindUser(user, params)
def contacts = []
def userId= params['user']['userId'] as Integer
log.debug "Save called for user ${userId}"
def oldUser = userId ? webServicesSession.getUserWS(userId) : null
def company_id = session['company_id']
def company = CompanyDTO.createCriteria().get {
eq("id", company_id)
fetchMode('contactFieldTypes', FM.JOIN)
}
if ( !oldUser || SpringSecurityUtils.ifAllGranted('ROLE_SUPER_USER') || SpringSecurityUtils.ifAllGranted('MY_ACCOUNT_162') ) {
UserHelper.bindUser(user, params)
UserHelper.bindContacts(user, contacts, company, params)
} else {
user= oldUser
contacts= userId ? webServicesSession.getUserContactsWS(userId) : null
}
if ( !oldUser || SpringSecurityUtils.ifAllGranted('ROLE_SUPER_USER') || SpringSecurityUtils.ifAllGranted('MY_ACCOUNT_161') ) {
UserHelper.bindPassword(user, oldUser, params, flash)
} else {
user.password= null
}
UserDTO loggedInUser = UserDTO.get(springSecurityService.principal.id)
if (flash.error) {
user = new UserWS()
UserHelper.bindUser(user, params)
contacts = []
UserHelper.bindContacts(user, contacts, company, params)
render view: 'edit', model: [user: user, contacts: contacts, company: company, loggedInUser: loggedInUser, roles: loadRoles()]
return
}
try {
if (!oldUser) {
log.debug("creating user ${user}")
user.userId = webServicesSession.createUser(user)
flash.message = 'user.created'
flash.args = [user.userId as String]
} else {
log.debug("saving changes to user ${user.userId}")
webServicesSession.updateUser(user)
flash.message = 'user.updated'
flash.args = [user.userId as String]
}
// save secondary contacts
if (user.userId) {
contacts.each {
webServicesSession.updateUserContact(user.userId, it);
}
}
} catch (SessionInternalError e) {
flash.clear()
viewUtils.resolveException(flash, session.locale, e)
contacts = userId ? webServicesSession.getUserContactsWS(userId) : null
if(!contacts && !userId){
contacts = [user.getContact()]
}
render view: 'edit', model: [user: user, contacts: contacts, company: company, loggedInUser: loggedInUser, roles: loadRoles()]
return
}
if ( SpringSecurityUtils.ifAnyGranted("MENU_99") || SpringSecurityUtils.ifAnyGranted("ROLE_SUPER_USER") ) {
chain action: 'list', params: [id: user.userId]
} else {
chain action: 'edit', params: [id: user.userId]
}
}
In UserHelper.groovy it is getting the error at this method
static def bindPassword(UserWS newUser, UserWS oldUser, GrailsParameterMap params, flash) {
if (oldUser) {
// validate that the entered confirmation password matches the users existing password
if (params.newPassword) {
//read old password directly from DB. API does not reveal password hashes
def oldPassword = UserDTO.get(oldUser.userId).password
PasswordEncoder passwordEncoder = Context.getBean(Context.Name.PASSWORD_ENCODER)
//fake user details so we can verify the customers password
//should we move this to the server side validation?
CompanyUserDetails userDetails = new CompanyUserDetails(
oldUser.getUserName(), oldPassword, true, true, true, true,
Collections.EMPTY_LIST, null,null,oldUser.getUserId(), oldUser.getMainRoleId(), oldUser.getEntityId(),
oldUser.getCurrencyId(), oldUser.getLanguageId()
)
if (!passwordEncoder.isPasswordValid(oldPassword, params.oldPassword, userDetails)) {
flash.error = 'current.password.doesnt.match.existing'
return
}
} else {
newUser.setPassword(null)
}
}
// verify passwords only when new password is present
if (params.newPassword) {
if (params.newPassword == params.verifiedPassword) {
if (params.newPassword)
newUser.setPassword(params.newPassword)
} else {
flash.error = 'passwords.dont.match'
}
} else {
newUser.setPassword(null)
}
}
My CompanayUserDetails.java
package com.sapienter.jbilling.client.authentication;
import com.sapienter.jbilling.server.user.db.UserDTO;
import org.springframework.security.core.GrantedAuthority;
import grails.plugin.springsecurity.userdetails.GrailsUser;
import java.util.Collection;
import java.util.Locale;
public class CompanyUserDetails extends GrailsUser {
private final UserDTO user;
private final Locale locale;
private final Integer mainRoleId;
private final Integer companyId;
private final Integer currencyId;
private final Integer languageId;
public CompanyUserDetails(String username, String password, boolean enabled, boolean accountNonExpired,
boolean credentialsNonExpired, boolean accountNonLocked,
Collection<GrantedAuthority> authorities,
UserDTO user, Locale locale,
Integer id, Integer mainRoleId, Integer companyId, Integer currencyId, Integer languageId) {
super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities, id);
this.user = user;
this.locale = locale;
this.mainRoleId = mainRoleId;
this.companyId = companyId;
this.currencyId = currencyId;
this.languageId = languageId;
}
public UserDTO getUser() {
return user;
}
public String getPlainUsername() {
return user.getUserName();
}
public Locale getLocale() {
return locale;
}
public Integer getMainRoleId() {
return mainRoleId;
}
public Integer getUserId() {
return (Integer) getId();
}
public Integer getCompanyId() {
return companyId;
}
public Integer getCurrencyId() {
return currencyId;
}
public Integer getLanguageId() {
return languageId;
}
#Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("CompanyUserDetails");
sb.append("{id=").append(getId());
sb.append(", username=").append("'").append(getUsername()).append("'");
sb.append(", mainRoleId=").append(getMainRoleId());
sb.append(", companyId=").append(getCompanyId());
sb.append(", currencyId=").append(getCurrencyId());
sb.append(", languageId=").append(getLanguageId());
sb.append(", enabled=").append(isEnabled());
sb.append(", accountExpired=").append(!isAccountNonExpired());
sb.append(", credentialsExpired=").append(!isCredentialsNonExpired());
sb.append(", accountLocked=").append(!isAccountNonLocked());
sb.append('}');
return sb.toString();
}
}
Well you are passing null into the constructor for UserDTO
see
for
Collection<GrantedAuthority> authorities, UserDTO user, Locale locale,
you are passing
Collections.EMPTY_LIST, null,null
so of course getPlainUsername will fail
In your call to new CompanyUserDetails
CompanyUserDetails userDetails = new CompanyUserDetails(
oldUser.getUserName(), oldPassword, true, true, true, true,
Collections.EMPTY_LIST, null, <--- param $8 is null
And the definition
public CompanyUserDetails(String username, String password, boolean enabled, boolean accountNonExpired,
boolean credentialsNonExpired, boolean accountNonLocked,
Collection<GrantedAuthority> authorities,
UserDTO user, <--- param #8
And finally the NPE In your call to getPlainUsername
return user.getUserName();
NPE, can not invoke method on null user object.
So to understand your problem you really need to understand error codes:
java.lang.NullPointerException
at com.sapienter.jbilling.client.authentication.CompanyUserDetails.getPlainUsername(CompanyUserDetails.java:84)
According to my editor line 84 was
sb.append(", languageId=").append(getLanguageId());
I would suggest as a test set all these to a value
private final Integer mainRoleId=0;
private final Integer companyId=0;
private final Integer currencyId=0;
private final Integer languageId=0;
then change
this.mainRoleId = mainRoleId;
this.companyId = companyId;
this.currencyId = currencyId;
this.languageId = languageId
to
if (mainRoleId) { this.mainRoleId = mainRoleId;}
if (companyId) { this.companyId = companyId; }
if (currencyId) { this.currencyId = currencyId; }
if (languageId ) { this.languageId = languageId }
bad coding causes bad problems
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.
I am looking to extend the aspnet_membership in an MVC 3 application by storing extra member details in a separate model/table. I am not looking at using the ASP.NET ProfileProvider.
I would like to use the userId of a member as the primary/foreign key in the additional model/table. How can I achieve this? Is the example code along the right lines?
Thanks for any help.
public class Profile
{
[Key]
public Guid ProfileId { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public virtual MembershipUser User
{
get { return Membership.GetUser(ProfileId); }
}
public string FullName
{
get { return LastName + ", " + FirstName; }
}
}
That's what I do in my project, I have an other class wich is Member and inside I have the Email. I have a AuthenticationService that I use to sing in my user here's the code of this AuthenticationService...
In the web.config I have two differents connection string, one for the application BD and the other for the membership BD.
public class AuthenticationService : IAuthenticationService
{
private readonly IConfigHelper _configHelper;
private readonly ISession _session;
public AuthenticationService(IConfigHelper configHelper, ISession session)
{
_configHelper = configHelper;
_session = session;
}
public bool IsValidLogin(string email, string password)
{
CheckLocked(email);
return Membership.ValidateUser(email, password);
}
public void SignIn(string email, bool createPersistentCookie)
{
if (String.IsNullOrEmpty(email)) throw new ArgumentException("Value cannot be null or empty.", "email");
FormsAuthentication.SetAuthCookie(email, createPersistentCookie);
}
public void SignOut()
{
FormsAuthentication.SignOut();
}
public User GetLoggedUser()
{
var email = GetLoggedInUserName();
if (IsMember())
return _session.Single<Member>(x => x.Email == email);
return _session.Single<DelegateMember>(x => x.Email == email);
}
public string GetLoggedInUserName()
{
return Membership.GetUser() != null ? Membership.GetUser().UserName : string.Empty;
}
public MembershipCreateStatus RegisterUser(string email, string password, string role)
{
MembershipCreateStatus status;
//On doit laisser Guid.NewGuid().ToString() sinon ça ne passe pas
Membership.CreateUser(email, password, email, Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), true, out status);
if (status == MembershipCreateStatus.Success)
{
Roles.AddUserToRole(email, role);
}
return status;
}
public MembershipUserCollection GetAllUsers()
{
return Membership.GetAllUsers();
}
public string GeneratePassword()
{
var alphaCaps = "QWERTYUIOPASDFGHJKLZXCVBNM";
var alphaLow = "qwertyuiopasdfghjklzxcvbnm";
var numerics = "1234567890";
var special = "##$";
var allChars = alphaCaps + alphaLow + numerics + special;
var r = new Random();
var generatedPassword = "";
for (int i = 0; i < MinPasswordLength - 1; i++)
{
double rand = r.NextDouble();
if (i == 0)
{
//First character is an upper case alphabet
generatedPassword += alphaCaps.ToCharArray()[(int)Math.Floor(rand * alphaCaps.Length)];
//Next one is numeric
rand = r.NextDouble();
generatedPassword += numerics.ToCharArray()[(int) Math.Floor(rand*numerics.Length)];
}
else
{
generatedPassword += allChars.ToCharArray()[(int)Math.Floor(rand * allChars.Length)];
}
}
return generatedPassword;
}
public int MinPasswordLength
{
get
{
return Membership.Provider.MinRequiredPasswordLength;
}
}
public string AdminRole
{
get { return "admin"; }
}
public string MemberRole
{
get { return "member"; }
}
public string DelegateRole
{
get { return "delegate"; }
}
public string AgentRole
{
get { return "agent"; }
}
public bool Delete(string email)
{
return Membership.DeleteUser(email);
}
public bool IsAdmin()
{
return Roles.IsUserInRole(AdminRole);
}
public bool IsMember()
{
return Roles.IsUserInRole(MemberRole);
}
public bool IsDelegate()
{
return Roles.IsUserInRole(DelegateRole);
}
public bool IsAgent()
{
return Roles.IsUserInRole(AgentRole);
}
public bool ChangePassword(string email, string oldPassword, string newPassword)
{
if (String.IsNullOrEmpty(email)) throw new ArgumentException("Value cannot be null or empty.", "email");
if (String.IsNullOrEmpty(oldPassword)) throw new ArgumentException("Value cannot be null or empty.", "oldPassword");
if (String.IsNullOrEmpty(newPassword)) throw new ArgumentException("Value cannot be null or empty.", "newPassword");
// The underlying ChangePassword() will throw an exception rather
// than return false in certain failure scenarios.
try
{
var currentUser = Membership.Provider.GetUser(email, true);
return currentUser.ChangePassword(oldPassword, newPassword);
}
catch (ArgumentException)
{
return false;
}
catch (MembershipPasswordException)
{
return false;
}
}
public string ResetPassword(string email)
{
if (String.IsNullOrEmpty(email)) throw new ArgumentException("Value cannot be null or empty.", "email");
Unlock(email);
var currentUser = Membership.Provider.GetUser(email, false);
return currentUser.ResetPassword();
}
public bool CheckLocked(string email)
{
if (String.IsNullOrEmpty(email)) throw new ArgumentException("Value cannot be null or empty.", "email");
var currentUser = Membership.Provider.GetUser(email, false);
if (currentUser == null) return false;
if (!currentUser.IsLockedOut) return false;
if (currentUser.LastLockoutDate.AddMinutes(30) < DateTime.Now)
{
currentUser.UnlockUser();
return false;
}
return true;
}
public bool Unlock(string email)
{
if (String.IsNullOrEmpty(email)) throw new ArgumentException("Value cannot be null or empty.", "email");
var currentUser = Membership.Provider.GetUser(email, false);
if (currentUser == null) return false;
currentUser.UnlockUser();
return true;
}
}
I hope it can help!