JavaFX/Classes/ApiCall.java

294 lines
7.9 KiB
Java

package Classes;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.json.JSONException;
import org.json.JSONObject;
import Interfaces.Callback;
import Interfaces.DelayedCallback;
import Interfaces.Event;
import Interfaces.EventObserver;
import javafx.util.Pair;
public class ApiCall implements Runnable, EventObserver {
/* (1) Attributes
---------------------------------------------------------*/
/* (1) Request data */
private String httpMethod; // HTTP method
private HttpURLConnection httpConnection; // HTTP connection
/* (2) Callback data */
private Boolean isDelayed = false; // If must wait for an event to start
private Callback callback; // Callback ran when the request ends
private DelayedCallback delayedCallback; // Callback ran when event handled, returns new URL and POST parameters
private String eventID; // awaited event ID
private String eventType; // awaited event Type
private Thread thread; // main Thread
private Boolean shouldStop = false; // If delayedCallback error -> run() must stop (notify)
/* (2) API call constructor
*
* @tUrl<String> Target URL
* @tMethod<String> HTTP method
* @callback<Callback> Callback run after the HTTP response received
*
---------------------------------------------------------*/
public ApiCall(String tUrl, String tMethod, Callback callback) {
/* (1) Store attributes
---------------------------------------------------------*/
this.callback = callback;
this.httpMethod = tMethod;
/* (2) Create the connection
---------------------------------------------------------*/
try {
/* (1) Set the target URL */
URL url = new URL(tUrl);
/* (2) Create the connection from URL */
this.httpConnection = (HttpURLConnection) url.openConnection();
/* (3) Set the HTTP method */
this.httpConnection.setRequestMethod(this.httpMethod);
/* (4) If GET request, forbid POST data to be sent */
this.httpConnection.setDoOutput( httpMethod == "POST" );
}catch( IOException e ){
/* (x) If cannot create connection -> trigger the onError() callback */
this.callback.onError("cannot create http connection");
}
}
/* (3) Add headers to the HTTP connection
*
* @headerMap<HashMap<String,String>> HashMap containing the header pairs (key, value)
*
---------------------------------------------------------*/
public void addHeaders(HashMap<String,String> headerMap){
/* (1) Create iterator */
Iterator it = headerMap.entrySet().iterator();
/* (2) Iterate until the end */
while( it.hasNext() ){
/* (2.1) Get the current header pair */
Map.Entry pair = (Map.Entry) it.next();
/* (2.2) Add the header to the @httpConnection */
this.httpConnection.setRequestProperty(
(String) pair.getKey(),
(String) pair.getValue()
);
/* (2.3) Avoid concurrency errors -> remove current header */
it.remove();
}
}
@Override
public void run() {
/* (1) If delayed call -> sleep the thread, wait for an event
---------------------------------------------------------*/
if( this.isDelayed ){
/* (1) Try to sleep the thread */
try{
synchronized(this.thread){ this.thread.wait(); }
/* (2) If cannot -> call callback onError() */
}catch(InterruptedException e){
this.callback.onError("cannot sleep thread");
return;
}
}
/* (2) If thread must stop (error during delayedCallback)
---------------------------------------------------------*/
if( this.shouldStop )
return;
/* (3) Manage response
---------------------------------------------------------*/
try {
/* (1) Get output stream to set Post data */
if( this.httpMethod == "POST" ){
DataOutputStream wr = new DataOutputStream( httpConnection.getOutputStream() );
// TODO: manage post data
wr.flush ();
wr.close ();
}
/* (2) Prepare to get response */
InputStream is = httpConnection.getInputStream(); // Get input stream
BufferedReader rd = new BufferedReader(new InputStreamReader(is)); // Input stream into readable buffer
String line;
StringBuffer response = new StringBuffer();
/* (3) Get response */
while( ( line = rd.readLine() ) != null ){
response.append(line);
}
rd.close();
/* (4) Parse JSON */
try{
// {4.1} Specific: replace 'null' by empty strings //
JSONObject parsedJson = new JSONObject(response.toString().replaceAll(":null,", ":\"\","));
// {4.2} If no error -> trigger onSuccess() //
this.callback.onSuccess(parsedJson);
/* (5) If cannot parse JSON -> trigger onError() */
}catch(JSONException jsonex){
this.callback.onError("json parse error");
}
/* (4) If cannot get response -> trigger onError()
---------------------------------------------------------*/
}catch (IOException ioex){
this.callback.onError("cannot get response");
}
}
/* (5) Launches the run() loop in a Thread
*
---------------------------------------------------------*/
public void send() {
/* (1) Create a new Thread for the current class */
this.thread = new Thread(this);
/* (2) Start the thread */
this.thread.start();
}
/* (6) Bind a delayed callback to the API call (chain another API call)
*
* @eventID<String> Event ID
* @eventType<String> Event Type
* @delayedCallback<DelayedCallback> Delayed callback
*
---------------------------------------------------------*/
public void setAsDelayedCall(String eventID, String eventType, DelayedCallback delayedCallback) {
/* (1) Notify the delayed callback */
this.isDelayed = true;
this.delayedCallback = delayedCallback;
this.eventID = eventID;
this.eventType = eventType;
}
@Override
public void handleEvent(Event e) {
/* (1) If delayed callback and right event received
---------------------------------------------------------*/
if( this.isDelayed && e.getObjectId() == this.eventID && e.getEventType() == this.eventType ){
try{
/* (1) Call the callback and fetch its retusn URL+POST params */
Pair<String, HashMap<String, String>> newData = this.delayedCallback.call();
URL newURL = new URL(newData.getKey());
//HashMap<String, String> newPostData = newData.getValue();
/* (2) Create the new connection */
HttpURLConnection newConnection = (HttpURLConnection) newURL.openConnection();
/* (3) Keep the same HTTP method as previous request */
String lastHttpMethod = this.httpConnection.getRequestMethod();
newConnection.setRequestMethod( lastHttpMethod );
/* (4) If GET request, forbid POST data to be sent */
newConnection.setDoOutput( lastHttpMethod == "POST" );
/* (5) Manage POST data */
if( this.httpMethod == "POST" ){
// TODO: implement POST params management
}
/* (6) Update current connection (replace with new) */
this.httpConnection = newConnection;
/* (7) Replay main thread */
synchronized(this.thread){ this.thread.notify(); }
/* (1.8) If error while creating new connection -> onError()
---------------------------------------------------------*/
}catch( IOException ioe ){
/* (1) Launch onError() callback method */
this.callback.onError("cannot create delayed http connection");
/* (2) Stop the main thread */
this.shouldStop = true;
synchronized(this.thread){ this.thread.notify(); }
}
/* (2) If not a delayed call, or not same event (id/type)
---------------------------------------------------------*/
}else{
/* (1) Launch onError() callback method */
this.callback.onError("unknown event");
/* (2) Stop the main thread */
this.shouldStop = true;
synchronized(this.thread){ this.thread.notify(); }
}
}
}