I'm trying to define my own custom exceptions. Basically i want to prevent a user to be created if the age is less than 16. Following some of the discussions / questions i've come up with this so far.
public enum Code {
USER_INVALID_AGE("The user age is invalid");
private String message;
Code(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
Exception class:
public class TrainingException extends RuntimeException {
private Code code;
public TrainingException(Code code) {
this.code = code;
}
public Code getCode() {
return code;
}
public void setCode(Code code) {
this.code = code;
}
}
In a Validator package, i have the following:
public class UserValidator implements Validator<User> {
/** {#inheritDoc} */
#Override
public void validate(User type) {
if (DateUtils.getYearDifference(type.getUserDetails().getBirthDate(), new DateTime())< 16) {
throw new TrainingException(Code.USER_INVALID_AGE);
}
}
}
I'm calling validate method in Services, where i try to create the user:
public User save(User user) {
validator.validate(user);
return userRepository.save(user);
}
So that's what i have so far, i tried to test this without success.
# Rule
public ExpectedException thrown = ExpectedException.none();
#Test
public void testInvalidAge() throws TrainingException{
thrown.expect(TrainingException.class);
thrown.expectMessage(Code.USER_INVALID_AGE.getMessage());
User user = userService.findAll().get(0);
UserDetails userDetails = new UserDetails();
userDetails.setBirthDate(UserTestUtils.buildDate(2000, 7, 21, 1, 1, 1));
user.setUserDetails(userDetails);
userService.save(user);
}
Here's what i get:
Expected: (an instance of
org.dnet.training.exceptions.TrainingException and exception with
message a string containing "The user age is invalid")
but: exception with message a string containing "The user age is invalid" message was null.
It's obvious that i'm missing something but i'm stuck, tried different stuff but without any success so far.
You create an exception by throw new TrainingException(Code.USER_INVALID_AGE); which doesn't set the message. In constructor of TrainingException call super(code.getMessage()); which will set the message for that instance of the exception.
Try to rewrite your custom exception like below, hope it helps :)
public class TrainingException extends RuntimeException {
private Code code;
public TrainingException(Code code) {
super(code.getgetMessage());
this.code = code;
}
public Code getCode() {
return code;
}
public void setCode(Code code) {
this.code = code;
}
}
In the TrainingException constructor call super(code.name()) first and then call this.code = code i.e.
public TrainingException(Code code) {super(code.name()) this.code = code;}
it will work.
Related
I am new to Java and trying my hands on the exception handling code. Everything was fine to me until I get unhandled exception error. Can anyone please help me to correct the code and tell my mistake so that I can never commit again?
Exception Class - Created this to retrieve message for different exceptions
// Implement user defined exception classes
class InvalidAgeException extends Exception{
public InvalidAgeException(String message) {
super(message);
}
}
class InvalidJobProfileException extends Exception{
public InvalidJobProfileException(String message) {
super(message);
}
}
class InvalidNameException extends Exception{
public InvalidNameException(String message) {
super(message);
}
}
Applicant Class - Class to set and get attributes of Applicant
class Applicant {
private String name;
private String jobProfile;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getJobProfile() {
return jobProfile;
}
public void setJobProfile(String jobProfile) {
this.jobProfile = jobProfile;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Validator Class - Class to check if the Applicant has a name or not
class Validator{
//Implement your code here
public boolean validateName(String name) throws Exception
{
if(getName().length()>0)
{
return true;
}
else{
return false;
}
}
public boolean validateJobProfile(String jobProfile) throws Exception
{
if (getJobProfile().equalsIgnoreCase("Associate") || getJobProfile().equalsIgnoreCase("Clerk") ||
getJobProfile().equalsIgnoreCase("Executive") || getJobProfile().equalsIgnoreCase("Officer"))
{
return true;
}
else{
return false;
}
}
public boolean validateAge(int age) throws Exception
{
if(getAge()>=18 && getAge()<=30)
{
return true;
}
else{
return false;
}
}
public void validate(Applicant applicant) throws Exception
{
if(validateName(getName())==false)
{
throw new InvalidNameException("Invalid Name");
}
if (validateJobProfile(getJobProfile())==false)
{
throw new InvalidJobProfileException("Invalid job post");
}
if (validateAge(getAge())==false)
{
throw new InvalidAgeException("Invalid Age");
}
}
}
Tester Class - Main Class where objects of different classes are created
class Tester {
public static void main(String[] args) {
try {
Applicant applicant= new Applicant();
applicant.setName("Jenny");
applicant.setJobProfile("Clerk");
applicant.setAge(25);
Validator validator = new Validator();
validator.validate(applicant);
System.out.println("Application submitted successfully!");
}
catch (InvalidNameException|InvalidJobProfileException|InvalidAgeException e) {
System.out.println(e.getMessage());
}
}
}
Your method declares that it throws Exception. Thus, you have to actually catch Exception. If you only want to have to catch either of the three custom exceptions, you need to declare your method as only throwing those three via throws InvalidNameException, InvalidJobProfileException, InvalidAgeException
Plus, your validateAge is declared as throwing an exception, but never actually does throw anything.
Your methods need to specify which exceptions they are actually throwing. At the moment you are simply writing that they throw the general Exception, which you then don't catch in your main.
Change
public void validate(Applicant applicant) throws Exception{...}
to
public void validate(Applicant applicant) throws InvalidNameException, InvalidJobProfileException, InvalidAgeException{...}
For the other methods you need to do it similarly.
In my use case, I want to return a user object from the lambda function as in the code below. Before posting my question, I try many similar questions like this and this but no theme solves my problem, here is my code:
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class AppDatabase {
private static final int NUMBER_OF_THREADS = 4;
public static final ExecutorService databaseWriteExecutor =
Executors.newFixedThreadPool(NUMBER_OF_THREADS);
}
public class MyClass {
private User mUser;
public User findUser(){
AppDatabase.databaseWriteExecutor.execute(() -> {
mUser = work();
});
return mUser;
}
public User work(){
//simulate get user from database
User user = new User();
user.setName("Toto");
return user;
}
}
public class Main {
public static void main(String[] args) {
MyClass myClass = new MyClass();
User user;
user = myClass.findUser();
System.out.println(user.getName()); //this line: Exception in thread "main" java.lang.NullPointerException
}
}
When I run this code, I get "Exception in thread" main "java.lang.NullPointerException". My question is how do I get the User object built by the work () function, but this function should run in a background thread like in code.
findUser returns right away, and it returns null because mUser hasn't been set yet. You need to either wait for it to be set, or return a Future<User> that the caller can wait on.
Summary :
I want to pass valid exception output given by one REST service end point to the end user by using my own Rest service.
What I did is, I have called that service in service class using RestTemplate class, it's giving valid output on valid post request. But when I am passing invalid input to it I am getting only '400 BAD REQUEST' result in my service class where I have called that API. But when I am calling that API separately using postman, there I'm getting expected output.
Code sample :
class Abc {
ResponseEntity<String> = response;
static final String url = "https://abc-xyz.com/client-rest-end-point-url";
public ResponseEntity getDetails(RequestInput requestInput) {
try{
response=restTemplate.postForObject(url,requestInput,String.class);
} catch(Exception e) {
ResponseEntity response = (ResponseEntity<ErrorModel>)restTemplate.postForEntity(url,requestInput,ErrorModel.class);
}//try-catch
}//getDetails method
}//class
You can create a custom exception class for your entire application and you can send data in JSON by using throw keyword
Suppose you have exception class is:
public class TestException extends Exception {
private static final long serialVersionUID = 1L;
private String code;
private String detailMessage;
public TestException() {
};
public TestException(String message, String code, String detailMessage) {
super(message);
this.code = code;
this.detailMessage = detailMessage;
}
public TestException(String message, String code) {
super(message);
this.code = code;
}
//TestExceptionResponseCode is another class for message data, if required.
public TestException(TestExceptionResponseCode testExceptionResponseCode) {
super(testExceptionResponseCode.getMessage());
this.code = testExceptionResponseCode.getCode();
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getDetailMessage() {
return detailMessage;
}
public void setDetailMessage(String detailMessage) {
this.detailMessage = detailMessage;
}
}
Now in your case throwing exception can be like :
class Abc {
ResponseEntity<String> = response;
static final String url = "https://abc-xyz.com/client-rest-end-point-url";
public ResponseEntity getDetails(RequestInput requestInput) {
if(requestInput==null){
throw new TestException("FAILED", "1", "Data can't be null");
}
}
Annotate your method with #ExceptionHandler annotation. You can code in seperate class from controller.
#ControllerAdvice
public class YourExceptionHandler {
#ExceptionHandler(CustomException.class)
public String xException() {
return "error/exception";
}
}
I have created a new exception class in my Dropwizard service that extends BadRequestException.
public class CustomBadRequestException extends BadRequestException {
private static final long serialVersionUID = 1L;
private List<ValidationFailureDto> validationFailures;
public CustomBadRequestException() {
super();
}
public CustomBadRequestException(final List<ValidationFailureDto> validationFailures) {
super();
this.validationFailures = validationFailures;
}
#ApiModelProperty(value = "List of validationFailures")
public List<ValidationFailureDto> getValidationFailures() {
return validationFailures;
}
}
When I throw that exception at first I was only getting back the deserialised BadRequestException, minus the additional property (validationFailures)
{
code: "400",
message: "Bad request"
}
This is because Dropwizard's internals have a default exception mapper that allows Jetty/Jackson to understand domain exceptions and how to send the appropriate HTTP response.
To overcome this you can implement your own ExceptionMapper class and register it with Dropwizard.
public class CustomBadRequestExceptionMapper implements ExceptionMapper<SamplePackOrderBadRequestException> {
/**
* Allows jackson to deserialise custom exceptions and its properties to JSON response
*
* #param exception exception
* #return response object
*/
#Override
public Response toResponse(final SamplePackOrderBadRequestException exception) {
if (exception instanceof SamplePackOrderBadRequestException) {
SamplePackOrderBadRequestException samplePackOrderBadRequestException
= (SamplePackOrderBadRequestException) exception;
return Response
.status(400)
.entity(samplePackOrderBadRequestException)
.build();
}
return Response.status(400).build();
}
}
However this issue with this is that it deserializes super (Throwable), so you get every single inherited property added in the response which I do not want.
To combat this I tried adding Jackson annotations like so:
#JsonIgnoreProperties(value = "stackTrace")
This is not an optimal solution as there are several properties other than stackTrace that I will need to ignore.
So to summarise, how can I get Dropwizard to properly deserialize my CustomException class without all the additional clutter that I do not need?
I think the easier option is to transform exception to a Error bean and return it as shown below.
public class CustomBadRequestExceptionMapper implements ExceptionMapper<SamplePackOrderBadRequestException> {
#Override
public Response toResponse(final SamplePackOrderBadRequestException exception) {
if (exception instanceof SamplePackOrderBadRequestException) {
SamplePackOrderBadRequestException ex
= (SamplePackOrderBadRequestException) exception;
return Response
.status(400)
.entity(new ErrorBean(400,ex.getMessage,ex.getgetValidationFailures()))
.build();
}
return Response.status(400).build();
}
}
And ErrorBean.java
public static class ErrorBean{
private int code;
private String message;
private List<ValidationFailureDto> failures;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public List<ValidationFailureDto> getFailures() {
return failures;
}
public void setFailures(List<ValidationFailureDto> failures) {
this.failures = failures;
}
}
I have a Spring controller that takes posts and it works. The only problem is that our SMS providers will be sending us headers that contain keys with a capitalized first letter, for example:
{
"FromPhoneNumber":"15177754077",
"ToPhoneNumber":"17572046106",
"ResponseReceiveDate":"7/29/2014 5:25:10 AM",
"Message":"PIN 1234"
}
Spring will throw an error like:
Could not read JSON: Unrecognized field "FromPhoneNumber" (class com.talksoft.spring.rest.domain.CDynePost), not marked as ignorable (4 known properties: "responseReceiveDate", "toPhoneNumber", "fromPhoneNumber", "message"])
So, there must be a way for me to override this behavior. Here is the controller method that handles the CDyne posts:
#RequestMapping(method = RequestMethod.POST, value="/celltrust")
public ResponseEntity<String> cellTrustPost(#RequestBody CDynePost cDynePost) {
String message = "FAILED";
UserInteraction userInteraction = getUserInteraction(cDynePost);
boolean success = someSpringService.logMessage(userInteraction);
if (success) {
message = "OK";
return new ResponseEntity<String>(message, HttpStatus.ACCEPTED);
} else {
return new ResponseEntity<String>(message, HttpStatus.FAILED_DEPENDENCY);
}
}
and here is the CDynePost class:
public class CDynePost {
private String FromPhoneNumber;
private String ToPhoneNumber;
private String ResponseReceiveDate;
private String Message;
public String getFromPhoneNumber() {
return FromPhoneNumber;
}
public void setFromPhoneNumber(String FromPhoneNumber) {
this.FromPhoneNumber = FromPhoneNumber;
}
public String getToPhoneNumber() {
return ToPhoneNumber;
}
public void setToPhoneNumber(String ToPhoneNumber) {
this.ToPhoneNumber = ToPhoneNumber;
}
public String getResponseReceiveDate() {
return ResponseReceiveDate;
}
public void setResponseReceiveDate(String ResponseReceiveDate) {
this.ResponseReceiveDate = ResponseReceiveDate;
}
public String getMessage() {
return Message;
}
public void setMessage(String Message) {
this.Message = Message;
}
}
I've looked at ObjectMapper but I am not sure how to work this into my controller, and truth be told I'd prefer not to have to write a bunch of extra classes if Spring will do it for free.
Simply annotate your field, getter, or setter with #JsonProperty, specifying the exact name that will appear in the JSON. For example
#JsonProperty("FromPhoneNumber")
private String FromPhoneNumber;