From bc3c576dc66965e5999cccc7321c6d6c9c40f101 Mon Sep 17 00:00:00 2001 From: Rohit Awate Date: Sun, 11 Feb 2018 22:26:33 +0530 Subject: [PATCH] Improved exception handling for 301, null response body and connection issues --- .../dashboard/DashboardController.java | 49 +++++++++++----- .../UnreliableResponseException.java | 49 ++++++++++++++++ .../requestsmanager/DELETERequestManager.java | 52 +++++++++-------- .../DataDispatchRequestManager.java | 56 +++++++++++-------- .../requestsmanager/GETRequestManager.java | 52 +++++++++-------- 5 files changed, 172 insertions(+), 86 deletions(-) create mode 100644 src/main/java/com/rohitawate/restaurant/exceptions/UnreliableResponseException.java diff --git a/src/main/java/com/rohitawate/restaurant/dashboard/DashboardController.java b/src/main/java/com/rohitawate/restaurant/dashboard/DashboardController.java index 6edb677..028b971 100644 --- a/src/main/java/com/rohitawate/restaurant/dashboard/DashboardController.java +++ b/src/main/java/com/rohitawate/restaurant/dashboard/DashboardController.java @@ -17,6 +17,7 @@ package com.rohitawate.restaurant.dashboard; import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXSnackbar; +import com.rohitawate.restaurant.exceptions.UnreliableResponseException; import com.rohitawate.restaurant.models.requests.DELETERequest; import com.rohitawate.restaurant.models.requests.DataDispatchRequest; import com.rohitawate.restaurant.models.requests.GETRequest; @@ -37,13 +38,12 @@ import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; +import javax.ws.rs.ProcessingException; import javax.ws.rs.core.Response; import java.io.FileNotFoundException; import java.io.IOException; -import java.net.ConnectException; import java.net.MalformedURLException; import java.net.URL; -import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; import java.util.ResourceBundle; @@ -115,6 +115,9 @@ public class DashboardController implements Initializable { snackBar = new JFXSnackbar(dashboard); bodyTab.disableProperty().bind(Bindings.and(httpMethodBox.valueProperty().isNotEqualTo("POST"), httpMethodBox.valueProperty().isNotEqualTo("PUT"))); + + errorTitle.setText("Oops... That's embarrassing!"); + errorDetails.setText("Something went wrong. Try to make another request.\nRestart RESTaurant if that doesn't work."); } @FXML @@ -166,13 +169,18 @@ public class DashboardController implements Initializable { }); requestManager.setOnFailed(e -> { loadingLayer.setVisible(false); - errorLayer.setVisible(true); - Throwable exception = requestManager.getException().getCause(); + promptLayer.setVisible(false); + Throwable exception = requestManager.getException(); - if (exception.getClass() == UnknownHostException.class) { - errorTitle.setText("No Internet Connection"); - errorDetails.setText("Could not connect to the server. Please check your connection."); + if (exception.getClass() == UnreliableResponseException.class) { + UnreliableResponseException URE = (UnreliableResponseException) exception; + errorTitle.setText(URE.getExceptionTitle()); + errorDetails.setText(URE.getExceptionDetails()); + } else if (exception.getClass() == ProcessingException.class) { + errorTitle.setText("RESTaurant couldn't connect."); + errorDetails.setText("Either you are not connected to the Internet or the server is offline."); } + errorLayer.setVisible(true); requestManager.reset(); }); requestManager.start(); @@ -213,11 +221,19 @@ public class DashboardController implements Initializable { }); requestManager.setOnFailed(e -> { loadingLayer.setVisible(false); - promptLayer.setVisible(true); - if (requestManager.getException().getClass() == ConnectException.class) - snackBar.show("Request timed out. Server is unavailable or didn't respond.", 10000); - else if (requestManager.getException().getClass() == FileNotFoundException.class) + promptLayer.setVisible(false); + Throwable exception = requestManager.getException(); + + if (exception.getClass() == UnreliableResponseException.class) { + UnreliableResponseException URE = (UnreliableResponseException) exception; + errorTitle.setText(URE.getExceptionTitle()); + errorDetails.setText(URE.getExceptionDetails()); + } else if (exception.getClass() == ProcessingException.class) { + errorTitle.setText("RESTaurant couldn't connect."); + errorDetails.setText("Either you are not connected to the Internet or the server is offline."); + } else if (exception.getClass() == FileNotFoundException.class) snackBar.show("File could not be found.", 5000); + errorLayer.setVisible(true); requestManager.reset(); }); requestManager.start(); @@ -253,13 +269,18 @@ public class DashboardController implements Initializable { }); requestManager.setOnFailed(e -> { loadingLayer.setVisible(false); - errorLayer.setVisible(true); - Throwable exception = requestManager.getException().getCause(); + promptLayer.setVisible(false); + Throwable exception = requestManager.getException(); - if (exception.getClass() == UnknownHostException.class) { + if (exception.getClass() == UnreliableResponseException.class) { + UnreliableResponseException URE = (UnreliableResponseException) exception; + errorTitle.setText(URE.getExceptionTitle()); + errorDetails.setText(URE.getExceptionDetails()); + } else if (exception.getClass() == ProcessingException.class) { errorTitle.setText("No Internet Connection"); errorDetails.setText("Could not connect to the server. Please check your connection."); } + errorLayer.setVisible(true); requestManager.reset(); }); requestManager.start(); diff --git a/src/main/java/com/rohitawate/restaurant/exceptions/UnreliableResponseException.java b/src/main/java/com/rohitawate/restaurant/exceptions/UnreliableResponseException.java new file mode 100644 index 0000000..0dd7512 --- /dev/null +++ b/src/main/java/com/rohitawate/restaurant/exceptions/UnreliableResponseException.java @@ -0,0 +1,49 @@ +/* + * Copyright 2018 Rohit Awate. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.rohitawate.restaurant.exceptions; + +/** + * Thrown when the server sends ambiguous responses. + * For example, some servers do not provide the new location in case of a 301 Moved Permanently. + *

+ * Used by DashboardController to display ErrorLayer. + */ +public class UnreliableResponseException extends Exception { + private String exceptionTitle; + private String exceptionDetails; + + public UnreliableResponseException(String exceptionTitle, String exceptionDetails) { + this.exceptionTitle = exceptionTitle; + this.exceptionDetails = exceptionDetails; + } + + public void setExceptionTitle(String exceptionTitle) { + this.exceptionTitle = exceptionTitle; + } + + public void setExceptionDetails(String exceptionDetails) { + this.exceptionDetails = exceptionDetails; + } + + public String getExceptionTitle() { + return exceptionTitle; + } + + public String getExceptionDetails() { + return exceptionDetails; + } +} diff --git a/src/main/java/com/rohitawate/restaurant/requestsmanager/DELETERequestManager.java b/src/main/java/com/rohitawate/restaurant/requestsmanager/DELETERequestManager.java index a724e07..226d6c5 100644 --- a/src/main/java/com/rohitawate/restaurant/requestsmanager/DELETERequestManager.java +++ b/src/main/java/com/rohitawate/restaurant/requestsmanager/DELETERequestManager.java @@ -19,6 +19,7 @@ package com.rohitawate.restaurant.requestsmanager; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +import com.rohitawate.restaurant.exceptions.UnreliableResponseException; import com.rohitawate.restaurant.models.requests.DELETERequest; import com.rohitawate.restaurant.models.responses.RestaurantResponse; import javafx.concurrent.Task; @@ -26,7 +27,6 @@ import javafx.concurrent.Task; import javax.ws.rs.client.Invocation; import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.Response; -import java.io.IOException; import java.util.HashMap; import java.util.Map; @@ -58,22 +58,22 @@ public class DELETERequestManager extends RequestManager { response.setTime(initialTime, System.currentTimeMillis()); if (serverResponse == null) - throw new IOException(); + throw new UnreliableResponseException("The server did not respond.", + "Like that crush from high school.."); else if (serverResponse.getStatus() == 301) { - response.setStatusCode(301); String newLocation = serverResponse.getHeaderString("location"); - String responseHelpText; + String responseHelpText; if (newLocation == null) - responseHelpText = "The resource has been permanently moved to another location.\n\n" + + responseHelpText = "The resource has been permanently moved to another location.\n" + "Here's what you can do:\n" + "- Find the new URL from the API documentation.\n" + "- Try using https instead of http if you're not already."; else - responseHelpText = "The resource has been permanently moved to: " + newLocation; + responseHelpText = "The resource has been permanently moved to: " + newLocation + + "\nRESTaurant doesn't automatically redirect your requests."; - response.setBody(responseHelpText); - return response; + throw new UnreliableResponseException("301: Resource Moved Permanently", responseHelpText); } String type = (String) serverResponse.getHeaders().getFirst("Content-type"); @@ -82,22 +82,26 @@ public class DELETERequestManager extends RequestManager { ObjectMapper mapper = new ObjectMapper(); mapper.configure(SerializationFeature.INDENT_OUTPUT, true); - switch (type.toLowerCase()) { - case "application/json; charset=utf-8": - case "application/json": - JsonNode node = mapper.readTree(responseBody); - response.setBody(mapper.writeValueAsString(node)); - break; - case "application/xml; charset=utf-8": - case "application/xml": - response.setBody(mapper.writeValueAsString(responseBody)); - break; - case "text/html": - case "text/html; charset=utf-8": - response.setBody(responseBody); - break; - default: - response.setBody(responseBody); + if (type != null) { + switch (type.toLowerCase()) { + case "application/json; charset=utf-8": + case "application/json": + JsonNode node = mapper.readTree(responseBody); + response.setBody(mapper.writeValueAsString(node)); + break; + case "application/xml; charset=utf-8": + case "application/xml": + response.setBody(mapper.writeValueAsString(responseBody)); + break; + case "text/html": + case "text/html; charset=utf-8": + response.setBody(responseBody); + break; + default: + response.setBody(responseBody); + } + } else { + response.setBody("No body found in the response."); } response.setMediaType(serverResponse.getMediaType()); diff --git a/src/main/java/com/rohitawate/restaurant/requestsmanager/DataDispatchRequestManager.java b/src/main/java/com/rohitawate/restaurant/requestsmanager/DataDispatchRequestManager.java index 1468882..f42ca52 100644 --- a/src/main/java/com/rohitawate/restaurant/requestsmanager/DataDispatchRequestManager.java +++ b/src/main/java/com/rohitawate/restaurant/requestsmanager/DataDispatchRequestManager.java @@ -19,6 +19,7 @@ package com.rohitawate.restaurant.requestsmanager; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +import com.rohitawate.restaurant.exceptions.UnreliableResponseException; import com.rohitawate.restaurant.models.requests.DataDispatchRequest; import com.rohitawate.restaurant.models.responses.RestaurantResponse; import javafx.concurrent.Task; @@ -32,7 +33,10 @@ import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.Form; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import java.io.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; import java.util.HashMap; import java.util.Map; @@ -132,22 +136,22 @@ public class DataDispatchRequestManager extends RequestManager { response.setTime(initialTime, System.currentTimeMillis()); if (serverResponse == null) - throw new IOException(); + throw new UnreliableResponseException("The server did not respond.", + "Like that crush from high school.."); else if (serverResponse.getStatus() == 301) { - response.setStatusCode(301); String newLocation = serverResponse.getHeaderString("location"); - String responseHelpText; + String responseHelpText; if (newLocation == null) - responseHelpText = "The resource has been permanently moved to another location.\n\n" + + responseHelpText = "The resource has been permanently moved to another location.\n" + "Here's what you can do:\n" + "- Find the new URL from the API documentation.\n" + "- Try using https instead of http if you're not already."; else - responseHelpText = "The resource has been permanently moved to: " + newLocation; + responseHelpText = "The resource has been permanently moved to: " + newLocation + + "\nRESTaurant doesn't automatically redirect your requests."; - response.setBody(responseHelpText); - return response; + throw new UnreliableResponseException("301: Resource Moved Permanently", responseHelpText); } String type = (String) serverResponse.getHeaders().getFirst("Content-type"); @@ -156,22 +160,26 @@ public class DataDispatchRequestManager extends RequestManager { ObjectMapper mapper = new ObjectMapper(); mapper.configure(SerializationFeature.INDENT_OUTPUT, true); - switch (type.toLowerCase()) { - case "application/json; charset=utf-8": - case "application/json": - JsonNode node = mapper.readTree(responseBody); - response.setBody(mapper.writeValueAsString(node)); - break; - case "application/xml; charset=utf-8": - case "application/xml": - response.setBody(mapper.writeValueAsString(responseBody)); - break; - case "text/html": - case "text/html; charset=utf-8": - response.setBody(responseBody); - break; - default: - response.setBody(responseBody); + if (type != null) { + switch (type.toLowerCase()) { + case "application/json; charset=utf-8": + case "application/json": + JsonNode node = mapper.readTree(responseBody); + response.setBody(mapper.writeValueAsString(node)); + break; + case "application/xml; charset=utf-8": + case "application/xml": + response.setBody(mapper.writeValueAsString(responseBody)); + break; + case "text/html": + case "text/html; charset=utf-8": + response.setBody(responseBody); + break; + default: + response.setBody(responseBody); + } + } else { + response.setBody("No body found in the response."); } response.setMediaType(serverResponse.getMediaType()); diff --git a/src/main/java/com/rohitawate/restaurant/requestsmanager/GETRequestManager.java b/src/main/java/com/rohitawate/restaurant/requestsmanager/GETRequestManager.java index 5db8af5..96e4ced 100644 --- a/src/main/java/com/rohitawate/restaurant/requestsmanager/GETRequestManager.java +++ b/src/main/java/com/rohitawate/restaurant/requestsmanager/GETRequestManager.java @@ -19,13 +19,13 @@ package com.rohitawate.restaurant.requestsmanager; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +import com.rohitawate.restaurant.exceptions.UnreliableResponseException; import com.rohitawate.restaurant.models.responses.RestaurantResponse; import javafx.concurrent.Task; import javax.ws.rs.client.Invocation.Builder; import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.Response; -import java.io.IOException; import java.util.HashMap; import java.util.Map; @@ -53,22 +53,22 @@ public class GETRequestManager extends RequestManager { response.setTime(initialTime, System.currentTimeMillis()); if (serverResponse == null) - throw new IOException(); + throw new UnreliableResponseException("The server did not respond.", + "Like that crush from high school.."); else if (serverResponse.getStatus() == 301) { - response.setStatusCode(301); String newLocation = serverResponse.getHeaderString("location"); - String responseHelpText; + String responseHelpText; if (newLocation == null) - responseHelpText = "The resource has been permanently moved to another location.\n\n" + + responseHelpText = "The resource has been permanently moved to another location.\n" + "Here's what you can do:\n" + "- Find the new URL from the API documentation.\n" + "- Try using https instead of http if you're not already."; else - responseHelpText = "The resource has been permanently moved to: " + newLocation; + responseHelpText = "The resource has been permanently moved to: " + newLocation + + "\nRESTaurant doesn't automatically redirect your requests."; - response.setBody(responseHelpText); - return response; + throw new UnreliableResponseException("301: Resource Moved Permanently", responseHelpText); } String type = (String) serverResponse.getHeaders().getFirst("Content-type"); @@ -77,22 +77,26 @@ public class GETRequestManager extends RequestManager { ObjectMapper mapper = new ObjectMapper(); mapper.configure(SerializationFeature.INDENT_OUTPUT, true); - switch (type.toLowerCase()) { - case "application/json; charset=utf-8": - case "application/json": - JsonNode node = mapper.readTree(responseBody); - response.setBody(mapper.writeValueAsString(node)); - break; - case "application/xml; charset=utf-8": - case "application/xml": - response.setBody(mapper.writeValueAsString(responseBody)); - break; - case "text/html": - case "text/html; charset=utf-8": - response.setBody(responseBody); - break; - default: - response.setBody(responseBody); + if (type != null) { + switch (type.toLowerCase()) { + case "application/json; charset=utf-8": + case "application/json": + JsonNode node = mapper.readTree(responseBody); + response.setBody(mapper.writeValueAsString(node)); + break; + case "application/xml; charset=utf-8": + case "application/xml": + response.setBody(mapper.writeValueAsString(responseBody)); + break; + case "text/html": + case "text/html; charset=utf-8": + response.setBody(responseBody); + break; + default: + response.setBody(responseBody); + } + } else { + response.setBody("No body found in the response."); } response.setMediaType(serverResponse.getMediaType());