Improved exception handling for 301, null response body and connection issues

This commit is contained in:
Rohit Awate 2018-02-11 22:26:33 +05:30
parent 6f9fba2c51
commit bc3c576dc6
5 changed files with 172 additions and 86 deletions

View file

@ -17,6 +17,7 @@ package com.rohitawate.restaurant.dashboard;
import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXSnackbar; import com.jfoenix.controls.JFXSnackbar;
import com.rohitawate.restaurant.exceptions.UnreliableResponseException;
import com.rohitawate.restaurant.models.requests.DELETERequest; import com.rohitawate.restaurant.models.requests.DELETERequest;
import com.rohitawate.restaurant.models.requests.DataDispatchRequest; import com.rohitawate.restaurant.models.requests.DataDispatchRequest;
import com.rohitawate.restaurant.models.requests.GETRequest; 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.HBox;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import javax.ws.rs.ProcessingException;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.net.ConnectException;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.net.UnknownHostException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.ResourceBundle; import java.util.ResourceBundle;
@ -115,6 +115,9 @@ public class DashboardController implements Initializable {
snackBar = new JFXSnackbar(dashboard); snackBar = new JFXSnackbar(dashboard);
bodyTab.disableProperty().bind(Bindings.and(httpMethodBox.valueProperty().isNotEqualTo("POST"), bodyTab.disableProperty().bind(Bindings.and(httpMethodBox.valueProperty().isNotEqualTo("POST"),
httpMethodBox.valueProperty().isNotEqualTo("PUT"))); 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 @FXML
@ -166,13 +169,18 @@ public class DashboardController implements Initializable {
}); });
requestManager.setOnFailed(e -> { requestManager.setOnFailed(e -> {
loadingLayer.setVisible(false); loadingLayer.setVisible(false);
errorLayer.setVisible(true); promptLayer.setVisible(false);
Throwable exception = requestManager.getException().getCause(); Throwable exception = requestManager.getException();
if (exception.getClass() == UnknownHostException.class) { if (exception.getClass() == UnreliableResponseException.class) {
errorTitle.setText("No Internet Connection"); UnreliableResponseException URE = (UnreliableResponseException) exception;
errorDetails.setText("Could not connect to the server. Please check your connection."); 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.reset();
}); });
requestManager.start(); requestManager.start();
@ -213,11 +221,19 @@ public class DashboardController implements Initializable {
}); });
requestManager.setOnFailed(e -> { requestManager.setOnFailed(e -> {
loadingLayer.setVisible(false); loadingLayer.setVisible(false);
promptLayer.setVisible(true); promptLayer.setVisible(false);
if (requestManager.getException().getClass() == ConnectException.class) Throwable exception = requestManager.getException();
snackBar.show("Request timed out. Server is unavailable or didn't respond.", 10000);
else if (requestManager.getException().getClass() == FileNotFoundException.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("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); snackBar.show("File could not be found.", 5000);
errorLayer.setVisible(true);
requestManager.reset(); requestManager.reset();
}); });
requestManager.start(); requestManager.start();
@ -253,13 +269,18 @@ public class DashboardController implements Initializable {
}); });
requestManager.setOnFailed(e -> { requestManager.setOnFailed(e -> {
loadingLayer.setVisible(false); loadingLayer.setVisible(false);
errorLayer.setVisible(true); promptLayer.setVisible(false);
Throwable exception = requestManager.getException().getCause(); 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"); errorTitle.setText("No Internet Connection");
errorDetails.setText("Could not connect to the server. Please check your connection."); errorDetails.setText("Could not connect to the server. Please check your connection.");
} }
errorLayer.setVisible(true);
requestManager.reset(); requestManager.reset();
}); });
requestManager.start(); requestManager.start();

View file

@ -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.
* <p>
* 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;
}
}

View file

@ -19,6 +19,7 @@ package com.rohitawate.restaurant.requestsmanager;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.SerializationFeature;
import com.rohitawate.restaurant.exceptions.UnreliableResponseException;
import com.rohitawate.restaurant.models.requests.DELETERequest; import com.rohitawate.restaurant.models.requests.DELETERequest;
import com.rohitawate.restaurant.models.responses.RestaurantResponse; import com.rohitawate.restaurant.models.responses.RestaurantResponse;
import javafx.concurrent.Task; import javafx.concurrent.Task;
@ -26,7 +27,6 @@ import javafx.concurrent.Task;
import javax.ws.rs.client.Invocation; import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget; import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -58,22 +58,22 @@ public class DELETERequestManager extends RequestManager {
response.setTime(initialTime, System.currentTimeMillis()); response.setTime(initialTime, System.currentTimeMillis());
if (serverResponse == null) 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) { else if (serverResponse.getStatus() == 301) {
response.setStatusCode(301);
String newLocation = serverResponse.getHeaderString("location"); String newLocation = serverResponse.getHeaderString("location");
String responseHelpText;
String responseHelpText;
if (newLocation == null) 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" + "Here's what you can do:\n" +
"- Find the new URL from the API documentation.\n" + "- Find the new URL from the API documentation.\n" +
"- Try using https instead of http if you're not already."; "- Try using https instead of http if you're not already.";
else 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); throw new UnreliableResponseException("301: Resource Moved Permanently", responseHelpText);
return response;
} }
String type = (String) serverResponse.getHeaders().getFirst("Content-type"); String type = (String) serverResponse.getHeaders().getFirst("Content-type");
@ -82,6 +82,7 @@ public class DELETERequestManager extends RequestManager {
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.INDENT_OUTPUT, true); mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
if (type != null) {
switch (type.toLowerCase()) { switch (type.toLowerCase()) {
case "application/json; charset=utf-8": case "application/json; charset=utf-8":
case "application/json": case "application/json":
@ -99,6 +100,9 @@ public class DELETERequestManager extends RequestManager {
default: default:
response.setBody(responseBody); response.setBody(responseBody);
} }
} else {
response.setBody("No body found in the response.");
}
response.setMediaType(serverResponse.getMediaType()); response.setMediaType(serverResponse.getMediaType());
response.setStatusCode(serverResponse.getStatus()); response.setStatusCode(serverResponse.getStatus());

View file

@ -19,6 +19,7 @@ package com.rohitawate.restaurant.requestsmanager;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.SerializationFeature;
import com.rohitawate.restaurant.exceptions.UnreliableResponseException;
import com.rohitawate.restaurant.models.requests.DataDispatchRequest; import com.rohitawate.restaurant.models.requests.DataDispatchRequest;
import com.rohitawate.restaurant.models.responses.RestaurantResponse; import com.rohitawate.restaurant.models.responses.RestaurantResponse;
import javafx.concurrent.Task; 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.Form;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; 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.HashMap;
import java.util.Map; import java.util.Map;
@ -132,22 +136,22 @@ public class DataDispatchRequestManager extends RequestManager {
response.setTime(initialTime, System.currentTimeMillis()); response.setTime(initialTime, System.currentTimeMillis());
if (serverResponse == null) 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) { else if (serverResponse.getStatus() == 301) {
response.setStatusCode(301);
String newLocation = serverResponse.getHeaderString("location"); String newLocation = serverResponse.getHeaderString("location");
String responseHelpText;
String responseHelpText;
if (newLocation == null) 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" + "Here's what you can do:\n" +
"- Find the new URL from the API documentation.\n" + "- Find the new URL from the API documentation.\n" +
"- Try using https instead of http if you're not already."; "- Try using https instead of http if you're not already.";
else 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); throw new UnreliableResponseException("301: Resource Moved Permanently", responseHelpText);
return response;
} }
String type = (String) serverResponse.getHeaders().getFirst("Content-type"); String type = (String) serverResponse.getHeaders().getFirst("Content-type");
@ -156,6 +160,7 @@ public class DataDispatchRequestManager extends RequestManager {
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.INDENT_OUTPUT, true); mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
if (type != null) {
switch (type.toLowerCase()) { switch (type.toLowerCase()) {
case "application/json; charset=utf-8": case "application/json; charset=utf-8":
case "application/json": case "application/json":
@ -173,6 +178,9 @@ public class DataDispatchRequestManager extends RequestManager {
default: default:
response.setBody(responseBody); response.setBody(responseBody);
} }
} else {
response.setBody("No body found in the response.");
}
response.setMediaType(serverResponse.getMediaType()); response.setMediaType(serverResponse.getMediaType());
response.setStatusCode(serverResponse.getStatus()); response.setStatusCode(serverResponse.getStatus());

View file

@ -19,13 +19,13 @@ package com.rohitawate.restaurant.requestsmanager;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.SerializationFeature;
import com.rohitawate.restaurant.exceptions.UnreliableResponseException;
import com.rohitawate.restaurant.models.responses.RestaurantResponse; import com.rohitawate.restaurant.models.responses.RestaurantResponse;
import javafx.concurrent.Task; import javafx.concurrent.Task;
import javax.ws.rs.client.Invocation.Builder; import javax.ws.rs.client.Invocation.Builder;
import javax.ws.rs.client.WebTarget; import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -53,22 +53,22 @@ public class GETRequestManager extends RequestManager {
response.setTime(initialTime, System.currentTimeMillis()); response.setTime(initialTime, System.currentTimeMillis());
if (serverResponse == null) 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) { else if (serverResponse.getStatus() == 301) {
response.setStatusCode(301);
String newLocation = serverResponse.getHeaderString("location"); String newLocation = serverResponse.getHeaderString("location");
String responseHelpText;
String responseHelpText;
if (newLocation == null) 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" + "Here's what you can do:\n" +
"- Find the new URL from the API documentation.\n" + "- Find the new URL from the API documentation.\n" +
"- Try using https instead of http if you're not already."; "- Try using https instead of http if you're not already.";
else 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); throw new UnreliableResponseException("301: Resource Moved Permanently", responseHelpText);
return response;
} }
String type = (String) serverResponse.getHeaders().getFirst("Content-type"); String type = (String) serverResponse.getHeaders().getFirst("Content-type");
@ -77,6 +77,7 @@ public class GETRequestManager extends RequestManager {
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.INDENT_OUTPUT, true); mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
if (type != null) {
switch (type.toLowerCase()) { switch (type.toLowerCase()) {
case "application/json; charset=utf-8": case "application/json; charset=utf-8":
case "application/json": case "application/json":
@ -94,6 +95,9 @@ public class GETRequestManager extends RequestManager {
default: default:
response.setBody(responseBody); response.setBody(responseBody);
} }
} else {
response.setBody("No body found in the response.");
}
response.setMediaType(serverResponse.getMediaType()); response.setMediaType(serverResponse.getMediaType());
response.setStatusCode(serverResponse.getStatus()); response.setStatusCode(serverResponse.getStatus());