/*******************************************************************************
* 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.http.client.Request;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.Response;
import com.google.gwt.user.client.Window;
import org.eclipse.che.ide.commons.exception.ServerDisconnectedException;
import org.eclipse.che.ide.commons.exception.ServerException;
import org.eclipse.che.ide.commons.exception.UnauthorizedException;
import static org.eclipse.che.ide.MimeType.APPLICATION_JSON;
import static org.eclipse.che.ide.rest.HTTPHeader.CONTENT_TYPE;
/**
* Callback class for receiving the {@link Response}.
*
* @param <T>
* the return type of the response the callback expects.
* Use {@link Void} for methods returning {@code void}.
*
* @author Valeriy Svydenko
*/
public abstract class AsyncRequestCallback<T> implements RequestCallback {
// HTTP code 207 is "Multi-Status"
// IE misinterpreting HTTP status code 204 as 1223 (http://www.mail-archive.com/jquery-en@googlegroups.com/msg13093.html)
private static final int[] DEFAULT_SUCCESS_CODES = {Response.SC_OK, Response.SC_CREATED, Response.SC_NO_CONTENT, 207, 1223};
private final Unmarshallable<T> unmarshaller;
private int[] successCodes;
private AsyncRequestLoader loader;
private T payload;
private AsyncRequest request;
public AsyncRequestCallback() {
this(null);
}
/**
* Constructor retrieves unmarshaller with initialized (this is important!) object.
* When response comes callback calls {@link Unmarshallable#unmarshal(com.google.gwt.http.client.Response)}
* which populates the object.
*
* @param unmarshaller
*/
public AsyncRequestCallback(Unmarshallable<T> unmarshaller) {
this.successCodes = DEFAULT_SUCCESS_CODES;
this.unmarshaller = unmarshaller;
}
/** @return the result */
public T getPayload() {
return payload;
}
/**
* @param successCodes
* the successCodes to set
*/
public void setSuccessCodes(int[] successCodes) {
this.successCodes = successCodes;
}
public final void setLoader(AsyncRequestLoader loader) {
this.loader = loader;
}
/** @see com.google.gwt.http.client.RequestCallback#onError(com.google.gwt.http.client.Request, java.lang.Throwable) */
@Override
public final void onError(Request request, Throwable exception) {
if (loader != null) {
loader.hide();
}
onFailure(exception);
}
/**
* @see com.google.gwt.http.client.RequestCallback#onResponseReceived(com.google.gwt.http.client.Request,
* com.google.gwt.http.client.Response)
*/
@Override
public final void onResponseReceived(Request request, Response response) {
if (loader != null) {
loader.hide();
}
// If there is no connection to the server then status equals 0 ( In Internet Explorer status is 12029 )
if (response.getStatusCode() == 0 || response.getStatusCode() == 12029) {
onServerDisconnected();
return;
}
if (response.getStatusCode() == HTTPStatus.UNAUTHORIZED) {
onUnauthorized(response);
return;
}
if (isSuccessful(response)) {
handleSuccess(response);
} else {
handleFailure(response);
}
}
private void handleFailure(Response response) {
Exception exception;
String contentType = response.getHeader(CONTENT_TYPE);
if (contentType != null && !contentType.contains(APPLICATION_JSON)) {
String message = generateErrorMessage(response);
exception = new Exception(message);
} else {
exception = new ServerException(response);
}
onFailure(exception);
}
private void handleSuccess(Response response) {
try {
if (unmarshaller != null) {
//It's needed for handling a situation when response DTO object is NULL
if (response.getStatusCode() != Response.SC_NO_CONTENT) {
unmarshaller.unmarshal(response);
}
payload = unmarshaller.getPayload();
}
onSuccess(payload);
} catch (Exception e) {
onFailure(e);
}
}
private String generateErrorMessage(Response response) {
StringBuilder message = new StringBuilder();
String protocol = Window.Location.getProtocol();
String host = Window.Location.getHost();
String url = this.request.getRequestBuilder().getUrl();
//deletes query params
url = url.substring(0, url.indexOf('?'));
message.append(response.getStatusCode())
.append(" ")
.append(response.getStatusText())
.append(" ")
.append(protocol)
.append("//")
.append(host)
.append(url);
return message.toString();
}
protected final boolean isSuccessful(Response response) {
if (successCodes == null) {
successCodes = DEFAULT_SUCCESS_CODES;
}
if ("Authentication-required".equals(response.getHeader(HTTPHeader.JAXRS_BODY_PROVIDED))) {
return false;
}
for (int code : successCodes) {
if (response.getStatusCode() == code) {
return true;
}
}
return false;
}
/**
* If response is successfully received and response status code is in set of success codes.
*
* @param result
* the response returned from the request. Will be {@code null} if
* {@code unmarshaller} wasn't set
*/
protected abstract void onSuccess(T result);
/**
* Called when an error received from the server or when request was failed.
*
* @param exception
* the exception thrown
*/
protected abstract void onFailure(Throwable exception);
/** If server disconnected. */
protected void onServerDisconnected() {
onFailure(new ServerDisconnectedException(request));
}
/**
* If unauthorized.
*
* @param response
*/
protected void onUnauthorized(Response response) {
onFailure(new UnauthorizedException(response, request));
}
public AsyncRequest getRequest() {
return request;
}
public void setRequest(AsyncRequest request) {
this.request = request;
}
}