/*
* WBI Indicator Explorer
*
* Copyright 2015 Sebastian Nogara <snogaraleal@gmail.com>
*
* This file is part of WBI.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package rpc.client;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import rpc.client.websocket.WebSocket;
import rpc.shared.call.CallRequest;
import rpc.shared.call.CallResponse;
import rpc.shared.call.InvalidPayload;
import rpc.shared.data.Serializer;
import rpc.shared.data.SerializerException;
/**
* WebSocket implementation of {@link Client}.
*/
public class WebSocketClient extends Client implements WebSocket.Listener {
private WebSocket socket;
private Map<ClientRequest<?>, CallRequest> waitingForSocket =
new HashMap<ClientRequest<?>, CallRequest>();
private Map<String, ClientRequest<?>> pendingByToken =
new HashMap<String, ClientRequest<?>>();
/**
* Initialize {@code WebSocketClient}.
*
* @param serializer Global serializer.
* @param url WebSocket endpoint.
*/
public WebSocketClient(Serializer serializer, String url) {
super(serializer);
socket = WebSocket.create(url);
socket.addListener(this);
}
/**
* Get whether this implementation of {@code Client} is supported.
*
* @return Whether {@code WebSocketClient} is supported.
*/
public static boolean isSupported() {
return WebSocket.isSupported();
}
@Override
public void send(ClientRequest<?> clientRequest) {
try {
CallRequest callRequest = waitingForSocket.get(clientRequest);
if (callRequest == null) {
callRequest = buildCallRequest(clientRequest);
pendingByToken.put(callRequest.getToken(), clientRequest);
}
try {
socket.send(toRequestMessage(callRequest));
waitingForSocket.remove(clientRequest);
} catch (Exception exception) {
waitingForSocket.put(clientRequest, callRequest);
}
} catch (SerializerException exception) {
clientRequest.finish(new ClientRequest.Error(exception));
}
}
@Override
public void cancel(ClientRequest<?> clientRequest) {
Iterator<Map.Entry<String, ClientRequest<?>>> iterator =
pendingByToken.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, ClientRequest<?>> entry = iterator.next();
if (entry.getValue() == clientRequest) {
iterator.remove();
}
}
}
@Override
public void onOpen(WebSocket socket) {
for (ClientRequest<?> clientRequest : waitingForSocket.keySet()) {
send(clientRequest);
}
waitingForSocket.clear();
}
@SuppressWarnings({"rawtypes", "unchecked"})
@Override
public void onMessage(WebSocket socket, String message) {
CallResponse callResponse;
try {
callResponse = fromResponseMessage(message);
} catch (InvalidPayload exception) {
return;
}
String token = callResponse.getToken();
String payload = callResponse.getPayload();
ClientRequest clientRequest = pendingByToken.get(token);
if (clientRequest == null) {
return;
}
try {
if (callResponse.isSuccess()) {
clientRequest.finish(
deserialize(payload, clientRequest.getExpected()));
} else {
clientRequest.finish(new ClientRequest.Error(payload));
}
} catch (SerializerException exception) {
clientRequest.finish(new ClientRequest.Error(exception));
}
pendingByToken.remove(token);
}
@Override
public void onError(WebSocket socket, String reason) {
for (ClientRequest<?> clientRequest : pendingByToken.values()) {
clientRequest.finish(new ClientRequest.Error(reason));
}
pendingByToken.clear();
}
@Override
public void onClose(WebSocket socket, String reason, boolean clean) {
for (ClientRequest<?> clientRequest : pendingByToken.values()) {
clientRequest.finish(new ClientRequest.Error(reason));
}
pendingByToken.clear();
}
}