2017-11-15 15:13:07 +00:00
|
|
|
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;
|
|
|
|
|
2017-11-15 21:53:52 +00:00
|
|
|
import org.json.JSONException;
|
|
|
|
import org.json.JSONObject;
|
|
|
|
|
2017-11-15 15:13:07 +00:00
|
|
|
import Interfaces.Callback;
|
2017-11-15 21:53:52 +00:00
|
|
|
import Interfaces.DelayedCallback;
|
|
|
|
import Interfaces.Event;
|
|
|
|
import Interfaces.EventObserver;
|
|
|
|
import javafx.util.Pair;
|
2017-11-15 15:13:07 +00:00
|
|
|
|
2017-11-15 21:53:52 +00:00
|
|
|
public class ApiCall implements Runnable, EventObserver {
|
2017-11-15 15:13:07 +00:00
|
|
|
|
2017-11-19 15:30:22 +00:00
|
|
|
/* (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
|
|
|
|
---------------------------------------------------------*/
|
2017-11-15 15:13:07 +00:00
|
|
|
try {
|
2017-11-19 15:30:22 +00:00
|
|
|
|
|
|
|
/* (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");
|
|
|
|
|
2017-11-15 15:13:07 +00:00
|
|
|
}
|
2017-11-19 15:30:22 +00:00
|
|
|
|
2017-11-15 15:13:07 +00:00
|
|
|
}
|
|
|
|
|
2017-11-19 15:30:22 +00:00
|
|
|
|
|
|
|
/* (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();
|
|
|
|
|
2017-11-15 15:13:07 +00:00
|
|
|
}
|
2017-11-19 15:30:22 +00:00
|
|
|
|
2017-11-15 15:13:07 +00:00
|
|
|
}
|
|
|
|
|
2017-11-19 15:30:22 +00:00
|
|
|
|
2017-11-15 15:13:07 +00:00
|
|
|
@Override
|
|
|
|
public void run() {
|
2017-11-19 15:30:22 +00:00
|
|
|
|
|
|
|
/* (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");
|
2017-11-15 21:53:52 +00:00
|
|
|
return;
|
2017-11-19 15:30:22 +00:00
|
|
|
|
2017-11-15 21:53:52 +00:00
|
|
|
}
|
2017-11-19 15:30:22 +00:00
|
|
|
|
2017-11-15 21:53:52 +00:00
|
|
|
}
|
2017-11-19 15:30:22 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* (2) If thread must stop (error during delayedCallback)
|
|
|
|
---------------------------------------------------------*/
|
|
|
|
if( this.shouldStop )
|
2017-11-15 21:53:52 +00:00
|
|
|
return;
|
2017-11-19 15:30:22 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* (3) Manage response
|
|
|
|
---------------------------------------------------------*/
|
2017-11-15 15:13:07 +00:00
|
|
|
try {
|
2017-11-19 15:30:22 +00:00
|
|
|
|
|
|
|
/* (1) Get output stream to set Post data */
|
|
|
|
if( this.httpMethod == "POST" ){
|
|
|
|
|
|
|
|
DataOutputStream wr = new DataOutputStream( httpConnection.getOutputStream() );
|
|
|
|
// TODO: manage post data
|
2017-11-15 21:53:52 +00:00
|
|
|
wr.flush ();
|
|
|
|
wr.close ();
|
2017-11-19 15:30:22 +00:00
|
|
|
|
2017-11-15 21:53:52 +00:00
|
|
|
}
|
2017-11-15 15:13:07 +00:00
|
|
|
|
2017-11-19 15:30:22 +00:00
|
|
|
/* (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 ){
|
2017-11-15 15:13:07 +00:00
|
|
|
response.append(line);
|
|
|
|
}
|
2017-11-19 15:30:22 +00:00
|
|
|
|
2017-11-15 15:13:07 +00:00
|
|
|
rd.close();
|
2017-11-19 15:30:22 +00:00
|
|
|
|
|
|
|
/* (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");
|
|
|
|
|
2017-11-15 21:53:52 +00:00
|
|
|
}
|
2017-11-15 15:13:07 +00:00
|
|
|
|
2017-11-19 15:30:22 +00:00
|
|
|
|
|
|
|
/* (4) If cannot get response -> trigger onError()
|
|
|
|
---------------------------------------------------------*/
|
|
|
|
}catch (IOException ioex){
|
|
|
|
|
|
|
|
this.callback.onError("cannot get response");
|
|
|
|
|
2017-11-15 15:13:07 +00:00
|
|
|
}
|
2017-11-19 15:30:22 +00:00
|
|
|
|
2017-11-15 15:13:07 +00:00
|
|
|
}
|
|
|
|
|
2017-11-19 15:30:22 +00:00
|
|
|
/* (5) Launches the run() loop in a Thread
|
|
|
|
*
|
|
|
|
---------------------------------------------------------*/
|
2017-11-15 15:13:07 +00:00
|
|
|
public void send() {
|
2017-11-19 15:30:22 +00:00
|
|
|
|
|
|
|
/* (1) Create a new Thread for the current class */
|
2017-11-15 21:53:52 +00:00
|
|
|
this.thread = new Thread(this);
|
2017-11-19 15:30:22 +00:00
|
|
|
|
|
|
|
/* (2) Start the thread */
|
2017-11-15 21:53:52 +00:00
|
|
|
this.thread.start();
|
2017-11-19 15:30:22 +00:00
|
|
|
|
2017-11-15 21:53:52 +00:00
|
|
|
}
|
2017-11-19 15:30:22 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* (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;
|
|
|
|
|
2017-11-15 21:53:52 +00:00
|
|
|
}
|
|
|
|
|
2017-11-19 15:30:22 +00:00
|
|
|
|
2017-11-15 21:53:52 +00:00
|
|
|
@Override
|
|
|
|
public void handleEvent(Event e) {
|
2017-11-19 15:30:22 +00:00
|
|
|
|
|
|
|
/* (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
|
2017-11-15 21:53:52 +00:00
|
|
|
}
|
2017-11-19 15:30:22 +00:00
|
|
|
|
|
|
|
/* (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(); }
|
|
|
|
|
2017-11-15 21:53:52 +00:00
|
|
|
}
|
2017-11-19 15:30:22 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* (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 */
|
2017-11-15 21:53:52 +00:00
|
|
|
this.shouldStop = true;
|
2017-11-19 15:30:22 +00:00
|
|
|
synchronized(this.thread){ this.thread.notify(); }
|
|
|
|
|
2017-11-15 21:53:52 +00:00
|
|
|
}
|
2017-11-19 15:30:22 +00:00
|
|
|
|
2017-11-15 15:13:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|