Java8 Stream Conditional Handling - java-8

I have a list of User objects.
public class User{
private String name;
private String role;
private String status;
}
I wanted to use stream API to perform two operations.
See if list has User name as "Admin" if not throw custom exception (e.g UserNotFoundException)
If User name "Admin" is available in the list then mark the status of that user as "Active" and status of rest all users as "InActive".
How can i perform above operations in a concise way.

The below snippet should do what you expect
List<User> userList = Arrays.asList(new User("Admin", null, "Draft"),
new User("User", null, "Draft"));
userList.stream()
.filter(u -> u.name.equals("Admin"))
.peek(v -> userList.forEach(u -> u.status = u.name.equals("Admin") ? "Active" : "Inactive"))
.findAny()
.orElseThrow(IllegalArgumentException::new);
userList.forEach(System.out::println);
In the peek function, I am streaming the userList once more to set the status. Even if we used a loop, to ensure correctness, we need to iterate twice. First to need to determine if Admin exist and again to set status.
Output:
User{name='Admin', status='Active'}
User{name='User', status='Inactive'}

You could do this in two steps, 1. filter wi admin and throw an exception if does not match
2. Update the status
users.stream()
.filter(user -> user.name.equals("Admin"))
.findAny().orElseThrow(UserNotFoundException::new).setStatus("Active");
users.stream()
.filter(user -> !user.name.equals("Admin"))
.forEach(user -> user.setStatus("Inactive"));

Related

How to fetch list of objects with same phone number

I have an entity for driving_info with lot of fields but one of them is a phone number ( from which was ordered ).
What I am trying to do is to fetch all drives that were ordered from that number. But when I try to pass the int of phoneNumber I get
query did not return a unique result: 5; nested exception is javax.persistence.NonUniqueResultException: query did not return a unique result: 5
org.springframework.dao.IncorrectResultSizeDataAccessException: query did not return a unique result: 5; nested exception is javax.persistence.NonUniqueResultException: query did not return a unique result: 5
I actually want the list of results so that I can get a response of list of all drives that were ordered from that phone number.
My controller method is
#GetMapping("/users/{phone}")
public List<User> getUserByPhone(#PathVariable int phone) {
List<User> users= userService.findByPhone(phone);
if(users == null) {
throw new RuntimeException("User not found with "+phone+" phone number");
}
return users;
}
And my DAO is
#Override
#Transactional
public List<User> findByPhone(int phone) {
Session currentSession = entityManager.unwrap(Session.class);
Query<User> theQuery = currentSession.createQuery("from User where phone=:phone",User.class);
List<User> users = theQuery.getResultList();
return users;
}
Try to correct your query in this way:
List<User> users = currentSession.createQuery(
"select u from User u where u.phone = :phone",
User.class
).setParameter( "phone", phone )
.getResultList();
Please note that as it's stated in the documentation:
Even though HQL does not require the presence of a select_clause, it is generally good practice to include one. For simple queries the intent is clear and so the intended result of the select_clause is easy to infer. But on more complex queries that is not always the case.
It is usually better to explicitly specify intent. Hibernate does not actually enforce that a select_clause be present even when parsing JPQL queries, however, applications interested in JPA portability should take heed of this.
You need to call theQuery.list() instead.

Dealing with m-to-n relations in #RepositoryRestResource

Preface
I want to create a sub-resource of another resource in one call. These resources have a #ManyToMany relationship: Users and Groups.
I do not want to create first a user, then the group and after that the relation as it is shown in Working with Relationships in Spring Data REST - simply because I think a resource that cannot exist on its own, such as a group, should only be created if at least one user is also associated with that resource. For this I require a single endpoint like this one (which is not working for me, otherwise I wouldn't be here) that creates a group and also sets the associated "seeding" user in one transaction.
Currently, the only way to make this work for me is to "synchronize" the relation manually like this:
public void setUsers(Set<AppUser> users) {
users.forEach(u -> u.getGroups().add(this));
this.users = users;
}
this would allow me to
POST http://localhost:8080/groups
{
"name": "Group X",
"users": ["http://localhost:8080/users/1"]
}
but my problem with that is that this does not feel right to me - it does seem like a workaround and not the actual Spring-way to make this requirement work. So ..
I'm currently struggling with creating relational resources using Spring's #RepositoryRestResource. I want to create a new group and associate it with the calling user like this:
POST http://localhost:8080/users/1/groups
{
"name": "Group X"
}
but the only result is the response 204 No Content. I have no idea why. This may or may not be related to another question of mine (see here) where I try to achieve the same by setting the relating resource in the JSON payload - that doesn't work either.
Server side I am getting the following error:
tion$ResourceSupportHttpMessageConverter : Failed to evaluate Jackson deserialization for type [[simple type, class org.springframework.hateoas.Resources<java.lang.Object>]]: java.lang.NullPointerException
Please let me know in case you need any specific code.
Tried
I added exported = false to the #RepositoryRestResource of UserGroupRepository:
#RepositoryRestResource(collectionResourceRel = "groups", path = "groups", exported = false)
public interface UserGroupRepository extends JpaRepository<UserGroup, Long> {
List<UserGroup> findByName(#Param("name") String name);
}
and I am sending:
PATCH http://localhost:8080/users/1
{
"groups": [
{
"name": "Group X"
}
]
}
However, the result is still just 204 No Content and a ResourceNotFoundException on the server side.
Unit Test
Essentially, the following unit test is supposed to work but I can also live with an answer why this cannot work and which also shows how this is done correctly.
#Autowired
private TestRestTemplate template;
private static String USERS_ENDPOINT = "http://localhost:8080/users/";
private static String GROUPS_ENDPOINT = "http://localhost:8080/groups/";
// ..
#Test
#DirtiesContext(classMode = ClassMode.BEFORE_EACH_TEST_METHOD)
public void whenCreateUserGroup() {
// Creates a user
whenCreateAppUser();
ResponseEntity<AppUser> appUserResponse = template.getForEntity(USERS_ENDPOINT + "1/", AppUser.class);
AppUser appUser = appUserResponse.getBody();
UserGroup userGroup = new UserGroup();
userGroup.setName("Test Group");
userGroup.setUsers(Collections.singleton(appUser));
template.postForEntity(GROUPS_ENDPOINT, userGroup, UserGroup.class);
ResponseEntity<UserGroup> userGroupResponse = template.getForEntity(GROUPS_ENDPOINT + "2/", UserGroup.class);
Predicate<String> username = other -> appUser.getUsername().equals(other);
assertNotNull("Response must not be null.", userGroupResponse.getBody());
assertTrue("User was not associated with the group he created.",
userGroupResponse.getBody().getUsers().stream()
.map(AppUser::getUsername).anyMatch(username));
}
However, the line
userGroup.setUsers(Collections.singleton(appUser));
will break this test and return a 404 Bad Request.
According to SDR reference:
POST
Only supported for collection associations. Adds a new element to the collection. Supported media types:
text/uri-list - URIs pointing to the resource to add to the association.
So to add group to user try to do this:
POST http://localhost:8080/users/1/groups (with Content-Type:text/uri-list)
http://localhost:8080/groups/1
Additional info.

Get the groups of a user

I'm developping an application using spring-boot. I use Ldaptemplate API to communicate with Ldap repository. I need to retrieve user details and groups included (membreOf). This is a snippet of my code:
public Person findCustomerByUid(String uid, String orgUnit) throws InvalidNameException {
Person p = new Person();
p.setUid(uid);
p.setOu(orgUnit);
Name dn = buildDn(p);
return (Person) ldapTemplate.lookup(dn, new PersonContextMapper());
}
private static class PersonContextMapper implements ContextMapper<Object> {
public Object mapFromContext(Object ctx) {
DirContextAdapter context = (DirContextAdapter) ctx;
Person p = new Person();
p.setFirstName(context.getStringAttribute("cn"));
p.setLastName(context.getStringAttribute("sn"));
p.setOu(context.getStringAttribute("ou"));
p.setEmail(context.getStringAttribute("mail"));
p.setUid(context.getStringAttribute("uid"));
p.setIdEventuate(context.getStringAttribute("title"));
// p.setLstRoles(context.getStringAttributes("roleNames")); // Doesn't work
return p;
}
}
This work but the list of roles is always null.
I tried this but without succes:
Object[] o1 = context.getObjectAttributes("memberOf"); // doesn't work
Would you have any ideas ?
This is a screenshot of a user usring Apache directory:
Best regards
It depends on several things:
If you have the memberof overlay configured and the users were added to their groups after memberof was configured, memberOf will work. If it doesn't, one of those conditions isn't true. In particular, memberof isn't retrospective.
If those conditions don't or can't hold, you will need to conduct a subtree search starting from the groups subtree, using a filter like uniqueMember={0} where the parameter is provided as the DN of the user.

Making an ldap call using spring

I'm trying to search for a user using an admin account, but the admin user can't "see" people in the regular Users group. My ldapTemplate is getting authenticated properly and I can query and see all the people in the Admin account's context, but I can't see objects in the actual group I need to search, which is called "Users". Here's my ldapTemplate call:
private void testLookup(String username) {
AndFilter andFilter = new AndFilter();
andFilter.and(new EqualsFilter("objectclass", "person"));
// andFilter.and(new EqualsFilter("memberOf", "Users"));
andFilter.and(new EqualsFilter("mail", <usernameInUsersGroup));
final SearchControls controls = new SearchControls(); //
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
ldapTemplate.setIgnorePartialResultException(true);
List<String> list =
ldapTemplate.search("",
List<String> list2 = ldapTemplate.search("", andFilter.encode(), controls, new EmailAttributesMapper());
controls,
new EmailAttributesMapper());
}
The list is always empty. It SHOULD find the person in the Users group I'm searching for. I've tried passing in a different "base" but I always get an error that it can't find the group. The only thing I don't get an error on is passing in "".

GroupSequence and ordered evaluation in JSR 303

In our application we have such a case:
Constraints should be evaluated in particular order. (cheap to expensive)
Constraints should not be evaluated after a violation per field.
All fields should be validated.
For first two, groupsequence is fitting very good. However for my 3rd requirement I could not find a way to solve.
public class AccountBean {
#CheepValidation
#ExpensiveValidation
#VeryExpensiveValidation
private String name;
#CheepValidation
#ExpensiveValidation
#VeryExpensiveValidation
private String surname
}
For example,
Let's say that, for name field VeryExpensiveValidationconstraint is violated and for surname field ExpensiveValidation constraint is violated.
For this case I should display:
For field name: Only VeryExpensiveValidation error message
For field surname: Only ExpensiveValidation error message
Note that for field surname we did not evaluate VeryExpensiveValidation constraint.
Is there a way to implement it with JSR 303?
Thanks
You can use groups and #GroupSequence, but it's a bit unwieldy.
public class AccountBean {
#CheapValidation(groups=Name1.class)
#ExpensiveValidation(groups=Name2.class)
#VeryExpensiveValidation(groups=Name3.class)
String name;
#CheapValidation(groups=Surname1.class)
#ExpensiveValidation(groups=Surname2.class)
#VeryExpensiveValidation(groups=Surname3.class)
String surname;
public interface Name1 {}
public interface Name2 {}
public interface Name3 {}
#GroupSequence({Name1.class, Name2.class, Name3.class})
public interface Name {}
public interface Surname1 {}
public interface Surname2 {}
public interface Surname3 {}
#GroupSequence({Surname1.class, Surname2.class, Surname3.class})
public interface Surname {}
}
Then validate with:
validator.validate(myAccountBean,
AccountBean.Name.class, AccountBean.Surname.class)
The key is to have two entirely independent group sequences.
Unfortunately, it seems you must explicitly list the groups for all the fields you want to validate. I wasn't able to get it working with a 'default' #GroupSequence. Can anyone improve on this?
I've implemented ordered validation with GroupSequence but, generally speaking, GroupSequence beans validation implementation is not transparent.
Meaning, untill first group is fully validated, you can not trigger the validation of the second group.
E.g.
I have 3 validated fields with custom validators. The idea is pretty straightforward: every field should be validated with the set of validators from top to bottom independently (descending cardinality).
#StringPropertyNotNullOrEmptyConstraint(message = "Group name is required", groups = {ValidationStep1.class})
private final StringProperty groupName;
#StringPropertyNotNullOrEmptyConstraint(message = "Group password is required", groups = {ValidationStep1.class})
#StringPropertyMatchConstraint(message = "The given password phrases do not match", dependentProperties = {"groupPasswordMatch"}, groups = {ValidationStep2.class})
private final StringProperty groupPassword;
#StringPropertyNotNullOrEmptyConstraint(message = "Group password match is required", groups = {ValidationStep1.class})
#StringPropertyMatchConstraint(message = "The given passwords phrases do not match", dependentProperties = {"groupPassword"}, groups = {ValidationStep2.class})
private final StringProperty groupPasswordMatch;
public interface ValidationStep1 {
}
public interface ValidationStep2 {
}
#GroupSequence({GroupDialogModel.class, ValidationStep1.class, ValidationStep2.class})
public interface GroupDialogModelValidationSequence {
}
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
Validator validator = validatorFactory.getValidator();
Set<ConstraintViolation<GroupDialogModel>> constraintViolations = validator.validate(this, GroupDialogModelValidationSequence.class);
The caveat of this approach is that each field should go through ValidationStep1 first and only after each validation of step 1 succeeds it goes to step 2. For example, even if password fields are not empty, but contain different values, validation for them succeeds if group name field does not contain any value. And only after I enter some value to the group name, ValidationStep1 group succeeds and then it displays validation result of ValidationStep2 (passwords do not match).
Making each group for each field in every sequence is bad practice IMHO, but it seems like there is no other choice.
Any other solution is much appreciated.

Resources