I'm communicating with an API, and I have utilised the use of the Optional class. But I feel like the error handling could be more elegant, so any suggestions on how to improve this will be well received. Also am I missing a exception handling in the actual api calls?
public Optional<Account> getGreenqloudAccount(String accountUUid) {
System.out.println("tmplog: GreenqloudAccountDao->getGreenqloudAccount");
for (Account account : apiClient.accountList()) {
if (account.getUuid().equals(accountUUid)) {
System.out.println("getGreenqloudAccount, account: " + account.toString());
return Optional.of(account);
}
}
return Optional.empty();
}
public Optional<String> getMarketplaceCustomerIdByUsername(String username) {
if (username == null || username.equals("")) {
return Optional.empty();
}
AwsMarketplace marketplaceData = apiClient.getMarketplaceData(getKeys(username));
if (marketplaceData == null) {
return Optional.empty();
}
return Optional.ofNullable(marketplaceData.getObjects().get(0).getCustomerId());
}
private Pair getKeys(String username) {
GetKeys getKeys = apiClient.getKeys(username);
return new Pair(getKeys.getApiPrivateKey(), getKeys.getApiPublicKey());
}
The main problem with your code: you throw plenty of very different outcomes into the same "bucket".
getMarketplaceCustomerIdByUsername() for example returns an empty Optional when:
the username is null
the username is "" (and think about it "" means empty, but " " isnt empty?!)
no AwsMarketplace instance can be found for the given user
As said, these are very different problems. The first one might indicate: the provided user name is bad, so you should tell your user about that. The last one means: "something is fishy, maybe the user is unknown, or something else happened".
Thus: consider to not reduce different results into an empty Optional. Rather consider to throw (different?) exceptions. You use Optional when "no result" is a valid result of the operation. But "no result, because bad user name" doesn't feel like a valid result.
If you mean handling corner cases, you could improve the code readability along with them as in the first method using findFirst such as:
public Optional<Account> getGreenqloudAccount(String accountUUid) {
System.out.println("tmplog: GreenqloudAccountDao->getGreenqloudAccount");
return apiClient.accountList().stream()
.filter(account -> account.getUuId().equals(accountUUid))
// you can 'peek' and log
.findFirst(); // you return the first account or empty
}
Moving further with the other API, notice Optional.map handles operations returning null values and returns Optional.empty for them implicitly. So you can use:
public Optional<String> getMarketplaceCustomerIdByUsername(String username) {
return Optional.ofNullable(username) // if username is null empty
.filter(name -> !name.isEmpty()) // empty string returns filtered out
.map(name -> apiClient.getMarketplaceData(getKeys(name))) // handles 'null' calue returned
.map(marketplaceData -> marketplaceData.getObjects().get(0).getCustomerId()); // here as well
}
You can use a check using Optional.isPresent()
or
use Optional.orElseThrow(Supplier<? extends X> exceptionSupplier)
Read JDK8 Doc about Optional here
Additionally, you might want to trim your input parameters before checking for empty strings
Related
Given two sessions (A & B) as strings, my goal is to check if A is valid in the database. If A exists, return it. If A is not provided (or does not exist in the database), I would like to proceed with checking session B in the database.
The problem with my code below is that I return regardless whether A has been found or not. Instead, I would like to check that if A is not found (is empty), proceed with checking B. Is there a way to check if a Mono does not exist?
String sessionA = "1";
String sessionB = "2";
public Mono<Session> getSession() {
if (!StringUtils.isEmpty(sessionA)) {
return myDatabaseService.findByKeyOne(sessionA); // Should only return if it exists, otherwise, check session B
}
if (!StringUtils.isEmpty(sessionB)) {
return myDatabaseService.findByAnotherSession(sessionB);
}
throw new Exception(); // If both sessions have not been found
}
// My Database Service (dummy code)
public Mono<Session> findByKeyOne(String session) {
try {
return myDatabase.find();
} catch (Exception e) {
return Mono.empty() // If an error occurrd or no result has been found, return empty
}
}
Expanding on #Eugene Botyanovsky's answer with addressing the optional sessionA, you may try something like this:
// Return an exception in case both sessionA and sessionB are empty.
if (StringUtils.isEmpty(sessionA) && StringUtils.isEmpty(sessionB)) {
return Mono.error(new Exception());
}
// simply return an empty Mono if sessionA is empty.
Mono<Session> sessionAMono = StringUtils.isEmpty(sessionA)
? Mono.empty()
: myDatabaseService.findByKeyOne(sessionA);
return sessionAMono
.switchIfEmpty(Mono.defer(() -> myDatabaseService.findByAnotherSession(sessionB)));
In this case, both scenarios when sessionA is empty, as well as when myDatabaseService.findByKeyOne(sessionA) returns an empty result (assuming that it does).
Edit:
Unrelated to your concern but I wanted to point out in your findByKeyOne function, since the myDatabase.find() is returning a Mono, wrapping it with try catch won't work since it's asynchronous. Which means you'll need to handle any potential error in the same stream. You can do something like:
return myDatabase.find()
.onErrorResume(throwable -> Mono.empty();
Try Mono#switchIfEmpty
This is as simple as:
myDatabaseService.findByKeyOne(sessionA)
.switchIfEmpty(myDatabaseService.findByAnotherSession(sessionB))
Now you only have to check your nullabilities
I've written two methods, findById searches for an item in the DB and throws an exception if the item is not found :
public Url findById(final Long id){
return urlRepository.findById(id)
.orElseThrow(() -> new ShortUrlNotFoundException("URL not found for the given ID"));
}
The second method, findByShortUrl searches for an item in the DB and uses the JPA method findByShortUrlIs which returns a List of size 1 if the item is found, there should never be more than 1 item in the DB for a given shortUrl :
public Optional<String> findByShortUrl(final String shortUrl){
List<Url> urlList = urlRepository.findByShortUrlIs(shortUrl);
if(urlList.isEmpty()){
return Optional.empty();
}
else {
return Optional.of(urlList.get(0).getLongUrl());
}
}
I like the pattern of using a ShortUrlNotFoundException if an item is not found. Should I use it also in findByShortUrl ? Then, findByShortUrl becomes:
public Optional<String> findByShortUrl(final String shortUrl){
List<Url> urlList = urlRepository.findByShortUrlIs(shortUrl);
if(urlList.isEmpty()){
throw new ShortUrlNotFoundException("URL not found for the given ID")
}
else {
return Optional.of(urlList.get(0).getLongUrl());
}
}
Why not using findFirst as this:
Optional<Url> findFirstByShortUrlIs(String shortUrl);
and then, you call:
public Optional<String> findByShortUrl(final String shortUrl){
return urlRepository.findFirstByShortUrlIs(shortUrl)
.map(Url::getLongUrl)
.map(Optional::of)
.orElseThrow(() -> new ShortUrlNotFoundException("URL not found for the given ID"));
}
Personally speaking, I would never use exception mechanism for both cases. Exceptions are designed for unexpected situations that aren't covered by conventional business logic. For example, findById should throw an exception for the search if and only if you do not expect that item in the database. The same story for the findByShortUrl. As far as I understand it's a normal case not to have a object in db, thus it should be expected case to have empty result. On other hand, you've mentioned that "there should never be more than 1 item in the DB", and that's the perfect case for an exception! If you see that return result set has more than 2 objects, so go and thrown an excpetion.
I have a REST Controller
#GetMapping("/getByClientId/{clientId}")
public ResponseEntity<Optional<List<EquityFeeds>>> getByClientId(#PathVariable("clientId") final String clientId) {
Optional<List<EquityFeeds>> cId = Optional.ofNullable(equityFeedsService.findByClientId(clientId));
System.out.println("Client Id: "+cId);
if(cId.isPresent()) {
return ResponseEntity.ok(cId);
} else {
cId.orElseThrow(() -> new ClientIdNotFoundException(clientId));
}
return ResponseEntity.ok(cId);
}
Service Class Code:
public List<EquityFeeds> findByClientId(String clientId) {
List<EquityFeeds> cId = equityFeedsRedisRepositoryImpl.findByClientId(clientId);
System.out.println("In EquityFeedService "+cId);
return cId;
}
Impl. Code (REDIS):
public List<EquityFeeds> findByClientId(String clientId) {
return (List<EquityFeeds>) listOperations.range(clientId, 0, -1);
}
Issue:
1) When the getClientId is called using a REST Controller and the clientId is not present in the REDIS Cache then:
Service class Code returns: In EquityFeedService []
The REST Controller returns: Client Id: Optional[[]]
In the REST Controller the code goes inside the if loop and displays nothing on the screen since the List is empty i.e.
if(cId.isPresent()) {
return ResponseEntity.ok(cId);
}
Why? Why cId.isPresent() returns true and the code goes inside the if loop. Ideally the code should go inside the else loop and throw an Exception since the List is empty. This is happening in case of List only it seems as my other method which has a return type of POJO doesn't have this issue.
Please help me understand this behavior and what should be done to fix this.
cId.isPresent() return true because
List<EquityFeeds> is not null , it's empty list
if(!cId.get().isEmpty()) {
return ResponseEntity.ok(cId);
} else {
throw new ClientIdNotFoundException(clientId);
}
The reason the Optional.isPresent returns true is that there is an actual value - an empty List. The Optional checks whether the value it holds is a null or not, nothing else. The isPresent checks whether the value is present inside the Optional, not inside the List itself.
So you have to treat Optional a bit different. Moreover, don't use Optional like that as substitution to the if-else constructs.
Here is a way to go:
return cId.filter(Predicate.not(List::Empty)) // if the list is not empty
.map(ResponseEntity::ok) // create a response
.orElseThrow(() -> // or else throw an exception
new ClientIdNotFoundException(clientId));
By the way, you don't want to return Optional wrapped inside the ResponseEntity. Unwrap it and return the List itself. If it is empty or null was already handled and the exception would be thrown first.
return cId.filter(Predicate.not(List::Empty))
.map(Optional::get) // exctract from the Optional
.map(ResponseEntity::ok)
.orElseThrow(() -> new ClientIdNotFoundException(clientId));
Edit: I ended up solving the problem myself through more experimentation. The code seems quite verbose though so there is probably a better solution that doesn't involve typecasting strings to other things.
Answer posted below.
For my school work, I am supposed to create a GET mapping to receive a list of all entities of a specific type. This GET mapping should return simply all the entities if no parameter is provided, or otherwise it will apply something in the entity repository to use a JPQL query and the provided parameter which is used as an ordinal query parameter to filter the returned results.
"If no request parameter is provided, the existing functionality of returning all events
should be retained.
If more than one request parameter is specified, an error response should be
returned, indicating that at most one parameter can be provided.
If the ‘?status=XXX’ parameter is provided, with a status string value that does not
match the AEventStatus enumeration, an appropriate error response should be
returned."
I have tried to alter my GET mapping to have 3 optional #RequestParameter variables, but I found out that it is tedious logic-wise to check for the existence of multiple or no parameters, and then do something again based on the existence of which parameter is there.
Instead I tried this (I was in the middle of this and it is not complete):
#RequestMapping(
value="/aevents",
method = RequestMethod.GET)
public ResponseEntity<Object> getAllAEvents(HttpServletRequest request) {
if (request.getParameterMap().size() == 0) {
return ResponseEntity.status(HttpStatus.OK).body(repository.findAll());
}
if (request.getParameterMap().size() > 1) {
return new ResponseEntity<>("Can only handle one request paramter: title=, status= or minRegistrastions=", HttpStatus.BAD_REQUEST);
}
//incomplete from here
}
And I am now not sure if this is the correct approach or if I am simply overlooking something. I suppose I might be able to check for the names of the parameters that were provided in the request and then return a bad request response again if I find something that isn't a valid parameter. But I am not sure how to check the parameter map for the names of the parameters or if the parameter map even does what I think it does.
The parameters provided are supposed to be either an int, a value from an enum or a string.
Am I overlooking a simpler way to do this? i.e. a way to check the amount of parameters and the existence of parameters in a signature like:
#GetMapping("/aevents")
public List<AEvent> getAllAEvents(#RequestParam(required = false) String title,
#RequestParam(required = false) AEventStatus status,
#RequestParam(required = false) int minRegistrations) {
//Do something here
}
Or is my current approach feasible, and if it is, how do I continue on it?
Yes, you would likely do it your way, though:
You can inject the map of parameters directly in Spring.
Throw a ResponseStatusException (available since Spring 5) instead of fumbling around with the ResponseEntity.
#GetMapping("/aevents")
public List<AEvent> getAllAEvents( #RequestParam Map<String, String> allRequestParams){
if(allRequestParams.size() >1){
throw new ResponseStatusException(
HttpStatus.BAD_REQUEST,"too many params");
}
// do something
return list;
}
Answer to my own question after solving it:
What is the parameter map?
The parameterMap is actually a map of string keys and string arrays. To get the value for a parameter name (the key) you can get values of this key and then access it like an array.
However, using the parameterMap was not necessary. Instead it was better to just use the built-in Spring way of doing it which is by using the #RequestParam annotation with simply a #RequestParam Map<String, String> params in the method body.
Credits to https://stackoverflow.com/users/2255293/marco-behler for giving me an idea as well as providing a better way to throw exceptions.
#GetMapping("/aevents")
public List<AEvent> getAllAEvents(#RequestParam Map<String, String> params) {
if (params.size() == 0) { //Default case, no params
return repository.findAll();
}
if (params.size() > 1) { //Refuse to handle more than one provided param
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Can only handle one request parameter: title=, status= or minRegistrations=");
}
if (params.containsKey("title")) {
String value = params.get("title");
return repository.findByQuery("AEvent_find_by_title", ("%" + value + "%"));
}
if (params.containsKey("status")) {
String stringValue = params.get("status").toUpperCase();
for (AEventStatus e : AEventStatus.values()) {
if (e.name().equals(stringValue)) {
AEventStatus value = AEventStatus.valueOf(stringValue);
return repository.findByQuery("AEvent_find_by_status", value);
}
}
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "status=" + stringValue + " is not a valid AEvent status value.");
}
if (params.containsKey("minRegistrations")) {
int value;
try {
value = Integer.parseInt(params.get("minRegistrations"));
} catch (NumberFormatException e) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Provided request parameter was not a valid number.");
}
return repository.findByQuery("AEvent_find_by_minRegistrations", value);
}
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid query parameters.");
}
This question already has answers here:
Null check chain vs catching NullPointerException
(19 answers)
Avoiding NullPointerException in Java
(66 answers)
Closed 4 years ago.
I need to check if some value is null or not. And if its not null then just set some variable to true. There is no else statement here. I got too many condition checks like this.
Is there any way to handle this null checks without checking all method return values?
if(country != null && country.getCity() != null && country.getCity().getSchool() != null && country.getCity().getSchool().getStudent() != null .....) {
isValid = true;
}
I thought about directly checking variable and ignoring NullpointerException. Is this a good practice?
try{
if(country.getCity().getSchool().getStudent().getInfo().... != null)
} catch(NullPointerException ex){
//dont do anything.
}
No, it is generally not good practice in Java to catch a NPE instead of null-checking your references.
You can use Optional for this kind of thing if you prefer:
if (Optional.ofNullable(country)
.map(Country::getCity)
.map(City::getSchool)
.map(School::getStudent)
.isPresent()) {
isValid = true;
}
or simply
boolean isValid = Optional.ofNullable(country)
.map(Country::getCity)
.map(City::getSchool)
.map(School::getStudent)
.isPresent();
if that is all that isValid is supposed to be checking.
You could use Optional here, but it creates one Optional object at each step.
boolean isValid = Optional.ofNullable(country)
.map(country -> country.getCity()) //Or use method reference Country::getCity
.map(city -> city.getSchool())
.map(school -> school.getStudent())
.map(student -> true)
.orElse(false);
//OR
boolean isValid = Optional.ofNullable(country)
.map(..)
....
.isPresent();
The object-oriented approach is to put the isValid method in Country and the other classes. It does not reduce the amount of null checks, but each method only has one and you don't repeat them.
public boolean isValid() {
return city != null && city.isValid();
}
This has the assumption that validation is the same everywhere your Country is used, but typically that is the case. If not, the method should be named hasStudent(), but this is less general and you run the risk of duplicating the whole School interface in Country. For example, in another place you may need hasTeacher() or hasCourse().
Another approach is to use null objects:
public class Country {
public static final Country NO_COUNTRY = new Country();
private City city = City.NO_CITY;
// etc.
}
I'm not sure it is preferable is this case (strictly you would need a sub class to override all modification methods), the Java 8 way would be to go with Optional as method in the other answers, but I would suggest to embrace it more fully:
private Optional<City> city = Optional.ofNullable(city);
public Optional<City> getCity() {
return city;
}
Both for null objects and Nullable only work if you always use them instead of null (notice the field initialization), otherwise you still need the null checks. So this option avoid null, but you code becomes more verbose to reduced null checks in other places.
Of course, the correct design may be to use Collections where possible (instead of Optional). A Country has a set of City, City has a set of Schools, which has set of students, etc.
As alternative to other fine usage of Optional, we could also use a utility method with a Supplier<Object> var-args as parameter.
It makes sense as we don't have many nested levels in the object to check but many fields to check.
Besides, it may easily be modified to log/handle something as a null is detected.
boolean isValid = isValid(() -> address, // first level
() -> address.getCity(), // second level
() -> address.getCountry(),// second level
() -> address.getStreet(), // second level
() -> address.getZip(), // second level
() -> address.getCountry() // third level
.getISO()
#SafeVarargs
public static boolean isValid(Supplier<Object>... suppliers) {
for (Supplier<Object> supplier : suppliers) {
if (Objects.isNull(supplier.get())) {
// log, handle specific thing if required
return false;
}
}
return true;
}
Suppose you would like to add some traces, you could so write :
boolean isValid = isValid( Arrays.asList("address", "city", "country",
"street", "zip", "Country ISO"),
() -> address, // first level
() -> address.getCity(), // second level
() -> address.getCountry(),// second level
() -> address.getStreet(), // second level
() -> address.getZip(), // second level
() -> address.getCountry() // third level
.getISO()
);
#SafeVarargs
public static boolean isValid(List<String> fieldNames, Supplier<Object>... suppliers) {
if (fieldNames.size() != suppliers.length){
throw new IllegalArgumentException("...");
}
for (int i = 0; i < suppliers.length; i++) {
if (Objects.isNull(suppliers.get(i).get())) {
LOGGER.info( fieldNames.get(i) + " is null");
return false;
}
}
return true;
}
Java doesn't have "null-safe" operations, like, for example Kotlin's null safety
You can either:
catch the NPE and ignore it
check all the references manually
use Optional as per the other answers
use some sort of tooling like XLST
Otherwise if you have control over the domain objects, you can redesign your classes so that the information you need is available from the top level object (make Country class do all the null checking...)
You could also look at vavr's Option which as the below posts describes is better than Java's Optional and has a much richer API.
https://softwaremill.com/do-we-have-better-option-here/