I have a search method to retrieve records from database and I want to restrict the user to only search by name. I have a SearchBy enum with list of search parameters and for particular SearchBy methods the user can only search by certain values.
public List<Book> getBooks(SearchBy identifierName,List<String> identifierList) throws UnsupportedOperationException{
List<Book> resultList = new ArrayList<Book>();
if (identifierName.equals(SearchBy.TITLE)) {
//returns list of BookObjects
} else if (identifierName.equals(SearchBy.AUTHOR)) {
//returns list of BookObjects
} else {
throw new UnsupportedOperationException("Books can be retrieved only using book titles or author names");
}
}
Instead of validating and throwing an exception, how can we make it clear that only the values TITLE and AUTHOR are allowed as input for identifier names?
I have not used it but this framework makes some sense for your requirement:
Java Argument Validation
An easy way for checking the preconditions of (public) methods and
constructors. All arguments can be checked, before an
IllegalArgumentException is thrown. Creating consistent messages for
inconveniences in argument values.
It's hard to tell what you mean, but if you are trying to validate input to a method it's common to throw an IllegalArgumentException for bad inputs and the client code can then handle this as they desire.
You would typically do something like this to validate input to a method:
public void method(String name) throws InvalidArgumentException {
if (isInvalid(name)) {
throw new IllegalArgumentException("The name is invalid");
}
else {
// rest of method ...
}
}
It's up to you to decide how to validate the actual name depending on the rules you want to enforce. You can then give a suitable message in the exception to explain why it might not have been valid. Perhaps a regex could be used for the validation code, but without knowing the validation requirements it's impossible to suggest one here.
Related
As it doesn't make sense to overload methods based on the exception clause.
Most of the times when we are writing the code, we came into a scenario where
we want some piece of code to throw an exception so that we can handle it accordingly
some times for the same piece of code we just want to ignore that exception
Example below -
// It is used at many places and we just want to take specific action if any exception is coming,
// like retry if SQLExceptions
private List<Contact> getContacts() throws Exceptions{
List<Contact> contacts = null;
try {
contacts = contactDao.getContacts();
} catch (SQLException ex) {
logger(ex);
throw ex;
}
return contacts;
}
private List<Contact> getContacts1() throws Exceptions{
List<Contact> contacts = null;
try {
contacts = contactDao.getContacts();
} catch (SQLException ex) {
logger(ex);
}
return contacts;
}
// It is used at some places and I want this method to throw an exception,
// I want to handle it using null check
What should be the correct ways to write such methods.
IMHO, you should have two methods since they'd do different things inside.
For example, at ROR, there is a convention to handle your problem. When methods are finished with a ! means that if something goes wrong an exception will be raised. So, when you want to save an item you can call
save!: it raises an exception if the item can't be saved or returns the item itself
or save: it doesn't raise anything and returns true or false according to the state of the operation
I'm not sure which language you are coding but maybe it could be helpful to find your language convention. If there is no convention at your current language/framework you could define one with your team and use it internally. For example, you can define a getContacts_unsafe method ann know that suffix means it doesn't raise an exception
I have this method signature:
public User getActiveUser(String personId, User mainUser) throws MyExceptions {
if (personId== null) return mainUser;
User innerUser = userRepository.getByPersonId(personId);
checkForNull(innerUser);
checkIsActive(innerUser);
return innerUser;
}
private void checkForNull(User innerUser) throws UNPExceptions {
if (innerUser == null) throw new MyExceptions(USER_NOT_FOUND);
}
private void checkIsActive(User innerUser) throws UNPExceptions {
if (!innerUser.getIsActive()) throw new MyExceptions(USER_BLOCKED);
}
And I call this method from different places like this:
User user = userService.getActive(userRequest.getPersonId(), requestEntity.getUser());
I do not like this code because:
1) I pass 2 parameters to this method getActiveUser(String personId, User mainUser)
mainUser always return if personId is null. I can move this check inside method but then I have to do it every time before calling the method. And the method is called from many places. so I moved the check to one place. But it looks crooked and I do not know how to get around it. I don’t want to pass the second parameter just to do this check inside the method, but it looks better than copying this check before each method call. I do not know which solution is better. maybe there is another solution.
2) method name - getActiveUser is lying. because inside I do more checks. but I do not know how to call it - getActiveUserAndCheck? this is also not correct as the method is responsible for several duties
3) Is it necessary to divide checks into separate methods? checkForNull(innerUser);
checkIsActive(innerUser);
If the mainUser is always the same user you don't have to pass it as a method parameter, you can store it as an instance field and initialize it when appropriate.
If that's not the case, you can use AOP to handle the case with null personId and the aspect component will be responsible for retrieving the mainUser.
Using Java8+, you can simply replace
checkForNull(innerUser);
checkIsActive(innerUser);
return innerUser;
by means of Optional, as:
return Optional.ofNullable(innerUser)
.filter(User::getIsActive)
.orElseThrow(() -> new MyExceptions(""));
If you want to default to mainUser, you could do something on these lines(just that this is not throwing your custom exception) :
return Optional.ofNullable(personId) // check for person 'null'
.map(p -> userRepository.getByPersonId(personId)) if present evaluate 'innerUser'
.filter(Objects::nonNull) // check if innerUser is null
.filter(User::getIsActive) // check if innerUser is active
.orElse(defaultUser); if any of above fails return defaultUser
I have a REST API which accepts query parameters. The query parameters are valid if and only if at a time only one query parameter is passed and it is among the list of valid query parameters.
Currently my logic for this is:
I am collecting the query params in a map. And then checking it's size. If size > 1 function throwing an error. If that is not the case then iterating through the map and if find a parameter other than valid ones the function throwing an error.
For example:
if(queryParam.size()>1) {
throw new FailureResponse();
}
queryParam.forEach(e->{
String key = e.getKey();
if(!key.equalsIgnoreCase("p1") && !key.equalsIgnoreCase("p2")) {
throw new FailureResponse();
}
});
But I think in this way I am violating the SOLID design principle which says a class should be open for extension but closed for modification.
I also thought of creating a file and then reading the acceptable params from it but that would add to the response time of the API as it involves reading a file.
Is there some way I can keep and read the valid query-params and it does not violate the design principles?
You could maintain an Enum of valid params and extend the enums as and when applicable like
public enum QueryParams{
PARAM_1("param1"),
PARAM_2("param2"),
private String paramValue;
QueryParams(String paramName){
this.paramValue = paramValue();
}
public void getParamValue(){
return this.value;
}
}
and then you could iterate over the set of values of this enum to filter out invalid values
List<String> validParams = Arrays.asList(QueryParams.values()).stream().map(QueryParams::getParamValue).collect(Collectors.toList());
queryParams.removeAll(validParams);
if(queryParams.size()!=0) {
throw new FailureResponse();
}
}
This helps you maintain the API class without any changes, whenever a new parameter is added, just extend the enum and all the rest is automatically extended as it all depends upon the value in the enum.
Need help to understand what i am doing wrong. Here is my test case with list of users, where the required fields for user set to null, If the required field is missing, throwing an argument exception with message.Also checked my method it seems good.
//Test case begins
#Test(groups = {"unit"})
public void testProcessOrderWithMissingFieldsForUsers() throws Exception {
GenericOrder order = getOrder();
List<User> users = order.getUsers();
User user = users.get(0);
user.setLastName(null);
user.setLoginName(null);
user.setEmailAddress(null);
user.setRole(null);
try {
orderService.processOrder(order);
} catch (ArgumentException aex) {
Assert.assertEquals(aex.getFieldErrors().get(0).getMessage(),"lastName is required");
Assert.assertEquals(aex.getFieldErrors().get(1).getMessage(), "loginName is required");
Assert.assertEquals(aex.getFieldErrors().get(2).getMessage(), "emailAddress is required");
Assert.assertEquals(aex.getFieldErrors().get(3).getMessage(), "role is required");
}
It is likely the errors are not in the order you expect them.
Further it would be better to split the test so that each validation is verified individually. In other words have ...WithMissingLastName, ...WithMissingLogin, ...WithMissingEmailAdress, ...WithMissingRole tests.
This makes for easier diagnostics and it is probably closer to what you really intend to test.
Also your test will not fail if the validation is never applied. One way to protect against that is to call the fail method inside the try/catch block.
Let's assume we have a class Student with the following constructor:
/** Initializes a student instance.
* #param matrNr matriculation number (allowed range: 10000 to 99999)
* #param firstName first name (at least 3 characters, no whitespace)
*/
public Student(int matrNr, String firstName) {
if (matrNr < 10000 || matrNr > 99999 || !firstName.matches("[^\\s]{3,}"))
throw new IllegalArgumentException("Pre-conditions not fulfilled");
// we're safe at this point.
}
Correct me if I'm wrong, but I think in this example, I followed the design by contract paradigm by simply specifiying the (rather static) constraints on the possible input values and raising a generic, unchecked exception if those are not fulfilled.
Now, there is a backend class that manages a list of students, indexed by their matriculation number. It holds a Map<Integer, Student> to save this mapping and provides access to it through an addStudent method:
public void addStudent(Student student) {
students.put(student.getMatrNr(), student);
}
Now let's assume there is a constraint on this method like "a student with the same matriculation number must not already exist in the database".
I see two options of how this could be realized:
Option A
Define a custom UniquenessException class that is raise by addStudent if a student with the same matr. number already exists. Calling code will then look something like this:
try {
campus.addStudent(new Student(...));
catch (UniquenessError) {
printError("student already existing.");
}
Option B
State the requirement as a pre-condition and simply raise an IAE if it doesn't hold. Additionally, provide a method canAddStudent(Student stud) that checks in advance whether addStudent will fail. Calling code would then look something like this:
Student stud = new Student(...);
if (campus.canAddStudent(stud))
campus.addStudent(stud);
else
printError("student already existing.");
I feel that option A is much cleaner from a software-engineering point of view, for at least the following reason:
It can easily be made thread-safe without modifying the calling code (Thanks to Voo for pointing me to TOCTTOU, which seems to describe that exact issue)
Thus I wonder:
Is there a third option which is even better?
Does option B have an advantage that I didn't think of?
Would it actually be allowed from a design by contract point of view to use option B and define the uniqueness as a pre-condition of the addStudent method?
Is there a rule of thumb when to define pre-conditions and simply raise IAE and when to use "proper" exceptions? I think "make it a pre-condition unless it depends on the current state of the system" could be such a rule. Is there a better?
UPDATE: It seems like there is another good option, which is to provide a public boolean tryAddStudent(...) method that doesn't throw an exception but instead signals error/failure using the return value.
(this is too long for a comment)
In your option B, I wouldn't use a Map<Integer,Student> and then do:
if (campus.hasStudent(12000))
printError("student already existing.");
else
campus.addStudent(new Student(...));
The Map abstraction isn't practical enough for your use case (you're mentionning concurrency issues), I'd use instead a ConcurrentMap<Integer,Student> and do something like this:
final Student candidate = new Student(...);
final Student res = putIfAbsent(student.getMatrNr(), candidate)
if ( res != null ) {
throw new IllegalStateException("Class contract violation: \"student already exists!\", please read the doc");
}
I don't believe the way that the backend class manages a list of students would be relevant to the contract--that is, that it holds a Map<Integer, Student> would not be part of the contract. Thus bringing the matriculation number into the contract in hasStudent(int matrNr) seems a little evil too.
I'd suggest the the campus probably should have a method Boolean hasStudent(Student student), which would check to see if the campus has the student based on whatever the condition. If uniqueness is required by contract, and is truly exceptional, you would then use the contractual check:
Student student= new Student(int matrNbr, String name);
if (campus.hasStudent(student) {
throw new UniquenessException();
}
else {
campus.add(student);
}
The exceptions thrown should be as relevant to the contract as the arguments and return values
UPDATE
If the add should simply fail if uniqueness is not met and is not exceptional, then don't throw the exception. Instead, make the success of the add a return value (as in java.util.HashSet.add()). This way, campus.add(Student) would return true if the student was actually added.