I have an enum:
public enum Roles {
USER,ADMIN;
}
And a CheckBoxGroup on my form:
private CheckBoxGroup<String> rolesCheckbox = new CheckBoxGroup<>("User roles");
Instance of a User class binded to this form has a field:
private List<Roles> roles;
And what I want to do now is just check correct checkboxes when opening form for a specific User instance. I probably has to do something insinde binder.forField but I don't really know how:
binder.forField(rolesCheckbox).bind(...
The example from the Vaadin docs uses String for brevity, but you can easily use objects. Suppose you have a Role enum and a User like below:
// possible roles
public enum Role {
ADMIN, USER, GUEST
}
// bean for binding
public class User {
private Set<Role> roles;
private String name;
public User(String name, Set<Role> roles) {
this.roles = roles;
this.name = name;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return "User{" +
"roles=" + roles +
", name='" + name + '\'' +
'}';
}
}
... you could create your form similar to:
public class CheckboxEnumBinding extends VerticalLayout {
public CheckboxEnumBinding() {
// form components
TextField nameTextField = new TextField("Name");
CheckBoxGroup<Role> roleCheckBoxGroup = new CheckBoxGroup<>("Roles", DataProvider.ofItems(Role.values()));
// binder setup
Binder<User> userBinder = new Binder<>();
// name binding
userBinder.forField(nameTextField)
.asRequired("Please provide a user name")
.bind(User::getName, User::setName);
// role binding
userBinder.forField(roleCheckBoxGroup)
.asRequired("Please select at least one role")
.bind(User::getRoles, User::setRoles);
// bind to bean
userBinder.setBean(new User("Morfic", EnumSet.of(Role.ADMIN, Role.GUEST)));
// simulate a save action
Button saveButton = new Button("Save", event -> Notification.show("Saved new user info: " + userBinder.getBean()));
// add fields to the UI
addComponents(nameTextField, roleCheckBoxGroup, saveButton);
}
}
Result:
Bonus: By default Vaadin will use the enum name to display the values. If you want something different, like capitalizing only the first letter you can use an ItemCaptionGenerator:
// spring boot app, don't reinvent the wheel
import org.springframework.util.StringUtils;
...
roleCheckBoxGroup.setItemCaptionGenerator(role -> StringUtils.capitalize(role.name().toLowerCase()));
Related
I have to create a very simple Spring "market" app.
No front-end needed
The Market:
The system must operate as a simplified market where users can be buyers or sellers.
Users:
user entity attributes: id:1, username:"User1", account:0
//account just gets incremented with each entry in the database.
The users can buy and sell items.
Items:
item entity attributes: id:3, name:Item1, ownerId:1.
example for interacting with items endpoints:
create: {id:1 name:"Item1", ownerId:1};
getAllItems with ownerId = 1 (use single query)
[
{
"id":3,
"name":”Item1”,
"ownerId":1,
“ownerUsername”:"User1"
}
]
Example:
"User1" owns "Item1". He wants to sell it for $100. He creates an active contract. Other users can review all active contracts and choose to participate. "User2" has enough money in her account and buys "Item1". The contract is now closed. "User1" receives $100 in his account. "User2" is the new owner of "Item1".
Contracts:
contract entity attributes: id, sellerId, buyerId, itemId, price,status. (The seller is the owner of the item and can not be the buyer)
endpoints - CRUD. Example for interacting with contracts endpoints:
create: {itemId : 3, price : 100}. Expected behavior: find the owner of item with id 3 in the DB (ownerId = 1) persist the new active contract in the DB:
{
"sellerId":1,
"itemId":3,
"price":100,
"active":true
}
update price of active contract by id: {"itemId":3, "price":200}
getAllActive contracts (use single native query):
[
{
"sellerId":1,
“sellerUsername”:"User1",
"itemId":3,
"price":200,
"active":true
}
]
closing active contract by id {"itemId":3, "buyerId":2}.
Expected behavior: update the accounts of users with id 1 and id 2.
getAllClosed contracts by optional parameters: itemId, sellerId, buyerId (use single native query):
[
{
"sellerId":1,
“sellerUsername”:"User1",
"buyerId":2,
“buyerUsername”:"User2",
"itemId":3,
"price":100,
"active":false
}
]
So far, these are my Entities:
BaseEntity:
#MappedSuperclass
public abstract class BaseEntity {
private Long id;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
Users:
#Entity
#Table(name = "users")
public class User extends BaseEntity{
private String username;
private Long account;
private Set<Item> items;
public User() {
}
#Column(name = "username", nullable = false)
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
#Column(name = "account", nullable = false)
public Long getAccount() {
return account;
}
public void setAccount(Long account) {
this.account = account;
}
#OneToMany(mappedBy = "id")
public Set<Item> getItems() {
return items;
}
public void setItems(Set<Item> items) {
this.items = items;
}
}
Items:
#Entity
#Table(name = "items")
public class Item extends BaseEntity{
private String name;
private String ownerUsername;
private User user;
public Item() {
}
#Column(name = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//get the id of the item's owner
#ManyToOne
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getOwnerUsername() {
return user.getUsername();
}
public void setOwnerUsername(String ownerUsername) {
this.ownerUsername = ownerUsername;
}
}
So, what should I do from here on?
If you've already created persistence layers (using Spring Data JPA or another mapper), You need to develop service logic and create a presentation layer.
like this (just user domain)
UserService (service layer)
#Service
#RequiredArgsConstructor
public class UserService {
private final UserJpaRepository repository;
#Transactional
public Long createUser(String username) {
User user = new User();
user.setUsername(username);
// other logic ...
repository.save(user);
return user.getId();
}
#Transactional(readonly = true)
public User getUser(Long id) {
return repository.findById(id)
.orElseThrow(() -> IllegalArgumentsException("Not Found Entity."))
}
}
UserAPIController (presentation layer)
#RestController
#RequiredArgsConstructor
public class UserAPIController {
private final UserService userService;
#PostMapping("/users")
public ResponseEntity<Long> createUser(#RequestBody CreateUserDTO dto) {
Long userId = userService.createUser(dto.getUsername());
return new ResponseEntity(userId, HttpStatus.CREATED);
}
#GetMapping("/users/{id}")
public ResponseEntity<User> getUser(#PathVariable Long id) {
User user = userService.getUser(id);
return new ResponseEntity(user, HttpStatus.OK);
}
}
I'm still in the process of learning Java / spring and I think I'm getting better. Now at this point I'm able to build a rest api BUT I'm at a lost at how to ensure I've no concurrency issues . I've read many topics regarding making the API stateless or making my POJO's immutable but I'm sure if in my case below I need to. And if I did, I'm actually unsure how my code can function by making everything final in my POJO.
If someone could help me learn here I'd be VERY grateful. Thank you for your time
Below i have a POJO called User:
#Getter
#Setter
#Document(collection = "UserProfiles")
public class User {
#Id
#JsonIgnore
private String _id;
#JsonView({ProfileViews.Intro.class, ProfileViews.Full.class})
private String userId;
#JsonView({ProfileViews.Intro.class, ProfileViews.Full.class})
private String name;
#JsonView({ProfileViews.Intro.class, ProfileViews.Full.class})
private String displayName;
#DBRef
#JsonView({ProfileViews.Full.class})
private UserInterests personalInterests;
#DBRef
#JsonIgnore
private ProfileFollows profileFollowDetails;
}
#Getter
#Setter
#Document(collection = "ProfileFollows")
public class ProfileFollows {
#Id
//Id of The Mongo Document
private String id;
//The Id of the User Profile who owns the document
private String userId;
//A list containing the Ids of the Users who have followed the Profile belonging to userId
private List<String> profileFollowedByUserIds;
//A list containing the Ids of the Profiles the current user has followed
private List<String> profileFollowingByUserList;
}
And here is my Service layer where I create and update the user
#Service
public class UserService {
#Autowired
UserDal userDal;
public User createNewUserAccount(String userId, String userName) {
//check If userId already in DB
if (checkIfUserIdExits(userId)) {
throw new UserAlreadyExistsException("Cannot create User with Id { " + userId + " }, a user with this Id already " +
"exists");
}
//Create a Empty / Base New User Object
User newUser = new User();
UserInterests userInterests = new UserInterests();
userInterests.setUserId(userId);
userInterests.setPersonalInterestsExtras(null);
userInterests.setCreatedDate(Instant.now());
userInterests.setLastUpdatedAt(Instant.now());
userInterestsDAL.save(userInterests);
newUser.setPersonalInterests(userInterests);
ProfileFollows userProfileFollows = new ProfileFollows();
userProfileFollows.setUserId(userId);
userProfileFollows.setProfileFollowedByUserIds(new ArrayList<>());
userProfileFollows.setProfileFollowingByUserList(new ArrayList<>());
newUser.setProfileFollowDetails(profileFollowsDAL.save(userProfileFollows));
newUser.setUserId(userId);
newUser.setDisplayName(generateUserDisplayName(userName));
newUser.setCreatedDate(Instant.now());
newUser.setLastUpdatedAt(Instant.now());
//save the new User Profile to the DB
return userDal.save(newUser);
}
Here is my UserDAL:
public interface UserDal {
/**
* Method to check if a user exists with a given user Id
* #param Id -- Id of user to look up where id is a string
* #return
*/
Boolean existsById(String Id);
/**
* Method to save a user to the DB
* #param user -- User object to save to the DB
* #return
*/
User save(User user);
}
My User Repository / DALImpl:
#Repository
public class UserDALImpl implements UserDal {
private final MongoTemplate mongoTemplate;
#Autowired
public UserDALImpl(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
#Override
public User save(User user) {
return mongoTemplate.save(user);
}
And lastly my controller:
#RestController
#RequestMapping("/profile")
public class CreateProfileController {
#Autowired
public CreateProfileController() {
}
#Autowired
UserService userService;
#ApiOperation(value = "Allows for the creation of a user Profile")
#PostMapping("/create")
public User createUserProfile(#RequestParam(name = "userId") String userId,
#RequestParam(name = "displayName", required = true, defaultValue = "AnonymousDev") String displayName) {
if (userId.equals("")) throw new BadRequestException("UserId cannot be blank");
if (userService.checkIfUserIdExits(userId)) {
throw new UserAlreadyExistsException("Unable to create user with Id { " + userId + " }, the " +
"userId already exists");
}
return userService.createNewUserAccount(userId, displayName);
}
}
I'm currently developing a web application in Java which will be accessed by multiple users at the same time and as such need to store userdata in order to tailor the application to their individual requirements (such as what company they are apart of etc).
There are 2 classes that i use to manage this. User, MainSystem detailed below:
User
#Entity
public class User implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
private String username;
private String password;
private String type;
private String company;
private String DOB;
private String email;
private int PayrollId;
public User(String firstName, String lastName, String username, String password, String type, String company, String DOB, String email) {
this.firstName = firstName;
this. lastName = lastName;
this.username = username;
this.password = password;
this.type = type;
this.company = company;
this.DOB = DOB;
this.email = email;
}
MainSystem:
public class MainSystem {
public UserController userController;
private UserRepository userRepository;
private static ThreadLocal<User> loggedInUser = new ThreadLocal<>();
public DbController dbController;
public MainSystem(){
userController = new UserController(userRepository);
loggedInUser.set(new User());
}
public Boolean Login(String username, String password) {
if(userController.checkUser(username,password)){
User aUser = userController.getUser(username);
setLoggedInUser(userController.getUser(username));
VaadinSession.getCurrent().setAttribute("username",loggedInUser.get().getUsername());
System.out.println("Logged in User: "+loggedInUser.get().getUsername());
return true;
}
else {
return false;
}
}
public static void setLoggedInUser(User user){
loggedInUser.set(user);
}
public static User getLoggedInUser() {
return loggedInUser.get();
}
Ideally what i'd like to do is access the ThreadLocal variable from another class, for instance the ViewProfile.View:
public class EditProfileView extends VerticalLayout implements View {
MainSystem main = new MainSystem();
NavigatorUI aUI = new NavigatorUI();
User aUser = main.getLoggedInUser();
TextField username = new TextField("Username");
TextField Id = new TextField("Id");
TextField email = new TextField("Email");
TextField firstName = new TextField("First name");
TextField lastName = new TextField("Last name");
TextField type = new TextField("Type");
PasswordField oldPassword = new PasswordField("Current Password");
PasswordField changePassword1 = new PasswordField("New Password");
PasswordField changePassword2 = new PasswordField("Confirm Password");
private UserController userController;
private UserRepository userRepository;
public EditProfileView() {
setDefaultComponentAlignment(Alignment.MIDDLE_CENTER);
userController = new UserController(userRepository);
setStyleName("backgroundImage");
setMargin(true);
setSizeFull();
addComponent(aUI.getHeader());
FormLayout content = new FormLayout(generateInfo());
Panel aPanel = new Panel("Edit User",content);
aPanel.setWidthUndefined();
content.setMargin(true);
aPanel.setStyleName(ValoTheme.PANEL_WELL);
addComponent(aPanel);
}
#Override
public void enter(ViewChangeListener.ViewChangeEvent event) {
try {
aUser = main.getLoggedInUser();
System.out.println( aUser.getUsername());
Id.setValue(aUser.getId().toString());
username.setValue(aUser.getUsername());
firstName.setValue(aUser.getFirstName());
lastName.setValue(aUser.getLastName());
type.setValue(aUser.getType());
email.setValue(aUser.getEmail());
aUI.setUserMenu();
aUI.refreshPayroll();}
catch (Exception e){
System.out.println(e.getMessage());
}
}}
However, i'm finding that it is presenting me with a "null" value? I fear i may have missunderstood how ThreadLocal works. but essentially what i'm trying to achieve is to Store an instance relevant variable of the User.Class in MainSystem for other classes to use?
Any help would be appreciated.
My Solution:
My solution to this was to store the User.class in a VaadinSession Attribute like so:
public Boolean Login(String username, String password) {
if(userController.checkUser(username,password)){
User aUser = userController.getUser(username);
VaadinSession.getCurrent().setAttribute("user",aUser);
VaadinSession.getCurrent().setAttribute("username",loggedInUser.get().getUsername());
System.out.println("Logged in User: "+loggedInUser.get().getUsername());
return true;
}
else {
return false;
}
}
Then in other classes if i want to use that attribute i retrieved it like so:
#Override
public void enter(ViewChangeListener.ViewChangeEvent event) {
try {
aUser = (User)VaadinSession.getCurrent().getAttribute("user");
}
catch (Exception e){
System.out.println(e.getMessage());
}
}
The problem is that there's no guarantee that MainSystem.Login() and EditProfileView.enter() will happen on the same thread. Every user action is processed as a separate HTTP request that the servlet container will run on any available thread.
For this kind of functionality, I would instead recommend storing the user information in the HTTP session. If you don't have any custom servlets or such, you could instead have a an field that contains the user object in your own custom UI class. Vaadin takes care of making UI.getCurrent() always return the right value in all code that is run through Vaadin.
If you instead also are integrating with other servlet functionality, you could store the user in the HttpSession instead. Generic servlet code can find the session through the getSession() method in e.g. servlet requests and response. In code run by Vaadin, you can use VaadinSession().getCurrent().getSession() to get a WrappedSession instance that is based on to the same HttpSession data.
There is also another approach. Instead it could be possible to make #SessionScoped user service, and either #Inject or #Autowire that depending whether you are using CDI or Spring. When doing this way, it will be the bean manager that takes care of binding correct entity with your view.
How to bind from Vaadin RadioButtonGroup country to Country country DAO object? Country is enum and DateField(vaadin) to LocalDate(DAO)
public class Person {
#Id
String id;
LocalDate dateBorn;
Country country;
......
public class PersonFormUI extends GridLayout {
RadioButtonGroup<Country> country;
DateField dateBorn;
........
public enum Country {
EN, DE, IT }
with this binding all fields are binded and runs well but how to bind enums and dates withConverter?
binder.bindInstanceFields(this);
binder.setBean(personDAO);
If I may add a couple of suggestions before going straight to code:
If you want a nicely aligned form you can use FormLayout
I'd suggest using a ComboBox instead of RadioButtonGroup to display the countries, as it uses less space and also allows you to quickly find what you want by typing in it. But if you really want the radio, replace the combo line with RadioButtonGroup<Country> countries = new RadioButtonGroup<>("Country", DataProvider.ofItems(Country.values()));
You can find below a sample of binding based on the suggestions above and Vaadin 8.3.1. I don't see the need for any conversions, since the framework will take care of it for you.
The only extra thing I did, was to add an ItemCaptionGenerator for the combo to display the full country name instead of the default, which uses the Enum name (same thing can be used with RadioButtonGroup).
Code:
public class PersonForm extends FormLayout {
public PersonForm() {
// form components
DateField birthDate = new DateField("Birth date");
ComboBox<Country> countries = new ComboBox<>("Country", Arrays.asList(Country.values()));
// use full country name instead of ugly Enum name
countries.setItemCaptionGenerator(Country::getFullName);
// do not allow the user to select "nothing"
countries.setEmptySelectionAllowed(false);
// binder setup
Binder<Person> userBinder = new Binder<>();
// birth date binding
userBinder.forField(birthDate)
.asRequired("Please provide a birth date")
.bind(Person::getDateBorn, Person::setDateBorn);
// country binding
userBinder.forField(countries)
.asRequired("Please select the country of residence")
.bind(Person::getCountry, Person::setCountry);
// bind to bean with some existing value (eg, loaded from DB for editing)
userBinder.setBean(new Person(LocalDate.now(), Country.RO));
// simulate a save action
Button saveButton = new Button("Save", event -> Notification.show("Saved new user info: " + userBinder.getBean()));
// add fields to the UI
addComponents(birthDate, countries, saveButton);
}
// beans
public class Person {
private LocalDate dateBorn;
private Country country;
public Person(LocalDate dateBorn, Country country) {
this.dateBorn = dateBorn;
this.country = country;
}
public LocalDate getDateBorn() {
return dateBorn;
}
public void setDateBorn(LocalDate dateBorn) {
this.dateBorn = dateBorn;
}
public Country getCountry() {
return country;
}
public void setCountry(Country country) {
this.country = country;
}
#Override
public String toString() {
return "Person{" +
"dateBorn=" + dateBorn +
", country=" + country +
'}';
}
}
public enum Country {
RO("Romania"), DE("Deutschland"), IT("Italy");
private String fullName;
Country(String fullName) {
this.fullName = fullName;
}
public String getFullName() {
return fullName;
}
}
}
Result:
You can go even further and disable the button based on the binder status (maybe you want to use the same form to add new people where there are no initial values):
// disable saving until all required data is available
userBinder.addStatusChangeListener(event -> saveButton.setEnabled(userBinder.isValid()));`
Result:
I have an entity called Checkout which has a user and an item. So when a user wants to create a new checkout object, they POST to "/checkout". Now, I was thinking to set the user and item, the user would instead include a username and a serial number, like this for example:
{
"id": "1",
"user": "hassan",
"item": "sdf2ljt234jt09jsd"
}
Then I would just write custom setters in my Checkout class that take Strings rather than Users and Items:
public void setUser(String username) {
this.user = ...
}
But Jersey is never calling my setter. Both the user and the item properties remain null, and I'm not sure why. If this isn't the proper way to set objects with Jersey/Jackson, is there something else I should be trying?
You can use #JsonDeserialize to specify a builder class that can hold the custom logic for going from a String to a User or Item:
#JsonDeserialize(builder = Checkout.Builder.class)
public class Checkout {
private Long id;
private User user;
private Item item;
// getters and setters
public static class Builder {
private Long id;
private String username;
private String serialNumber;
#JsonProperty("id")
public Builder setId(Long id) {
this.id = id;
return this;
}
#JsonProperty("user")
public Builder setUsername(String username) {
this.username = username;
return this;
}
#JsonProperty("item")
public Builder setSerialNumber(String serialNumber) {
this.serialNumber = serialNumber;
return this;
}
public Checkout build() {
Checkout checkout = new Checkout();
checkout.setId(id);
checkout.setUser(/* lookup user by username */);
checkout.setItem(/* lookup item by serialNumber */);
return checkout;
}
}
}
Notice the use of #JsonProperty on each of the setters of the Builder class and how they use the names of the properties in your JSON. That's important to make sure your JSON structure maps properly into the builder fields.