Replace dedicated RequestManagers with a single one

This commit is contained in:
Rohit Awate 2018-08-12 12:02:10 +05:30
parent 2e8cb06cb7
commit a8413e8b5c
No known key found for this signature in database
GPG key ID: 1051D7B79CF2EE25
10 changed files with 232 additions and 351 deletions

View file

@ -26,7 +26,7 @@ import com.rohitawate.everest.controllers.state.FieldState;
import com.rohitawate.everest.controllers.visualizers.TreeVisualizer; import com.rohitawate.everest.controllers.visualizers.TreeVisualizer;
import com.rohitawate.everest.controllers.visualizers.Visualizer; import com.rohitawate.everest.controllers.visualizers.Visualizer;
import com.rohitawate.everest.exceptions.RedirectException; import com.rohitawate.everest.exceptions.RedirectException;
import com.rohitawate.everest.exceptions.UnreliableResponseException; import com.rohitawate.everest.exceptions.NullResponseException;
import com.rohitawate.everest.format.FormatterFactory; import com.rohitawate.everest.format.FormatterFactory;
import com.rohitawate.everest.logging.LoggingService; import com.rohitawate.everest.logging.LoggingService;
import com.rohitawate.everest.misc.EverestUtilities; import com.rohitawate.everest.misc.EverestUtilities;
@ -34,9 +34,9 @@ import com.rohitawate.everest.misc.Services;
import com.rohitawate.everest.misc.ThemeManager; import com.rohitawate.everest.misc.ThemeManager;
import com.rohitawate.everest.models.requests.DELETERequest; import com.rohitawate.everest.models.requests.DELETERequest;
import com.rohitawate.everest.models.requests.DataRequest; import com.rohitawate.everest.models.requests.DataRequest;
import com.rohitawate.everest.models.requests.EverestRequest;
import com.rohitawate.everest.models.requests.GETRequest; import com.rohitawate.everest.models.requests.GETRequest;
import com.rohitawate.everest.models.responses.EverestResponse; import com.rohitawate.everest.models.responses.EverestResponse;
import com.rohitawate.everest.requestmanager.DataRequestManager;
import com.rohitawate.everest.requestmanager.RequestManager; import com.rohitawate.everest.requestmanager.RequestManager;
import com.rohitawate.everest.requestmanager.RequestManagersPool; import com.rohitawate.everest.requestmanager.RequestManagersPool;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
@ -239,7 +239,7 @@ public class DashboardController implements Initializable {
getRequest.setTarget(address); getRequest.setTarget(address);
getRequest.setHeaders(headerTabController.getHeaders()); getRequest.setHeaders(headerTabController.getHeaders());
requestManager = RequestManagersPool.get(); requestManager = RequestManagersPool.manager();
requestManager.setRequest(getRequest); requestManager.setRequest(getRequest);
break; break;
case "POST": case "POST":
@ -284,7 +284,7 @@ public class DashboardController implements Initializable {
dataRequest.setContentType(MediaType.APPLICATION_FORM_URLENCODED); dataRequest.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
} }
requestManager = RequestManagersPool.data(); requestManager = RequestManagersPool.manager();
requestManager.setRequest(dataRequest); requestManager.setRequest(dataRequest);
break; break;
case "DELETE": case "DELETE":
@ -294,7 +294,7 @@ public class DashboardController implements Initializable {
deleteRequest.setTarget(address); deleteRequest.setTarget(address);
deleteRequest.setHeaders(headerTabController.getHeaders()); deleteRequest.setHeaders(headerTabController.getHeaders());
requestManager = RequestManagersPool.delete(); requestManager = RequestManagersPool.manager();
requestManager.setRequest(deleteRequest); requestManager.setRequest(deleteRequest);
break; break;
default: default:
@ -322,8 +322,8 @@ public class DashboardController implements Initializable {
Exception exception = (Exception) throwable; Exception exception = (Exception) throwable;
LoggingService.logWarning(httpMethodBox.getValue() + " request could not be processed.", exception, LocalDateTime.now()); LoggingService.logWarning(httpMethodBox.getValue() + " request could not be processed.", exception, LocalDateTime.now());
if (throwable.getClass() == UnreliableResponseException.class) { if (throwable.getClass() == NullResponseException.class) {
UnreliableResponseException URE = (UnreliableResponseException) throwable; NullResponseException URE = (NullResponseException) throwable;
errorTitle.setText(URE.getExceptionTitle()); errorTitle.setText(URE.getExceptionTitle());
errorDetails.setText(URE.getExceptionDetails()); errorDetails.setText(URE.getExceptionDetails());
} else if (throwable.getClass() == ProcessingException.class) { } else if (throwable.getClass() == ProcessingException.class) {
@ -338,7 +338,7 @@ public class DashboardController implements Initializable {
return; return;
} }
if (requestManager.getClass() == DataRequestManager.class) { if (requestManager.getRequest().getClass().equals(DataRequest.class)) {
if (throwable.getCause() != null && throwable.getCause().getClass() == IllegalArgumentException.class) { if (throwable.getCause() != null && throwable.getCause().getClass() == IllegalArgumentException.class) {
errorTitle.setText("Did you forget something?"); errorTitle.setText("Did you forget something?");
errorDetails.setText("Please specify a body for your " + httpMethodBox.getValue() + " request."); errorDetails.setText("Please specify a body for your " + httpMethodBox.getValue() + " request.");

View file

@ -78,7 +78,7 @@ public class HomeWindowController implements Initializable {
e.printStackTrace(); e.printStackTrace();
} }
// Using LinkedHashMap because they retain order // Using LinkedHashMap because it retains order
tabStateMap = new LinkedHashMap<>(); tabStateMap = new LinkedHashMap<>();
recoverState(); recoverState();
@ -199,9 +199,10 @@ public class HomeWindowController implements Initializable {
} }
private void removeTab(Tab newTab) { private void removeTab(Tab newTab) {
DashboardState closedState = tabStateMap.remove(newTab); DashboardState state = tabStateMap.remove(newTab);
closedState = null; state = null;
tabPane.getTabs().remove(newTab); tabPane.getTabs().remove(newTab);
newTab.setOnCloseRequest(null);
newTab = null; newTab = null;
} }

View file

@ -20,11 +20,11 @@ import com.rohitawate.everest.controllers.DashboardController.ComposerTab;
import com.rohitawate.everest.controllers.DashboardController.ResponseLayer; import com.rohitawate.everest.controllers.DashboardController.ResponseLayer;
import com.rohitawate.everest.controllers.DashboardController.ResponseTab; import com.rohitawate.everest.controllers.DashboardController.ResponseTab;
import com.rohitawate.everest.exceptions.RedirectException; import com.rohitawate.everest.exceptions.RedirectException;
import com.rohitawate.everest.exceptions.UnreliableResponseException; import com.rohitawate.everest.exceptions.NullResponseException;
import com.rohitawate.everest.logging.LoggingService; import com.rohitawate.everest.logging.LoggingService;
import com.rohitawate.everest.models.requests.DataRequest;
import com.rohitawate.everest.models.requests.EverestRequest; import com.rohitawate.everest.models.requests.EverestRequest;
import com.rohitawate.everest.models.responses.EverestResponse; import com.rohitawate.everest.models.responses.EverestResponse;
import com.rohitawate.everest.requestmanager.DataRequestManager;
import com.rohitawate.everest.requestmanager.RequestManager; import com.rohitawate.everest.requestmanager.RequestManager;
import javafx.event.Event; import javafx.event.Event;
@ -106,8 +106,8 @@ public class DashboardState {
Exception exception = (Exception) throwable; Exception exception = (Exception) throwable;
LoggingService.logWarning(this.composer.httpMethod + " request could not be processed.", exception, LocalDateTime.now()); LoggingService.logWarning(this.composer.httpMethod + " request could not be processed.", exception, LocalDateTime.now());
if (throwable.getClass() == UnreliableResponseException.class) { if (throwable.getClass() == NullResponseException.class) {
UnreliableResponseException URE = (UnreliableResponseException) throwable; NullResponseException URE = (NullResponseException) throwable;
errorTitle = URE.getExceptionTitle(); errorTitle = URE.getExceptionTitle();
errorDetails = URE.getExceptionDetails(); errorDetails = URE.getExceptionDetails();
} else if (throwable.getClass() == ProcessingException.class) { } else if (throwable.getClass() == ProcessingException.class) {
@ -131,7 +131,7 @@ public class DashboardState {
errorDetails = "Something went wrong. Try to make another request.Restart Everest if that doesn't work."; errorDetails = "Something went wrong. Try to make another request.Restart Everest if that doesn't work.";
} }
if (requestManager.getClass() == DataRequestManager.class) { if (requestManager.getRequest().getClass().equals(DataRequest.class)) {
if (throwable.getCause() != null && throwable.getCause().getClass() == IllegalArgumentException.class) { if (throwable.getCause() != null && throwable.getCause().getClass() == IllegalArgumentException.class) {
errorTitle = "Did you forget something?"; errorTitle = "Did you forget something?";
errorDetails = "Please specify a body for your " + this.composer.httpMethod + " request."; errorDetails = "Please specify a body for your " + this.composer.httpMethod + " request.";

View file

@ -33,6 +33,7 @@ public class TreeVisualizer extends Visualizer {
public TreeVisualizer() { public TreeVisualizer() {
visualizer = new TreeView<>(); visualizer = new TreeView<>();
visualizer.setShowRoot(false); visualizer.setShowRoot(false);
visualizer.setCache(true);
setContent(visualizer); setContent(visualizer);
} }
@ -62,6 +63,7 @@ public class TreeVisualizer extends Visualizer {
items.add(new TreeItem<>(i++ + ": " + EverestUtilities.trimString(currentNode.toString()))); items.add(new TreeItem<>(i++ + ": " + EverestUtilities.trimString(currentNode.toString())));
} else if (currentNode.isObject()) { } else if (currentNode.isObject()) {
TreeItem<String> newRoot = new TreeItem<>(); TreeItem<String> newRoot = new TreeItem<>();
newRoot.setExpanded(true);
items.add(newRoot); items.add(newRoot);
populate(newRoot, i++ + ": [Anonymous Object]", currentNode); populate(newRoot, i++ + ": [Anonymous Object]", currentNode);
} }
@ -79,6 +81,7 @@ public class TreeVisualizer extends Visualizer {
+ EverestUtilities.trimString(currentNode.toString()))); + EverestUtilities.trimString(currentNode.toString())));
} else if (currentNode.isArray() || currentNode.isObject()) { } else if (currentNode.isArray() || currentNode.isObject()) {
TreeItem<String> newRoot = new TreeItem<>(); TreeItem<String> newRoot = new TreeItem<>();
newRoot.setExpanded(true);
items.add(newRoot); items.add(newRoot);
populate(newRoot, currentEntry.getKey(), currentNode); populate(newRoot, currentEntry.getKey(), currentNode);
} }
@ -90,5 +93,6 @@ public class TreeVisualizer extends Visualizer {
public void clear() { public void clear() {
visualizer.setRoot(null); visualizer.setRoot(null);
System.gc();
} }
} }

View file

@ -21,11 +21,11 @@ package com.rohitawate.everest.exceptions;
* <p> * <p>
* Used by DashboardController to display ErrorLayer. * Used by DashboardController to display ErrorLayer.
*/ */
public class UnreliableResponseException extends Exception { public class NullResponseException extends Exception {
private String exceptionTitle; private String exceptionTitle;
private String exceptionDetails; private String exceptionDetails;
public UnreliableResponseException(String exceptionTitle, String exceptionDetails) { public NullResponseException(String exceptionTitle, String exceptionDetails) {
this.exceptionTitle = exceptionTitle; this.exceptionTitle = exceptionTitle;
this.exceptionDetails = exceptionDetails; this.exceptionDetails = exceptionDetails;
} }

View file

@ -1,45 +0,0 @@
/*
* 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.everest.requestmanager;
import com.rohitawate.everest.models.responses.EverestResponse;
import javafx.concurrent.Task;
import javax.ws.rs.ProcessingException;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.core.Response;
public class DELETERequestManager extends RequestManager {
@Override
protected Task<EverestResponse> createTask() throws ProcessingException {
return new Task<EverestResponse>() {
@Override
protected EverestResponse call() throws Exception {
Invocation invocation = requestBuilder.buildDelete();
initialTime = System.currentTimeMillis();
Response serverResponse = invocation.invoke();
finalTime = System.currentTimeMillis();
processServerResponse(serverResponse);
return response;
}
};
}
}

View file

@ -1,181 +0,0 @@
/*
* 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.everest.requestmanager;
import com.rohitawate.everest.models.requests.DataRequest;
import com.rohitawate.everest.models.responses.EverestResponse;
import javafx.concurrent.Task;
import org.glassfish.jersey.media.multipart.FormDataMultiPart;
import org.glassfish.jersey.media.multipart.file.FileDataBodyPart;
import javax.ws.rs.ProcessingException;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Map;
/**
* Processes DataRequest by automatically determining whether it
* is a POST, PUT or PATCH request.
*/
public class DataRequestManager extends RequestManager {
private DataRequest dataRequest;
private String requestType;
@Override
protected Task<EverestResponse> createTask() throws ProcessingException {
return new Task<EverestResponse>() {
@Override
protected EverestResponse call() throws Exception {
dataRequest = (DataRequest) request;
requestType = dataRequest.getRequestType();
Invocation invocation = appendBody();
initialTime = System.currentTimeMillis();
Response serverResponse = invocation.invoke();
finalTime = System.currentTimeMillis();
processServerResponse(serverResponse);
return response;
}
};
}
/**
* Adds the request body based on the content type and generates an invocation.
*
* @return invocation object
*/
private Invocation appendBody() throws Exception {
/*
Checks if a custom mime-type is mentioned in the headers.
If present, it will override the logical Content-Type.
*/
String overriddenContentType = request.getHeaders().get("Content-Type");
Invocation invocation = null;
Map.Entry<String, String> mapEntry;
switch (dataRequest.getContentType()) {
case MediaType.MULTIPART_FORM_DATA:
FormDataMultiPart formData = new FormDataMultiPart();
// Adding the string tuples to the request
HashMap<String, String> pairs = dataRequest.getStringTuples();
for (Map.Entry entry : pairs.entrySet()) {
mapEntry = (Map.Entry) entry;
formData.field(mapEntry.getKey(), mapEntry.getValue());
}
String filePath;
File file;
boolean fileException = false;
String fileExceptionMessage = null;
pairs = dataRequest.getFileTuples();
// Adding the file tuples to the request
for (Map.Entry entry : pairs.entrySet()) {
mapEntry = (Map.Entry) entry;
filePath = mapEntry.getValue();
file = new File(filePath);
if (file.exists())
formData.bodyPart(new FileDataBodyPart(mapEntry.getKey(),
file, MediaType.APPLICATION_OCTET_STREAM_TYPE));
else {
fileException = true;
// For pretty-printing FileNotFoundException to the UI
fileExceptionMessage = " - " + filePath + "\n";
}
}
if (fileException) {
throw new FileNotFoundException(fileExceptionMessage);
}
formData.setMediaType(MediaType.MULTIPART_FORM_DATA_TYPE);
if (requestType.equals("POST"))
invocation = requestBuilder.buildPost(Entity.entity(formData, MediaType.MULTIPART_FORM_DATA_TYPE));
else
invocation = requestBuilder.buildPut(Entity.entity(formData, MediaType.MULTIPART_FORM_DATA_TYPE));
break;
case MediaType.APPLICATION_OCTET_STREAM:
if (overriddenContentType == null)
overriddenContentType = MediaType.APPLICATION_OCTET_STREAM;
filePath = dataRequest.getBody();
File check = new File(filePath);
if (!check.exists()) {
throw new FileNotFoundException(filePath);
}
FileInputStream stream = new FileInputStream(filePath);
if (requestType.equals("POST"))
invocation = requestBuilder.buildPost(Entity.entity(stream, overriddenContentType));
else
invocation = requestBuilder.buildPut(Entity.entity(stream, overriddenContentType));
break;
case MediaType.APPLICATION_FORM_URLENCODED:
if (overriddenContentType == null)
overriddenContentType = MediaType.APPLICATION_FORM_URLENCODED;
Form form = new Form();
for (Map.Entry entry : dataRequest.getStringTuples().entrySet()) {
mapEntry = (Map.Entry) entry;
form.param(mapEntry.getKey(), mapEntry.getValue());
}
if (requestType.equals("POST"))
invocation = requestBuilder.buildPost(Entity.entity(form, overriddenContentType));
else
invocation = requestBuilder.buildPut(Entity.entity(form, overriddenContentType));
break;
default:
// Handles raw data types (JSON, Plain text, XML, HTML)
String originalContentType = dataRequest.getContentType();
if (overriddenContentType == null)
overriddenContentType = originalContentType;
switch (requestType) {
case "POST":
invocation = requestBuilder
.buildPost(Entity.entity(dataRequest.getBody(), overriddenContentType));
break;
case "PUT":
invocation = requestBuilder
.buildPut(Entity.entity(dataRequest.getBody(), overriddenContentType));
break;
case "PATCH":
invocation = requestBuilder
.build("PATCH", Entity.entity(dataRequest.getBody(), overriddenContentType));
break;
}
}
return invocation;
}
}

View file

@ -1,42 +0,0 @@
/*
* 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.everest.requestmanager;
import com.rohitawate.everest.models.responses.EverestResponse;
import javafx.concurrent.Task;
import javax.ws.rs.ProcessingException;
import javax.ws.rs.core.Response;
public class GETRequestManager extends RequestManager {
@Override
protected Task<EverestResponse> createTask() throws ProcessingException {
return new Task<EverestResponse>() {
@Override
protected EverestResponse call() throws Exception {
initialTime = System.currentTimeMillis();
Response serverResponse = requestBuilder.get();
finalTime = System.currentTimeMillis();
processServerResponse(serverResponse);
return response;
}
};
}
}

View file

@ -16,23 +16,55 @@
package com.rohitawate.everest.requestmanager; package com.rohitawate.everest.requestmanager;
import com.rohitawate.everest.exceptions.RedirectException; import com.rohitawate.everest.exceptions.RedirectException;
import com.rohitawate.everest.exceptions.UnreliableResponseException; import com.rohitawate.everest.exceptions.NullResponseException;
import com.rohitawate.everest.models.requests.DELETERequest;
import com.rohitawate.everest.models.requests.DataRequest;
import com.rohitawate.everest.models.requests.EverestRequest; import com.rohitawate.everest.models.requests.EverestRequest;
import com.rohitawate.everest.models.requests.GETRequest;
import com.rohitawate.everest.models.responses.EverestResponse; import com.rohitawate.everest.models.responses.EverestResponse;
import com.rohitawate.everest.settings.Settings; import com.rohitawate.everest.settings.Settings;
import javafx.concurrent.Service; import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.concurrent.WorkerStateEvent; import javafx.concurrent.WorkerStateEvent;
import javafx.event.EventHandler; import javafx.event.EventHandler;
import org.glassfish.jersey.client.ClientProperties; import org.glassfish.jersey.client.ClientProperties;
import org.glassfish.jersey.client.HttpUrlConnectorProvider; import org.glassfish.jersey.client.HttpUrlConnectorProvider;
import org.glassfish.jersey.media.multipart.FormDataMultiPart;
import org.glassfish.jersey.media.multipart.MultiPartFeature; import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.media.multipart.file.FileDataBodyPart;
import javax.ws.rs.ProcessingException;
import javax.ws.rs.client.Client; import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.Invocation.Builder; import javax.ws.rs.client.Invocation.Builder;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Map;
public abstract class RequestManager extends Service<EverestResponse> { /**
* Manages all the requests made through Everest.
* Converts EverestRequests into JAX-RS Invocations are then processed by Jersey.
* Also, parses the response and returns EverestResponses.
*
* Previously, Everest used separate managers for GET, Data (POST, PUT and PATCH) and DELETE
* requests. However, RequestManager extends JavaFX's Service class which is expensive to create objects of.
* This made the creation of separate pools for every kind of RequestManager too expensive memory-wise.
* Thus, now a single class manages all kinds of Requests thus requiring only a single kind of pool.
* Also, this enables us to re-use inactive RequestManagers for all kinds of Requests. For example, previously,
* if a GETRequestManager was requested, and all GETRequestManagers were running, a new one would be created even
* if a DELETERequestManager was idle.
*
* TLDR: At the cost of some reduced semantic clarity, the old, separate-for-every-type-of-request RequestManagers
* are now replaced by this single works-for-all one to save some serious amount of memory and for better re-use.
*/
public class RequestManager extends Service<EverestResponse> {
private static final Client client; private static final Client client;
static { static {
@ -49,12 +81,46 @@ public abstract class RequestManager extends Service<EverestResponse> {
client.property(ClientProperties.READ_TIMEOUT, Settings.connectionReadTimeOut); client.property(ClientProperties.READ_TIMEOUT, Settings.connectionReadTimeOut);
} }
long initialTime; private long initialTime;
long finalTime; private long finalTime;
EverestRequest request; private EverestRequest request;
EverestResponse response; private EverestResponse response;
Builder requestBuilder; private Builder requestBuilder;
/**
* Creates a JavaFX Task for processing the required kind of request.
*/
@Override
protected Task<EverestResponse> createTask() throws ProcessingException {
return new Task<EverestResponse>() {
@Override
protected EverestResponse call() throws Exception {
Response serverResponse = null;
if (request.getClass().equals(GETRequest.class)) {
initialTime = System.currentTimeMillis();
serverResponse = requestBuilder.get();
finalTime = System.currentTimeMillis();
} else if (request.getClass().equals(DataRequest.class)) {
DataRequest dataRequest = (DataRequest) request;
Invocation invocation = appendBody(dataRequest);
initialTime = System.currentTimeMillis();
serverResponse = invocation.invoke();
finalTime = System.currentTimeMillis();
} else if (request.getClass().equals(DELETERequest.class)) {
initialTime = System.currentTimeMillis();
serverResponse = requestBuilder.delete();
finalTime = System.currentTimeMillis();
}
processServerResponse(serverResponse);
return response;
}
};
}
public void setRequest(EverestRequest request) { public void setRequest(EverestRequest request) {
this.request = request; this.request = request;
@ -71,10 +137,14 @@ public abstract class RequestManager extends Service<EverestResponse> {
requestBuilder.header("User-Agent", "Everest"); requestBuilder.header("User-Agent", "Everest");
} }
void processServerResponse(Response serverResponse) /**
throws UnreliableResponseException, RedirectException { * Takes a ServerResponse and extracts all the headers, the body, the response time and other details
* into a EverestResponse.
*/
private void processServerResponse(Response serverResponse)
throws NullResponseException, RedirectException {
if (serverResponse == null) { if (serverResponse == null) {
throw new UnreliableResponseException("The server did not respond.", throw new NullResponseException("The server did not respond.",
"Like that crush from high school.."); "Like that crush from high school..");
} else if (serverResponse.getStatus() == 301 || serverResponse.getStatus() == 302) { } else if (serverResponse.getStatus() == 301 || serverResponse.getStatus() == 302) {
throw new RedirectException( throw new RedirectException(
@ -108,4 +178,122 @@ public abstract class RequestManager extends Service<EverestResponse> {
removeEventHandler(WorkerStateEvent.WORKER_STATE_FAILED, getOnFailed()); removeEventHandler(WorkerStateEvent.WORKER_STATE_FAILED, getOnFailed());
removeEventHandler(WorkerStateEvent.WORKER_STATE_CANCELLED, getOnCancelled()); removeEventHandler(WorkerStateEvent.WORKER_STATE_CANCELLED, getOnCancelled());
} }
/**
* Adds the request body based on the content type and generates an invocation.
* Used for DataRequests.
*
* @return invocation object
*/
private Invocation appendBody(DataRequest dataRequest) throws Exception {
/*
Checks if a custom mime-type is mentioned in the headers.
If present, it will override the logical Content-Type.
*/
String overriddenContentType = request.getHeaders().get("Content-Type");
Invocation invocation = null;
Map.Entry<String, String> mapEntry;
String requestType = dataRequest.getRequestType();
switch (dataRequest.getContentType()) {
case MediaType.MULTIPART_FORM_DATA:
FormDataMultiPart formData = new FormDataMultiPart();
// Adding the string tuples to the request
HashMap<String, String> pairs = dataRequest.getStringTuples();
for (Map.Entry entry : pairs.entrySet()) {
mapEntry = (Map.Entry) entry;
formData.field(mapEntry.getKey(), mapEntry.getValue());
}
String filePath;
File file;
boolean fileException = false;
String fileExceptionMessage = null;
pairs = dataRequest.getFileTuples();
// Adding the file tuples to the request
for (Map.Entry entry : pairs.entrySet()) {
mapEntry = (Map.Entry) entry;
filePath = mapEntry.getValue();
file = new File(filePath);
if (file.exists())
formData.bodyPart(new FileDataBodyPart(mapEntry.getKey(),
file, MediaType.APPLICATION_OCTET_STREAM_TYPE));
else {
fileException = true;
// For pretty-printing FileNotFoundException to the UI
fileExceptionMessage = " - " + filePath + "\n";
}
}
if (fileException) {
throw new FileNotFoundException(fileExceptionMessage);
}
formData.setMediaType(MediaType.MULTIPART_FORM_DATA_TYPE);
if (requestType.equals("POST"))
invocation = requestBuilder.buildPost(Entity.entity(formData, MediaType.MULTIPART_FORM_DATA_TYPE));
else
invocation = requestBuilder.buildPut(Entity.entity(formData, MediaType.MULTIPART_FORM_DATA_TYPE));
break;
case MediaType.APPLICATION_OCTET_STREAM:
if (overriddenContentType == null)
overriddenContentType = MediaType.APPLICATION_OCTET_STREAM;
filePath = dataRequest.getBody();
File check = new File(filePath);
if (!check.exists()) {
throw new FileNotFoundException(filePath);
}
FileInputStream stream = new FileInputStream(filePath);
if (requestType.equals("POST"))
invocation = requestBuilder.buildPost(Entity.entity(stream, overriddenContentType));
else
invocation = requestBuilder.buildPut(Entity.entity(stream, overriddenContentType));
break;
case MediaType.APPLICATION_FORM_URLENCODED:
if (overriddenContentType == null)
overriddenContentType = MediaType.APPLICATION_FORM_URLENCODED;
Form form = new Form();
for (Map.Entry entry : dataRequest.getStringTuples().entrySet()) {
mapEntry = (Map.Entry) entry;
form.param(mapEntry.getKey(), mapEntry.getValue());
}
if (requestType.equals("POST"))
invocation = requestBuilder.buildPost(Entity.entity(form, overriddenContentType));
else
invocation = requestBuilder.buildPut(Entity.entity(form, overriddenContentType));
break;
default:
// Handles raw data types (JSON, Plain text, XML, HTML)
String originalContentType = dataRequest.getContentType();
if (overriddenContentType == null)
overriddenContentType = originalContentType;
switch (requestType) {
case "POST":
invocation = requestBuilder
.buildPost(Entity.entity(dataRequest.getBody(), overriddenContentType));
break;
case "PUT":
invocation = requestBuilder
.buildPut(Entity.entity(dataRequest.getBody(), overriddenContentType));
break;
case "PATCH":
invocation = requestBuilder
.build("PATCH", Entity.entity(dataRequest.getBody(), overriddenContentType));
break;
}
}
return invocation;
}
} }

View file

@ -19,70 +19,26 @@ package com.rohitawate.everest.requestmanager;
import java.util.ArrayList; import java.util.ArrayList;
/** /**
* Provides the various RequestManagers employed by Everest. * Provides a dynamically-growing pool RequestManagers used by Everest.
* <p> * <p>
* Pools are created as needed i.e. the first DELETE request * The manager() method when invoked, searches the pool linearly.
* will create the pool of DELETERequestManagers. If a DELETE
* request is never made, the pool won't be created. Same applies
* for all other types of requests.
* <p>
* When demanding a RequestManager, the pool is checked linearly.
* The first RequestManager which is not currently running will be * The first RequestManager which is not currently running will be
* returned to the caller. If all the managers in the pool are running, * returned to the caller. If all the managers in the pool are running,
* a new one is created, added to the pool, and returned. * a new one will be created, added to the pool, and returned.
*/ */
public class RequestManagersPool { public class RequestManagersPool {
private static ArrayList<GETRequestManager> getManagers; private static ArrayList<RequestManager> pool = new ArrayList<>();
private static ArrayList<DataRequestManager> dataManagers;
private static ArrayList<DELETERequestManager> deleteManagers;
public static GETRequestManager get() { public static RequestManager manager() {
if (getManagers == null) for (RequestManager manager: pool) {
getManagers = new ArrayList<>(); if (!manager.isRunning()) {
manager.reset();
for (GETRequestManager getManager : getManagers) { return manager;
if (!getManager.isRunning()) {
getManager.reset();
return getManager;
} }
} }
GETRequestManager newManager = new GETRequestManager(); RequestManager newManager = new RequestManager();
getManagers.add(newManager); pool.add(newManager);
return newManager;
}
public static DataRequestManager data() {
if (dataManagers == null)
dataManagers = new ArrayList<>();
for (DataRequestManager dataManager : dataManagers) {
if (!dataManager.isRunning()) {
dataManager.reset();
return dataManager;
}
}
DataRequestManager newManager = new DataRequestManager();
dataManagers.add(newManager);
return newManager;
}
public static DELETERequestManager delete() {
if (deleteManagers == null)
deleteManagers = new ArrayList<>();
for (DELETERequestManager deleteManager : deleteManagers) {
if (!deleteManager.isRunning()) {
deleteManager.reset();
return deleteManager;
}
}
DELETERequestManager newManager = new DELETERequestManager();
deleteManagers.add(newManager);
return newManager; return newManager;
} }