/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.rest;
import com.google.gwt.core.client.Callback;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestBuilder.Method;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.user.client.Timer;
import org.eclipse.che.api.promises.client.Promise;
import org.eclipse.che.api.promises.client.callback.CallbackPromiseHelper;
import org.eclipse.che.ide.commons.exception.JobNotFoundException;
import org.eclipse.che.ide.commons.exception.ServerException;
import static com.google.gwt.http.client.Response.SC_ACCEPTED;
import static com.google.gwt.http.client.Response.SC_NOT_FOUND;
/**
* Wrapper under {@link RequestBuilder} to simplify the stuffs.
*
* @author Artem Zatsarynnyi
*/
public class AsyncRequest {
protected RequestBuilder requestBuilder;
protected int asyncTaskCheckingPeriodMillis = 5000;
private AsyncRequestCallback<?> callback;
private boolean async;
private Timer checkAsyncTaskStatusTimer;
private AsyncRequestCallback<String> asyncRequestCallback;
private String asyncTaskStatusURL;
private AsyncRequestLoader loader;
private RequestStatusHandler asyncTaskStatusHandler;
/**
* Create new {@link AsyncRequest} instance.
*
* @param method
* request method
* @param url
* request URL
* @param async
* if {@code true} - request will be send in asynchronous mode (as asynchronous EverRest task).<br>
* See <a href="https://github.com/codenvy/everrest/wiki/Asynchronous-Requests">
* EverRest Asynchronous requests</a> for details.
*/
protected AsyncRequest(Method method, String url, boolean async) {
if (async) {
if (url.contains("?")) {
url += "&async=true";
} else {
url += "?async=true";
}
}
this.requestBuilder = new RequestBuilder(method, url);
this.async = async;
this.checkAsyncTaskStatusTimer = new CheckEverRestTaskStatusTimer();
this.asyncRequestCallback = new EverRestAsyncRequestCallback();
}
public AsyncRequest header(String header, String value) {
requestBuilder.setHeader(header, value);
return this;
}
public AsyncRequest user(String user) {
requestBuilder.setUser(user);
return this;
}
public AsyncRequest password(String password) {
requestBuilder.setPassword(password);
return this;
}
public AsyncRequest data(String requestData) {
requestBuilder.setRequestData(requestData);
return this;
}
public AsyncRequest loader(AsyncRequestLoader loader) {
this.loader = loader;
return this;
}
/**
* Set period for checking asynchronous task status. (5000 ms by default).<br>
* Makes sense for request sent in async mode only.
*
* @param period
* period of checking asynchronous task status (in milliseconds)
* @return this {@link AsyncRequest}
*/
public AsyncRequest period(int period) {
this.asyncTaskCheckingPeriodMillis = period;
return this;
}
/**
* Set handler of async task status. Makes sense for request sent in async mode only.
*
* @param handler
* handler to set
* @return this {@link AsyncRequest}
*/
public AsyncRequest requestStatusHandler(RequestStatusHandler handler) {
this.asyncTaskStatusHandler = handler;
return this;
}
/**
* Sends an HTTP request based on the current {@link AsyncRequest} configuration.
*
* @return promise that may be resolved with the {@link Void} or rejected in case request error
*/
public Promise<Void> send() {
return CallbackPromiseHelper.createFromCallback(new CallbackPromiseHelper.Call<Void, Throwable>() {
@Override
public void makeCall(final Callback<Void, Throwable> callback) {
send(new AsyncRequestCallback<Void>() {
@Override
protected void onSuccess(Void result) {
callback.onSuccess(null);
}
@Override
protected void onFailure(Throwable exception) {
callback.onFailure(exception);
}
});
}
});
}
/**
* Sends an HTTP request based on the current {@link AsyncRequest} configuration.
*
* @param unmarshaller
* unmarshaller that should be used to deserialize a response
* @return promise that may be resolved with the deserialized response value or rejected in case request error
*/
public <R> Promise<R> send(final Unmarshallable<R> unmarshaller) {
return CallbackPromiseHelper.createFromCallback(new CallbackPromiseHelper.Call<R, Throwable>() {
@Override
public void makeCall(final Callback<R, Throwable> callback) {
send(new AsyncRequestCallback<R>(unmarshaller) {
@Override
protected void onSuccess(R result) {
callback.onSuccess(result);
}
@Override
protected void onFailure(Throwable exception) {
callback.onFailure(exception);
}
});
}
});
}
/**
* Sends an HTTP request based on the current {@link AsyncRequest} configuration.
*
* @param callback
* the response handler to be notified when the request fails or completes
*/
public void send(AsyncRequestCallback<?> callback) {
this.callback = callback;
try {
if (async) {
this.callback.setRequest(this);
sendRequest(asyncRequestCallback);
} else {
sendRequest(callback);
}
} catch (RequestException e) {
callback.onFailure(e);
}
}
private void sendRequest(AsyncRequestCallback<?> callback) throws RequestException {
callback.setLoader(loader);
callback.setRequest(this);
requestBuilder.setCallback(callback);
if (loader != null) {
loader.show();
}
requestBuilder.send();
}
/**
* Returns the callback of current {@link AsyncRequest}, or null if no callback was set.
*
* @return the callback that to be notified when the request fails or completes
*/
public AsyncRequestCallback<?> getCallback() {
return callback;
}
public RequestBuilder getRequestBuilder() {
return requestBuilder;
}
/** Timer that checks status of the EverRest asynchronous task caused by this request. */
private class CheckEverRestTaskStatusTimer extends Timer {
@Override
public void run() {
final RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.GET, asyncTaskStatusURL);
requestBuilder.setCallback(new RequestCallback() {
@Override
public void onResponseReceived(Request request, Response response) {
if (SC_NOT_FOUND == response.getStatusCode()) {
callback.onError(request, new JobNotFoundException(response));
if (asyncTaskStatusHandler != null) {
asyncTaskStatusHandler.requestError(asyncTaskStatusURL, new JobNotFoundException(response));
}
} else if (response.getStatusCode() != SC_ACCEPTED) {
callback.onResponseReceived(request, response);
if (asyncTaskStatusHandler != null) {
// check is response successful, for correct handling failed responses
if (callback.isSuccessful(response)) {
asyncTaskStatusHandler.requestFinished(asyncTaskStatusURL);
} else {
asyncTaskStatusHandler.requestError(asyncTaskStatusURL, new ServerException(response));
}
}
} else {
if (asyncTaskStatusHandler != null) {
asyncTaskStatusHandler.requestInProgress(asyncTaskStatusURL);
}
CheckEverRestTaskStatusTimer.this.schedule(asyncTaskCheckingPeriodMillis);
}
}
@Override
public void onError(Request request, Throwable exception) {
if (asyncTaskStatusHandler != null) {
asyncTaskStatusHandler.requestError(asyncTaskStatusURL, exception);
}
callback.onError(request, exception);
}
});
try {
requestBuilder.send();
} catch (RequestException e) {
if (asyncTaskStatusHandler != null) {
asyncTaskStatusHandler.requestError(asyncTaskStatusURL, e);
}
callback.onFailure(e);
}
}
}
/** Callback that will be called on response to a request that was sent in EverRest async mode. */
private class EverRestAsyncRequestCallback extends AsyncRequestCallback<String> {
EverRestAsyncRequestCallback() {
super(new LocationUnmarshaller());
setSuccessCodes(new int[]{SC_ACCEPTED});
}
@Override
protected void onSuccess(String result) {
asyncTaskStatusURL = result;
if (asyncTaskStatusHandler != null) {
asyncTaskStatusHandler.requestInProgress(asyncTaskStatusURL);
}
checkAsyncTaskStatusTimer.schedule(asyncTaskCheckingPeriodMillis);
}
@Override
protected void onFailure(Throwable exception) {
if (asyncTaskStatusHandler != null) {
asyncTaskStatusHandler.requestError(asyncTaskStatusURL, exception);
}
callback.onError(null, exception);
}
}
}