package jstellarapi.connection;
import java.io.IOException;
import java.net.URI;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import javax.websocket.*;
import javax.xml.bind.DatatypeConverter;
import jstellarapi.core.DenominatedIssuedCurrency;
import jstellarapi.core.StellarAddress;
import jstellarapi.core.StellarPaymentTransaction;
import jstellarapi.core.StellarSeedAddress;
import jstellarapi.core.StellarTransactionHistory;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
@ClientEndpoint
public class StellarDaemonWebsocketConnection extends StellarDaemonConnection {
private Session session;
private int requestCounter = 1;
public final static URI TEST_SERVER_URL = URI.create("ws://test.stellar.org:9001");
public final static URI LIVE_SERVER_URL = URI.create("ws://live.stellar.org:9001");
public final static URI LOCALHOST_SERVER_URL = URI.create("ws://localhost:5006");
JSONResponseHolder responseHolder = new JSONResponseHolder();
JSONSubscribtionFeed ledgerFeed = new JSONSubscribtionFeed();
JSONSubscribtionFeed transactionFeed = new JSONSubscribtionFeed();
public StellarDaemonWebsocketConnection(URI StellardURI) throws Exception {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
session = container.connectToServer(this, StellardURI);
}
@OnMessage
public void onMessage(String msg) {
JSONObject jsonMessage = null;
try {
jsonMessage = (JSONObject) new JSONParser().parse(msg);
} catch (ParseException e) {
return;
}
Object messageType = jsonMessage.get("type");
if ("response".equals(messageType)) {
responseHolder.setResponseContent(jsonMessage);
} else if ("error".equals(messageType)) { // FIXME Can we remove this?
// Errors are responses
// also?
responseHolder.setResponseError(jsonMessage);
} else if ("ledgerClosed".equals(messageType)) {
ledgerFeed.add(jsonMessage);
} else if ("transaction".equals(messageType)) {
transactionFeed.add((JSONObject) jsonMessage.get("transaction"));
} else {
// TODO Notify the subscribtions
System.out.println("subscription of type " + messageType + " " + jsonMessage);
}
}
public boolean isSecure() {
return session.isSecure();
}
public boolean isOpen() {
return session.isOpen();
}
public void close() throws Exception {
session.close();
}
public <T extends JSONSerializable> FutureJSONResponse<T> sendCommand(JSONObject command, T unserializedResponse) {
try {
command.put("id", requestCounter);
FutureJSONResponse<T> pendingResponse = new FutureJSONResponse<T>(requestCounter, responseHolder, unserializedResponse);
responseHolder.addPendingResponse(pendingResponse);
session.getBasicRemote().sendText(command.toJSONString());
requestCounter++;
return pendingResponse;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
public boolean ping() {
JSONObject pingComand = new JSONObject();
pingComand.put("command", "ping");
Future<GenericJSONSerializable> pingResponse = sendCommand(pingComand, new GenericJSONSerializable());
try {
return pingResponse.get() != null;
} catch (InterruptedException | ExecutionException e) {
return false;
}
}
public String random() {
JSONObject pingComand = new JSONObject();
pingComand.put("command", "random");
Future<GenericJSONSerializable> pingResponse = sendCommand(pingComand, new GenericJSONSerializable());
try {
return pingResponse.get().jsonCommandResult.get("random").toString();
} catch (InterruptedException | ExecutionException e) {
return null;
}
}
public Future<StellarAddressPublicInformation> getAccountInfoFuture(String account) {
JSONObject accountInfoComand = new JSONObject();
accountInfoComand.put("command", "account_info");
accountInfoComand.put("account", account);
return sendCommand(accountInfoComand, new StellarAddressPublicInformation());
}
public StellarAddressPublicInformation getAccountInfo(String account) {
try {
return getAccountInfoFuture(account).get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return null;
}
}
public Future<ExchangeOffers> getAccountOffersFuture(String account) {
JSONObject accountOffersComand = new JSONObject();
accountOffersComand.put("command", "account_offers");
accountOffersComand.put("account", account);
return sendCommand(accountOffersComand, new ExchangeOffers());
}
public ExchangeOffers getAccountOffers(String account) {
Future<ExchangeOffers> futureOffersResponse = getAccountOffersFuture(account);
try {
return futureOffersResponse.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return null;
}
}
public Future<OrderBook> getOrderBookFuture(String takerGetsIssuerStr, String takerGetsCurrency, String takerPaysCurrency, int nbEntries) {
JSONObject jsonTakerGets = new JSONObject();
if (takerGetsIssuerStr != null) {
jsonTakerGets.put("issuer", takerGetsIssuerStr);
}
jsonTakerGets.put("currency", takerGetsCurrency);
JSONObject jsonTakerPays = new JSONObject();
// if(takerPays.issuerStr!=null){
// jsonTakerPays.put("issuer", takerPays.issuerStr);
// }
jsonTakerPays.put("currency", takerPaysCurrency);
JSONObject orderBookComand = new JSONObject();
orderBookComand.put("command", "book_offers");
orderBookComand.put("limit", nbEntries);
orderBookComand.put("taker_gets", jsonTakerGets);
orderBookComand.put("taker_pays", jsonTakerPays);
orderBookComand.put("snapshot", true);
return sendCommand(orderBookComand, new OrderBook());
}
public OrderBook getOrderBook(String issuerStr, String takerGets, String takerPays, int nbEntries) {
try {
return getOrderBookFuture(issuerStr, takerGets, takerPays, nbEntries).get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return null;
}
}
public Future<RandomString> getRandomFuture() {
JSONObject randomComand = new JSONObject();
randomComand.put("command", "random");
FutureJSONResponse randomResponse = sendCommand(randomComand, new RandomString());
return randomResponse;
}
public String getRandom() {
try {
RandomString randomString = getRandomFuture().get();
return randomString.random;
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return null;
}
}
public GenericJSONSerializable subscribeToLedgers(boolean doSubscribe) {
try {
return subscribeToLedgersFuture(doSubscribe).get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return null;
}
}
public FutureJSONResponse<GenericJSONSerializable> subscribeToLedgersFuture(boolean doSubscribe) {
JSONObject command = new JSONObject();
if (doSubscribe) {
command.put("command", "subscribe");
} else {
command.put("command", "unsubscribe");
}
JSONArray streams = new JSONArray();
streams.add("ledger");
command.put("streams", streams);
return sendCommand(command, new GenericJSONSerializable());
}
public GenericJSONSerializable subscribeToTransactionOfAddress(String stellarAddressToMonitor) {
try {
return subscribeToTransactionOfAddressFuture(stellarAddressToMonitor).get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return null;
}
}
public FutureJSONResponse<GenericJSONSerializable> subscribeToTransactionOfAddressFuture(String stellarAddressToMonitor) {
JSONObject command = new JSONObject();
command.put("command", "subscribe");
// Only if you want to see all transactions
// JSONArray streams = new JSONArray();
// streams.add("transactions");
// command.put("streams", streams);
JSONArray accounts = new JSONArray();
accounts.add(stellarAddressToMonitor);
command.put("accounts", accounts);
return sendCommand(command, new GenericJSONSerializable());
}
public GenericJSONSerializable unsubscribeToTransactionOfAddress(String StellarAddressToMonitor) {
JSONObject command = new JSONObject();
command.put("command", "unsubscribe");
JSONArray accounts = new JSONArray();
accounts.add(StellarAddressToMonitor);
command.put("accounts", accounts);
try {
return sendCommand(command, new GenericJSONSerializable()).get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return null;
}
}
public FutureJSONResponse<GenericJSONSerializable> sendPaymentFuture(StellarSeedAddress payer, StellarAddress payee, DenominatedIssuedCurrency amount) {
JSONObject jsonTx = new StellarPaymentTransaction(payer.getPublicStellarAddress(), payee, amount, 1).toTxJSON();
JSONObject command = new JSONObject();
command.put("command", "submit");
command.put("tx_json", jsonTx);
command.put("secret", payer.toString());
return sendCommand(command, new GenericJSONSerializable());
}
public GenericJSONSerializable sendPayment(StellarSeedAddress payer, StellarAddress payee, DenominatedIssuedCurrency amount) {
try {
return sendPaymentFuture(payer, payee, amount).get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return null;
}
}
public Future<GenericJSONSerializable> setCreditLineFuture(StellarSeedAddress creditorAccount, StellarAddress debtorAccount, DenominatedIssuedCurrency creditAmount) {
JSONObject command = new JSONObject();
command.put("command", "submit");
JSONObject jsonTx = new JSONObject();
jsonTx.put("TransactionType", "TrustSet");
jsonTx.put("Account", creditorAccount.getPublicStellarAddress().toString());
jsonTx.put("LimitAmount", creditAmount.toJSON());
command.put("tx_json", jsonTx);
command.put("secret", creditorAccount.toString());
return sendCommand(command, new GenericJSONSerializable());
}
public GenericJSONSerializable setTrustLine(StellarSeedAddress creditorAccount, StellarAddress debtorAccount, DenominatedIssuedCurrency creditAmount) {
try {
return setCreditLineFuture(creditorAccount, debtorAccount, creditAmount).get();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public Future<TrustLines> getCreditLinesFuture(String ourAccount) {
JSONObject command = new JSONObject();
command.put("command", "account_lines");
command.put("account", ourAccount);
return sendCommand(command, new TrustLines());
}
public TrustLines getCreditLines(String ourAccount) {
try {
return getCreditLinesFuture(ourAccount).get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return null;
}
}
public Future<StellarPaymentTransaction> signTransactionFuture(StellarSeedAddress secret, StellarPaymentTransaction txToSign) {
JSONObject command = new JSONObject();
command.put("command", "sign");
command.put("secret", secret.toString());
command.put("tx_json", txToSign.toTxJSON());
return sendCommand(command, txToSign);
}
public StellarPaymentTransaction signTransaction(StellarSeedAddress secret, StellarPaymentTransaction txToSign) {
try {
return signTransactionFuture(secret, txToSign).get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return null;
}
}
public Future<GenericJSONSerializable> submitTransactionFuture(byte[] signedTransactionBytes) {
JSONObject command = new JSONObject();
command.put("command", "submit");
command.put("tx_blob", DatatypeConverter.printHexBinary(signedTransactionBytes));
return sendCommand(command, new GenericJSONSerializable());
}
public GenericJSONSerializable submitTransaction(byte[] signedTransactionBytes) {
try {
return submitTransactionFuture(signedTransactionBytes).get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return null;
}
}
public JSONSubscribtionFeed getLedgerFeed() {
return ledgerFeed;
}
public JSONSubscribtionFeed getTransactionFeed() {
return transactionFeed;
}
public StellarTransactionHistory getTransactionsForAccount(String StellarAddress, long startFromLedgerNumber) {
StellarTransactionHistory txHistory = new StellarTransactionHistory();
while (true) {
int oldSize = txHistory.size();
getTransactionsForAccount(StellarAddress, txHistory, oldSize, startFromLedgerNumber);
if (txHistory.size() == oldSize) {
break;
}
}
return txHistory;
}
public StellarTransactionHistory getTransactionsForAccount(String StellarAddress, StellarTransactionHistory txHistory, int offset, long startFromLedgerNumber) {
try {
JSONObject command = new JSONObject();
command.put("command", "account_tx");
command.put("account", StellarAddress);
if (startFromLedgerNumber < StellarDaemonConnection.GENESIS_LEDGER_NUMBER) {
startFromLedgerNumber = -1;
}
command.put("ledger_index_min", startFromLedgerNumber);
command.put("ledger_index_max", -1);
// command.put("binary", true); //Default to false
command.put("count", false);
command.put("descending", false);
command.put("offset", offset);
// command.put("limit", 30); //Let the server choose it's own limits
command.put("forward", false);
return sendCommand(command, txHistory).get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return null;
}
}
}