package ch.loway.oss.ari4java.tools;
import ch.loway.oss.ari4java.generated.Message;
import ch.loway.oss.ari4java.tools.WsClient.WsClientConnection;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Base functionality for ARI actions
*
* Provides asynchronous and synchronous methods for forwarding requests to the
* HTTP or WebSocket server.
*
* Provides serialize/deserialize interface for JSON encoded objects
*
* @author lenz
* @author mwalton
*/
public class BaseAriAction {
// Shared ObjectMapper
private static final ObjectMapper mapper = new ObjectMapper();
private String forcedResponse = null;
private HttpClient httpClient;
private WsClient wsClient;
protected List<HttpParam> lParamQuery;
protected List<HttpParam> lParamForm;
protected List<HttpParam> lParamBody;
protected List<HttpResponse> lE;
protected String url;
protected String method;
protected boolean wsUpgrade = false;
protected WsClientConnection wsConnection;
/**
* Reset contents in preparation for new RPC
*/
protected synchronized void reset() {
lParamQuery = new ArrayList<HttpParam>();
lParamForm = new ArrayList<HttpParam>();
lParamBody = new ArrayList<HttpParam>();
lE = new ArrayList<HttpResponse>();
url = null;
wsUpgrade = false;
}
public synchronized void forceResponse(String r) {
forcedResponse = r;
}
/**
* Initiate synchronous HTTP interaction with server
*
* @return Response from server
* @throws RestException
*/
protected synchronized String httpActionSync() throws RestException {
if (forcedResponse != null) {
return forcedResponse;
} else {
if (httpClient == null) {
throw new RestException("HTTP client implementation not set");
} else {
return httpClient.httpActionSync(this.url, this.method, this.lParamQuery, this.lParamForm, this.lParamBody, this.lE);
}
}
}
/**
* Initiate asynchronous HTTP or WebSocket interaction with server
*
* @param asyncHandler
*/
private synchronized void httpActionAsync(AriAsyncHandler<?> asyncHandler) {
if (forcedResponse != null) {
asyncHandler.handleResponse(forcedResponse);
} else if (wsUpgrade) {
// Websocket connection
if (wsClient == null) {
asyncHandler.getCallback().onFailure(new RestException("WebSocket client implementation not set"));
return;
}
if (wsConnection != null) {
asyncHandler.getCallback().onFailure(new RestException("This action is already connected to a WebSocket"));
return;
}
try {
wsConnection = wsClient.connect(asyncHandler, this.url, this.lParamQuery);
} catch (RestException e) {
asyncHandler.getCallback().onFailure(e);
}
} else if (httpClient == null) {
asyncHandler.getCallback().onFailure(new RestException("HTTP client implementation not set"));
} else {
try {
httpClient.httpActionAsync(this.url, this.method, this.lParamQuery, this.lParamForm, this.lParamBody, this.lE, asyncHandler);
} catch (RestException e) {
asyncHandler.getCallback().onFailure(e);
}
}
}
// Different styled asynchronous methods
protected void httpActionAsync(AriCallback<Void> callback) {
httpActionAsync(new AriAsyncHandler<Void>(callback, Void.class));
}
protected <S, T extends S> void httpActionAsync(AriCallback<S> callback, Class<T> klazz) {
httpActionAsync(new AriAsyncHandler<T>(callback, klazz));
}
protected <A, C extends A> void httpActionAsync(
AriCallback<List<A>> callback, TypeReference<List<C>> klazzType) {
httpActionAsync(new AriAsyncHandler(callback, klazzType));
}
/**
* Deserialize a type
*
* @param json
* @param klazz
* @return Deserialized type
*/
public static <T> T deserializeJson(String json, Class<T> klazz) throws RestException {
try {
return mapper.readValue(json, klazz);
} catch (IOException e) {
e.printStackTrace(System.err);
throw new RestException("Decoding JSON: " + e.getMessage());
}
}
/**
* Deserialize a list
*
* @param json
* @param klazzType
* @return Deserialized list
*/
public static <T> T deserializeJson(String json, TypeReference<T> klazzType) throws RestException {
try {
return mapper.readValue(json, klazzType);
} catch (IOException e) {
throw new RestException("Decoding JSON list: " + e.toString());
}
}
/**
* Deserialize an object as a list of interface. Class erasure in Java plain
* sucks, I tell ya.
*
* In theory we should be safe given the condition that A extends C. I hope
* at least. This is bug #17 - Avoid Lists of ? extends something
*
* @param <A> The abstract type for members of the list.
* @param <C> The concrete type for members of the list.
* @param json Data in
* @param refConcreteType The reference concrete type, should be a List<C>
* @return a list of A's
* @throws RestException
*/
public static <A, C extends A> List<A> deserializeJsonAsAbstractList(String json, TypeReference<List<C>> refConcreteType) throws RestException {
try {
List<C> lC = mapper.readValue(json, refConcreteType);
List<A> lA = (List<A>) lC;
return lA;
} catch (IOException e) {
throw new RestException("Decoding JSON list: " + e.toString());
}
}
/**
* Deserialize the event.
*
* @param json
* @param klazz
* @return The event deserialized.
* @throws RestException
*/
public static Message deserializeEvent(String json, Class<?> klazz) throws RestException {
try {
return (Message) mapper.readValue(json, klazz);
} catch (IOException e) {
e.printStackTrace(System.err);
throw new RestException("Decoding JSON event: " + e.toString());
}
}
/**
* Close the WebSocket connection
*
* @throws RestException
*/
public synchronized void disconnectWs() throws RestException {
if (wsConnection != null) {
wsConnection.disconnect();
}
wsConnection = null;
}
public synchronized void setHttpClient(HttpClient httpClient) {
this.httpClient = httpClient;
}
public synchronized void setWsClient(WsClient wsClient) {
this.wsClient = wsClient;
}
}
//